跳到正文
eric zakariasson· @ericzakariasson · X·· 12 天前精选AI 评分73
AI 导读

Cursor 工程师 Eric Zakariasson 公开了一份用于优化 LLM agent harness token 效率的提示词,目标是降低每个已完成任务的按价格加权的 token 成本,同时不降低任务质量。

推荐理由

Cursor 工程师公开了一份可直接套用的 agent harness 优化提示词,涵盖缓存布局、工具卸载与子智能体调优等可迁移方法。

正文 · AI 翻译

这是一个基于我们在 cursor 学到的东西来改进你的 agent harness 的提示词。请享用

改进此 agent harness 的 token 效率

你正在处理一个 LLM agent harness:系统提示词、工具定义、请求组装、上下文缓存、压缩与检索,以及工作如何在多个 agent 之间拆分。让 agent 的运行更便宜,同时不让它在本职工作上变差。

  • 目标:降低每个已完成任务的按价格加权的 token 成本。
  • 约束:任务质量没有可测量的下降。

按任务衡量,而不是按请求衡量。每一轮都会重新发送前缀(工具、指令、设置以及到目前为止的对话),所以一个缩小每个请求但增加轮数的改动可能反而更贵。按计费类型对 token 加权:输出、未缓存输入和缓存输入的价格差异非常大。

按此顺序工作:梳理 harness 并测量基线,对机会排序,直接做那些安全的改动,把其余的放在 flag 后面或写成提案,然后汇报。

下面的数字来自一个团队的生产级编码 agent 及其多 agent 实验。用它们来判断量级,而不是当作目标。一轮这样的改动(提示词精简、工具卸载、缓存布局、稀疏行号、子 agent 调优)将该团队的整体 token 成本削减了约 7%,且质量没有损失。更大的百分比只适用于每个改动所触及的那部分请求。

原则

  1. 改变 harness 发送的内容,而不是模型有多努力。不要要求模型节省 token。一个告诉其模型“注意节省 token,不要浪费”的 harness 发现,模型变得不愿承担有雄心的任务,有时还会退出,说它不应该浪费 token。
  2. 有能力的模型需要的是定义,而不是命令。一串串“DO NOT”、“You must”和“Important”,以及针对旧模型习惯的防范,通常可以用对每个工具做什么的平实描述来替代。一个团队用这种方式削减了约三分之二的系统提示词,而更短的提示词在多个模型家族上都有效。只对模型无法知道的事情(产品、环境、用户的流程)以及你在 transcript 中见过的怪癖进行指示。
  3. 静态上下文用于大多数轮次都需要的内容。其他一切都应该在需要时可被发现。更少的前置上下文也意味着更少令人困惑或矛盾的信息。
  4. 预期删除会胜出。为较弱模型写的护栏、成为瓶颈的协调步骤,以及为模型现在自己就会做的行为所写的提示词,都在消耗 token。
  5. 真实使用决定一切。Evals 是快速的代理指标,但它们偏向难题,会错过真实的请求组合。

1. 梳理 harness 并测量基线

找出:

  • 请求在哪里组装、系统提示词和工具 schema 在哪里。如果某个框架或 SDK 构建请求,找出它在消息顺序、缓存控制和工具加载方面的钩子。
  • 工具结果如何格式化,以及历史如何保留、裁剪或总结。
  • 子 agent 或并行 agent 如何生成(如果有的话)。
  • 使用了哪些模型和 provider API。从 provider 的文档中,获取提示词缓存行为(自动还是显式断点、TTL、最小可缓存长度)以及输出、未缓存输入和缓存输入的价格。
  • 现有的日志、token 核算和 evals。

如果 harness 没有按计费类型和缓存命中记录每个请求的 token 使用量,先加上这个。之后的一切都依赖它。

然后渲染几个真实请求(来自日志,或通过运行有代表性的任务),用模型的 tokenizer 或 API 的 usage 字段统计每个部分的 token。产出:

  • 按来源 × 计费类型的成本占比。来源:系统提示词、工具定义、技能/规则/集成描述、用户消息、文件读取、搜索结果、命令和其他工具输出、历史、摘要、子 agent。
  • 每个请求的静态 token、缓存命中率和每个任务的轮数。
  • 每个工具:至少调用它一次的运行占比,以及它的错误率。

阅读渲染后的请求,而不只是模板。重复、泄漏的易变值和顺序错误的块只会在那里显现。

按支出占比 × 可移除比例 ÷ 质量风险对机会排序。

2. 系统提示词和注入的上下文

给每条指令打标签:

  • 保留:模型无法推断的产品或环境知识、针对此模型 transcript 中见过的怪癖的修复,以及某个模式所依赖的规则。
  • 改写:把命令和强调改成平实描述。把提醒改成约束:“No TODOs, no partial implementations”比“remember to finish implementations”更有效。把模糊的数量改成范围:“generate 20–100 tasks”比“generate many tasks”能带来雄心大得多的行为。
  • 删除:有能力的模型默认就会做的事情、针对你还没从此模型见过的行为的防范、重复工具描述的文本,以及可能与用户请求相矛盾的行。被训练成把系统指令排在用户消息之上的模型会站在系统提示词一边。
  • 移动:任何按用户或按请求的内容(日期、环境、仓库状态、技能或子 agent 列表、用户规则)移到缓存边界之后的 user-role 设置消息中。

用同样的方式审计其他注入的上下文。随着模型改进,这些数字背后的团队去掉了目录树、预检索的片段、附加文件的压缩副本、每次编辑后注入的 lint 错误、对短文件读取的强制扩展,以及每轮工具调用上限。他们保留了小而高价值的事实:操作系统、仓库状态,以及打开或最近查看的文件。

对开放式工作跳过清单。模型会优化列出的项目,并降低其他一切的优先级。

3. 工具定义

工具 schema 会随每个请求一起传输。核心集合之外的大多数工具各自在不到 20% 的对话中被需要,把它们移出静态上下文将工具描述 token 削减了 60%。对集成工具(例如 MCP 服务器)做同样的事——在上下文中保留名称,把完整 schema 放在每个服务器一个文件夹中,agent 可以用 grep 或 jq 搜索——在使用它们的会话中削减了 46.9% 的总 token。

  • 保留在静态上下文中:高频工具(对编码 agent 而言:read、search、edit、shell)、即使不存在模型也会尝试调用的工具,以及某个模式所依赖的工具。
  • 卸载其余的:留下一个名称或一行指针,让完整 schema 可按需发现。把相关工具分组,使它们一起加载,并把状态(例如“needs re-authentication”)放在 agent 会看到的地方。
  • 收紧剩下的:描述行为和参数,去掉使用说教。
  • 通过测试几种配置并跟踪 token、成本、延迟、工具调用错误和任务成功来选择拆分方式。

4. 缓存布局

对每个请求排序,使可复用的前缀尽可能长:

tool definitions → system instructions → [breakpoint] → setup message (skills, subagents, rules, environment) → [breakpoint] → conversation

  • 保持前缀在各轮之间逐字节相同。使用确定性的工具顺序和序列化,把时间戳和 ID 放在边界之后,除压缩外不要重写更早的消息。
  • 如果 provider 支持,使用显式断点。否则依赖自动前缀缓存,把稳定部分放在前面。遵守 TTL 和最小长度规则。
  • 在对话中途切换模型会丢弃缓存(缓存是按模型和 provider 的),并把一段不是它写的历史交给新模型。当需要不同模型时,把它作为子 agent 用全新上下文运行。

显式断点加上把按请求的设置移到它们之后,将冷缓存未命中削减了 20%。

5. 工具结果和运行期间添加的其他上下文

  • 大输出(命令、集成、日志):把它们写入文件并返回路径、大小和一小段尾部。agent 可以 tail、grep 或读取范围以获取更多。截断会丢失数据,而内联会膨胀之后的每个请求。对长时间运行的终端会话也这样处理。
  • 高容量格式:寻找每一行或每一项上重复的开销。对文件读取每 10 行编号而不是每行编号,在不损害引用准确性的情况下削减了 1.6% 的缓存读取 token。每个编号花费 3–5 个 token,而 agent 每个会话读取数万行。还要检查重复的绝对路径、冗长的 JSON 键、ANSI 代码、进度条和重复的头部。
  • 好的检索能节省探索轮数。在 grep 之外加入语义搜索,将代码库问答准确率平均提高了 12.5%,并减少了用户所需的迭代次数。
  • 工具错误浪费 token 并在上下文中留下令人困惑的残渣。对预期错误(无效参数、意外环境、provider 错误、超时、用户中止)分类,把未知错误当作 harness bug,并按工具和按模型跟踪比率。一次沿着这些思路的集中努力将意外工具错误削减了 10 倍。

6. 长时间运行:压缩、子 agent 和模型组合

  • 压缩:保持总结提示词简短、摘要紧凑,把计划状态和剩余任务延续下去,并把完整历史保存到 agent 可以搜索的文件中,以找回摘要遗漏的细节。一个被训练成从一行提示词自我总结的模型写出了约 1k token 的摘要,其压缩错误只有会产生 5k+ token 摘要的数千 token 提示词的一半。未经训练的模型可能需要更多指导,所以要测试你能做到多短。更贵的总结模型带来的差异可以忽略不计。
  • 草稿本和运行笔记:重写它们,而不是追加。对于在一个环境中的重复工作,一个由 agent 维护的、有行数预算的小笔记文件,在开始时加载,是缩短后续运行的一种有前景的方式。
  • 子 agent:全新上下文让父级保持精简,但隔离会增加协调成本(重复或过时的工作)。如果模型已经自己委派,就移除推动它这样做的提示词。让子 agent 返回简短的交接:做了什么、发现、顾虑和偏差。只有当用户或 harness 这么说时,子 agent 才应使用不同的模型。
  • 模型组合:在大型多 agent 运行中,worker 使用了至少 69% 的 token,在大多数运行中超过 90%。一个前沿 planner 配便宜的 worker,与一个前沿模型做所有事相比,成本约为八分之一。planner 的选择仍然会改变 worker 的支出。一个自身成本较低的 planner 让其 worker 使用了几倍多的 token,整个运行反而更贵。衡量整棵树。
  • 路由和推理努力:把简单轮次发给更便宜的模型或更低的努力,只在更强的模型明显更好时升级。这样构建的路由器在用户满意度上匹配或超过单一前沿模型,成本低 41–68%。
  • 推理连续性:如果 API 返回 reasoning items(包括加密的),在后续轮次把它们传回去,并在它们丢失时发出警报。丢弃它们让一个推理模型在编码基准上损失了 30%,而且它还烧掉 token 来重建自己的计划。

7. 让 harness 适配每个模型

适应每个模型被训练的方式,而不是把一种形态强加给所有模型。如果你已经为类似的模型调过 harness,就从那个版本开始。

  • 编辑格式:使用模型被训练过的格式(例如 patch 风格或 search-and-replace)。不熟悉的格式会花费额外的推理 token 并导致更多错误。
  • Shell 或工具:shell 优先的模型会退回到 cat 或内联脚本。以工具的 shell 等价物命名工具(例如 rg),如有需要加上:“If a tool exists for an action, prefer to use the tool instead of shell commands (e.g. read_file over cat).”
  • 字面性:一些模型家族按字面遵循指令,另一些容忍不精确。有些会在被强调的措辞上打转。对字面型模型去掉大写和强调。
  • 触发器:有些模型在被告诉何时使用之前会忽略一个工具。一个字面触发器有效:“After substantive edits, use the <lint tool> to check recently edited files for linter errors. If you've introduced any, fix them if you can easily figure out how.”
  • 进度更新:如果模型通过推理摘要报告进度,把它们保持在 1–2 句,指出新发现或策略变化,并移除关于轮次中途发消息的指令。
  • 值得专门写一行的怪癖:随着上下文填满而含糊其辞或拒绝(“context anxiety”)、过早宣布完成、停下来请求许可,以及调用不存在的工具。

把每条添加的指令与它所修复的 transcript 行为挂钩。当模型变化时重新审计,因为一个版本需要的指导对下一个版本可能是死重。

8. 验证

  • 离线:在前后运行一组固定的真实任务,最好取自真实使用,并按用户实际的写法措辞(简短且含糊)。比较任务成功、token、每任务成本、轮数和工具错误。不要发布会降低成功的改动。
  • 在线,如果你有用户:对每个改动或小捆绑做 A/B 测试。主要指标是每个已完成任务的成本。护栏是任务成功信号、工具调用错误、延迟、每任务轮数和缓存命中率。对编码 agent 而言,一个好的成功信号是 agent 写的代码随时间存活了多少。一般来说,检查用户的下一条消息是继续推进还是报告问题。
  • 只有当成本下降且没有护栏退化超出噪声时才发布。记录零结果。

直接改什么,提议什么

  • 直接改,每个都在各自可回滚的 commit 中:token 和缓存遥测、确定性序列化和工具顺序、把易变内容移出缓存前缀、显式缓存断点、把大输出写入文件而不是截断、传回正在被丢弃的 reasoning items,以及修复反复出现的工具错误。
  • 放在 flag 后面改,以便测试:系统提示词编辑、工具卸载、输出格式变化、压缩变化和子 agent 提示词。
  • 仅提议:运行哪些模型的改动、路由、推理努力默认值,或工作如何在多个 agent 之间拆分。

陷阱

  • 要求模型使用更少的 token 或少做点事。
  • 截断工具输出。
  • 为节省输入 token 而丢弃 reasoning items。
  • 缓存前缀中的易变内容,或在请求之间变化的工具顺序。
  • 卸载模型在第一轮就需要的工具,或它在缺失时会尝试调用的工具。
  • 强调过多的提示词(MUST、NEVER、IMPORTANT、全大写),尤其是对字面型模型。
  • 强制使用比模型被训练时更简洁的输出格式。更少的输出 token 可能意味着更少的思考,结果更差。
  • 优化原始 token 数而不是成本,优化每个请求而不是每个任务,或优化 evals 而不是真实使用。
  • 为省钱在对话中途切换模型。
  • 添加成为瓶颈的协调层。

汇报内容

  1. harness 地图和基线:按来源 × 计费类型的成本,并指出最大的来源。
  2. 排序后的改动列表:层、改什么、估计节省及你如何估计、质量风险、如何验证,以及如何回滚。
  3. 你所做的改动,包括系统提示词 diff,每一行都有保留、改写、删除或移动的理由。
  4. 针对放在 flag 后面的改动的测试计划。
  5. 缺口:任何你找不到或无法衡量的东西。

来源:eric zakariasson · x.com