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工具调用完整参考)
7.7 KiB
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:
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 IDname: 工具名称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 前端看到的事件序列
values事件: 含tool_calls的 AIMessagevalues事件: ToolMessage(工具结果)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 |