4.2 工具选择与路由


4.2 工具选择与路由

本节摘要:工具给模型,谁来挑?默认答案是模型自选(function calling):schema 全量装入上下文,模型按任务自行决定调谁。它在工具少(经验上十来个以内,示意值)、任务开放时近乎完美;但工具一多、模型一小、成本一敏感,自选就会退化——选错率上升、L3 层 schema 税变重、小模型在几十个工具里晕头。此时在模型前面加一道路由器:或规则路由(确定性映射),或判断原语路由(约 100ms 的类型安全语义判断,详见《Jev 决策编程》第 6.2 节——它把"这个任务需要哪些工具"变成一次近乎免费的函数调用,LangChain 已将其做成 ModelRouterMiddleware 中间件)。工程上最常用的往往是两段式混合:路由器筛子集、模型在子集内精挑。本节给出三种方式的选择表与两段式的接法。

学习目标

阅读完本节,你应当能够:

  1. 说出模型自选的适用区与三个退化信号。
  2. 区分规则路由与判断原语路由的适用场景。
  3. 实现两段式混合路由并接入 assemble_contextdispatch
  4. 解释"路由是上下文工程问题"这句话。

一、默认方案:模型自选

function calling 的工作方式:上下文 L3 层装入全部工具 schema,模型在每步决策时输出"我要调哪个工具 + 什么参数"。它应该永远是你的第一选择,因为:

  • 模型是语境理解最强的组件——"任务进行到该跑测试了"这种意图级判断,规则写不出来;
  • 零额外工程:registry.schemas() 直接喂给循环;
  • 工具少时准确率很高(主流模型的 function calling 在小工具集上已高度可靠,官方)。

但自选有三个退化信号(经验阈值均为示意,应以自家回归集实测为准):

退化信号 表现 根因
选择准确率下降 错选近义工具(该 grep 去读了全文件) 工具一多,边界描述稀释
L3 税变重 几十个 schema 吃掉一至两成窗口 schema 是常驻成本
延迟与成本抬升 每步都在大清单上做选择 选择空间大,决策变贵

二、加一道筛子:路由器

路由器在模型之前回答一个问题:这个任务/这一步,需要哪些工具? 输出是一个小子集(或直接一个工具)。两种实现:

规则路由(确定性):任务类型 → 工具子集的静态映射。适合边界清晰的组织级场景:客服任务只挂工单类工具、代码任务只挂仓库类工具。零成本、可审计,但写不出语义判断("用户在骂人"这种)。

判断原语路由(语义、轻量):以 Jev 为代表的判断原语——读取程序状态、回答带类型的约束问题、返回类型安全答案 + 校准概率,单次约 100ms、近乎免费(官方自报)——让"该挂哪些工具"这个语义判断便宜到可以放进主循环热路径。典型用法:一个 Choice 问题("该任务属于哪类工作")或一个多选判断,映射到工具子集。LangChain 把这类用法做成了现成的 harness 中间件 ModelRouterMiddleware(官方):路由与护栏都成为可插拔标准件——这正是第 1.2 节"判断原语化"伏笔在工具层的兑现。原理与选型详见《Jev 决策编程》第 6.2 节(模型路由),本书不展开。

模型自选 规则路由 判断原语路由
判断力 最强(语境级) 无(静态映射) 中(语义级,可校准)
延迟/成本 已含在决策里 ≈0 ≈100ms、近零成本(官方自报)
可审计性 中(概率 + 类型答案)
适用 工具少、任务开放 任务类型可枚举 工具多、需语义分流

一条经验分界(示意,以自家实测修正):工具 15 个以内,自选就好;15~40 个,上两段式;40 个以上,路由是必选项,而且应按任务域把工具拆成多个"工具包"分别挂载。分界线也随模型强弱漂移:旗舰模型的舒适区更大,小模型要更早引入路由。

三、工程主流:两段式混合

真实的 harness 很少二选一,而是两段式:路由器筛子集,模型在子集内自选:

# routing.py —— 两段式工具路由(写法示意,以实际工程为准) def route_tools(task: str, all_tools: list[str]) -> list[str]: """第一段:筛子集。实现可为规则映射,也可为判断原语调用。""" if 规则可判(task): # 确定性优先 return RULE_MAP[classify_by_rule(task)] return primitive_route(task, all_tools) # 判断原语,约 100ms # primitive_route 详见《Jev 决策编程》第 6.2 节 def assemble_context(state, budget): # 接入 3.3 节的供料策略 subset = state.tool_subset or route_tools(state.task, ALL) msgs = [system_prompt(state), user_task(state), *registry.schemas(only=subset)] # L3 只装子集 ...

两段式的三个收益:L3 税从"全集"降到"子集";小模型在子集上选择准确率回升;安全面收窄——没挂载的工具连被误用的可能都没有(这同时是权限设计的纵深,第 5 章会再见到"不可用即最安全"原则)。

一个量级感受(示意数字,实际以你的工具表与回归集为准):40 个工具的 schema 全集约 6k tokens;路由到 8 个工具的子集约 1.2k tokens——省下的预算够装两三个中型文件,这就是"路由即供料"的直观账本。

⚠️ 路由错了怎么办?给路由器留逃生门:工具子集里永远保留一个 list_tools(列出全部工具并说明用途)与一个"申请换工具集"的说明——模型发现缺工具时可以显式请求扩集。没有逃生门的路由器,会把"筛掉噪音"变成"筛掉氧气"。

逃生门的实现成本极低(一个只读工具 + 一段提示词),却被大量自研 harness 遗漏——它是两段式方案里"信任但可验证"的那颗保险丝。

四、路由是上下文工程问题

把本章放回全景图会发现:工具路由表面是"选工具",实质是上下文组装的一部分——它决定的正是 L3 层装什么。由此两条推论:其一,路由策略应该住在 assemble_context 里(如上代码),而不是散在业务代码各处;其二,第 7 章的上下文预算管理天然涵盖 L3 税——工具一多的 harness,"路由 + 压缩"是同一格(供料)里的两只手。

💡 检验你的路由是否"住对了地方":如果换一套路由策略需要改多处业务代码,说明它散了;正确的位置是 assemble_context 这一个入口——换策略只改一处,且每次路由决策都能被第 9 章的日志记录下来。

本节要点回顾

  1. 模型自选是默认答案,三个退化信号(准确率、L3 税、延迟)提示该加路由。
  2. 路由器两种:规则(确定性、可审计、无语义)与判断原语(约 100ms 语义判断,详见《Jev 决策编程》第 6.2 节;LangChain 已有 ModelRouterMiddleware 中间件)。
  3. 两段式混合是主流:路由筛子集 + 模型子集内自选;务必留逃生门(list_tools / 申请扩集)。
  4. 路由属于供料:它是 L3 层的组装策略,应住在 assemble_context 里。

工具会被选中、被执行了——但执行不会总成功。错误消息发出后模型看到什么,决定它下一步是自救还是螺旋坠毁。下一节:错误反馈设计。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U