项目: knowledge-platform(内部知识查询平台)
日期: 2026-06-16
第一部分:核心概念
1.1 三种系统对比
类型 定义 谁决定流程 项目对应
Chatbot 一问一答,无外部能力 无流程 直接调 LLM 聊天
RAG 检索增强生成,固定流水线 Java 代码写死 QaService.ask()
Agent LLM + 目标 + 工具 + 循环决策 LLM 在循环里决定 尚未实现(Day 2 起)
一句话总结: RAG = 你写流程图,LLM 填最后一格;Agent = LLM 自己画流程图,你提供工具和边界。
1.2 Agent 四大组件
组件 作用 项目已有
LLM(大脑) 理解、推理、规划 LlmAnswerSynthesizer
Tools(手脚) 搜索、查库、HTTP 等外部能力 各 Service(待抽象为 AgentTool)
Memory(记忆) 对话历史、中间结果 QaSession + QaMessage
Planning Loop ReAct 多步决策循环 暂无(QaService 是单次流水线)
1.3 五个必记术语
术语 含义
Prompt 发给 LLM 的指令;System Prompt 定义 Agent 角色与边界
Function Calling LLM 返回 JSON(工具名 + 参数),Java 解析后执行
Tool Use Function Calling 的落地实现
Context Window 一次能塞进模型的 token 上限
Hallucination(幻觉) 模型编造内容;用 Tool 拿真实数据缓解
1.4 现有 RAG 流水线
用户提问
→ ① search_documents(DocumentFeignClient)
→ ② check_relevance(AnswerRelevanceEvaluator)
→ ③ generate_from_chunks(AnswerGenerator)
→ ④ 失败 → web_search(WebSearchService)
→ ⑤ synthesize_answer(LlmAnswerSynthesizer)
→ ⑥ 仍失败 → 返回「未找到」
1.5 ReAct 循环
步骤 英文 含义
想 Thought 分析现状,决定下一步
做 Action 调用 Tool,或给出 Final Answer
看 Observation 工具返回结果,写回上下文
循环直到 Final Answer 或超过 MAX_STEPS(通常 5~8)。
MAX_STEPS 的作用: 防死循环、控成本、控延迟、限制系统访问次数。
1.6 ReAct 与 Function Calling
• ReAct :思维模式(Thought → Action → Observation)
• Function Calling :工程实现(LLM 返回 tool_calls JSON,Java 执行后再调 LLM)
第二部分:作业 A 标准答案(Tool 清单)
作业要求: 列出至少 5 个 Tool,写出 name、description、parameters、returns、对应类。
Tool 1:search_documents
• 对应类: DocumentFeignClient.search()
• description: 在企业内部知识库中检索与问题相关的文档片段。
• 适用于: 公司制度、报销流程、部署规范、员工手册、项目文档;用户问题含「公司」「内部」「我们」等语境时优先使用。
• 不适用于: 外部产品文档(如 Cursor、Docker 官方文档)、实时新闻、通用百科且内部库可能无收录(应改用 web_search)。
• parameters: question: string(搜索关键词或完整问题);limit: integer(返回条数,默认 8)
• returns: List<ChunkSearchResult>
Tool 2:check_relevance
• 对应类: AnswerRelevanceEvaluator.evaluate()
• description: 评估文档检索结果与用户问题的语义相关性,过滤掉跑题、弱相关的片段。
• 适用于: search_documents 返回结果后,判断能否基于这些片段直接生成答案。
• 不适用于: 尚未检索文档时(应先调用 search_documents);联网搜索结果的质量评估(web_search 结果可直接交给 synthesize_answer)。
• parameters: question: string;chunks: List<ChunkSearchResult>
• returns: Result { relevant: boolean, chunks: List, topicMismatch: boolean }
Tool 3:classify_intent
• 对应类: QuestionIntentClassifier.classify()
• description: 识别用户问题的意图类型,用于选择答案格式和写作风格。
• 适用于: 需要决定答案结构时(如「什么是 X」vs「如何部署 X」)。可识别 CONCEPT、PROCEDURAL、COMPARISON、TROUBLESHOOTING、POLICY、LIST 等。
• 不适用于: 已有明确工具链且无需调整格式时(可跳过以节省一步)。
• parameters: question: string
• returns: AnswerType 枚举值
Tool 4:generate_from_chunks
• 对应类: AnswerGenerator.generate()
• description: 基于企业内部文档片段,用规则引擎提取要点并格式化答案(不调用 LLM)。
• 适用于: check_relevance 判定为相关;问题为操作步骤、命令、列举类;需要快速、低成本地从本地文档生成答案。
• 不适用于: 仅有联网搜索结果;本地片段质量差或为空;需要深度推理、对比分析(应使用 synthesize_answer)。
• parameters: question: string;chunks: List<ChunkSearchResult>(经 check_relevance 过滤后)
• returns: GeneratedAnswer { answer, answerType }
Tool 5:web_search
• 对应类: WebSearchService.search()
• description: 通过 Bing/百度等搜索引擎检索互联网公开信息。
• 适用于: search_documents 无结果或结果不相关;外部软件/开源技术/行业通用概念;用户明确询问外部产品。
• 不适用于: 公司内部机密制度、未公开流程;已有高质量内部文档可回答的问题。
• parameters: query: string(搜索关键词,注意不是 question)
• returns: List<WebSearchItem>(title, snippet, url)
Tool 6:synthesize_answer
• 对应类: LlmAnswerSynthesizer.synthesize()
• description: 调用大语言模型,综合多源参考资料生成自然流畅的中文答案。
• 适用于: 已有联网搜索结果或本地+联网混合资料;概念解释、对比分析类问题;generate_from_chunks 无法产出满意答案时的兜底。
• 不适用于: 没有任何参考资料(应先 search_documents 或 web_search);简单列举类且本地文档已足够(优先 generate_from_chunks 以节省成本)。
• parameters: question: string;sources: 参考资料(WebSearchItem 列表或文档片段)
• returns: 格式化后的答案文本
典型调用顺序(RAG 视角)
search_documents → check_relevance
├─ 相关 → classify_intent → generate_from_chunks → 返回答案
└─ 不相关 → web_search → classify_intent → synthesize_answer → 返回答案
Tool Description 写法原则(评分要点)
• 写清「何时用」「何时不用」
• 用 LLM 能理解的业务语言,不是 Java 方法名
• 相邻 Tool 边界要互斥,减少 LLM 选错
• 补充 parameters、returns、对应类
• 违法/敏感内容在 Agent System Prompt 统一拦截,不要写在单个 Tool 里
第三部分:作业 B 标准答案(ReAct 轨迹)
作业要求: 选一个真实问题,逐步写出 Thought → Action → Observation → ... → Final Answer,至少 2 个 Thought、2 个 Action、1 个 Final Answer。
标准答案一:什么是 Redis?
Step 1 --- Thought
用户问的是通用技术概念「Redis」。先查内部知识库,看是否有架构文档、培训材料或术语表收录;若有则优先用内部口径回答。
Step 1 --- Action
{
"tool": "search_documents",
"arguments": {
"question": "什么是redis",
"limit": 8
}
}
Step 1 --- Observation
{
"chunks": \[\]
}
或弱相关:
{
"chunks": [
{
"documentTitle": "AI术语表",
"content": "...缓存...消息队列..."
}
]
}
Step 2 --- Thought
检索结果为空或弱相关,需确认是否可用。
Step 2 --- Action
{
"tool": "check_relevance",
"arguments": {
"question": "什么是redis",
"chunks": ...
}
}
Step 2 --- Observation
{
"relevant": false,
"topicMismatch": true
}
Step 3 --- Thought
内部资料不足以回答。Redis 是业界通用开源组件,应联网获取权威定义与典型用途。
Step 3 --- Action
{
"tool": "web_search",
"arguments": {
"query": "Redis 是什么 内存数据结构存储"
}
}
Step 3 --- Observation
{
"items": [
{
"title": "Redis 官方介绍",
"snippet": "Redis is an in-memory data structure store...",
"url": "https://redis.io/..."
},
{
"title": "Redis 中文文档",
"snippet": "Redis 是一个开源的内存数据结构存储系统,可用作数据库、缓存和消息中间件",
"url": "..."
}
]
}
Step 4 --- Thought
联网已有清晰定义,意图为概念解释(CONCEPT),需用 LLM 综合多条摘要,写成定义+用途+场景的中文回答。
Step 4 --- Action
{
"tool": "classify_intent",
"arguments": { "question": "什么是redis" }
}
然后:
{
"tool": "synthesize_answer",
"arguments": {
"question": "什么是redis",
"sources": "上一步 web_search 的 items"
}
}
Step 4 --- Observation
{
"answerType": "CONCEPT",
"draft": "(LLM 合成稿)"
}
Step 5 --- Final Answer
Redis 是一个开源的内存数据结构存储系统,数据主要放在内存中,读写非常快。它常被用作:缓存(减轻数据库压力)、会话存储、排行榜/计数器,以及消息队列等场景。支持字符串、哈希、列表、Set、Sorted Set 等多种结构,持久化可选 RDB/AOF。在微服务架构里,Redis 经常和 MySQL 配合:热数据放 Redis,冷数据落库。
易错点提醒:
• web_search 参数是 query,不是 question,也没有 limit 参数
• web_search 返回 items(title/snippet/url),不是 chunks
• relevant 字段来自 check_relevance,不是 search_documents 或 web_search 自带
• 概念题 Final Answer 需:第一句定义 + 用途 + 场景,不能只有一句话
标准答案二:Docker Compose 怎么部署 qa-service?
Step 1 --- Thought
用户问的是部署操作,属于内部项目文档或运维规范。应先搜企业内部知识库,看有没有 qa-service / Docker 相关文档。
Step 1 --- Action
{
"tool": "search_documents",
"arguments": {
"question": "Docker Compose 部署 qa-service",
"limit": 8
}
}
Step 1 --- Observation
找到 README、docker/README.md,内容主要是 java -jar 启动,无明确 docker-compose.yml。
Step 2 --- Thought
本地有 jar 启动方式,但用户明确问 Compose。需确认问题类型,并判断是否需要联网补充 Compose 模板。
Step 2 --- Action
{
"tool": "classify_intent",
"arguments": {
"question": "Docker Compose 怎么部署 qa-service?"
}
}
Step 2 --- Observation
{
"answerType": "PROCEDURAL"
}
Step 3 --- Thought
意图是操作步骤;内部文档可能不完整,应联网查 Docker Compose 部署 Spring Boot 微服务的通用做法,再结合本项目结构补充。
Step 3 --- Action
{
"tool": "web_search",
"arguments": {
"query": "Docker Compose 部署 Spring Boot 微服务"
}
}
Step 3 --- Observation
多条结果含 Compose 服务定义、端口映射、环境变量等模板。
Step 4 --- Thought
已有内部启动说明 + 联网 Compose 模板,信息足够,应综合写一份面向本项目的部署步骤。
Step 4 --- Action
{
"tool": "synthesize_answer",
"arguments": {
"question": "Docker Compose 怎么部署 qa-service?",
"sources": "内部文档 + web items"
}
}
Step 5 --- Final Answer
在本项目中部署 qa-service,建议按以下步骤:
- 编译:在 backend 目录执行 mvn clean package -DskipTests,生成 qa-service/target/qa-service-1.0.0-SNAPSHOT.jar。
- 编写 Compose 文件:创建 docker-compose.yml,定义 qa-service 服务,映射 8083:8083。
- 配置依赖:qa-service 通过 Feign 调用 document-service,需配置 Eureka、document-service 等地址。
- 启动:docker compose up -d,验证问答接口。
- 若仓库暂无 compose 文件,可先按 README 用 java -jar 本地验证,再容器化。
Agent 价值说明: 看到本地只有 jar 启动、缺 Compose 后,主动决定联网,而不是有片段就强行本地答。
标准答案三:报销需要哪些材料?(简单 3 步)
Step 1 --- Thought
用户问公司内部报销材料,应优先搜内部知识库中的财务制度文档。
Step 1 --- Action
{
"tool": "search_documents",
"arguments": {
"question": "报销 材料",
"limit": 8
}
}
Step 1 --- Observation
找到《财务报销制度》等文档片段,含材料清单。
Step 2 --- Thought
检索结果与问题高度相关,意图为制度/流程类,可直接从文档提取材料清单。
Step 2 --- Action
check_relevance → classify_intent(POLICY/PROCEDURAL)→ generate_from_chunks
Step 2 --- Observation
{
"relevant": true,
"answerType": "POLICY"
}
Step 3 --- Final Answer
根据公司财务报销制度,报销通常需要: - 报销单(填写完整并签字)
- 合法有效的发票原件
- 费用明细及相关审批凭证
- 差旅报销另需行程单、住宿水单等
具体以最新版《财务报销制度》为准。
说明: 此场景不需要 web_search 和 synthesize_answer,RAG 固定流水线即可高效处理。
第四部分:作业 C 标准答案(LlmAnswerSynthesizer 改造)
作业要求: 对照 LlmAnswerSynthesizer.callChatCompletions(),标出从 RAG 改为 Agent 需要新增/修改的 3 处。
改造点 1:请求体增加 tools 定义
现状(RAG): 请求体只有 model、temperature、max_tokens、messages。
改造(Agent): 在 body 中增加 tools 数组和 tool_choice 字段。
// 现有代码(约 106-112 行)
ObjectNode body = objectMapper.createObjectNode();
body.put("model", model);
body.put("temperature", properties.getTemperature());
body.put("max_tokens", properties.getMaxTokens());
ArrayNode messages = body.putArray("messages");
messages.addObject().put("role", "system").put("content", SYSTEM_PROMPT);
messages.addObject().put("role", "user").put("content", userPrompt);
// Agent 需新增
ArrayNode tools = body.putArray("tools");
for (ToolDefinition tool : toolRegistry.all()) {
tools.add(tool.toJson());
}
body.put("tool_choice", "auto");
改造点 2:响应解析支持 tool_calls
现状(RAG): 只解析 message.content 文本。
改造(Agent): 需分支处理 content 与 tool_calls。
// 现有代码(约 129-134 行)
JsonNode content = root.path("choices").path(0).path("message").path("content");
return content.asText().trim();
// Agent 需改为
JsonNode message = root.path("choices").path(0).path("message");
JsonNode toolCalls = message.path("tool_calls");
if (toolCalls.isArray() && !toolCalls.isEmpty()) {
return LlmResponse.withToolCalls(parseToolCalls(toolCalls));
}
return LlmResponse.withContent(message.path("content").asText());
改造点 3:单次调用改为多轮循环
现状(RAG): Java 决定流程,LLM 只被调用一次生成最终文本。
改造(Agent): 外层 ReAct 循环,每轮 append messages。
public String runAgent(String question, List tools) {
List messages = initMessages(question);
for (int step = 0; step < MAX_STEPS; step++) {
LlmResponse resp = callChatCompletions(messages, tools);
if (resp.hasFinalAnswer()) {
return resp.getContent();
}
ToolCall call = resp.getToolCall();
String observation = executeTool(tools, call);
messages.add(assistantToolCallMessage(call));
messages.add(toolResultMessage(call.getId(), observation));
}
return "超过最大步数,请简化问题后重试";
}
对比总结表
维度 RAG(现在) Agent(目标)
谁决定流程 Java if/else LLM 在循环中决定
LLM 调用次数 通常 1 次 多次(每步 1 次)
请求体 仅 messages messages + tools
响应解析 仅 content content 或 tool_calls
Tool 结果 Java 变量传递 append 到 messages 再调 LLM
第五部分:自测题标准答案
Q1:search_documents 和 web_search 的 description 怎么写?
search_documents: 在企业内部知识库中搜索与问题相关的文档片段。优先用于公司制度、流程、内部规范类问题。不适用于外部产品文档、实时新闻、通用百科。
web_search: 通过搜索引擎检索互联网公开信息。当内部知识库无结果,或问题涉及外部产品、通用技术概念时使用。不适用于公司内部机密制度、未公开流程。
原则: 写清何时用/何时不用;两个 Tool 边界互斥;用业务语言而非 Java 方法名。
Q2:为什么需要 MAX_STEPS?
• 防无限循环:模型可能反复调用同一工具
• 控成本:每步 ≈ 1 次 LLM + N 次外部 API
• 控延迟:用户等待有上限
• 安全:限制 Agent 对系统的访问次数
超限策略: 返回「请缩小问题范围」,或将已有 Observation 交给 LLM 做部分回答。
Q3:固定流水线 vs Agent 优缺点?
固定流水线(QaService) Agent
优点 行为可预测;易调试;延迟低;成本低 灵活;可处理复杂多步任务;可组合工具
缺点 无法应对需多步推理的复杂问题;流程变更要改代码 不可预测;调试难;成本高;可能幻觉或死循环
适用 简单 FAQ、单轮问答 对比分析、先查 A 再查 B 再总结
结论: 不是替代关系。简单 FAQ 用 RAG 足够;复杂任务用 Agent;演进路径为 RAG 作默认、复杂问题走 Agent。
附录 A:ReAct 作业模板
用户问题:
Step 1
Thought :(为什么先做这一步)
Action :{ "tool": "", "arguments": {} }
Observation:(工具返回什么)
Step 2
Thought :...
Action :...
Observation:...
Step N
Thought :...
Final Answer :...
附录 B:后续课程预告
Day 主题
Day 2 AgentTool 接口 + 第一个 Tool 实现
Day 3 Function Calling + ReAct 循环(Java)
Day 4 多轮对话记忆与上下文管理
Day 5 Mini Agent:自主搜文档 + 联网