当前位置: 首页
AI教程
AgentScope Java新手村第16讲:从RAG到多路检索

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

热心网友 时间:2026-07-01
转载

许多开发者或许已经察觉到,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("http://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

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
批处理BAT入门教程第一篇

批处理BAT入门教程第一篇

提供13个批处理实战技巧,覆盖全盘查找并删除文件夹或文件、拷贝移动文件、创建畸形文件夹及设置隐藏属性等场景,可一键完成系统维护与文件管理工作,极大提升自动化操作效率和便捷性。

时间:2026-07-03 16:15
从零开始批处理命令For循环详解与实战案例

从零开始批处理命令For循环详解与实战案例

批处理For命令支持 d、 l、 r、 f四个参数。 d仅列出当前目录下的目录名; r递归搜索指定路径及其子目录中的文件; l生成数值序列; f可解析文件、字符串或命令输出,通过delims、tokens、skip、eol等选项灵活处理内容。

时间:2026-07-03 16:14
批评你的人是你生命中的贵人

批评你的人是你生命中的贵人

批评你的人往往最值得珍惜,因为他们关注你、助你成长。面对批评应包容反思,用行动改进而非辩解。接受批评是自我完善的过程,能让人少走弯路,避免重复犯错。这样的人正是生命中的贵人,值得感恩与珍惜。

时间:2026-07-03 16:14
测试人员角色定位与职责详解

测试人员角色定位与职责详解

测试人员角色经历了从找问题、保证质量到分析风险的转变,最终核心职责是提供关键信息,协助团队创造优秀产品。这包括识别问题、评估风险及帮助团队了解项目状态,而非单纯把关或追求完美。

时间:2026-07-03 16:14
经营成功测试生涯的实用方法与策略

经营成功测试生涯的实用方法与策略

一、测试生涯的起点 1989年,我在田纳西大学攻读研究生时,意外地从软件开发人员转行成为一名软件测试工程师。这并非我主动选择,说起来还有些戏剧性——某个早晨,教授质问我为何缺席那么多开发会议,我解释说这些会议总是安排在周末早上,对我这个第一次离家、刚入学的学生来说实在不便。结果呢?等待我的不是解聘通

时间:2026-07-03 16:14
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜