ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

工具提示词压缩91%:Pi Agent 扩展优化实战指南

工具提示词压缩91%:Pi Agent 扩展优化实战指南 先说明白一件事我这个月把 Pi Agent 项目里十几个扩展工具的提示词从平均 2800 字符左右压到了 250 字符上下总降幅 91%工具调用准确率不但没掉反而还稳了一点。这个数字不是一个伪需求算出来的安慰值而是先把工具提示词里“没用的东西”全删掉之后自然得到的结果。这篇文章就是把这套方法完整拆给你看无论你是 Pi Agent 的普通用户还是打算给 Pi Agent 写扩展的作者都能直接照着做。先说清楚边界。我聊的“工具提示词”指的是你在扩展定义里告诉 Agent“这个工具是干什么的、有什么参数、什么时候该调用它”的那段文本不包含系统提示词和人类会话上下文。Pi Agent 这类 Coding Agent 在编排工具时会把所有工具的提示词塞进同一个上下文窗口里所以工具提示词往往被低估了它不是“反正模型能读”的摆设而是每一轮任务都真实占用 token、影响注意力分配、决定工具调用成功率的关键输入。\n## 1. 先搞清楚工具提示词到底大在哪里1.1 解剖一个典型的工具提示词我拆过不少团队分享出来的扩展定义发现大家的工具提示词超标方式几乎一模一样。给你看一个“看起来很认真”的典型模板工具名称send_email 工具描述向指定收件人发送一封电子邮件。该工具会先检查收件人地址是否符合电子邮件格式规范 如果格式不正确会返回错误信息。邮件发送采用 SMTP 协议默认支持附件上传附件大小不能超过 10MB。 发送成功后返回 true失败时返回 false 并附带具体错误码。当用户需要联系某人、通知团队、 发送报告或者回复邮件的时候可以调用此工具。邮件内容支持纯文本和 HTML 两种格式。 注意请确保收件人字段不能为空白如果用户没有明确指定主题请使用默认主题 发送前请检查网络连接是否正常…… 参数 - tostring必填收件人地址 - subjectstring可选邮件主题 - bodystring可选邮件正文 - ccarray可选抄送人列表 - attach_pathsarray可选附件路径数组这个工具描述大概 280 个汉字换算成 token 在 400 以上。如果项目里有 15 个这样的工具光工具提示词就吃掉 6000 token。这还只是“静态开销”每次请求都要带着不管这次任务跟发邮件有没有关系。1.2 谁在用 token 换空气可能有人觉得模型上下文窗口都到几十万 token 了几千 token 算什么。但问题不在“装不装得下”而在“装进去了会不会干扰”。第一工具的静态提示词是每一轮都进上下文的。你聊十轮天气发邮件工具的描述也会被模型读十遍。这不是存储问题是每次请求的实际计费问题。第二提示词越长关键信息越容易被稀释。模型在决定“该调用哪个工具”时靠的是对整个工具列表的语义理解。工具描述写得越长干扰项越多模型反而更容易在自己脑补的“丰富度”里挑错工具。我在实践里见过最典型的翻车一个工具描述里写了“通知”两个字另一个工具描述里写了“发消息”三个字模型在两个相似描述之间犹豫半天最终选错了。长描述不会让模型更聪明只会让它更困惑。第三过度描述会吃掉“可扩展性”。Pi Agent 生态里一个用户可能同时加载官方工具、社区扩展和自己写的私有工具。每个作者都想把自己的工具描述得功能全一点结果合在一起就是一场 token 超载。到最后用户不得不在“少装几个工具”和“多付几十倍 token”之间做选择。这个局面不是用户的错是扩展作者没把提示词当资源来管理。1.3 91% 是怎么算出来的在我的项目里12 个工具包括文件读写、代码搜索、Git 操作、HTTP 请求、数据库查询等原来的提示词加起来是 33680 字符经过去掉冗余、改成结构化签名、砍散文之后变成了 2900 字符左右。33680 里面去掉重复的“请确保”“注意”“如果用户没有明确”这类无效表达再去掉参数说明里被 JSON Schema 重复覆盖的信息最后剩下来的就是 2900。33680 减到 2900节省了 30780 字符占比约 91.3%。这不是我发明的极端案例我后面会完整展示一个工具的改造过程你可以自己拿手里的工具量一下大概率也能砍到这个幅度。\n## 2. 精简三原则名字、签名、约束2.1 名称里带上“意图”和“对象”工具名称是模型首先看到、也最容易记住的信息。可惜多数扩展作者把工具名当编程接口取名起得像内部函数file_operations_1、http_client、exec_command一个比一个抽象。在 Pi Agent 这种对自然语言理解的场景里工具名应该更像“动词短语”git_commit_all比commit_operation好search_code_in_repo比repo_search_utility好。名称里同时包含“动作”和“对象”模型在快速扫描工具列表时能秒懂用途根本不需要读描述。我这里有个补全建议如果工具名本身已经表达清楚了描述甚至可以写成“名称的无害重复”——比如工具名send_email描述只写“发送邮件”把其余所有细节全部交给参数约束。别小看这个改动光是名称写清楚就能把 60% 以上工具的提示词整体砍掉一半。2.2 参数用类型和约束说话不要用散文大多数工具提示词超标的第二个来源是作者不信任参数定义非要在描述里把所有参数再解释一遍。这完全多余因为在 Pi Agent 这类 Agent 框架里参数的“类型、默认值、必填性、枚举范围”本来就在参数定义里而且参数定义是结构化的、能被框架直接用来做校验和提示。你在散文里再写一遍只是给模型增加两段互相印证、偶尔还互相打架的文本。对比一下两种方式。先说“散文式”参数 - tostring必填收件人地址不能为空必须是有效的电子邮箱格式 - subjectstring可选邮件主题如不传则使用默认主题 - bodystring可选邮件正文内容再说“约束式”参数 - to: string必填格式: email - subject: string - body: string第二种写法把“格式校验”“是否可选”交给了结构化约束模型看到的是精确信息框架执行校验也更可靠。散文里的“不能为空”“必须是有效格式”一遍遍重复除了把上下文撑爆没有别的用处。这不是我拍脑袋的建议是多次对比测试后验证过的结论同样一个发邮件工具散文式参数描述需要 150 字符约束式只需要 40 字符而且约束式的参数解析失败率更低。2.3 描述只写“什么时候用”不写“怎么用”这是一个特别容易踩的误区。很多扩展作者写描述时像是在给实习生写操作手册工具内部怎么连接数据库、失败时重试几次、网络断了怎么办全写进去。模型并不需要这些。模型只需要知道“什么场景该触发这个工具”剩下的执行细节交给程序代码。描述里真正有信息量的是触发场景比如“当用户提到发邮件、联系某人、发送通知时使用”“当用户要求搜索仓库代码、查找函数定义时使用”“当用户需要查看当前工作目录下的文件列表时使用”而下面这些内容基本上是纯噪音“该工具基于 SMTP 协议连接超时时间为 30 秒”“发送失败时会自动重试三次”“如果收件人为空会返回错误码 40001”“请注意保持网络稳定”这些细节里如果对用户有提示价值应该写进工具返回的错误信息里而不是提前塞给模型。模型调用这个工具得出错信息自然会把问题反馈给用户。你要是在描述里就把所有内部逻辑写完相当于把一本菜单抄送给顾客顾客只会更饿不会更聪明。2.4 保留一条“不能做清单”就够了有些工具确实有明确的禁用场景。比如一个“删除文件”的工具如果直接给模型用很容易误删关键目录。这种风险控制信息必须保留但要用最精简的方式写。我推荐用一条“约束”字段黑白分明地写清楚不展开、不解释。示例描述删除指定路径的文件。 约束禁止删除.git目录、node_modules目录以及路径中包含“backup”的文件。这样写模型一眼就能识别红线。不要在约束里写“请注意”“建议不要”“尽量不”模型对模糊约束的执行力非常差——你在约束里用了“尽量”模型就会认为可以偶尔不遵守。把红线写成断言式用“禁止、必须、只能”这类强约束词效果要好一个数量级。\n## 3. 动手改造从 2800 字压到 250 字的全程实录3.1 改造前的工具定义节选一个我真实改造过的“Git 提交并推送”工具。改造前它的定义长这样工具sync_git_changes 描述对当前仓库执行 Git 提交并推送到远程。此工具会自动检测当前分支 先执行 git add -A 将所有变更加入暂存区然后执行 git commit 生成一个提交记录 最后执行 git push 推送到远程分支。如果当前分支没有对应的远程分支工具会自动创建并设置上游。 如果提交信息为空工具会返回错误提示要求用户补充提交信息。工具支持 push 标志位控制是否推送。 当用户说“提交代码”“推代码”“提交并推送”“保存代码”时可以调用此工具。 参数 - messagestring必填提交信息。提交信息应当清晰描述本次提交的目的。 - pushboolean可选是否在执行提交后推送到远程默认值为 true。 - filesarray可选需要提交的文件列表。如果为空提交全部变更。这段定义接近 290 个汉字。看着信息量挺大但真正对模型决策有用的信息只有两段“用户说提交/推送代码时调用”和“参数是什么”。其余关于 git add、git commit、自动建上游、失败返回错误提示都属于实现细节模型知道了也不改变调用结果。3.2 改造后的工具定义同一个工具遵循上面三条原则重写后变成name: git_submit(message: string, push: booleantrue, files: string[]) description: 提交并推送代码改动。当用户说“提交/推代码/保存代码”时使用。 restriction: 禁止在 message 为空时执行提交禁止覆盖他人未保存的改动。300 字符?没有。我算一下加了 name 和参数签名大概 110 字符description 22 个字符restriction 30 个字符。总共不到 170 字符比原文的 290 汉字约 400 字符少了 60% 以上。如果项目里每个工具都这么压缩整体 91% 是完全可以做到的。有人会问那你把git add -A和自动建上游这些逻辑都删了模型还知道工具会干什么吗答案是不需要知道。这些逻辑在执行代码里工具被调用后自然会执行正确的操作。我们要相信工具实现是可靠的提示词只负责“让模型在正确的时机调用正确的工具”剩下的是代码的责任。把提示词当成代码的说明书来写你就永远省不下来把提示词当成“模型的搜索引擎摘要”来写你立刻会发现能省一大半。3.3 给 Pi Agent 用户的配置清单作为 Pi Agent 的普通用户你未必写扩展但你大概率会在配置里引入别人的扩展。此时你可以做三件事加载扩展后先看一下它的工具清单凡是描述超过 200 个汉字且内容大量是“How to 实现”的果断给它换一个替代品或自己写个精简版。自己写自定义工具配置时强迫自己遵守“三行规则”工具声明占一行描述占一行约束占一行。超过三行说明你在写说明书不是提示词。在每个新扩展进入正式工作流之前先用一轮带 token 统计的对话测试它的工具提示词占用比如 Pi Agent 这类工具的会话详情里都会有 token 明细。如果发现某个扩展的工具提示词占到总上下文的 20% 以上你可以考虑是不是它定位过宽频繁加载却很少用到。用户能改扩展文件的就尽量改改不了就用“别名覆盖”的方式自己写一份精简配置把原始描述覆盖掉。很多框架支持运行时覆盖工具描述我之前在一个项目里就是用这种办法把第三方扩展的工具提示词整体压掉了 70%效果立竿见影。3.4 给扩展作者的发布前自检清单作为扩展作者我可以给你一份比“三行规则”更细的发布前自查清单。每条都来自我真实踩过的坑你的每个工具能不能用“动词对象”的格式重新命名如果能把描述里第一句话删掉因为名称已经表达清楚了。参数是否全部走了结构化 schema如果工具支持 JSON Schema 或等价格式请坚决使用不要再用自然语言重复参数类型。参数里的“必填”“可选”“枚举值”交给 schema 管。描述里有没有“注意事项”段落有就删。工具的注意事项应该出现在错误信息里你已经没法控制它了至少别让它占上下文。描述里有没有“工具内部实现”段落有就删。实现是你代码的事模型的提示词不需要知道。有没有为了“显得专业”而写的套话比如“本工具用于高效地处理……为……提供支持”“在……场景下具有良好的表现”。这类话在博客里是好的在提示词里是垃圾直接删。你在描述里写的所有触发场景能不能用“当……说/要求/提到……”格式压缩成一句能就不再改动不能就把场景拆分或者重新组织。检查完这六条你再重读一遍几乎重写的工具定义。如果还能找到半句“请确保”“如果用户没有明确”之类的表述那说明还没删干净。再删一轮。\n## 4. 验证与量化怎么知道自己真的省了 91%4.1 用 token 统计说话不要用感觉我改造完成后并不急着上线先做了一轮量化对比。方法很简单我把旧版工具配置和新版工具配置分别写进一个只跑“空任务”的会话里——就是不管用户说什么反正让 Agent 先加载所有工具然后做一个最简单的回复——然后对比两次会话的 token 消耗。在这个基准测试里旧版配置一次请求吃掉约 4200 token 的工具提示词新版只有约 380 token。380/4200 约等于 9%所以节省了 91%。这个数字不是玄学是一次实际请求里可以被复现的差异。工具列表越复杂省得越多——因为省的是“静态上下文”与你跑多少次任务无关每次反正都在省。建议你也把这个基准测试放在任何一次扩展优化后做一次。不引入统计、全靠“感觉轻量多了”的优化方案常常只是觉得变量少、实际上并没有少。4.2 用意图识别回归测试验证效果光省 token 不够还得确认模型的工具选择能力没被打折。我每次压缩完工具提示词都会跑一组固定的意图测试用例覆盖每个工具的主要触发场景。我的测试集长这样简化版场景 A“帮我把现在的代码改一下然后提交到远程”——应该命中 Git 工具场景 B“看看当前项目里有没有哪里用到 setState”——应该命中代码搜索工具场景 C“给小明发一封邮件说今晚不加班了”——应该命中发邮件工具场景 D“这份日志文件太大了删掉吧”——应该命中文件删除工具每一轮测试记录“命中工具是否正确、参数是否填写正确”。在压缩前和压缩后各跑一遍对比结果。我在自己的项目里做了 50 个用例压缩前准确率约 84%压缩后反而到 89%——因为描述更精简模型在工具选择上的注意力更集中了猜错的概率反而下降。尤其注意一个问题压缩之后如果某个工具的命中率下降大概率不是你描述写得太短而是你的触发场景没写对。模型不是靠读懂你的工具“能干什么”来选择工具的而是靠“用户需求”与“你写的触发场景”之间的语义匹配。这就是为什么我反复强调描述部分你要写“什么时候用”而不是“怎么实现”。4.3 验证过程中不能丢的“不变量”压缩工具提示词时有三样东西无论如何不能动否则你的优化就变成纯粹的自嗨禁止条件不能丢。工具在哪些场景必须拒绝调用这种“限制信息”是唯一值得用额外字符去写的元信息。丢了它模型可能在没有权限时照样调用工具轻则报错重则造成破坏。参数必填信息不能丢。必填参数和可选参数的区别必须在约束里明确表达。你可以不写“必填”两个字但参数 schema 里那个required标记不能少。返回格式的关键说明不能丢。如果工具返回的数据对后续任务至关重要比如返回一个文件路径、一个错误码、一个分析结果你要给一句极其简短的话说明返回值怎么读。不过大多数工具返回值自解释性很强只有复杂返回结构才需要这一条。这三个“不变量”也是我拆解别人扩展时最先检查的三处。只要这三处信息还在其余部分随便精简。如果这三处信息本身不完整那不是提示词太长的问题而是工具设计本身就有缺陷。\n## 5. 常见问题与排查实录5.1 描述太短模型开始乱猜怎么办有读者看了精简原则后把描述删到只剩一个工具名然后跑测试发现模型开始在一些无关场景乱调用这个工具。这不是“描述太短”的问题而是“触发场景完全缺失”的问题。工具名写得再好也顶多表达“我是干什么的”表达不了“用户哪句话跟你有关系”。举例说工具叫list_files模型知道它能列文件但当用户说“帮我看看这个项目里有哪些文档”时模型可能觉得“文档”跟“文件”相关就调了它其实用户想要的是search_by_extension或find_docs。这种歧义只有在描述里写“当用户提到 xx 时使用”才能消除。所以描述最短的底线是“触发场景”可以没有“实现细节”不能没有“触发词”。我自己的实践底线是工具名一句触发场景一行约束。低于这个信息量就该开始补所谓补也不是把删掉的散文找回来而是精修触发场景的表达用更多触发词覆盖更多用户表达方式。5.2 场景相似的工具互相打架“搜索代码”和“读取文件”两个工具在模型眼里可能经常分不清。用户说“看看这段代码的实现”时模型可能一会儿调搜索一会儿调读取。这不是一个描述就能解决的更像工具边界设计问题。我的处理方式有两种。第一种是“给老工具加职责禁区”比如搜索工具的描述里写“只用于定位代码位置不返回文件全文”读取工具的描述里写“当已知文件路径时直接使用不用于模糊搜索”。这样两句话就把分工画清楚了。第二种是“在描述里明确说不是干什么的”比如search_code的描述写“查找符号或文本位置不负责读取文件内容”一句话让模型避开和read_file的重叠。这两种处理方式都必须简短本质是花极少字符给模型画清工具边界。你花 200 字详细解释搜索算法模型还是分不清边界花 20 字写“不负责什么”边界立刻清晰。5.3 参数校验失败后的信息要闭环压缩提示词之后我发现一个之前没注意的问题参数提取错误率略有上升尤其是把参数类型写复杂之后模型偶尔会把字符串传成数组。这时候工具代码的“错误返回信息”就变得特别关键。最佳实践是参数校验失败的返回信息里不仅要写“哪个参数错了”还要写“正确格式的示例”。举个例子错误参数 files 格式不正确期望 string[]。 示例[src/main.py, src/utils.py]这段错误信息不占提示词空间它在工具调用失败后才生成但效果立竿见影——模型拿到错误信息后能立刻纠正自己的参数提取。这也是我认为“提示词精简不影响模型表现”的底气之一很多纠错能力不在工具提示词里而在工具执行时的实时反馈闭环里。5.4 工具更新后旧配置可能悄悄变慢极容易踩的坑扩展作者更新了工具实现工具描述或参数 schema 变了但用户本地还留着旧配置两套描述合并后出现了冗余模型又开始犹豫不决。我见过一个团队升级扩展后忘了清理旧配置工具提示词多出整整一倍的重复项模型命中率反而跌了 12 个百分点。所以如果你是扩展作者工具定义改动时最好对旧版描述里的关键信息做“兼容性处理”——要么快速废弃要么明确标注如果你是用户升级扩展后第一时间跑一下意图回归测试确认没有残留描述。这个问题的本质是“提示词是配置的一部分它有生命周期不跟踪生命周期就会腐烂”。5.5 三种工具再省也不能砍我这套方法不适用所有工具切记。有三类工具的提示词要主动加厚不要硬压缩高风险操作工具比如删除、批量修改、生产环境变更这类工具的约束必须写得极其明确宁可多用几十个字符把红线画死也不能为了省 token 而让模型在“可以/不可以”之间猜测。返回结构复杂的工具工具返回的数据如果包含多层嵌套结构且模型后续还要基于返回值继续推理那返回值格式说明必须保留。否则模型拿到一坨 JSON 不知道怎么用不如调用失败。多种调用模式的工具同一个工具兼了“查询”“创建”“更新”三种模式参数里用一个 mode 字段区分时每个模式的触发条件必须写明。这种工具提示词天然比单一模式长压缩空间的重点应该放在参数描述上而不是砍模式说明。识别出这三类工具之后再套前面的压缩公式就安全了。把这三类除外剩下那些“查一下、列一下、发一下”的量级工具大胆压缩完全没问题。\n6. 总结与补充我的个人体会好其实我上面该讲的都讲完了但既然写到这了最后再补充几个操作层面的小技巧。一是工具提示词的定义要跟着 Pi Agent 的实际版本走。不同版本的 Agent 对工具描述字段的支持力度不一样有的支持单独的限制字段有的支持参数描述直接做类型标注有的工具名甚至可以直接用自然语言字符串而不是驼峰命名。每次升级框架后重新看一眼工具定义可能会有新的压缩空间。二是关于“91%”这件事我不建议把它当成一个必须达到的硬指标。不同项目场景差异太大有的扩展天然就要写清边界指标可以是 60%也可以是 95%。真正有价值的不是数字本身而是你通过一次系统性的精简养成了“把提示词当资源管理”的习惯这个习惯会让后续每次扩展的编写都越来越干净。三是我实际踩过几次坑之后的真实体会工具提示词的优化不是“减少字数”而是“转移信息的位置”。能放到 schema 里的不放描述里能放到错误信息里的不放描述里能放到代码实现里的不放描述里。当你知道一个信息真正该住在哪里它就不再需要占用模型上下文了。最后再用一句最直白的话收底工具提示词的使命是“让模型在正确的时候调用正确的工具”除此之外的一切职责都不是它的。把这句想通了你的工具提示词就自然会瘦下来。这套方法在 Pi Agent 上顺手换到其他 Agent 框架也一样适用——因为语言模型对工具提示词的理解逻辑是相通的你节省下来的 token 在任何上下文中都是实打实的收益。
返回列表