title: "GitHub 如何在保证任务质量的同时提高 AI 编程的成本效率"
source_url: "https://github.blog/ai-and-ml/github-copilot/how-we-make-ai-coding-more-cost-efficient-without-sacrificing-task-quality/"
author: "The GitHub Blog"
excerpt: "公共早报 GitHub 披露 Copilot 的成本优化实验:保留有用上下文、压缩重复输出、精简提示词,并合并后台任务通知。团队通过离线评测与线上实验追踪任务质量,发现部分压缩会引发重复读取。文章提供了从完整任务衡量成本、验证优化效果的具体方法。"
在使用 AI 编程助手时,输出质量很重要,但真正的效率来自于快速、高效地完成工作,并获得正确的上下文。
这就是为什么单独看每次交互的 token 数量并不是衡量效率的有意义指标。目标不应该是使用更少的 token,而是获取适量上下文来推动任务进展。一个简洁的工具响应有时可能需要额外的调用或操作才能补充代理所需的信息,最终反而使任务变得更慢、更昂贵。
这就是为什么我们要优化的是最终结果,而不是工具调用本身。本文探讨了 GitHub Copilot 中的四项变更,实践了这一原则:
在减少重复输出的同时保留有用的上下文。
移除对任务毫无价值的格式。
在不改变有用行为的前提下精简指令。
直接交付已完成的后台工作,无需额外的获取步骤。
候选变更首先通过代理编程基准测试进行离线评估,然后最有前景的变更再通过对照在线实验进行验证后上线。本文中的示例来自 GitHub Copilot CLI。GitHub Copilot 应用和 Copilot 代码审查等其他多个产品也使用相同的底层测试框架,并同样通过这些改进提升了效率。
图 1:四项独立 A/B 实验,使用相同的 AI 额度指标。图中各段放在一起便于比较;其效果不一定严格叠加。
局部指标陷阱
一种常见的降低代理成本的方式是缩短每次工具调用的输出。RTK(Rust Token Killer)就是一个在代理读取前缩短 shell 输出的工具。我们使用代理编程基准测试评估了它对 GitHub Copilot 的效果。
在我们的测试框架和基准配置下,RTK 缩短了一些响应,但当被省略的文本内容重要时,模型有时会重新打开原始输出或重新运行命令来恢复所需信息。
这些恢复步骤增加了对话轮次,并携带了更多上下文向前推进。单个工具响应变短了,但平均而言,任务使用了更多 token 且耗时更长。我们在局部节省了 token,却在全球范围内花费了更多。
图 2:当缺失的细节迫使代理重新读取输出、重新运行命令并携带更多上下文时,更短的工具响应反而会使完成任务的成本更高。
这一结果适用于我们测试的集成和工作负载,而非适用于所有 RTK 配置或输出压缩场景。这意味着每个工具调用的 token 数并非正确的目标。效率变更必须从用户请求到最终结果,对整个完整任务进行评估。
更有意义的问题是:我们可以移除什么而不至于让模型重复工作。
压缩噪音,保留有用信息
目标是缩短重复输出,同时保留代理完成任务所需而不必重复操作的上下文。
对基准测试运行的分析表明,install、build、test 和 lint 的输出通常包含重复的噪音,而源代码类输出和任意命令结果更可能包含代理所需的信息。这项分析催生了一个选择性输出压缩器,其设计参考了 RTK 及类似方案。
该原型在代理编程基准测试和一系列开源代码仓库上进行了评估,涵盖了它们的 build、test 和 lint 系统。
早期版本过于激进。它们导致模型重复工作或读取完整保存的输出,增加了端到端成本并降低了任务成功率。例如,我们最初对 git diff 进行了压缩,但在基准任务显示代理会重新打开原始输出来恢复缺失信息后,移除了该过滤器。
这些早期失败促成了三部分策略:
保留源代码类和任意输出。
cat、git diff、git show和任意脚本等命令的输出保持不变。重组搜索结果而不丢失内容。
grep等工具返回的匹配结果和文件列表可以更高效地分组,同时保留每一条结果。选择性压缩重复噪音。 仅当节省效果显著时,才对 install、build、test 和进度输出进行压缩。
上线版本经过了反复评估和打磨。它是保守的,不是因为目标是构建一个保守的压缩器,而是因为评估结果支持这样做。
当输出被压缩时,代理仍然可以通过直接的恢复路径获取完整的原始内容。
图 3:上线的压缩器保留源代码类输出,在不丢失任何匹配的情况下重组搜索结果,仅对可预测的重复噪音进行压缩,同时保留完整原始内容。
该恢复路径既是安全机制,也是评估信号。我们追踪代理是否打开了保存的原始输出、重新运行命令、重复探索、缩小搜索范围或采取了额外轮次。如果恢复操作频繁发生,则说明压缩器移除了有价值的内容。
在触发输出压缩的离线任务中,未检测到任务成功率出现统计意义上的显著回归,代理也极少打开保存的原始输出。在线实验中,平均成本略有下降,而跟踪的质量指标未检测到实质性回归。
先移除格式,再移除信息
一个干净的 token 优化来自 view 工具,代理使用它将文件内容读取到上下文中。
此前,view 在向模型展示内容之前会给每一行加上数字前缀。早期的文件编辑工具使用这些数字来定位修改,但当前工具改为匹配周围代码,不再使用行号。行号前缀保留了下来,尽管正常的工作流程已经不再使用它们。
每个前缀都很小。然而,当它贯穿每个文件的每一行重复出现时,这些无用的格式在一个会话中不断累积。因此,我们移除了它。
图 4:移除行号前缀精确保留了源代码,同时消除了每次读取文件时重复添加的格式。
行号在 diff 和短代码片段中仍然有用。但在这里是浪费的,因为它们被附加到每次文件读取中,却不为当前编辑工作流程服务。
移除它们后,离线代理编程基准测试中模型推理成本下降约 5%。成功率保持在预期的运行间方差范围内,编辑失败率没有增加。
随后我们用 Copilot CLI 用户测试了这一变更。在线实验将每位用户每日平均模型推理成本降低约 3%,而跟踪的质量和满意度指标未检测到实质性回归。
对开发者而言,这意味着更多上下文窗口可以用于实际工作,而不是浪费在代理不使用的格式上。
这是理想的变更:对模型无需新的指令,没有信息需要恢复,也没有额外的决策要做。文件内容原封不动地到达模型。
压缩提示词但不压缩意图
提示词携带指导代理工作的指令,每一轮都会发送给模型。只有在代理保留开发者所依赖的行为的前提下,缩短提示词才能提升效率。
在 GitHub Copilot 中,task 工具启动专门化代理进行并行工作。其指导内容由工具描述、schema、代理定义、系统指令和配套工具累积而成。
一个元提示词循环——Copilot 迭代编写自己的提示词——将该提示词减少了约一半。Copilot 生成并优化更小的候选版本,而目标行为测试则检验我们想要保留的需求是否满足。
第一次在线实验发现了初始离线评估遗漏的回归问题。元提示词循环将谨慎的并行指导重写为硬性调度策略,导致独立的自定义代理顺序执行。
我们停止了实验。在再次修改提示词之前,我们为用户暴露的行为编写了回归评估。最终的修复用一个句子替换了显式的允许列表和拒绝列表:
独立代理可以并行运行;但需考虑副作用。
这句话更短且限制更少;它将是否并行运行子代理的选择权交给了模型,而不是之前那种显式指导。有了它,我们的新行为测试通过了,且没有导致任何现有行为测试失败。
提示词行为需要测试。如果一个行为没有被测试,更短的提示词可能在无人察觉的情况下将其移除。
图 5:只有在回归测试暴露出代理被序列化、一个句子的修复恢复了并行性之后,提示词压缩才变得安全;由此产生的 token 节省在每个模型轮次中持续生效。
上线的提示词在每个轮次中减少了约 1,300 个 task-tool 提示词 token,相当于每个会话的总提示词 token 减少约 1.8%,每有效小时的标准化成本降低 2.9%,而跟踪的评估中未检测到质量回归。
直接交付已完成的后台工作,无需额外的获取轮次
代理经常在后台运行独立工作,例如在子代理调查的同时运行一个长时间执行的 shell 命令。通知机制让代理可以持续工作,直到该工作完成为止,而无需消耗工具调用来等待。
如果代理没有明确等待任一任务,框架会在 shell 命令或子代理完成时唤醒模型并通知它。
此前,该通知不包含已完成的结果,因此代理必须再花费一个轮次来获取 Copilot 已经收到的输出。当多个任务几乎同时完成时,这种绕行会重复出现。Copilot 现在将符合条件的完成通知批量处理,直接在现有工具结果格式中交付已完成的结果。代理可以继续使用所需信息,无需再花费额外轮次去请求。仍在运行的工作的显式读取行为与之前相同。
 图 6:此前,每个后台任务完成都可能唤醒一个仅用于获取的模型轮次。之后,框架将符合条件的完成批量处理,在现有工具结果格式中交付已完成的结果。
在此变更之前,每个已完成任务需要一次模型调用来请求其结果,再花一次调用来处理它。对于上面所示的 shell 命令和子代理,这意味着继续工作前需要四次模型调用。
现在,框架将两个完成批量处理并一起提供结果,因此单次模型调用就可以处理两者。移除这些获取绕行也避免了在不必要的调用中携带完整的会话上下文。
通过直接交付已完成结果,不压缩、不总结、不隐瞒任何内容,框架按 AI 额度衡量的平均 token 相关使用量减少了约 2.3%。
在具体上下文中衡量变更
在一个 Copilot 工作流中节省 token 的变更可能在另一个工作流中增加成本。
例如,一套更精简的文件工具指令的灵感来自 Copilot 代码审查中的正面结果。在 Copilot CLI 在线实验中,它反而增加了成本,因此我们没有上线。
相比之下,移除行号前缀和有选择性地压缩输出,在使用生产模型的大量 Copilot 代码审查任务的独立评估中,每个审查的平均提示词 token 各自减少约 5%。我们未检测到跟踪的审查质量指标发生实质性变化。
这些发现与此前将 Copilot 代码审查迁移到共享文件工具是分开的——后者连同审查指令调优,使代码审查成本降低了约 20%。
每项变更都需要在其运行的工作流中衡量。
构建高效 AI 编程代理的五条经验
优化已完成的任务,而非工具调用。 如果代理需要更多轮次来恢复被移除的内容,更短的输出并不会更便宜。
优化编排,而不仅仅是模型输出。 消除模型执行框架可以确定性完成的工作所需的模型轮次。
按输出所代表的内容进行压缩。 保留精确内容,优先使用无损转换,并衡量代理使用恢复路径的频率。
提示词重写有时会产生意想不到的后果。 验证预期行为是否得到保留。
证据与具体工作负载相关。 在离线基准测试、在线实验以及上线的每个产品界面中重新评估变更。
这些变更没有一项让模型变得更聪明。它们只是移除了模型从未需要完成的工作。
本文描述的变更正在使用相同底层框架的 GitHub Copilot 各项体验中逐步上线。