详情

首页手游攻略 AgentScope Java新手村系列16 从RAG到多路检索

AgentScope Java新手村系列16 从RAG到多路检索

佚名 2026-07-20 09:12:09
# 第十六章 从 RAG 到多路检索:subagent 查文件,向量工具查语义

>1.x 的 `SimpleRAGKnowledge`、`BgeRAGKnowledge`、`EmbeddingRAGKnowledge` 等 RAG 实现类在 RC2 中**已被移除**(它们是 1.x 特有的 API,2.0 中不存在)。

原因:传统 RAG 抽象把"文档分块 嵌入 检索"做成黑盒。2.0 改为"**subagent 文件检索 Skill 仓库**":业务方把文档放 `./docs` 目录里,subagent 用 `grep_files` 按关键词搜文件、用 `read_file` 读原文内容,不切片、不嵌入。LLM 自己决定搜什么词、读哪段,比硬编码检索管道灵活得多。

本章先给出 1.x 旧 API 的最小示例(**仅供维护老项目参考**),再讲 2.0 推荐的新做法。

## 16.1 1.x RAG 旧 API 回顾

```

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(...)` 在 RC2 中标记为 `@Deprecated(forRemoval = true)`,编译通过但会告警。新项目请不要使用。

16.2 2.0 推荐的"subagent 文件检索"

### 为什么不用 1.x 的 RAG 了?

1.x 的 RAG 是一条**固定管道**:

```

文档 → 切片 → 嵌入(向量化) → 存向量库

用户提问 → 同样嵌入 → 余弦相似度搜索 → 取 top-k 切片 → 拼进 prompt → LLM 回答

```

这条管道有三个问题:

|问题|影响|

|:-:|:-:|

|**黑盒检索**|向量相似度 ≠ 语义相关。搜出来的切片可能"看起来像但内容不相关",你没地方 debug|

|**管道路径固定**|先切片、再嵌入、再搜索。LLM 全程不参与检索决策,搜什么、怎么搜全由管道定|

|**依赖外部组件**|需要 embedding 模型(DashScope/Qwen Embedding) 向量库(Milvus/Pinecone),多两个微服务|

### 2.0 怎么做?

**把检索权交给 LLM**。agent 自带 `grep_files`(关键词搜索)和 `read_file`(读文件),LLM 自己决定搜什么关键词、读哪个文件、读多少内容。不切片、不嵌入、不向量库。

```

2.0 工作流:

用户提问 → LLM 自己决定用什么关键词 grep → 自己读相关文件 → 自己综合答案

```

对比一下:

|1.xRAG|2.0subagent 文件检索|

|:-:|:-:|

|**检索决策者**|管道(程序)|LLM(agent)|

|**检索方式**|向量相似度|`grep_files` 关键词 `read_file` 精读|

|**外部依赖**|embedding 模型 向量库|无,全是 agent 内置工具|

|**可调试性**|差(你不知道为啥搜到这个切片)|好(agent 日志里能看到 grep 了什么、读了什么)|

|**灵活性**|固定管道|LLM 可以分步检索:先 grep 找文件名 → 再 read 读内容 → 不够再 grep 扩大范围|

**核心思想**:与其让程序猜"哪段和用户问题最像",不如让 LLM 自己想"我应该搜什么词、读哪个文件"。`HarnessAgent` 自带 `read_file` / `grep_files` / `glob_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`,后者用 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);

}

```

把工具注册到 agent / subagent 即可。这就是 2.0 推荐的"该用什么工具就用什么工具",不必拘泥于 `RAGKnowledge` 抽象。

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

|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_files` `read_file` 在 `./docs` 目录做文件级关键词检索。适合"退货政策是什么""怎么开发票"这类事实性问题,零外部依赖。

- **`vector_search` 工具**:调 Milvus/ES 做语义级向量检索。适合用户写"心情不好"要找"沮丧相关文档"这类模糊匹配。

**这不是冗余,是互补**:subagent 查文件快但只能精确匹配,向量检索慢但能理解语义。LLM 看到问题类型后自己选路:事实问题 spawn subagent,模糊问题调 vector_search。两个可以共存于同一个 agent。

```

public class Chapter16_Full {

public static void main(String[] args) {

// 文件检索 subagent:用内置工具,不额外写 Java 代码

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` 自由实现即可。","createTime":1782891275,"ext":{"closeTextLink":0,"comment_ban":0,"description":"","focusRead":0},"favNum":0,"html":"","isOriginal":0,"likeNum":0,
相关资讯
点击查看更多
游戏推荐
推荐专题
热门阅读
推荐下载