第 6 章:工具与中间件 —— Agent 的能力不是一张平铺列表
DeerFlow 的工具系统本质上是一条 capability pipeline:发现、去重、授权、延迟暴露、技能约束、执行保护、结果整形。
工具来自四个源头
get_available_tools() 汇总:
| 来源 | 例子 | 优先级 |
|---|---|---|
config.yaml 反射加载 | bash、ls、read/write、搜索工具 | 1 |
| 内置工具 | present_file、ask_clarification、review_skill_package | 2 |
| MCP 缓存 | 外部 server 暴露的 tools | 3 |
| ACP | invoke_acp_agent | 4 |
启用子代理时再加 task;模型支持视觉时加 view_image;开启 skill evolution 时加管理工具。最后按真实 tool.name 去重,优先保留前面的来源。
配置名和对象名不一致会告警,因为 schema 给模型的名字与 runtime router 识别的名字不一致,会产生最难排查的“模型明明调用了却找不到工具”。
七道能力闸门
1. 配置与工具组
每个 tool config 有 group 和 use。Custom agent 可只启用指定 group。使用 LocalSandboxProvider 时,宿主 bash 默认不暴露;只有明确允许或使用隔离 shell provider 才开放。
2. 组装期授权
apply_tool_authorization() 根据可信运行上下文构造 Principal,先从模型 schema 中移除角色永远不能用的工具。这样既节省 token,也减少诱导模型尝试被拒绝能力。
3. 延迟工具
MCP server 多时,全部 schema 会占据大量上下文。assemble_deferred_tools() 将候选工具做成 DeferredToolCatalog,只立即暴露核心工具和 tool_search;找到匹配项后,把名字写进 ThreadState 的 promoted。
catalog 带 hash。配置变化后 hash 不同,旧晋升名单整体丢弃,避免“过去的 send 恰好指向现在另一个 server 的 send”。
4. Skills 工具策略
技能的 allowed-tools 不是安装时永久授权。只有 slash 激活或真实读取技能文件后,SkillToolPolicyMiddleware 才将它应用到 schema 可见性、tool search 结果和执行路径。技能元数据本身不构成授权。
5. 执行期再裁决
组装期过滤只能根据启动时已知信息;某个具体参数是否允许,需要在 tool call 发生时由 guardrail/authorization provider 再判断。两层共同形成“能力集合最小化 + 每次行为裁决”。
中间件链按职责分组
Lead Agent 的实际列表会随配置变化,但可以分成五段:
- 基础安全与错误:输入清洗、工具错误、结果清洗、read-before-write、audit、guardrail;
- 上下文构造:DynamicContext、SkillActivation、SkillPolicy、DurableContext、Summarization;
- 产品状态:Todo、TokenUsage、Title、Memory、ViewImage;
- 能力路由:MCP 自动晋升、DeferredToolFilter、SystemMessageCoalescing;
- 循环护栏:SubagentLimit、LoopDetection、TokenBudget、TerminalResponse、Length/Safety、Clarification。
几个典型中间件
ReadBeforeWriteMiddleware:要求模型先读取目标文件再改,降低盲写覆盖。它是行为协议,不是文件权限替代品。
ToolOutputBudgetMiddleware:大工具输出会吞掉上下文。中间件截断、保存 artifact 或生成 synopsis,让模型知道结果存在但不把全部内容塞回消息。
LoopDetectionMiddleware:识别重复工具调用模式,达到阈值后终止并留下 stop reason,避免模型在相同失败上烧完整预算。
TerminalResponseMiddleware:provider 在工具后返回空 AIMessage 时重试一次;仍为空则写可见 fallback,避免运行显示 success 而 UI 空白。
SafetyFinishReasonMiddleware:如果 provider 因安全策略终止,会先清除可能同时返回的 tool_calls,使后续中间件不会执行它们。
ClarificationMiddleware:把 ask_clarification 的 ToolMessage artifact 转成可恢复的人机输入协议。它最后注册,确保响应侧执行顺序正确。
工具返回值不止字符串
LangChain Tool 可返回 Command(update=...)。DeerFlow 用它原子更新消息和 ThreadState,例如:
present_file更新artifacts;task写带 subagent 元数据的 ToolMessage;- 沙箱首次懒加载时写
sandbox_id; view_image更新viewed_images元数据。
工具因此既是外部动作,也是图状态的受控写入节点。
失败语义
“工具出错”分三类:业务错误应返回模型可理解文本;策略拒绝应留下可审计原因;运行时异常由 ToolError middleware 归一化。把三者都抛成 Python exception 会让图无法判断该重试、换方案还是停止。
源码锚点
下一章深入所有文件类工具共同依赖的沙箱与虚拟路径。