把数据库查询结果整坨塞回上下文,是我今年见过最多的 agent 性能事故。不是模型不行,是接口没设计好。工具该返回的是答案,不是数据——分页、裁剪、聚合,都是工具作者的责任,不是模型的。但这里有个反直觉的地方:裁太狠,模型会反复调同一个工具凑数据,账单反而更难看。这周有几个素材正好能佐证两边。
一个 538 分的 skill,说明问题在返回值
HN 上 i-have-ADHD 这个 skill 拿了 538 分。它干的事很简单:让 coding agent 别把答案埋在噪音里。
一个 skill 能有这个热度,本身就说明大家都被同一件事烦过。Agent 跑完,输出一大坨,你翻三屏才找到那句「测试挂了,因为 X」。我第一反应是「模型不够聪明」,后来想想不对——模型只是把工具给它的东西又原样倒了出来。
问题在返回值这一侧。
「答案」和「数据」是两回事
举个我自己踩过的:查订单状态。我第一版工具直接把 ORM 对象序列化返回,一个订单的 JSON 能刷好几屏,里面一大半字段这次根本用不上。模型拿到之后确实能答对,但它每次都在花上下文做我该在 SQL 里做的事。
改法很土:工具内只返回 status、eta、updated_at,再塞一个「还有更多」的提示字段。同一个问题,返回从刷屏变成几个字段。模型答得更快,也更准。
我的判断是:如果一个工具的返回值里,字段数超过模型这次任务真正需要的,那这个工具还没写完。分页也是同理——limit 和 offset 该由工具实现并暴露,而不是把一万行丢给模型让它自己数。
但裁太狠,会更贵
这是我想说的重点,也是很多人不愿意承认的。
模型调用是有往返成本的。你把返回值砍到只剩一个 ID,模型下一轮就得再调一次拿详情,再下一轮拿关联数据。几轮下来,省下的 token 远不够赔——因为每次调用都带着前面全部的上下文重新过一遍,延迟还是叠加的。
我见过更糟的:工具返回被截断,模型看不到完整列表,于是它换个参数再调一次,又截断,再调。循环到 max turns 为止。这不是省钱,这是烧钱。
所以裁剪的目标不是「最小」,是「一次够用」。这两者差很远。
那个阈值,得自己测
别人给你一个通用数字,你别直接用。上下文预算不一样,工具在 loop 里的位置不一样,这个数就不一样。
我自己的土办法是翻 trace:把每次工具返回的 token 数标出来,再看两件事。一是这个工具的返回有没有经常挤进上下文占比前几名;二是同一个工具在一次任务里被重复调了几次。前者高、后者也高,基本可以确定是裁太狠——模型在挤牙膏。前者高、后者低,说明模型拿到了但没怎么用,那是裁太松。
调的方向也简单:宁可让工具在服务端多花点时间做聚合,也别让模型多跑两轮。服务端的时间是线性的,模型往返是带着上下文一起涨的。
测试输出是重灾区
danluu 那篇 agentic testing 值得读,讲 agent 用测试和验证技术用得怎么样。读的时候我一直想到同一件事:跑一次测试动辄几千行输出,agent 真正关心的只有失败的用例和最后那行 summary。
这件事工具层完全能做。失败用例给完整堆栈,通过的折叠成一行计数。不需要模型更聪明,需要工具作者肯花点时间写个 formatter。
厂商在往哪个方向走
OpenAI Agents API 把工具定义、handoff 这些做成了一等公民,方向是对的:接口的形状该由框架和工具作者定,不该指望模型临场发挥。
Cognition 那边说 Devin 用 GPT-6 Astra 自测,目标是让工程师少 review 一些代码。这句话反过来读更有意思——现在大家要看的东西太多了。Perplexity 用 Astra 管端到端系统,检查频率比之前低了不少。模型少问,意味着每次问的时候拿到的信息得更完整。
这两条对工具返回的要求其实是相反的:一次给够,但别给多。听着矛盾,落到代码里就是前面那件事——工具得知道模型拿这个要回答什么问题。
按我现在看,返回值粒度是个产品决策,不是技术细节。写工具的时候先问一句「模型拿这个要干嘛」,大部分坑就避开了。也许半年后上下文便宜到可以不在乎,那这套就该推翻。但现在还不行。