当前位置: 首页 > news >正文

从字节跳动 DeerFlow 源码看 Agent 平台设计(二):工具系统设计 — 从全量绑定到按需加载

系列导航

  • 第一篇:什么是 Agent?一个成熟 Agent 平台的 8 个核心组件
  • 本篇:工具系统设计 — 从全量绑定到按需加载
  • 第三篇:五个核心中间件深度解析
  • 第四篇:Agent 生命周期与状态管理

摘要

当 Agent 接入大量 MCP 工具时,将所有工具的完整 JSON Schema 一次性绑定给模型会导致严重的 token 浪费和选择困难问题。本文分析 DeerFlow 如何通过tool_search延迟加载机制实现"仅暴露名称、按需加载 schema"的工具发现策略,并讨论该方案在业界的独特性。


一、三层工具架构

DeerFlow 的工具系统采用三层来源架构,由get_available_tools()函数统一合并:

1.1 Built-in Tools(内置工具)

代码级内置,提供 Agent 运行时的基础能力:

BUILTIN_TOOLS=[present_file_tool,# 向用户展示文件ask_clarification_tool,# 向用户发起澄清提问]SUBAGENT_TOOLS=[task_tool,# 委派任务给子 Agent]

此外还有条件性内置工具:

  • view_image_tool:仅当模型配置supports_vision: true时加载
  • skill_manage_tool:仅当skill_evolution.enabled时加载
  • update_agent:仅自定义 Agent(非默认 Agent)时加载

1.2 Configured Tools(配置工具)

通过config.yaml声明,启动时通过反射加载:

tools:-name:web_searchuse:deerflow.community.search.duckduckgo:ddg_search_toolgroup:search-name:bashuse:deerflow.sandbox.tools:bash_toolgroup:bash-name:read_fileuse:deerflow.sandbox.tools:read_file_toolgroup:file

resolve_variable(cfg.use, BaseTool)"package.module:variable"格式的字符串解析为实际的工具实例。DeerFlow 会校验配置中的name字段与工具实例的.name属性是否一致,不一致时输出警告日志。

1.3 MCP Tools(协议工具)

通过 MCP(Model Context Protocol)服务器动态加载。MCP 服务器配置在extensions_config.json中:

{"mcpServers":{"github":{"enabled":true,"type":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-github"],"env":{"GITHUB_TOKEN":"$GITHUB_TOKEN"}}}}

MCP 工具通过langchain-mcp-adapters库适配为 LangChain 的BaseTool接口,支持 stdio、SSE、HTTP 三种传输方式。工具列表带有文件 mtime 缓存失效机制——当配置文件变更时自动重新初始化 MCP 客户端。

1.4 工具合并与去重

all_tools=loaded_tools+builtin_tools+mcp_tools+acp_tools# 按名称去重,优先级:config > builtin > MCP > ACPseen_names:set[str]=set()unique_tools:list[BaseTool]=[]fortinall_tools:ift.namenotinseen_names:unique_tools.append(t)seen_names.add(t.name)

二、全量绑定的问题

在标准的 LangChain/LangGraph 实现中,所有可用工具的完整 JSON Schema 通过model.bind_tools(tools)一次性发送给模型。当工具数量较少时(10 个以内),这种方式没有问题。

但当接入多个 MCP 服务器时(GitHub、Postgres、Slack、Puppeteer 等),每个服务器可能暴露 5-20 个工具,总计可达 50-100 个。此时全量绑定面临三个问题:

2.1 Token 浪费

每个工具的 JSON Schema 包含名称、描述、参数定义(类型、必填、枚举值等),平均占 200-500 token。50 个工具即占用 10000-25000 token 的上下文空间,严重挤压实际对话内容的空间。

2.2 模型选择困难

研究表明,当可选工具过多时,LLM 的工具选择准确率会下降。模型可能会:

  • 选择功能相似但不正确的工具
  • 产生"幻觉调用"——调用参数格式正确但逻辑错误的工具
  • 在多个候选工具之间犹豫,增加推理 token 消耗

2.3 前缀缓存失效

LLM 推理服务通常支持 KV Cache 前缀缓存——如果系统提示词(包含工具 schema)不变,则可复用先前的 KV Cache 计算结果。工具列表的频繁变动(如 MCP 服务器重启后工具列表变化)会导致缓存失效,增加推理延迟。


三、DeerFlow 的 tool_search 延迟加载方案

DeerFlow 设计了一套完整的延迟加载机制,由四个协作组件构成。

3.1 构建时:识别与分类

在 Agent 构建阶段(_make_lead_agent()),经过技能策略过滤后,调用build_deferred_tool_setup()对工具进行分类:

defbuild_deferred_tool_setup(filtered_tools,*,enabled:bool)->DeferredToolSetup:ifnotenabled:returnDeferredToolSetup(None,frozenset(),None)# 仅 MCP 工具被标记为延迟加载deferred=[tfortinfiltered_toolsif_is_mcp_tool(t)]ifnotdeferred:returnDeferredToolSetup(None,frozenset(),None)catalog=DeferredToolCatalog(tuple(deferred))returnDeferredToolSetup(tool_search_tool=build_tool_search_tool(catalog),deferred_names=catalog.names,catalog_hash=catalog.hash)

识别依据是工具的metadata标记——在get_available_tools()中,MCP 来源的工具会被打上{"deerflow_mcp": True}标签:

fortinmcp_tools:t.metadata={**(t.metadataor{}),"deerflow_mcp":True}

3.2 系统提示词:仅列名称

get_deferred_tools_prompt_section()生成一段包含在系统提示词中的内容:

<available-deferred-tools>github_create_issue github_list_repos postgres_query slack_send_message</available-deferred-tools>

模型能看到这些工具的名称(知道它们存在),但没有参数 schema,因此无法直接调用。

3.3 运行时中间件:拦截未授权调用

DeferredToolFilterMiddleware承担两项职责:

职责一:过滤模型绑定的工具 schema

def_filter_tools(self,request:ModelRequest)->ModelRequest:hide=self._hidden(request.state)# 延迟集 - 已提升集active=[tfortinrequest.toolsift.namenotinhide]returnrequest.override(tools=active)

即使 ToolNode 持有所有工具实例(用于执行),模型通过bind_tools()看到的 schema 只包含活跃工具和已提升工具。

职责二:拦截越权调用

def_blocked_tool_message(self,request:ToolCallRequest)->ToolMessage|None:name=request.tool_call.get("name")ifnameinself._hidden(request.state):returnToolMessage(content=f"Error: Tool '{name}' is deferred and has not been promoted yet. "f"Call tool_search first.",status="error")

如果模型凭记忆尝试直接调用未提升的工具,系统返回错误而非执行,引导模型先通过tool_search获取 schema。

3.4 提升机制:tool_search 解锁

当模型调用tool_search工具时:

@tooldeftool_search(query:str,tool_call_id:Annotated[str,InjectedToolCallId])->Command:matched=catalog.search(query)[:MAX_RESULTS]content=json.dumps([convert_to_openai_function(t)fortinmatched])names=[t.namefortinmatched]returnCommand(update={"promoted":{"catalog_hash":catalog_hash,"names":names},"messages":[ToolMessage(content=content,tool_call_id=tool_call_id)]})

Command(update={"promoted": ...})将提升记录写入图状态。ThreadStatemerge_promotedreducer 处理合并逻辑:

defmerge_promoted(existing,new):ifexisting.get("catalog_hash")!=new["catalog_hash"]:returnnew# catalog 变更 → 替换(防漂移)return{"catalog_hash":existing["catalog_hash"],"names":list(dict.fromkeys(existing["names"]+new["names"]))# 去重合并}

下一轮模型调用时,中间件检测到该工具已提升,不再隐藏其 schema,模型即可正常调用。


四、搜索能力设计

DeferredToolCatalog支持三种查询模式:

4.1 精确选取

select:github_create_issue,postgres_query

直接按名称选取指定工具,适合模型已明确知道需要哪个工具的场景。

4.2 前缀 + 关键词排序

+slack send

要求名称必须包含slack,再按send关键词对候选排序。

4.3 正则搜索

notebook jupyter

对工具名称和描述做正则匹配,名称匹配权重(2)高于描述匹配(1),返回得分最高的前 5 个结果。

defsearch(self,query:str)->list[BaseTool]:try:regex=re.compile(query,re.IGNORECASE)exceptre.error:regex=re.compile(re.escape(query),re.IGNORECASE)scored=[]fortinself.tools:searchable=f"{t.name}{t.descriptionor''}"ifregex.search(searchable):scored.append((2ifregex.search(t.name)else1,t))scored.sort(key=lambdax:x[0],reverse=True)return[tfor_,tinscored][:MAX_RESULTS]

五、安全设计

5.1 Catalog Hash 防漂移

每次构建目录时计算所有工具 schema 的 SHA256 哈希:

@cached_propertydefhash(self)->str:canon=[{"name":t.name,"schema":convert_to_openai_function(t)}fortinsorted(self.tools,key=lambdat:t.name)]blob=json.dumps(canon,sort_keys=True,ensure_ascii=False,default=str)returnhashlib.sha256(blob.encode("utf-8")).hexdigest()[:16]

merge_promoted在合并时校验 hash——如果 MCP 服务器重启后工具列表发生了变化(名称相同但参数变了),旧的 promoted 记录自动失效,防止暴露已不存在或已变更的工具。

5.2 Fail-closed 策略

def_assemble_deferred(filtered_tools,*,enabled:bool):setup=build_deferred_tool_setup(filtered_tools,enabled=enabled)ifenabledandnotsetup.deferred_namesandany(_is_mcp_tool(t)fortinfiltered_tools):raiseRuntimeError("tool_search enabled and MCP tools survived policy filtering, ""but no deferred set was recovered — refusing to bind MCP schemas (fail-closed).")

如果配置启用了 tool_search 但目录构建失败(逻辑异常),系统拒绝启动而非退化为全量暴露。

5.3 每次最多 5 个

MAX_RESULTS = 5限制每次tool_search最多返回 5 个工具 schema,避免模型通过一次搜索获取全量 schema(绕过延迟加载的初衷)。


六、完整数据流

┌────────────────────────────────────────────────────────────────┐ │ Agent 构建阶段 │ │ │ │ 所有工具(config + builtin + MCP) │ │ ↓ 技能策略过滤 │ │ filtered_tools │ │ ↓ build_deferred_tool_setup() │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ DeferredToolSetup: │ │ │ │ deferred_names = {tool_a, tool_b, tool_c, ...} │ │ │ │ catalog_hash = "abc123..." │ │ │ │ tool_search_tool = <closure over DeferredToolCatalog> │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ final_tools = filtered_tools + [tool_search_tool] │ │ system_prompt 包含 <available-deferred-tools> 名称列表 │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 运行时 — 第 1 轮 │ │ │ │ DeferredToolFilterMiddleware: │ │ hidden = deferred_names - promoted = {tool_a, tool_b, ...} │ │ bind_tools → 只暴露非延迟工具 + tool_search │ │ │ │ 模型看到系统提示词中的工具名称列表 │ │ 模型决定需要 tool_a → 调用 tool_search("select:tool_a") │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ tool_search 执行 │ │ │ │ 1. catalog.search("select:tool_a") → 找到 tool_a 实例 │ │ 2. 序列化为 JSON Schema │ │ 3. Command(update={"promoted": {hash, ["tool_a"]}}) │ │ → 写入 ThreadState.promoted │ │ 4. 返回 ToolMessage(content=完整 schema JSON) │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ 运行时 — 第 2 轮 │ │ │ │ DeferredToolFilterMiddleware: │ │ hidden = deferred_names - {tool_a} = {tool_b, tool_c, ...} │ │ bind_tools → 暴露非延迟工具 + tool_search + tool_a │ │ │ │ 模型正常调用: tool_a(param1="xxx", param2="yyy") │ │ ToolNode 执行 → 返回结果 │ └────────────────────────────────────────────────────────────────┘

七、与业界方案的对比

方案工具发现方式适用场景
LangChain 标准全量 bind_tools工具数 < 15
CrewAI全量绑定 + 角色分工减少每个 Agent 可见工具多 Agent 协作
AutoGen全量注册工具数可控
MCP 协议tools/list一次性返回所有工具协议层面无按需发现
OpenAI function calling全量声明讨论过 pagination 但未公开实现
DeerFlow tool_search名称暴露 + 按需搜索获取 schemaMCP 工具数量大

DeerFlow 的 tool_search 方案在当前开源 Agent 框架中尚属独创。其本质类似操作系统的虚拟内存/页面置换——工具"存在但不加载",直到实际需要时才"换入"上下文窗口。


八、配置与启用

该机制通过config.yaml控制:

tool_search:enabled:true# 默认 false,渐进推广中

当前默认关闭(enabled: false),说明该特性仍在渐进推广阶段。对于 MCP 工具数量有限(< 15 个)的部署场景,全量绑定仍是更简单的选择。


下一篇预告

第三篇将深入分析 DeerFlow 的五个核心中间件:SandboxMiddleware(执行隔离)、SummarizationMiddleware(上下文管理)、MemoryMiddleware(长期记忆)、ClarificationMiddleware(澄清中断)、SafetyFinishReasonMiddleware(安全防护),探讨每个中间件的设计动机、实现机制和关键决策。

http://www.jsqmd.com/news/1010914/

相关文章:

  • 别再为不同部门网络不通发愁了!手把手教你用VLAN和三层交换机搞定企业多网段互通
  • novel-downloader:200+小说网站智能保存方案,打造永久个人数字图书馆
  • 重新定义Windows生态:APK安装器的颠覆性技术突破
  • 企业分支互联如何选?MPLS Hub-Spoke vs Full-Mesh,从成本、安全和运维角度一次讲清
  • 从FPA到NEON:一文理清ARM浮点与向量计算单元的演进与选型指南
  • 手把手教你排查USB麦克风或声卡的兼容性问题:是驱动、系统还是UAC协议版本在作怪?
  • 专业验金称重,合肥卖金安心首选 - 讯息早知道
  • 2026海东本地贵金属变现门店精选前五+黄金铂金白银金条回收合规商家名录 含地址电话 - 诚金汇钻回收公司
  • SAP FIORI实战:手把手教你用ICMR App搞定公司间对账(附详细操作截图)
  • 别再只会用Google了!这5个免费OSINT工具,帮你像黑客一样高效收集公开信息
  • 朝阳市漏水检测公司先进设备,消防管供暖管供水管网地埋管全方位测漏 - 同城资讯
  • AMD Ryzen处理器深度调校:SMUDebugTool完全解析
  • 还在用.NET 4.8?手把手教你迁移到.NET 8.0,性能提升和跨平台真香!
  • 从PyTorch转Rust?tch-rs、Candle、Burn、DFDX保姆级上手对比(附代码示例)
  • 别再死记硬背优化器公式了!用PyTorch代码实战SGD、Momentum、Adam,看完就会用
  • 别只当操作手册!深入解读SAP FIORI ICMR对账App的设计逻辑与业务价值
  • Anthropic协议直通架构:消除LLM服务胶水层实现延迟归零
  • 写文章10分钟_发平台1小时_用AI内容多平台适配把时间抢回来
  • 2026池州全城黄金回收口碑商户盘点 TOP铂金回收白银回收旧料回收门店电话地址一览 - 信誉隆金银铂奢回收
  • 2026臻选:上城区四季青疏通下水道 724 小时运维保障 居顺联家政疏通靠谱服务详解 - 居顺联家政疏通
  • 2026东营市民高频光顾的 5 家线下黄金回收白银铂金回收实体店实地走访测评 - 中安检金银铂钻回收
  • 2026河南本地贵金属变现门店精选前五+黄金铂金白银金条回收合规商家名录 含地址电话 - 诚金汇钻回收公司
  • 2026阜阳本地贵金属变现门店精选前五+黄金铂金白银金条回收合规商家名录 含地址电话 - 诚金汇钻回收公司
  • 2026实力之选:机床空调及机柜电柜电箱控制箱无冷水空调制造工厂深度解析 - 品牌发掘
  • CNN中Pooling层的本质:空间鲁棒性构建与实战避坑指南
  • Python Turtle 画生日蛋糕保姆级教程:从数学函数到动画效果的完整实现
  • SAP CK11N成本估算实战:BAPI与BDC两种自动化方案详解与避坑指南
  • 多平台发文最烦调格式_AI自动排版发布帮我搞定了
  • 大连瓦房店市专业房屋漏水检测,精准定位漏水点,快速解决各类渗漏难题-2026年大连房屋漏水检测推荐公司 - 同城资讯
  • 2026大理市民高频光顾的 5 家线下黄金回收白银铂金回收实体店实地走访测评 - 中安检金银铂钻回收