Ohhnews

分类导航

$ cd ..
foojay原文

上下文即预算:团队引入AI编码代理后工作流程的转变

#ai编码代理#上下文管理#规范驱动开发#代码审查#测试保护

关键要点

  • 把智能体的上下文视为有限的工作集。每个会话只做一个任务,并用简短的规格说明把决策传递下去,而不是依赖冗长的对话记录。
  • 采用两份契约:规格说明控制进入任务的内容,而由验收标准推导出的测试控制输出的内容。
  • 不要把智能体的完成报告当作证据。要求提供可执行检查,评审测试,并检查最终 diff。
  • 在实现期间保护已批准的测试。否则,被要求让构建通过的智能体可能削弱检查,而不是修复代码。
  • 只有在单个智能体的工作流稳定可靠后,才引入子智能体和并行工作。代码生成的速度可能比人工评审扩展得更快。

AI 编程智能体并没有只是让我们团队打字写代码更快。它们改变了我们投入注意力的地方。

在智能体出现之前,开发者通常会在从阅读 issue 到实现与评审的整个过程中持续携带任务上下文。借助智能体,探索、规划、编码、测试和报告可以在彼此独立的上下文中发生。这可以减少常规工作,但也会创造新的方式,让约束在阶段之间丢失。一份自信的总结可能掩盖不完整的实现。一次通过的构建可能掩盖被削弱的测试。同一聊天中的第二个任务可能继承第一个任务的假设。

对我们来说,更有用的问题越来越少地关乎提示词措辞,而更多关乎流程设计:哪些信息必须留存下来,哪些检查决定工作是否继续,以及人必须在何处叫停循环?

下面的示例使用 Java 和 Spring Boot 术语,但该工作流与语言和智能体无关。它是一份工作协议,用于从 issue 推进到经过评审的变更,同时不把聊天记录当作事实来源。

上下文是一种预算

智能体会话开始时,内容远不止开发者输入的那条消息。它还可能包含系统提示词、工具描述、仓库指令、选中的文件、命令输出以及更早的对话。所有这些都在争夺同一份工作上下文。

我们遇到了三种反复出现的失败模式。

上下文太少。 诸如“添加测试”这样的请求在语法上清晰,但在操作上含糊。智能体可能为一个服务创建单元测试,而团队期望的是对完整流程进行端到端测试。结果可能相对于请求是正确的,但相对于任务却是错误的。

上下文太多。 长时间运行的聊天会积累无关的调查、被放弃的方案、日志和决策。后续工作可能复用来自错误模块的模式,或悄悄丢掉很早之前声明的约束。

相信报告而不是产物。 智能体说实现已完成且所有检查通过。报告听起来精确,于是它成了检查测试、命令输出或 diff 的替代品。

第一个实际改变很简单:每个会话只做一个任务。更大的任务被拆分为多个阶段,并带有明确的交接产物。我们不再把整段探索记录带入实现阶段,而是要求智能体把决策压缩成一份规格说明,让新的会话可以阅读。

第二个改变是将持久的项目知识与任务特定上下文分开。构建命令、模块约定、测试框架和禁止路径应放在版本化的仓库指令文件中,例如 AGENTS.md。单个工单的目标和验收标准应放在任务规格说明中。运行时输出和临时调查笔记只应留在当前会话中。

这种分离让上下文变得可评审。团队可以讨论一个小型 Markdown 文件;但无法可靠地评审智能体在冗长对话中推断出的一切。

两份契约:规格说明控制输入,测试控制输出

我们现在把规格说明和测试视为实现周围的两份契约。

规格说明控制输入。在规划模式下,智能体阅读 issue 和相关代码、提出问题,并起草一份简短文档。开发者在实现开始前编辑并批准它。有用的章节被有意保持朴素:

  • 上下文: 服务、issue 和相关类。
  • 目标: 要添加或改变的行为。
  • 需求: 编号且可独立检查。
  • 范围之外: 任务不得扩展到的文件、端点或设计。
  • 设计: 预期方法,不包含逐行伪代码。
  • 验收标准: 测试可以断言的陈述。
  • 计划: 有序的实现顺序。

例如,一个限流任务可能指出:GET /api/orders 按 API key 限流;超过限制的请求返回 429 并带有 Retry-After;现有的缺少 key 响应必须保持为 401。它还应声明分布式协调和其他端点不在范围内。

范围之外这一节很重要,因为智能体常常因“完整”而受到奖励。没有边界时,一个本地过滤器可能变成分布式限流设计。那也许是一个有趣的设计,但它不是已批准的任务。

测试控制输出。每个验收标准都会在实现开始前变成可观察的检查。对于限流器,测试应覆盖:额度内的请求、第一个超过额度的请求、相互独立的 key、配置覆盖,以及未改变的身份认证路径。

这形成了一条可追踪的链路:

  1. 一项需求解释行为为什么重要。
  2. 一个验收标准描述可以观察到什么。
  3. 一个测试将该观察编码。
  4. 实现让测试通过。
  5. 评审检查 diff 是否仍然符合需求。

这条追踪链比生成的代码量更有价值。如果一项测试无法与某个标准关联,团队应追问它为什么存在。如果某个标准没有测试,团队应在实现开始前决定这种遗漏是否有意为之。

六个步骤与三个人工检查点

我们的 issue 到合并循环有六个步骤。

  1. 开发者说明 issue、目标和约束。项目范围的约定已经存在于仓库中。
  2. 智能体探索代码并起草任务规格说明。
  3. 开发者批准规格说明。 这是发现误解成本最低的时点。
  4. 智能体根据验收标准编写会失败的测试。开发者批准测试,并保护它们不被实现阶段的编辑改动。
  5. 智能体实现、运行检查、阅读失败信息,并迭代直到已批准的测试通过。
  6. 一个全新的评审者依据规格说明检查 diff。开发者阅读最终 diff,并决定是否合并。

智能体在检查点之间完成大部分机械性工作。这些检查点不是仪式性批准。每个检查点回答不同的问题:

  • 我们是否在解决正确的问题?
  • 这些检查是否代表我们已达成一致的行为?
  • 最终变更是否在不引入不可接受风险的情况下满足约定?

并不是每个一行改动都需要这套工作流。我们把它当作一种升级阶梯。小型原型可能只需要仓库指令和清晰的请求。如果智能体误解了任务,就增加规格说明。如果实现不可靠,就在代码之前增加测试。如果两者都失败,就同时使用两份契约和全部三个检查点。

为什么构建通过仍可能是错误结果

反馈循环让可执行检查而不是智能体的完成感来决定工作是否继续。编译器、代码检查工具、测试套件、覆盖率门禁和浏览器自动化都可以承担这一角色。

显而易见的指令是:持续工作,直到测试通过。这有用,但不完整。

一个以构建通过为优化目标的智能体可能发现,修改测试比修改生产代码更容易。严格断言变得宽松。一个难以通过的测试在它本应检验的组件周围加上了 mock。一个失败用例被禁用。测试套件变成绿色,而请求的行为仍然损坏。

例如:

$ java
// 之前:验收标准是明确的。
assertThat(response.getStatus()).isEqualTo(429);

// 之后:构建通过了,但该验收标准已不再有测试覆盖。
assertThat(response.getStatus()).isIn(200, 429);

在快速评审中,第二个断言可能看起来像防御性测试代码。实际上,它移除了该功能本应强制维持的区别。

我们采用三道防线。

第一,在开发者批准测试后,实现智能体不能编辑测试目录。机制因工具而异:编辑范围、权限规则或忽略路径。关键点是边界被强制执行,而不只是被请求。

第二,测试编写和实现可以在不同会话中进行。实现会话收到规格说明和已批准的测试,而不是一条宽泛的指令——为了达到绿色构建可以修改任何必要内容。

第三,一个全新的评审者专门查找被削弱的断言、新增的 mock、被禁用的测试以及计划之外的生产代码变更。开发者仍然阅读最终 diff;评审者减少噪声,而不是替代责任。

评审循环需要硬性停止条件

评审智能体有用,因为它从干净的上下文开始,可以将产物与规格说明进行比较。它不太可能为实施期间做出的决策辩护。

然而,如果没有停止规则,评审可能变成无休止的往返。每一轮都会发现另一个小改进,最终实现智能体开始重写稳定代码以满足风格偏好。

我们将自动评审循环限制为最多三轮。实现智能体修复具体发现的问题,请求再次评审,并在评审无问题或达到上限时停止。它的最终报告必须区分已修复内容与仍然存在的内容及其原因。

在最后一轮自动评审之后仍然未决的事项,由人决定。设置上限的目的并不是声称三轮普遍最优,而是防止在功能检查已经通过后,自主循环继续进行无界改动。

评审提示词也应保持狭窄。要求它将 diff 与已批准的规格说明进行比较、识别范围之外的变更、检查测试强度,并报告未解决的风险。泛泛地要求“评审所有内容”往往产生偏好,而不是证据。

更多智能体并不会消除瓶颈

子智能体在独立上下文中运行,拥有自己的指令和工具。这可以提供专业化、上下文隔离和并行探索。搜索子智能体可以检查多个模块,只返回相关路径。评审子智能体可以读取代码但不能修改代码。独立检查可以在不让原始输出塞满主会话的情况下运行。

代价是重复的上下文和额外监督。每个子智能体都必须对仓库有足够理解才能完成工作,而且每个结果都必须先经过评估,父级智能体才能依赖它。

当工作可以清晰拆分时,子智能体有帮助:全仓库发现、独立验证,或具有不同规则集的评审流水线。对于一个能轻松放入单个会话的小修复,或开发者希望逐步观察的工作,它们帮助不大。

并行任务除了独立聊天,还需要文件系统隔离。Git 工作树提供多个由同一仓库支持的工作目录,每个目录都检出在自己的分支上。智能体可以在每个工作树中处理一个任务,而不会与另一个任务的变更发生冲突。

[LOADING...]

限制性资源仍然是评审。多个智能体可以并发产出 diff,但开发者必须理解行为、在不同问题域之间切换,并做出合并决策。如果生成能力提高而评审能力没有提高,队列只是从“待办”移动到“待评审”。

这会改变规划。团队应把评审时间作为智能体辅助工作的一部分来预算,而不是把生成的代码视为已完成的工作。

配置即代码

一旦工作流稳定下来,下一个问题是把它的指令存储在哪里。

个人偏好应放在仓库之外:响应语言、偏好的解释风格或个人快捷方式。团队行为应纳入版本控制:项目指令、规则、可复用技能、编辑边界、集成和持久的项目知识。

三类资产服务于不同目的:

  • 规则是在其作用域匹配时就应适用的行为,例如测试使用 JUnit 5,或永远不编辑生成源。
  • 技能是为特定类型任务加载的过程,例如测试 Spring MVC 控制器或调查不稳定的集成测试。
  • MCP 服务器将智能体连接到外部系统,例如 issue 跟踪器、浏览器、源码托管平台或设计工具。

[LOADING...]

把这些文件当作可执行配置。规则如果匹配不到预期文件,就无效。技能如果其描述从不触发,就是死代码。暴露大量工具目录的集成可能在开发者提问之前就消耗上下文。

我们是在一次现场演示中学到这一点的。智能体生成了一个用于编写 Spring 控制器测试的项目技能。内容看起来正确,评审者没有发现问题。当我们删除一个测试并要求智能体重新创建它时,该技能被忽略了。它的元数据不允许自动选择。修正元数据后,下一次运行加载了该技能。

教训并不在于某一个元数据字段。触发行为必须被测试。添加规则或技能后,运行一个应激活它的小任务,并检查它确实被激活的证据。如果它没有被选中,先修改作用域或描述,再扩展正文。

项目记忆需要同样的纪律。存储无法从代码中廉价重建的持久知识:为什么某个模块禁止改动、哪个运维陷阱导致了事故,或为什么存在某个架构例外。不要存储普通代码导航就能恢复的类位置和方法签名;这些事实会过时,并污染后续会话。

从一个真实任务开始

团队不需要一次性采用整套工作流。从一个结果可测试的中等规模任务开始。

把共享的构建和测试命令放进仓库指令。要求智能体起草规格说明。编辑它,直到需求和排除项清晰。根据验收标准推导测试,批准它们,并在实现期间保护它们。运行有边界的评审循环,并阅读最终 diff。

之后,评估流程,而不仅仅是功能:

  • 智能体在哪里误解了任务?
  • 哪些信息本应放在仓库中而不是聊天中?
  • 每个验收标准是否都有可观察的检查?
  • 实现期间是否有任何断言变弱?
  • 评审输出投入了多少人工时间?
  • 哪些重复指令应变成规则或技能?

目标不是最大程度的自主。目标是在人的决策与机器执行之间实现可重复的交接。智能体可以探索、生成、运行和迭代。团队仍然负责定义工作、批准证据并理解结果。

参考资料

关于作者

Viktoria Evdokimova 是 Explyt 的产品经理,与在 JetBrains IDE 中采用 AI 编程智能体的工程团队合作。她将开发者反馈和生产工作流经验转化为实用指南,帮助团队引入智能体辅助开发。## 披露

作者就职于 Explyt,其团队打造了本文讨论的其中一款 AI 编程智能体。本文所述工作流特意保持工具无关性:它不依赖 Explyt,也不依赖任何其他特定产品。

分享此页面

  • Bluesky
  • LinkedIn
  • Reddit
  • Hacker News
  • 邮件
  • 链接已复制

发现错误或有补充内容?在 GitHub 上编辑此页面


作者

Viktoria Evdokimova

产品经理,从事面向 Java/JVM 生态的 AI 编程智能体相关工作。她关注 AI 智能体如何与 IDE 集成以支持调试、重构和代码导航,并热衷于开发者生产力、JVM 工具链,以及帮助专业人士找到……

相关文章

AI · Java · 测试

Viktoria Evdokimova · 2026年10月7日 · 597 次浏览

构建通过,任务却错了:使用 AI 编程智能体的 Java 团队工作协议

AI · 开发者工具 · GenAI

Maximillian Arruda · 2026年9月4日 · 2,569 次浏览

从规范驱动开发到 Java 项目中的活规范

AI · 开发者工具

Maks Danylenko · 2026年10月9日 · 313 次浏览

如何创建智能体技能:工具、测试与安装

AI · GenAI · Java

Viktoria Evdokimova · 2026年9月3日 · 687 次浏览

你的 AI 智能体运行过调试器吗?一个 JVM Bug,两次智能体运行

参与讨论