loading

Loading

探索与发现

首页 AI 工程化

Agent 工具描述 token 膨胀:几百个 schema 先吃掉上下文

分类:AI 工程化
字数: (2466)
阅读: (4)
0
摘要:工具一多,光 JSON schema 就能吃掉半个上下文。这篇讲工具定义膨胀与结果污染的两笔账、按需检索与网关语义路由两条路的代价,以及为什么该先摸清固定成本再动手。

用工具调用的 Agent 做了一段时间,最早碰上的问题通常都是同一个:工具一多,模型变得越来越不听话。而且这和模型的强弱关系不大,把一本好好的问答助手接上三十几个内部接口,系统提示不变、问题不变,回答质量就是掉了。

多出来的内容其实是 JSON schema

在开启函数调用能力之后,每个工具都需要往请求里放一份描述:名称、用途、参数的 JSON schema 以及各字段的含义。Anthropic 对这个机制的总结很直接,把它叫作 Tool Definition Bloat(工具定义膨胀)。几十个以内的工具体会还不明显,上百甚至上千个时,光这些定义就可能占掉窗口的一大半。对话还没开始,模型的“记忆”已经被压缩了。

更隐蔽的一层是中段数据污染。ReAct 式的执行轨迹是一步调用 API、取得结果、完整反复送回模型,再由模型决定下一步是继续调用还是收尾。一次任务要是跨了十几个工具,中间返回的两批大体量查库结果都会驻留在上下文里,不管后面用不上点。这既花 token,也换来信息密度下降:每一轮决策模型都得在那几万 token 里反复抓相关片段,注意力被摊平,关键那几句藏在中间。

这两个问题叠加。工具定义是固量的开销、每轮都要重复,中间结果是流量的开销、随时间线性增长。线上任务一个回合几十轮的情况完全常见。

把工具拆成两步:先找再调

Anthropic 给出的思路不止一种,核心是不要让模型一开始看到全部。社区里有更激进的简化版,把接入方式做成两个工具:一个按语义查工具,返回候选的工具 ID;另一个按 ID 和执行参数真正执行。工具库涨到一千多个时,固定的常驻定义只剩这两条,放大到数量级也不再线性增加 token。

检索那一层就是常规的语义搜索。用轻量的句向量模型把各个工具的说明编码成向量,查询时用当前任务描述去寻求最相近的 Top-K。也有做得更重的:用文本大模型对着刚出现的任务具体重新想一遍该查什么。

这个方案好在实现简单,代价更要紧,我先把它说清楚。它实质上增加了大约一轮调用:先查工具描述、再拿参数调。TTFT(首 token 的到场响应时延)上升,可以接受的场景是长任务或者工具很分散的系统;但如果用户点一下就期待半秒内出首个字符,这个方案就不乘。工具描述本身也是有成本的,写得简略就用不准,如果把参数补齐了再带上几个示例,选最优最差的 topK 结果又占了本想要的节省空间。

网关级的语义工具路由

阿里在 AgentScope Java 加 Higress 组合里做成了另一种形态:语义检索不写在 Agent 进程里,而是放进 AI 网关。Agent 只拿一个端点,附带自己的任务描述;网关那一侧用嵌入向量招回候选工具,再可选地用重排模型精排,把 Top-K 工具子集物化输回。检索的开销跟 Agent 的展开和推理完全分开,向量库和维护留在平台侧。

他们在延迟上的公开量比较克制:采用 Weight 混合算法后,语义搜索整体控制在 350 毫秒以内,相比纯向量检索只多约 30 毫秒。命中上混比纯向量检索轻微领先。这个量怎么说:属于网关侧的一段固定开销,跟模型推理本身的关系不大,对大多数内部任务能受益。

另外一类附带好处是边界更好做。只要所有 MCP 工具统一备案到一个入口,一次检索只会下发本次授权范围内的工具范围;参数不变、不相关的工具到不了模型。

别先拆工具,先量预算

处理方式遇到执行时要考虑收益比率。优先做几步:

  • 盘点工具定义的固定成本。启动前把系统提示的全部工具 schema 按一个 token 近似算法大致集合算一遍,乘以每轮平均工具数。这笔开销是多少决定了问题严重性。
  • 按步骤观测账单。很多系统的 token 监控只有总数。一次轮花了几万,Prompt、记忆、工具调用与多轮推理各占多少,值得拆出来。没有阶段拆分,后面所有优化的收益你都归不了因。
  • 先做工具合并与裁剪。真正排一下每个工具的月调用次数,总会有一批看起来有什么实际没人用的“探坦”接口。这部分临时下掉减少得最直接。
  • 先按需启用(如 Skills 类的渐进加载)把不常用的工具收回到按需加载。前提是在确定的入口下把回调成可控的交互流程、先暴露两轮候选与路由后再出。

    这些做完仍是数字量级庞大再做语义路由。切的时候注意策略降沉问题:没送到模型的工具确实选不出来,不能因为已收严报给工具漏报来表功——多源层出现一批可根因子误路由的任务超时,比多省一半 token 更有破坏力。因此这类任务经常需要用固定,高频回退任务

    再作静态。与固定的提示通路,一部分直接写死在 Agent 的常用工具里,使用过程中稳定任务不走不通路。

    提示压缩为什么更难

    很多人反过来主张靠 LLM 自总结思考上下文压缩,这当然是可以。但对工具定义不太适用:压缩一次几千字得花一轮调用、几十秒,还可能把精确的参数名漂掉。描述里的字段说明——建议修改、避参说明没保住,模型就会乖乖把整段参数当例字段推理补全。压缩压缩自己的开销,压缩结束处的汇总也并不会让它消耗后突增。压缩可以做,但不应担信拆工具这种常态方案。工具体定义对要求精确匹配的文本,压缩自然会损失。

    先归这一点小差别:当聊上下文上下文处理中那个框架给出分层的理由是很轻松的语境覆盖——值都是描述清楚的问题。很多结构化的定义不变中冗余,参数定义只有实际文档里的参数定义才能精准匹配。

    所以归策略也不同:多步结果可以压缩,工具定义更适合精简和分层。常在一层用没两个总预算也当不了估算。

    最终上线前把两套,比监控并发时的 token 节均得回到数字层面:放个几百次回放的任务集,A/B 对比上下文步数与总 token、成败率。如果省钱 80% 但步就往往多花了两三是很多更正常的权衡结果。

    但前提是继续把评估做在一条接近真实的处理口径上——回放自己产的任务里,失败往往比首次请求就能失败的还多。”Agent 工具展开预算是算的工具描述一个动态构建的词汇流,先估好每个任务工具的真实长度,才好决定花在快速消耗项——把预算花在历史比不重要的地方,怎么耗。”

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-engineering/658.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图