游乐游手机版
首页/AI教程/文章详情

AgentScope Java新手村第16讲:从RAG到多路检索

时间:2026-07-01 17:32
AgentScope2 0废弃了1 x的固定RAG管道,改用subagent结合grep_files和read_file进行文件级关键词检索,将检索决策权交给大模型。同时支持Skill仓库实现结构化路由;对于大规模或语义检索场景,可通过自定义@Tool调用向量库。

许多开发者或许已经察觉到,AgentScope 2.0 在核心架构上实现了重大转向。尤其在知识检索领域,广为人知的 SimpleRAGKnowledgeBgeRAGKnowledge 等 1.x API,在最新的 RC2 版本中已正式被标记为“过去式”。

为什么框架要放弃看似成熟且易用的“文档分块→嵌入→检索”黑盒管道?简而言之,设计理念发生了根本变化。1.x 版本将其固化为一个只需配置参数的固定“体质”,而 2.0 的思路更加激进:将检索的决策权真正交还给大模型(LLM)本身。

新方案将“检索”拆解为“subagent + 文件检索 + Skill 仓库”的组合。你的文档无需再经过切片、向量化处理,直接放置在 ./docs 目录下即可。Subagent 可以像人类一样,先用 grep_files 搜索关键词定位文件,再通过 read_file 读取原文。搜索什么词语、读取哪段内容,都由 LLM 自主权衡,这比硬编码的检索管道灵活得多。

为了帮助大家平滑承接与迁移,本章我们先回顾 1.x 旧 API 作为参考(主要面向维护老项目的同学),随后详细展开 2.0 推荐的新实践。

16.1 1.x RAG 旧 API 回顾

首先展示一个 1.x 的典型用法,算是留个纪念:

import io.agentscope.core.rag.SimpleRAGKnowledge;
import io.agentscope.core.rag.Knowledge;
import io.agentscope.core.rag.loader.LocalFileLoader;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.model.DashScopeChatModel;

public class Chapter16_LegacyRag {

    public static void main(String[] args) {
        // 1. 准备知识库
        Knowledge knowledge = new SimpleRAGKnowledge(
          new LocalFileLoader("./docs"),
          new DashScopeEmbeddingModel());

        // 2. agent 装上 knowledge
        ReActAgent agent = ReActAgent.builder()
          .name("doc_qa")
          .sysPrompt("你是文档问答助手。")
          .model(DashScopeChatModel.builder()
                  .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                  .modelName("qwen-plus")
                  .build())
          .knowledge(knowledge)  // 1.x 的套路
          .build();

        // 3. 调用时 agent 内部会先 retrieve 再回答
    }
}

即便在 2.0 中,这段代码依然可以编译通过。只是 ReActAgent.knowledge(...) 方法上已被标记 @Deprecated(forRemoval = true),编译时会产生告警。因此,新项目无需再走回头路。

16.2 2.0 推荐的“subagent + 文件检索”

为什么不再使用 1.x 的 RAG?

我们来深入剖析 1.x 那条**固定管道**究竟有哪些局限性:

文档 → 切片 → 嵌入(向量化) → 存向量库
    ↓
用户提问 → 同样嵌入 → 余弦相似度搜索 → 取 top-k 切片 → 拼进 prompt → LLM 回答

这条流程至少存在三大痛点:

**痛点一:黑盒检索,查无对症。** 向量相似度较高并不代表语义真正相关。搜索出的片段可能“形似”,但与实际问题可能牛头不对马嘴。更棘手的是,你无法有效调试为何选中了这段而非那一段。

**痛点二:管道路径固定,模型沦为“局外人”。** 切片、嵌入、搜索等环节均被管道固化。LLM 在整个检索过程中完全无法参与决策,它只能被动接受喂入 prompt 的内容。

**痛点三:外部依赖过多。** 需要依赖 Embedding 模型(如 DashScope / Qwen Embedding),还需搭建向量数据库(如 Milvus、Pinecone),凭空增加两个微服务需维护,整体复杂度显著上升。

2.0 如何实现?

核心思路是将“选择权”交还给 LLM。Agent 内置了 grep_files(关键词搜索)和 read_file(文件读取)两大工具,让 LLM 自主决定搜索哪些关键词、读取哪个文件以及读多少内容。

整个工作流演变为:

用户提问 → LLM 判断应使用什么关键词进行 grep → 打开并读取相关文件 → 综合信息给出答案

对比之下,差异十分明显:

| 比较维度 | 1.x RAG | 2.0 subagent 文件检索 | | :--- | :--- | :--- | | **检索决策者** | 管道(程序) | LLM(agent) | | **检索方式** | 向量相似度 | `grep_files` 关键词搜索 + `read_file` 精读 | | **外部依赖** | Embedding 模型 + 向量库 | 无,全依赖 agent 内置工具 | | **可调试性** | 差(无法了解管道为何选取某段) | 好(agent 日志会记录 grep 了哪些词、读了哪个文件) | | **灵活性** | 固定管道,一成不变 | LLM 可自主分步:先 grep 找文件名 → 再 read 读内容 → 线索不足时再次 grep 扩大范围 |

这一思路的本质是:与其让程序猜测“哪段文本与用户问题最相似”,不如让 LLM 主动思考“我应该搜索什么关键词、读取哪个文件”。

在代码实现上,HarnessAgent 已自带 read_filegrep_filesglob_files 三个内置工具。Subagent 可直接使用它们:

import io.agentscope.harness.agent.subagent.SubagentDeclaration;
import io.agentscope.harness.HarnessAgent;

public class Chapter16_NewRag {

    public static void main(String[] args) {
        // 文档检索 subagent:用 grep_files + read_file 代替传统向量检索
        SubagentDeclaration docReader = SubagentDeclaration.builder()
          .name("doc_reader")
          .description("""
            文档检索 subagent。
            拿到问题后:
            1. 先用 grep_files 在 ./docs 找关键词
            2. 用 read_file 读最相关的 1-2 个文件
            3. 综合内容回答
            """)  // LLM 根据这个 description 决定什么时候 spawn 它
          .inlineAgentsBody("你是一个文档检索员," + 
                         "只用 read_file / grep_files 找答案。") // subagent 自己的系统提示词
          .build();

        HarnessAgent host = HarnessAgent.builder()
          .name("qa")
          .sysPrompt("你是问答助理,需要查文档时 spawn doc_reader。")
          .model(model())
          .workspace(Path.of("./workspace"))
          .subagent(docReader)  // 注册 subagent
          .build();
    }
}

当你调用 host.call(...) 时,LLM 一旦发现用户问题中包含“文档”等字眼,便会主动 spawn 之前注册的 doc_reader subagent,后者再通过 grep 和 read 工具自主决定如何进行检索。

16.3 进阶:用 Skill 仓库实现“结构化 RAG”

若你的文档数量庞大,且希望按主题进行“切分与管理”,将每份文档直接制作成一个 **Skill** 是更加明智的做法(具体可参考第 18 章)。你的目录结构可设计如下:

workspace/
└── skills/
    ├── product-faq/
    │   └── SKILL.md
    ├── engineering-handbook/
    │   └── SKILL.md
    └── legal-policies/
        └── SKILL.md

每个 SKILL.md 好比一份说明书,描述了“这个 skill 负责什么”:

# product-faq/SKILL.md
name: product-faq
description: |
  产品 FAQ:当用户询问"如何退款 / 如何开发片 / 如何修改地址"时优先使用。
allowed-tools: [read_file, grep_files]

主 Agent 在 prompt 中被告知“遇到问题先查看 SKILL.md 来决定选用哪个 skill”。LLM 会根据 description 将问题路由至对应的 Skill,进而读取 SKILL.md 中**人工维护的文档链接**。

这种做法的优势切实可见:

- **路由策略透明,产品经理也能轻松理解。** 需要调整路由?只需修改 SKILL.md 中的描述文本即可。

- **节省 token。** 每次仅载入相关 Skill 的元信息,无需将所有文档一次性塞入 prompt。

- **管理成本低。** 文档更新时,只需改动对应 Skill 的内容即可。

16.4 何时仍应使用真正的“嵌入 + 向量检索”

话说回来,新方案并非万能。如果你的业务场景符合以下任一条件,传统 RAG 仍然有它的应用价值:

- **文档量级巨大**,例如超过 10 万条。此时 subagent 使用 grep_files 进行关键词检索效率会显著下降,难以满足性能要求。

- **检索核心是“语义相似”**。比如用户抱怨“心情不好”,你希望系统能找到关于“沮丧”、“低落”的文档片段。关键词检索难以胜任此类需求。

- **需要 Hybrid Search(混合搜索)**。即同时运行 BM25(关键词)和向量搜索,然后按照权重合并两种结果,以获得更优排序。

在这些情况下,2.0 框架推荐的做法是:**自行编写一个 @Tool**。

@Tool(name = "vector_search", description = "向量检索")
public String vectorSearch(
        @ToolParam(name = "query") String query,
        @ToolParam(name = "topK", required = false) Integer topK) {
    // 调你自己的向量库(Milvus / Elasticsearch / PGVector)
    return vectorStore.search(query, topK == null ? 5 : topK);
}

写好这个普通的 Ja va 工具后,注册到你的 Agent 或 Subagent 中即可。这正是 2.0 框架所推崇的核心理念:**该用什么工具,就用什么工具**,无需再受限于名为 RAGKnowledge 的抽象层。

16.5 最小迁移清单(1.x RAG → 2.0)

最后,若你正从 1.x 进行迁移,下表可助你快速找到“新路径”:

| 1.x 你做了什么 | 2.0 怎么做 | | :--- | :--- | | 调用 RAGKnowledge.retrieve(...) 自动检索 | Subagent 使用内置 grep_files + read_file 进行检索 | | 使用 SimpleRAGKnowledge 等内置分块、嵌入功能 | 框架已移除内置管道。如需嵌入,自行编写 @Tool 调用向量库 | | 配置分块策略、嵌入模型 | 在自定义 @Tool 中自由实现,框架不再限制 | | agent.knowledge(knowledge) | .subagent(retriever).toolkit(toolkit) | | agent.call(..., retriever=knowledge) | 拆分为 subagent + 工具,LLM 自主决定何时检索 |

总结所有变化的根本:1.x 的 RAG 是框架内置的固定管道,你只需配置参数;而 2.0 框架不再内置具体实现,但赋予了极大的自由度。你可以通过 @Tool 实现任意你想要的检索逻辑。检索的决策权,也从管道转移到了 LLM 手中。

16.6 完整可运行示例

为了让你获得更直观的感受,下面展示一个完整的示例。这个例子要说明什么?

一个 QA agent 配备了**两种检索方式**,LLM 会像“专家系统”一样,根据问题的类型自主决定使用哪一种:

- **doc_reader subagent**:使用内置的 grep_filesread_file,在 ./docs 目录下进行文件级关键词检索。适合“退货政策是什么?”“怎么开发片?”这类事实性、精准的问答,零外部依赖。

- **vector_search 工具**:调用后端 Milvus/ES 服务,进行语义级向量检索。适合用户输入“心情不好”需要找“沮丧相关文档”这类模糊、需联想的问题。

这并非功能冗余,而是互补。Subagent 查文件快但仅能精确匹配,向量检索虽慢但能理解语义。LLM 就像一个聪明的调度员,遇到事实性问题则 spawn subagent,遇到模糊问题则调 vector_search 工具。两者可以在同一个 agent 中和谐共存。

public class Chapter16_Full {

    public static void main(String[] args) {
        // 文件检索 subagent:使用内置工具,无需额外编写 Ja va 代码
        SubagentDeclaration docReader = SubagentDeclaration.builder()
          .name("doc_reader")
          .description("文档检索;输入问题,输出从 ./docs 找出的相关段落")
          .inlineAgentsBody("""
              你是一个文档检索员。
              1. 用 grep_files 在 ./docs 下找关键词
              2. 用 read_file 读最相关 2 份文件
              3. 把内容整理成 200 字以内回答
              """)
          .build();

        // 语义检索工具:业务方自行对接向量库(可选)
        Toolkit toolkit = new Toolkit();
        toolkit.registerTool(new VectorSearchTool("https://localhost:19530"));

        HarnessAgent host = HarnessAgent.builder()
          .name("qa")
          .sysPrompt("""
              你是问答助理。
              优先 spawn doc_reader 查本地文档;若用户提出模糊的语义类问题,则调 vector_search 工具。
              """)
          .model(model())
          .workspace(Path.of("./workspace"))
          .subagent(docReader)   // 文件检索(关键词)
          .toolkit(toolkit)      // 向量检索(语义)
          .build();

        // 事实性问题 → LLM 会 spawn doc_reader
        host.call(
          List.of(new UserMessage("user", "退款政策是什么?")),
          RuntimeContext.empty())
          .block();

        // 模糊问题 → LLM 会调 vector_search
        host.call(
          List.of(new UserMessage("user", "有哪些和用户不满意相关的政策?")),
          RuntimeContext.empty())
          .block();
    }
}

16.7 本章小结

- 1.x 的 RAGKnowledge 在 2.0 中已被标记为弃用,并将在未来版本中移除。

- 2.0 推荐的做法是 **“subagent + 文件检索”** 或是 **“业务方手写向量检索 @Tool”**。

- 对于大量结构化文档,可设计为 Skill 仓库,利用其 description 实现智能路由,管理更为便捷。

- 当确实需要嵌入和向量数据库时,完全可以通过 @Tool 自由实现,框架不再束缚你的实现方式。

来源:https://cloud.tencent.com.cn/developer/article/2701241
上一篇Linux基础命令apachectl用法详解 下一篇API接口赋能1688采购全流程从商品获取到下单支付一键贯通
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
RAG四标融合企业知识资产体系四库协同GEO优化实践
AI教程 · 2026-07-01

RAG四标融合企业知识资产体系四库协同GEO优化实践

生成式AI正在彻底改写信息检索的底层逻辑。传统SEO依赖关键词堆砌和外链建设的策略,在大模型的内容采信规则下已经基本失效。取而代之的,是生成式引擎优化(GEO)。它不再关注外链数量,而是重点衡量你的知识是否结构化、证据链是否坚实、信源是否可靠——这些维度才是RAG(检索增强生成)架构真正看重的核心指

一个普通上班人分享WorkBuddy使用心得与真实体验
AI教程 · 2026-07-01

一个普通上班人分享WorkBuddy使用心得与真实体验

前言 最近我开始使用WorkBuddy——这是腾讯推出的一款AI办公工作台。差不多用了一周时间,趁印象还新鲜,把真实的使用感受记录下来,给还在犹豫的朋友做个参考。不吹不黑,只说实际体验。 初印象:不只是聊天机器人 之前用过不少AI工具,大多数就是个对话框,你问它答,答完就结束了。WorkBuddy不

AI幻觉变真功能实战教程:App Inventor 2视频录制拓展一周开发实录
AI教程 · 2026-07-01

AI幻觉变真功能实战教程:App Inventor 2视频录制拓展一周开发实录

先讲一个颇具戏剧性的开端。 这件事的开端颇显荒诞——有用户前来咨询,称AI Pro版的介绍中提到我们有一款“视频录制拓展”。团队全体成员都感到困惑,翻遍产品列表,发现根本不存在该组件。AI那种“一本正经胡说八道”的能力,这次确实让我们陷入尴尬。 按常理,此事到此便可结束——一句“抱歉,暂时没有这个拓

别再混淆OLAP和SQL-on-Hadoop两者查询本质不同
AI教程 · 2026-07-01

别再混淆OLAP和SQL-on-Hadoop两者查询本质不同

OLAP和SQL-on-Hadoop虽都使用SQL查询数据,但本质不同。SQL-on-Hadoop负责海量数据批量计算与ETL,查询速度秒级至分钟级;OLAP通过预聚合实现毫秒级多维分析,适合BI报表。两者在数据平台分工协作,前者是后厨加工,后者是前台快速服务。

GEO优化深度解析:AI偏好FAQ还是长文内容?
AI教程 · 2026-07-01

GEO优化深度解析:AI偏好FAQ还是长文内容?

在GEO优化中,AI对内容形式无统一偏好:FAQ在简单查询中引用率41%,长文在复杂查询中达58%。内容应基于用户意图选择形式,FAQ适配简单事实类问题,长文建立主题权威,两者互补而非替代。