跳到正文
北京时间
原文
GitHub Blog· Erik Kristensen·· 27 天前精选AI 评分62

GitHub Copilot 如何在不牺牲任务质量的前提下降低 AI 编码成本

How we make AI coding more cost efficient without sacrificing task quality

AI 导读

GitHub 工程师 Erik Kristensen 分享了 Copilot 降本的四项改动:选择性压缩工具输出、移除 view 工具行号前缀(线下推理成本降约 5%,线上用户日均推理成本降约 3%)、压缩 task-tool 提示词(每轮省约 1300 token,每活跃小时归一化成本降 2.9%)、后台任务完成后直接交付结果(AI Credits 用量降约 2.3%)。

推荐理由

GitHub 用四项改动说明压缩 token 为何要看整个任务而非单次调用,并给出可复用的评估方法与踩坑教训。

正文 · AI 翻译

在使用 AI 编程智能体时,输出质量固然重要,但真正的效率来自于快速、高效地完成工作,并且拥有正确的上下文。

因此,单次交互的 token 数量本身并不能作为衡量效率的有效指标。目标不应该是使用更少的 token,而是利用适量的上下文来推进任务。如果工具响应过于简洁,遗漏了智能体所需的信息,有时反而需要额外的调用或工作,最终使任务变得更慢、成本更高。

这就是为什么我们希望优化最终结果,而不是优化工具调用本身。这篇文章探讨了 GitHub Copilot 中践行这一原则的四项改动:

  • 在减少重复输出的同时,保留有用的上下文。
  • 移除对任务没有价值的格式。
  • 在不改变有用行为的前提下缩短指令。
  • 无需额外的检索步骤即可交付已完成的背景工作。

可能的改动会使用智能体编码基准进行离线评估。最有前景的改动在发布前会通过受控的在线实验进行验证。本文中的示例来自 GitHub Copilot CLI。其他多个 Copilot 产品,例如 GitHub Copilot 应用和 Copilot 代码审查,使用相同的底层框架,也通过这些改进而变得更加高效。

Chart showing 3.1% 'Remove view previxes', 5.5% 'Selective output compaction', 2.9% 'Compact task-tool prompt', and 2.3% 'Reduce notification roundtrips'.
图 1:使用同一 AI 信用度指标进行的四次独立 A/B 实验。各分段并列展示以便对比;它们的效果并不一定严格可叠加。

局部指标的陷阱

缩短每次工具调用的输出,是降低智能体成本的常见做法。RTK(Rust Token Killer)是一个在智能体读取 shell 输出之前先对其进行缩短的工具。我们使用智能体编码基准测试评估了它对 GitHub Copilot 的影响。

在我们的测试框架和基准配置中,RTK 确实缩短了部分响应,但当被省略的文本很关键时,模型有时会重新打开原始输出或重新运行命令,以恢复它所需的信息。

这些恢复步骤增加了对话轮次,并携带了更多上下文向前推进。单次工具响应变短了,但平均而言,整个任务消耗了更多 token,耗时也更长。我们在局部省下了 token,却在全局花掉了更多。

Flow chart showing: RTK, compresses shell output > Local win, tool output gets shorter > Useful detail is missing > Recovery, reread or rerun > More turns and context carried forward. Then the option of finishing at 'End-to-end result, Tokens and cost up, Task duration up, Task completion: steady,' or 'Recovery repeats' going back to 'useful detail is missing'.
图 2:当缺失的细节迫使智能体重读输出、重跑命令并携带更多上下文向前推进时,更短的工具响应反而可能让整个任务的成本更高。

这一结果适用于我们所测试的集成方案和工作负载,并不适用于所有 RTK 配置,也不代表对输出压缩的普遍结论。这意味着“每次工具调用的 token 数”是错误的优化目标。一项效率改动必须放在完整任务中评估——从用户的请求一直到最终结果。

更有用的思路是:看看哪些内容可以移除,而又不会让模型重复工作。

压缩噪声,保留有用信息

目标是缩短重复性输出,同时保留智能体完成任务所需的上下文,使其无需回溯步骤。

对基准测试运行的分析表明,安装、构建、测试和 lint 输出中往往包含重复性噪声,而类源代码输出和任意命令结果更可能包含智能体所需的信息。这一分析催生了一个选择性输出压缩器,其设计部分参考了 RTK 及类似方法。

该原型在智能体编码基准测试以及一系列开源代码仓库上进行了评估,覆盖了它们的构建、测试和 lint 系统。

早期版本过于激进。它们导致模型重复工作或读取完整保存的输出,从而增加了端到端成本并降低了任务成功率。例如,我们最初压缩了 git diff,但在基准测试任务显示智能体会重新打开原始输出以找回缺失信息后,我们移除了该过滤器。

这些早期的失败催生了一套三部分策略:

  1. 保留类源代码输出和任意输出。 诸如 cat、git diff、git show 等命令以及任意脚本均原样返回,不做改动。
  2. 在不丢失内容的前提下重新组织搜索结果。 来自 grep 等工具的匹配项和文件列表可以在保留每一条结果的同时进行更高效的分组。
  3. 有选择地压缩重复性噪音。 安装、构建、测试和进度输出仅在节省量可观时才进行压缩。

最终发布的版本经过了反复评估和打磨。它之所以保守,并不是因为目标是构建一个保守的压缩器,而是因为评估结果支持这样做。

当输出被压缩时,智能体仍然可以通过直接的恢复路径获取完整的原始内容。

Flowchart showing how GitHub Copilot handles shell-command output. Copilot calls a shell command, classifies the output, then chooses one of three paths: keep arbitrary/source output unchanged, reorganize search results without losing any matches, or selectively compress repetitive noise (like install/build/test logs) while preserving full output and providing a recovery path. The processed result is returned to Copilot.
图 3:最终发布的压缩器保留类源代码输出,无损失地重新组织搜索结果,并且只压缩可预测的重复性噪音,同时保留完整的原始内容。

这条恢复路径既是安全机制,也是评估信号。我们跟踪了智能体是否打开保存的原始内容、重新运行命令、重复探索、缩小搜索范围或增加额外的交互轮次。频繁的恢复操作将表明压缩器删除了某些有价值的内容。

在触发输出压缩的离线任务中,未检测到具有统计显著性的任务成功率下降,而且智能体极少打开保存的原始内容。在在线实验中,平均成本略有下降,在跟踪的质量指标中未检测到实质性回退。

先移除格式,再移除信息

一个干净的 token 优化来自 view 工具,智能体用它把文件内容读入上下文。

此前,view 在把内容展示给模型之前,会给每一行加上行号前缀。早期的文件编辑工具用这些行号来定位修改,但当前的工具改为匹配周围代码,不再使用行号。尽管常规工作流已不再需要行号前缀,它却仍然保留着。

每个前缀本身很小。然而,在每一次文件读取的每一行上反复出现,这些无用的格式就会在整个会话中不断累积。于是,我们移除了它。

Before-and-after image of code snippets. The line-number prefixes re removed from the 'After' image.
图 4:移除行号前缀在精确保留源码的同时,消除了每次文件读取时都会重复出现的格式。

行号在 diff 和短代码片段中仍然有用。但在这里它们是浪费的,因为它们附着在每一次文件读取上,却并未服务于当前的编辑工作流。

移除行号后,在离线智能体编码基准测试中,模型推理成本下降了约 5%。成功率保持在预期的逐次运行波动范围内,编辑失败率也没有上升。

随后,我们与 Copilot CLI 用户一起测试了这一改动。在线实验将每位用户的日均模型推理成本降低了约 3%,在我们跟踪的质量或满意度指标上未检测到实质性回退。

对开发者而言,这意味着上下文窗口中有更多空间可用于实际工作本身,而不是用于智能体不会使用的格式。

这是一次理想的改动:无需为模型增加新的指令,没有需要恢复的信息来源,也没有额外的决策需要做出。文件内容原封不动地到达了模型。

压缩提示词,但不压缩意图

提示词承载着塑造智能体工作方式的指令,并且在每一轮交互中都会发送给模型。只有在智能体保持开发者所依赖的行为的前提下,缩短提示词才能真正提升效率。

在 GitHub Copilot 中,任务工具会启动专门的智能体进行并行工作。其指导说明分散累积在工具描述、schema、智能体定义、系统指令和配套工具中。

通过一个元提示循环——Copilot 在其中迭代地编写自己的提示词——该提示词被缩减了大约一半。Copilot 生成并优化了更小的候选版本,而针对性的行为测试则检验了我们希望保留的要求。

第一次在线实验发现了一个最初的离线评估未能捕捉到的回退问题。元提示循环将原本谨慎的并行指导改写成了硬性的调度策略,导致独立的自定义智能体被串行执行。

我们停止了这项实验。在再次修改提示词之前,我们针对用户暴露出的行为编写了一个回归测试。最终的修复方案是用一句话替换了原先显式的允许列表和拒绝列表:

独立智能体可以并行运行;请考虑副作用。

这句话更短,限制也更少;它将是否并行运行子智能体的决定权交给了模型,而不是之前那种显式的指引。有了这句话,我们的新行为测试通过了,同时也没有导致任何现有行为测试失败。

提示词行为需要测试。如果一个行为没有被测试覆盖,那么一个更短的提示词就可能在无人察觉的情况下将其移除。

Three-stage diagram labeled Compression → Regression + fix → Completed. Left panel shows an original prompt compressed by about 50%. Middle panel highlights a regression where agents became serialized, then a fix by editing one sentence to restore parallelism. Right panel shows final shipped prompt with restored behavior and cumulative savings of about 1,300 fewer tokens per turn across steps.
图 5 只有在回归测试暴露了串行化的智能体、并且一句话的修复恢复了并行性之后,提示词压缩才变得安全;由此带来的 token 节省会在模型的每一轮生成中持续体现。

最终发布的提示词每轮生成可减少约 1,300 个 task-工具提示词 token,相当于每次会话的总提示词 token 减少约 1.8%,每活跃小时归一化成本降低 2.9%,并且在所衡量的各项评估中没有检测到质量回退。

无需额外的检索轮次即可交付已完成的后台工作

智能体经常在后台运行独立任务,例如在子智能体调查的同时执行一个长时间运行的 shell 命令。通知机制让智能体可以继续工作,直到该任务就绪,而无需花费一次工具调用来等待。

如果智能体没有显式等待其中任一任务,当 shell 命令或子智能体完成时,执行框架会唤醒模型并通知它。

此前,该通知并不包含已完成的结果,因此智能体不得不额外花费一轮来取回 Copilot 已经收到的输出。当多个任务在相近时间内完成时,这种绕路可能会反复发生。现在,Copilot 会批量发送符合条件的完成通知,并直接以现有的工具结果格式交付已完成的结果。智能体可以直接利用所需信息继续工作,无需额外花费一轮再次请求。对于仍在运行中的任务的显式读取,行为与之前保持一致。

Before-and-after sequence diagram comparing orchestration behavior.

Before: model waits on separate shell and sub-agent completions, causing retrieval detours and four LLM calls to process two results.
After: a harness batches related completions and emits synthetic tool events so background work continues while waiting; both results are processed together in a single LLM call.
The visual emphasizes reduced latency and fewer model round trips.
图 6 之前,每次后台任务完成都可能唤醒一轮仅用于取回结果的模型轮次。之后,执行框架会批量发送符合条件的完成通知,并以现有的工具结果格式交付已完成的结果。

在此变更之前,每项已完成的任务都需要一次模型调用来请求其结果,另一次调用来处理该结果。对于上面展示的 shell 命令和子智能体,这意味着在任务可以继续之前需要四次模型调用。

现在,执行框架会批量处理这两个完成事件并一起提供其结果,因此单次模型调用即可处理两者。消除这些取回结果的绕路,也避免了在非必要的调用中携带完整的会话上下文。

通过直接交付完整结果,不压缩、不概括、不保留任何内容,该工具使以 AI Credits 计量的平均 token 相关用量降低了约 2.3%。

在上下文中衡量变化

在一种 Copilot 工作流中节省 token 的改动,可能会在另一种工作流中增加成本。

例如,一套更精简的文件工具指令,其灵感来自 Copilot 代码审查中的积极结果。但在 Copilot CLI 在线实验中,它反而增加了成本,因此我们没有发布它。

相比之下,在使用生产模型对大量 Copilot 代码审查任务进行的独立评估中,移除行号前缀和对输出进行选择性压缩,各自使每次审查的平均提示词 token 减少了约 5%。我们未检测到所跟踪的审查质量指标发生实质性变化。

这些发现与此前 Copilot 代码审查向共享文件工具的迁移是分开的;后者连同审查指令调优,使代码审查成本降低了约 20%。

每一项改动都需要在其运行的工作流中进行衡量。

构建高效 AI 编程智能体的五条经验

  1. 优化的是完成的任务,而不是工具调用本身。如果智能体需要花费更多轮次来恢复被移除的内容,那么更短的输出并不一定更便宜。
  2. 优化编排,而不仅仅是模型输出。 消除那些执行可由工具框架确定性完成的工作的模型轮次。
  3. 根据输出所代表的内容进行压缩。 保留精确内容,优先采用无损转换,并衡量智能体使用恢复路径的频率。
  4. 提示词重写有时会带来意想不到的后果。 验证预期行为是否得到保留。
  5. 证据与工作负载密切相关。 在离线基准测试、在线实验以及变更所发布到的每个产品界面中重新评估这些变更。

这些更改都没有让模型变得更聪明。它们只是移除了模型本不需要执行的工作。

本文中描述的更改正在 GitHub Copilot 使用相同底层工具框架的各项体验中推出。

将智能体工作流带到你的终端
使用 GitHub Copilot CLI >

来源:GitHub Blog · github.blog