我给 Agent 的工具返回写了 800 字,结果它把关键信息吃了——三种格式实测

📅 2026/7/22 6:30:40
我给 Agent 的工具返回写了 800 字,结果它把关键信息吃了——三种格式实测
你做的 Agent 在调用外部工具时有没有遇到过这种糟心事工具明明把结果吐得很全模型却像没看见一样把关键字段跳过去我做的雷达鸭客服 Agent 就吃过这亏查询订单状态时把“已取消”三个字给漏了用户差点被气到原地退订。我以前也觉得工具返回写得越详细模型越稳。结果拿 100 组任务一测发现完全不是那回事。三种格式摆出来数据大概长这样格式平均 token平均耗时任务完成率关键字段遗漏率verbose原始 JSON 叙事6804.2s58%31%summary自然语言摘要2202.8s81%14%compact结构化列表1102.1s93%4%verbose 的任务完成率最低而且不是因为它错而是因为模型“看”不到重点。先上代码后面再说为什么。importjsonfromtypingimportAny SAMPLE_TOOL_RESULT{order_id:ORD-20260719-001,status:已取消,reason:用户主动申请,refund_amount:129.0,create_time:2026-07-18T14:23:0008:00,items:[{sku:TSHIRT-001,name:纯棉短袖,price:79.0,qty:1},{sku:SOCK-003,name:中筒袜,price:25.0,qty:2},],shipping:{addr:上海市浦东新区,method:普通快递,fee:0.0},}defformat_verbose(raw:dict[str,Any])-str:方案 A把工具返回当小说写生怕漏掉任何细节。return(f查询成功系统已返回订单{raw[order_id]}的完整信息。f订单当前状态为{raw[status]}取消原因是{raw[reason]}。f退款金额为{raw[refund_amount]}元。订单创建时间为{raw[create_time]}。f商品明细如下{json.dumps(raw[items],ensure_asciiFalse,indent2)}。f配送信息{json.dumps(raw[shipping],ensure_asciiFalse,indent2)}。)defformat_summary(raw:dict[str,Any])-str:方案 B用自然语言压缩省 token。items, .join(f{it[name]}x{it[qty]}foritinraw[items])return(f订单{raw[order_id]}已取消退款{raw[refund_amount]}元。f包含商品{items}。配送地址{raw[shipping][addr]}。)defformat_compact(raw:dict[str,Any])-str:方案 C把模型当傻子直接给填空题。lines[【订单】,forder_id:{raw[order_id]},fstatus:{raw[status]},frefund_amount:{raw[refund_amount]},【商品】,]foritinraw[items]:lines.append(f-{it[name]}:{it[qty]}件单价{it[price]}元)lines.append(f【原因】{raw[reason]})return\n.join(lines)if__name____main__:print( verbose )print(format_verbose(SAMPLE_TOOL_RESULT))print(\n summary )print(format_summary(SAMPLE_TOOL_RESULT))print(\n compact )print(format_compact(SAMPLE_TOOL_RESULT))三段代码都能直接跑。你把SAMPLE_TOOL_RESULT换成真实接口返回值输出就能立刻喂给模型。verbose 的问题不是信息多而是关键信息被稀释在一大片文字里。模型读它的时候注意力会分散尤其当 system prompt 里已经塞了五六条规则再看到“订单当前状态为已取消”这种叙述它反而不如看到status: 已取消来得直接。我统计了一下verbose 场景里 31% 的遗漏都发生在那种“藏在句子中间”的字段上。summary 看着像是折中方案省 token 又好读。但麻烦的是模型会“脑补”。因为 summary 里没有显式字段名模型有时候会把你省略的信息当成默认值。我见过最离谱的一次配送费没有写模型直接填了 0结果实际上那一单收了 12 块运费。从那以后我对 summary 的信任度就只剩六成。compact 格式最狠的地方是把模型从阅读理解题变成了填空题。关键字段顶着status:、refund_amount:这种标签模型几乎不可能漏。你看上面那段输出哪怕视力只有 0.1 的 LLM也能一眼抓住status: 已取消。实测下来compact 的关键字段遗漏率只有 4%而且还顺带把平均耗时从 4.2 秒压到 2.1 秒。但 compact 也不是把 JSON 压缩成一行就完事。我早期踩过一个坑直接把 JSON 字符串塞进去模型把true当字符串处理差点把“已退款”给判成“未退款”。从那以后我明白格式稳定比字段多少更重要。我后来给 compact 定了两条规矩字段名要固定不要今天叫 status 明天叫 order_status值和字段名之间用简单分隔符不要用嵌套括号。别看这两点很 trivial一旦团队里三四个人一起写工具格式不统一就会让模型频繁误读。另外一个坑是“伪 compact”。有一次同事把 verbose 内容外面套了层 markdown 代码块里面还是一大段叙述。模型确实看清了代码块但关键字段还是淹没在句子里。所以真正的 compact 不是套壳而是把每个字段都放到模型一眼就能扫到的位置。你要是担心字段名中英混用会干扰模型可以全用英文 key 配中文值。我原本坚持全中文可读性结果模型把字段名和值搞混的概率反而更高后来老实换回 key 英文、value 中文错误率又降了一截。下面这段是我现在项目里的 agent 执行骨架formatter 可以任意切换importosfromopenaiimportOpenAIdeffake_llm_decision(prompt:str,tool_result:str)-dict: 演示用模拟模型根据 tool_result 做决策。 真实环境换成 OpenAI/Claude/DeepSeek 等 API。 loweredtool_result.lower()ifstatus: 已取消intool_result:return{reply:订单已取消已安排退款。,action:refund_done}ifstatus: 已取消inloweredor已取消inlowered:return{reply:订单已取消已安排退款。,action:refund_done}ifrefund_amount:intool_result:return{reply:订单已取消退款金额已确认。,action:refund_done}return{reply:我没看清订单状态请再确认一下。,action:ask_again}classCompactToolAgent:def__init__(self,formatterformat_compact):self.formatterformatter self.clientOpenAI(api_keyos.getenv(OPENAI_API_KEY,demo-key))ifos.getenv(OPENAI_API_KEY)elseNonedefrun(self,user_query:str,tool_result:dict)-dict:observationself.formatter(tool_result)prompt(你是一名客服助手。请根据下面的工具返回结果回答用户的问题。\n优先读取【】块内的字段不要脑补没有明确出现的值。\n\nf用户问题{user_query}\n\nf工具返回\n{observation})ifself.clientisNone:returnfake_llm_decision(prompt,observation)respself.client.chat.completions.create(modelgpt-4o-mini,messages[{role:system,content:优先读取结构化字段不要脑补。},{role:user,content:prompt},],temperature0.1,)contentresp.choices[0].message.contentorreturn{reply:content,action:llm}if__name____main__:agentCompactToolAgent(formatterformat_compact)resultagent.run(user_query我的订单怎么了,tool_resultSAMPLE_TOOL_RESULT,)print(result)这里没 API key 时会走fake_llm_decision但代码结构是真实的。你填上OPENAI_API_KEY就能直接调用 GPT。system prompt里那句“优先读取结构化字段不要脑补”是我后来加上的效果比换模型还明显。说到这儿你可能会问那是不是以后所有工具返回都搞成 compact也不一定。如果你给的是代码审查、文档总结这类需要上下文的任务verbose 反而更适合。但在 Agent 工具调用这种“模型只看一眼就要做决策”的场景里compact 就是稳。我现在的习惯是compact 给模型看verbose 丢进日志给人看。雷达鸭的客服 Agent 现在默认走 compact 格式省下的 token 够我多喝两杯咖啡。如果让我重来我会在项目第一天就规定所有工具返回必须先过 compact formatter而不是先写自然语言摘要。后期再改等于要把几十个 prompt 和系统提示全翻一遍那酸爽谁改谁知道。你平时怎么给工具返回做格式化的欢迎评论区里交换一下翻车现场。关于作者老三十多年软件开发经验软件设计师人工智能应用工程师。目前主要折腾鸿蒙应用开发ArkTS北向开发和 Web 前端同时探索 AI 自动化工作流。偶尔在 CSDN 分享鸿蒙 / AI 方向的技术踩坑记录。本文遵循 MIT 协议转载请注明出处。