跳到主要内容

主动工具发现与动态加载

检索式预筛选的内在局限是什么?​

  • 局限: 它按用户的初始查询做一次性匹配,任务开始时无法预见所有需求;
  • 反例: “Debug the file”这类看似简单的请求,实际可能牵出文件访问、代码分析、命令执行等多步骤、跨领域的工具链;

主动工具发现的机制是什么?​

  • 思路: 让 Agent 从被动接受者变为主动发现者——在执行过程中意识到能力缺口时,主动用自然语言声明“我需要什么能力”,系统再动态匹配并注入;
  • 代表工作 MCP-Zero: 系统提示词中不预置任何工具 schema,Agent 在思考中生成结构化请求块(如“GitHub 服务器:搜索仓库并返回元数据”);
  • 匹配方式: 通过服务器级 → 工具级的两层语义路由,从数千候选中匹配注入;
  • 数据: 论文报告在约 2800 个工具上比全量注入节省约 98% 的 token;
  • 工程等价实现: 系统提示词里只保留少数基础工具(web search、code interpreter)外加一个“工具搜索工具”,Agent 用自然语言描述需求即可检索并加载,Anthropic 在 Claude API 中提供的 Tool Search Tool 即属此类;
  • 共同点: 都是“Agent 声明缺口、系统按需注入”;

层次化匹配与降级如何设计?​

  • 分组依据: 工具在 MCP 等协议中按服务器分组(类似手机上的 App,每个 App 提供一组相关功能),于是匹配可分两层——先按能力描述定位相关服务器,再在服务器内匹配具体工具;
  • 收益: 搜索空间从“数千个工具”缩小为“数十个服务器 × 每个服务器数十个工具”,既省算力也减少跨领域的语义混淆;
  • 依赖: 一个离线构建、支持增量更新的嵌入索引;
  • 降级: 若两层匹配的候选相似度都低于阈值,应明确返回“未找到”,让 Agent 改写需求重试、用基础工具手工实现,或干脆创造一个新工具;

动态加载如何避免破坏 KV Cache?​

  • 问题: 若把全部工具定义放进静态前缀,每加载一个新工具就会使整段缓存失效;
  • 破解: 把新工具的完整 schema 追加到上下文末尾,静态前缀保持稳定,schema 此后固定在轨迹的原位置,作为普通历史消息继续命中缓存;
  • 状态栏: 只维护一份简短的工具名列表;
  • API 原生支持: OpenAI 的 tool_search 与 defer_loading、Anthropic 的 tool_reference、Codex CLI 默认开启的 tool_search;
  • 缓存保证: OpenAI 要求后续请求保持 tool_search_output 项的原位置,同一工具无需重复加载;Anthropic 在会话历史的原位置内联展开 tool_reference block,官方文档明确表示后续每一轮都能保持缓存命中;
  • 上下文全貌: 静态前缀只保留系统提示词、核心工具与工具搜索元工具,历次发现的工具 schema 散落在轨迹各处,并固定在首次注入的位置;
  • 代价: “工具定义必须在上下文最前面”不再是铁律,模型必须在后训练中学会理解散落在上下文各处的工具定义;

哪些情况会导致缓存重算?​

  • Prompt Cache 的 TTL 过期: 整段前缀一起重算,并非工具定义特有的代价;
  • 工具集变动: 修改、移除或重排已加载的工具集时,缓存从变动点起失效;

小参数量模型的实验结论是什么?​

  • 设置: Qwen3-4B 访问一个含 120+ 工具的 MCP 服务器,任务是跨领域工具协作(查询股价并搜索新闻、搜索并下载 arXiv 论文、分析 GitHub 贡献者并生成报告);
  • 对照组: 120+ 工具的完整 schema 一次性注入 system prompt(超 50K tokens),4B 模型在如此长的上下文下指令遵循能力严重退化——可能错选通用 Web Search 而非专门的 Yahoo Finance 工具,或“忘记”工具列表中某些工具导致任务失败;
  • 实验组: system prompt 仅保留 web_search、code_interpreter 和 discover_tools 元工具;discover_tools 接受自然语言需求(如“我需要查询股票价格的能力”),用嵌入向量相似度返回 3-5 个候选工具及完整 schema,新工具定义追加到对话历史并更新状态栏的工具名列表;
  • 结论: 主动工具发现不仅帮助能力较强的大模型应对成千上万工具的场景,更让小参数量模型在上百工具的场景下保持可用,准确率与任务完成率显著提升;