fix(runtime): 工具调用 P0 修复 — after_tool_call 接入 + stream_errored 工具抢救
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

P0-1: after_tool_call 中间件从未被调用
- 流式模式(run_streaming)和非流式模式(run)均添加 middleware_chain.run_after_tool_call()
- ToolErrorMiddleware 错误计数恢复逻辑现在生效
- ToolOutputGuardMiddleware 敏感信息检测现在生效

P0-2: stream_errored 跳过所有工具执行
- 新增 completed_tool_ids 跟踪哪些工具已收到完整 ToolUseEnd
- 流式错误时区分完整工具和不完整工具
- 完整工具照常执行(产物创建等不受影响)
- 不完整工具发送取消 ToolEnd 事件(前端不再卡"执行中")
- 工具执行后若 stream_errored,break outer 阻止无效 LLM 循环

参考文档:
- docs/references/zclaw-toolcall-issues.md (10项问题分析)
- docs/references/deerflow-toolcall-reference.md (DeerFlow工具调用完整参考)
This commit is contained in:
iven
2026-04-24 12:20:14 +08:00
parent 4c31471cd6
commit c12b64150b
5 changed files with 412 additions and 4 deletions

View File

@@ -0,0 +1,212 @@
# DeerFlow 工具调用系统参考文档
> 调研 DeerFlow 的工具调用完整流程,为 ZCLAW 工具调用问题排查提供参考。
> 分析日期2026-04-24
---
## 一、端到端数据流
```
用户消息
→ FastAPI Gateway (/api/threads/{id}/runs/stream)
→ services.start_run() → asyncio.create_task(run_agent(...))
→ LangGraph Agent Graph (create_agent)
→ LLM Model (ChatOpenAI / Claude)
→ AIMessage (含 tool_calls 列表)
→ 14 层 Middleware 链处理
→ ToolNode (LangGraph 内置, 按 tool_call.name 路由)
→ ToolMessage (执行结果)
→ 再次调用 LLM (带着 ToolMessage 继续)
→ StreamBridge.publish() → asyncio.Queue
→ SSE → 前端 useStream hook
→ React 组件渲染
```
## 二、工具注册与执行
### 2.1 注册入口
**文件**: `G:/deerflow/backend/packages/harness/deerflow/tools/tools.py``get_available_tools()`
工具来自四个来源:
| 来源 | 加载方式 | 示例 |
|------|----------|------|
| Config 工具 | YAML 配置 + 反射导入 (`module:variable`) | `deerflow.sandbox.tools:bash_tool` |
| Builtin 工具 | 硬编码导入 | `present_file_tool`, `ask_clarification_tool` |
| MCP 工具 | `MultiServerMCPClient` 从 MCP 服务器缓存获取 | 第三方 MCP 工具 |
| ACP 工具 | `build_invoke_acp_agent_tool()` 动态构建 | 外部 agent 调用 |
### 2.2 Sandbox 工具清单
**文件**: `G:/deerflow/backend/packages/harness/deerflow/sandbox/tools.py`
| 工具名 | 功能 |
|--------|------|
| `bash` | 沙箱中执行命令 |
| `ls` | 列出目录 |
| `read_file` | 读取文件 |
| `write_file` | 写入文件(触发产物面板自动打开) |
| `str_replace` | 字符串替换(触发产物面板自动打开) |
### 2.3 Builtin 工具
**文件**: `G:/deerflow/backend/packages/harness/deerflow/tools/builtins/`
| 工具 | 功能 |
|------|------|
| `ask_clarification` | 向用户提问澄清(中断执行等待回复) |
| `present_file` | 展示文件给用户(触发产物卡片) |
| `setup_agent` | 自定义 agent 创建 |
| `task_tool` | 子 agent 任务委派 |
| `view_image` | 图片查看(仅视觉模型) |
| `tool_search` | 延迟工具搜索MCP 工具按需暴露) |
## 三、中间件链14 层)
**文件**: `G:/deerflow/backend/packages/harness/deerflow/agents/lead_agent/agent.py``_build_middlewares()`
与工具调用相关的关键中间件:
### 3.1 DanglingToolCallMiddleware
**文件**: `dangling_tool_call_middleware.py`
`wrap_model_call` 中检测消息历史中缺失 ToolMessage 的 AIMessage自动注入占位 ToolMessage
```python
ToolMessage(
content="[Tool call was interrupted and did not return a result.]",
tool_call_id=tc_id,
name=tc.get("name", "unknown"),
status="error",
)
```
### 3.2 ToolErrorHandlingMiddleware
**文件**: `tool_error_handling_middleware.py`
`wrap_tool_call` 中捕获工具执行异常,转换为错误 ToolMessage 而非让整个 run 崩溃。
### 3.3 LoopDetectionMiddleware
**文件**: `loop_detection_middleware.py`
`after_model` 中检测重复工具调用:
- 阈值 3 次 → 注入警告 HumanMessage
- 阈值 5 次 → 直接清空 tool_calls强制 LLM 产出文本回答
### 3.4 DeferredToolFilterMiddleware
**文件**: `deferred_tool_filter_middleware.py`
`wrap_model_call` 中过滤延迟注册的 MCP 工具 schema仅在 LLM 通过 `tool_search` 发现后才暴露。
### 3.5 ClarificationMiddleware
拦截 `ask_clarification` 工具调用,中断执行等待用户回复。
### 3.6 SubagentLimitMiddleware
截断过多的并行子 agent 调用。
## 四、工具结果回传
### 4.1 格式
LangChain 的 `ToolMessage`,包含:
- `content`: 执行结果文本
- `tool_call_id`: 匹配 AIMessage 中的 tool_call ID
- `name`: 工具名称
- `status`: `"error"` 或省略
### 4.2 特殊工具
`present_file_tool` 返回 `Command` 而非纯字符串,同时更新 `artifacts``messages` 两个 state channel。
## 五、前端工具调用展示
### 5.1 消息分组
**文件**: `G:/deerflow/frontend/src/core/messages/utils.ts``groupMessages()`
| 分组类型 | 触发条件 | 展示 |
|----------|----------|------|
| `assistant:processing` | AI 消息含 tool_calls 或 reasoning | MessageGroup (折叠) |
| `assistant` | AI 消息有文本无 tool_calls | MessageListItem (气泡) |
| `assistant:present-files` | 含 present_files tool call | ArtifactFileList |
| `assistant:clarification` | ask_clarification 结果 | MarkdownContent |
| `assistant:subagent` | 含 task tool call | SubtaskCard |
### 5.2 工具状态推断
前端**没有显式状态机**。通过消息序列推断:
- AI 消息含 tool_calls 但无对应 ToolMessage → 正在执行
- ToolMessage 出现 → 执行完成
- `assistant:processing` 组由 `ChainOfThought` 折叠组件包裹
### 5.3 工具调用 UI
**文件**: `message-group.tsx` 第 186-423 行
按工具名渲染不同图标和内容:
- `bash` → 终端图标 + 命令代码块
- `read_file`/`write_file`/`str_replace` → 文件图标 + 路径链接(点击打开产物面板)
- `web_search` → 搜索图标 + 结果链接
- 默认 → 扳手图标 + 工具名
## 六、流式处理中的工具调用
### 6.1 架构
```
agent.astream(stream_mode=["values"])
→ StreamBridge (asyncio.Queue per run, maxsize=256)
→ sse_consumer() → SSE frames → 前端
```
### 6.2 关键特征
- 工具调用**不中断**流。LangGraph 自动在 agent_node 和 tool_node 之间路由
- 每次状态变更产出完整的 `values` 快照,前端通过 `seen_ids` 去重
- 15 秒心跳包保持 SSE 连接
### 6.3 前端看到的事件序列
1. `values` 事件: 含 `tool_calls` 的 AIMessage
2. `values` 事件: ToolMessage工具结果
3. `values` 事件: LLM 基于工具结果的最终回答
整个过程连续,不中断 SSE 连接。
## 七、与 ZCLAW 对比(工具调用)
| 维度 | DeerFlow | ZCLAW |
|------|----------|-------|
| 框架 | LangGraph (graph-based) | 自研 loop_runner (循环) |
| 工具生命周期 | LangGraph ToolNode 自动管理 | 手动 ToolRegistry + loop_runner |
| after_tool_call 中间件 | ✅ wrap_tool_call 钩子完整 | ❌ 流式和非流式模式均未调用 |
| 并行工具执行 | LangGraph 自动处理 | 非流式有 JoinSet流式全串行 |
| 悬挂修复 | DanglingToolCallMiddleware | DanglingToolMiddleware (有) |
| 错误恢复 | ToolErrorHandlingMiddleware (异常→ToolMessage) | ToolErrorMiddleware (计数器) |
| 循环检测 | LoopDetectionMiddleware (3次警告/5次强停) | LoopGuardMiddleware (有) |
| 前端状态 | 消息序列推断 | 显式 ToolCallStep 状态机 |
| MCP 工具 | 延迟注册 + tool_search 按需暴露 | 全量注册 |
## 八、关键文件索引
| 功能 | DeerFlow 文件 |
|------|-------------|
| Agent 工厂 | `backend/packages/harness/deerflow/agents/lead_agent/agent.py` |
| 中间件组装 | `backend/packages/harness/deerflow/agents/factory.py` |
| 工具注册 | `backend/packages/harness/deerflow/tools/tools.py` |
| Sandbox 工具 | `backend/packages/harness/deerflow/sandbox/tools.py` |
| Builtin 工具 | `backend/packages/harness/deerflow/tools/builtins/` |
| 错误处理中间件 | `agents/middlewares/tool_error_handling_middleware.py` |
| 悬挂修复中间件 | `agents/middlewares/dangling_tool_call_middleware.py` |
| 循环检测中间件 | `agents/middlewares/loop_detection_middleware.py` |
| 延迟过滤中间件 | `agents/middlewares/deferred_tool_filter_middleware.py` |
| 流式 Bridge | `runtime/stream_bridge/memory.py` |
| 前端消息分组 | `frontend/src/core/messages/utils.ts` |
| 前端工具调用组件 | `frontend/src/components/workspace/messages/message-group.tsx` |