推理优化
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Failing after 6m36s

This commit is contained in:
2026-05-06 04:26:06 +08:00
parent 1260bef5cb
commit ef6fbc1521
12 changed files with 313 additions and 129 deletions

131
README.md
View File

@@ -2043,3 +2043,134 @@ curl http://115.190.121.151:6333/collections
## 🤝 贡献
欢迎提交 Issue 和 Pull Request
---
### TODO
## 1.llm的BUG修复
看了你的代码,目前的核心问题在于**推理节点和回答节点的职责虽然分开了,但实现上仍有混淆**,导致流程混乱。下面帮你梳理清楚。
---
## 当前架构分析
### ✅ 正确的部分
- **`llm_call` 节点**已经使用了**无工具模型**`models[model_name]`,没有 `bind_tools`),只负责根据上下文生成最终回答。这是对的。
- **`AIAgentService`** 中,`process_message_stream` 处理了 `tool_calls` 事件,说明你预料到推理 LLM 可能产生 tool call。
### ❌ 混乱的根源
- **推理 LLM 很可能仍然绑定了工具**。
`build_react_main_graph(chat_services=self.chat_services, tools=self.tools, ...)` 中,你很可能在构建推理节点时,将 `tools` 传给了推理 LLM`llm.bind_tools(tools)`)。这会导致:
1. 推理 LLM 直接输出 OpenAI 格式的 function call而不是你期望的 `{"action": "RETRIEVE_RAG"}` 文本。
2. LangGraph 会自动解析这些 tool call 并执行(如果工具节点被正确连接),这就绕过了你自己的路由逻辑(条件边)。
3. 你的流式处理代码(`_handle_message_chunk`)收到了 `tool_calls`,但只是把它们当作事件转发给前端,并没有真正由你的代码去执行工具,导致工具调用可能由 LangGraph 自动完成,或者完全丢失。
- **流式处理中的 `_handle_message_chunk`** 处理了 `tool_calls`,但实际上这些 tool call 如果被 LangGraph 自动执行了,前端会看到工具调用,但之后突然就跳到了回答,中间缺乏透明控制。如果推理 LLM 输出的是文本动作(比如 JSON`tool_calls` 永远不会出现,这段处理逻辑就成了死代码。
---
## 应该实现的目标架构(也是你一贯讨论的)
```
推理节点 (react_reason)
├─ 使用不带工具绑定的 LLM
├─ Prompt 中列出可用动作RETRIEVE_RAG, WEB_SEARCH, DIRECT_RESPONSE
├─ 输出结构化 JSON{"action": "...", "query": "...", "reasoning": "..."}
└─ 你的代码解析 JSON → 修改 state.next_action
条件边 (route_by_reasoning)
├─ 读取 state.next_action
├─ 映射到工具节点rag_retrieve, web_search_node
└─ 或直接进入 llm_call 节点
工具节点 (rag_retrieve / web_search_node)
├─ 执行实际检索/搜索
└─ 将结果写回 state.rag_context, state.retrieved_docs
回答节点 (llm_call)
├─ 使用无工具 LLM
├─ 基于 state.rag_context 生成最终回答
└─ 绝不调用任何工具
```
---
## 需要现在修改的地方(按优先级)
### 1. 推理节点:移除工具绑定,改为文本决策
`build_react_main_graph` 中(或在推理节点的构造代码里),确保推理 LLM 是通过 `prompt | llm` 而不是 `prompt | llm.bind_tools(tools)` 调用的。
**做法**
- 推理节点的 system prompt 里列出可用动作及格式要求。
- 推理 LLM 只输出 JSON例如
```json
{"action": "RETRIEVE_RAG", "search_query": "吕布 事迹 三国演义"}
```
- 在推理节点后增加一个解析函数,将 JSON 解析为具体的 `action` 和参数,更新到 state 中。
### 2. 条件边:根据解析的 `action` 干净路由
确保 `route_by_reasoning` 使用解析后的 `state.next_action`(字符串)进行路由,而不是再去检查 `history` 或 `retrieved_docs`。同时将**置信度阈值判断**放在路由之前或放在推理节点的 prompt 中(让 LLM 决策时就遵守规则),避免在条件边里重复判断。
### 3. llm_call 节点:保持当前的无工具状态 ✅
当前已正确,无需改动。唯一建议:确保 `models[model_name]` 确实没有绑定工具。
### 4. 流式处理:简化事件处理
如果推理节点不再输出 tool call可以将 `_handle_message_chunk` 中的 tool_call 处理分支删除或注释,避免混淆。
未来如果你想展示推理过程,可以发送 custom event如你现在做的 react_reasoning 事件)。
---
## 你现在应该做的具体步骤
1. **检查 `build_react_main_graph` 函数**,找到推理节点的创建代码,确认是否调用了 `llm.bind_tools()`。如果调用了,改为 `llm`,并更新 prompt 为 JSON 输出。
2. **确保推理节点的 prompt 包含以下内容**
- 当前状态RAG 置信度、尝试次数、已有的检索结果摘要)
- 决策规则(置信度阈值、最大重试次数等)
- 要求输出纯 JSON不要代码块标记格式为 `{"action": "...", "args": {...}}`
3. **在推理节点返回后,添加一个解析函数**,提取 `action` 和参数,设置 `state.next_action`、`state.rag_query` 等字段。
4. **修改条件边**,直接根据 `state.next_action` 跳转。
5. **测试**:运行“吕布的事迹?”查询,应该看到推理节点输出 `RETRIEVE_RAG`,然后 `rag_retrieve` 执行,再次推理(如果置信度低),或者直接 `DIRECT_RESPONSE` → `llm_call` 生成回答。最终回答应基于检索到的吕布相关文本,而不是无关片段。
---
## 总结
**当前逻辑有问题**,主要是因为推理节点可能仍绑定了工具,导致 tool call 自动执行打乱了你的路由控制。按上述方案调整后Agent 的决策和执行会变得透明、可控,职责分明。如果你需要,我可以帮你重写推理节点的核心逻辑。
## 2.优化:实现推理验证
1. 在 React 循环中增加“验证”步骤
在推理 LLM 输出 DIRECT_RESPONSE 后,不直接返回给用户,而是先进入一个 validate_answer 节点:
text
推理节点 → DIRECT_RESPONSE → validate_answer → 合格?→ 返回用户
↓ 不合格
重新规划动作(如重新检索)
验证内容:检查回答是否自洽、引用依据是否充分、是否回答了用户问题等。
2. 使用 LLM 自省Self-Reflection
在 validate_answer 节点里调用一个专门的校验 LLM可以是轻量模型给它这样的 prompt
text
你是一个严格的校验员。请检查以下回答是否满足要求:
【用户问题】
{user_query}
【检索到的资料】
{rag_context}
【生成的回答】
{llm_response}
请判断:
1. 回答是否基于给定的资料?
2. 回答是否直接回应了用户问题?
3. 回答是否存在事实错误或逻辑漏洞?
输出 JSON{"pass": true/false, "reason": "..."}
如果 pass = false则退回推理节点重新规划如重新检索或联网搜索
3. 在 System Prompt 里要求推理节点评估回答质量
你可以在推理节点的 prompt 里增加一条规则:
text
当你决定 DIRECT_RESPONSE 并收到 llm_call 的回答后,必须自我检查:
- 回答是否与检索到的资料一致?
- 是否回答了用户核心问题?
如果发现不一致或遗漏,必须重新规划。
这相当于把反思逻辑融入了推理循环。