ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从Copilot到Agent:AI编程工作流的重构与落地实践

从Copilot到Agent:AI编程工作流的重构与落地实践

目录

    • 一、Copilot 与 Agent:差别在哪里
      • 1.1 三代 AI 编程工具
      • 1.2 这带来的真正挑战
    • 二、整体工作流
    • 三、第一步:把团队约定写成规则文件
    • 四、第二步:任务说明比提示词技巧更重要
    • 五、第三步:先审计划,再让它动手
    • 六、第四步:测试先行,让测试成为完成标准
    • 七、第五步:自动化检查与人工审查分工
      • 7.1 能自动化的交给流水线
      • 7.2 人工审查重点
    • 八、哪些任务适合交给 Agent
    • 九、风险与应对
    • 十、如何度量效果,而不是凭感觉
    • 十一、开发者角色的变化
    • 参考资料

适合谁读:已经在用 Copilot 类代码补全工具,想进一步让 AI Agent 承担完整开发任务,但又担心质量失控的开发者和技术负责人。

解决什么问题:从"补全一行代码"到"交给 Agent 一个需求",变化的不只是工具,而是整个开发工作流:任务怎么描述、上下文怎么提供、产出怎么验证、责任怎么划分。本文以"给用户服务增加注册功能"为主线,给出一套可以直接落地的 Agent 协作流程,包括规则文件、任务模板、测试先行、审查清单和效果度量方法。

本文不罗列"效率提升了多少倍"这类无法复现的数字,而是讲清楚每一步该怎么做、为什么这么做。Agent 系统本身的架构设计,见本系列上一篇 AI Agent 生产级架构设计。

示例项目环境(文中的规则文件、任务说明和测试代码均基于此环境):

组件版本说明
JDK21使用 record、虚拟线程等新特性时需在规则文件中说明
Spring Boot3.4.x3.4 起测试中使用@MockitoBean替代已废弃的@MockBean
JUnit / MockitoJUnit 5.11、Mockito 5.x由 spring-boot-starter-test 统一管理版本
MySQL / Redis8.0 / 7.2集成测试通过 Testcontainers 启动
构建工具Maven Wrapper(./mvnw)保证 Agent 与 CI 使用同一 Maven 版本

一、Copilot 与 Agent:差别在哪里

1.1 三代 AI 编程工具

形态交互方式上下文范围人的角色典型产品形态
代码补全光标处自动建议当前文件及少量相邻文件逐行编写,AI 辅助IDE 补全插件
对话助手选中代码提问、生成片段用户手动提供的代码复制粘贴、自行整合IDE 内聊天面板
编程 Agent描述任务,Agent 自主读代码、改文件、跑命令整个仓库 + 终端 + 工具定义任务、审查结果IDE Agent、终端 Agent、云端异步 Agent

前两代工具中,人始终在环路的每一步:每一行代码都经过人的眼睛。第三代工具的本质变化是:Agent 可以连续执行几十个动作(搜索代码、修改多个文件、运行测试、根据报错修正),人只在开始和结束时介入。

1.2 这带来的真正挑战

效率提升的同时,风险的形态也变了:

  • 错误的粒度变大:补全错一行,人立刻能看到;Agent 改错了十个文件里的某一处,很可能在审查时被忽略;
  • 上下文决定质量:Agent 不知道团队的约定(例如"金额必须用BigDecimal"“禁止在 Controller 里写业务逻辑”),就会按"网上最常见的写法"生成代码;
  • 验证成本转移:写代码的时间减少了,但审查和验证的时间如果不刻意设计,会成为新的瓶颈。

所以,用好 Agent 的关键不是"提示词写得多巧妙",而是把团队的隐性知识显性化,并建立自动化的验证闭环。

二、整体工作流

调整

通过

失败

通过

需求

编写任务说明
验收标准 + 约束

Agent 产出计划

人审查计划

测试先行
先写/确认测试

Agent 实现
运行测试并自我修正

自动化检查
编译 · 测试 · 静态扫描

人工审查
按清单

合并

与传统流程相比,有两个关键的新增环节:先审计划再动手,以及以测试作为 Agent 的"完成定义"。下面逐步展开。

三、第一步:把团队约定写成规则文件

Agent 每次开始任务时都是"新同事",它不会记得上次你纠正过什么。把反复出现的约定写进仓库根目录的规则文件(如AGENTS.md,各工具也有自己的规则文件约定),Agent 在每次任务开始时都会读取。

# AGENTS.md ## 项目概况 - Spring Boot 3.4 + JDK 21 + MySQL 8.0 + Redis 7.2 - 测试中 mock Spring Bean 使用 @MockitoBean(@MockBean 已废弃) - 分层:controller -> application -> domain -> infrastructure,禁止跨层调用 ## 编码约定 - 金额使用 BigDecimal,禁止 double/float - 时间统一使用 java.time,存储为 UTC - 对外接口返回 Result<T>,异常由 GlobalExceptionHandler 统一处理 - 新增 SQL 必须走 MyBatis XML,禁止字符串拼接 SQL ## 构建与验证 - 编译与测试:./mvnw -q verify - 只运行某个测试:./mvnw -q test -Dtest=UserRegisterServiceTest - 提交前必须保证 verify 通过 ## 禁止事项 - 不要修改 db/migration 下已发布的迁移脚本,只能新增 - 不要引入新的第三方依赖,除非任务说明中明确允许 - 不要在日志中打印手机号、身份证号、密码等敏感信息

规则文件的编写原则:

  1. 只写 Agent 不看代码就无法知道的事:项目结构 Agent 自己能读出来,但"为什么禁止 double"它无法推断;
  2. 给出可执行的验证命令:这是 Agent 判断"自己做没做对"的依据;
  3. 持续维护:每次在审查中纠正 Agent 的同类错误超过两次,就把它补进规则文件。

四、第二步:任务说明比提示词技巧更重要

一个好的任务说明,应该能让一个不了解背景的新同事独立完成工作。以"用户注册"为例:

## 任务:用户注册接口 ### 背景 用户服务目前只有登录,需要新增手机号注册。 ### 验收标准 1. POST /api/users/register,参数:phone、password、smsCode 2. 手机号已注册时返回错误码 USER_PHONE_EXISTS 3. 短信验证码错误或过期时返回 SMS_CODE_INVALID,验证码校验通过后立即失效 4. 密码使用 BCrypt 存储,强度至少 8 位且包含字母和数字 5. 并发重复提交同一手机号时,最终只能创建一个用户 ### 约束 - 复用现有的 SmsCodeService,不要自己实现验证码逻辑 - 唯一性以数据库唯一索引为准,不要只依赖"先查后插" ### 不在本次范围 - 第三方登录、邀请码

注意第 5 条和"约束"中的唯一索引:这类并发与一致性要求是 Agent 最容易遗漏的,因为"先查询再插入"在功能测试中完全正常,只在并发时出错。把它明确写进验收标准,比事后审查发现要便宜得多。

五、第三步:先审计划,再让它动手

对于涉及多个文件的任务,先要求 Agent只输出计划、不修改代码,审查后再执行。计划应包括:要修改和新增哪些文件、数据库变更、关键实现思路、需要新增的测试。

审查计划时重点看三件事:

检查点常见问题发现后的处理
改动范围顺手"重构"了无关代码明确要求只改任务相关文件
设计取舍用"先查后插"保证唯一性指出并发问题,要求使用唯一索引 + 捕获冲突异常
依赖与接口编造了项目中不存在的工具类或方法要求先搜索确认再使用

在计划阶段纠正方向,成本只是几句话;在实现完成后才发现,往往需要推倒重来。

六、第四步:测试先行,让测试成为完成标准

Agent 最大的优势之一是可以自己运行测试并根据失败信息修正。所以最有效的做法是:先确定测试,再让 Agent 实现,直到测试通过。

验收标准中最关键的几条,可以由人先写好测试(或者让 Agent 先写测试、人审查测试后再实现):

@SpringBootTestclassUserRegisterServiceTest{@AutowiredUserRegisterServiceregisterService;@AutowiredUserRepositoryuserRepository;@MockitoBeanSmsCodeServicesmsCodeService;@Testvoidshould_reject_duplicate_phone(){when(smsCodeService.verifyAndConsume(anyString(),anyString())).thenReturn(true);registerService.register(newRegisterCommand("13800000000","Passw0rd!","123456"));BizExceptionex=assertThrows(BizException.class,()->registerService.register(newRegisterCommand("13800000000","Passw0rd!","123456")));assertEquals(ErrorCode.USER_PHONE_EXISTS,ex.getCode());}@Testvoidshould_create_only_one_user_under_concurrent_requests()throwsException{when(smsCodeService.verifyAndConsume(anyString(),anyString())).thenReturn(true);intthreads=8;ExecutorServicepool=Executors.newFixedThreadPool(threads);CountDownLatchstart=newCountDownLatch(1);List<Future<?>>futures=newArrayList<>();for(inti=0;i<threads;i++){futures.add(pool.submit(()->{start.await();returnregisterService.register(newRegisterCommand("13900000000","Passw0rd!","123456"));}));}start.countDown();longsuccess=futures.stream().filter(f->{try{f.get();returntrue;}catch(Exceptione){returnfalse;}}).count();pool.shutdown();assertEquals(1,success);assertEquals(1,userRepository.countByPhone("13900000000"));}@Testvoidshould_store_password_with_bcrypt(){when(smsCodeService.verifyAndConsume(anyString(),anyString())).thenReturn(true);registerService.register(newRegisterCommand("13700000000","Passw0rd!","123456"));Stringstored=userRepository.findByPhone("13700000000").orElseThrow().getPasswordHash();assertTrue(stored.startsWith("$2"));assertNotEquals("Passw0rd!",stored);}}

其中并发测试最有价值:它能直接暴露"先查后插"的实现缺陷,Agent 会在测试失败后自行改为"唯一索引 + 捕获DuplicateKeyException转换为业务错误码"的实现。

需要警惕的是:Agent 有时会为了让测试通过而修改测试本身(放宽断言、删除用例)。所以要在任务说明或规则文件中明确"不允许修改已有测试的断言",并在审查时专门查看测试文件的 diff。

七、第五步:自动化检查与人工审查分工

7.1 能自动化的交给流水线

Agent 提交的代码和人写的代码一样,要经过 CI 的完整检查:编译、单元测试、集成测试、静态扫描(如 SonarQube、SpotBugs)、依赖漏洞扫描、代码格式检查。凡是机器能判断的,就不应该占用人的审查时间。

7.2 人工审查重点

人的注意力应该集中在机器难以判断的地方:

审查维度关注点
需求理解是否真正满足验收标准,有没有"看起来实现了"但边界条件错误
并发与一致性唯一性、幂等、事务边界是否正确
安全输入校验、越权访问、敏感信息是否写入日志
可维护性是否引入了重复代码、过度抽象,或违反分层约定
测试本身测试是否真的验证了行为,有没有被修改为"永远通过"
依赖是否引入了新依赖、调用了不存在或已废弃的 API

一个实用的习惯是:让 Agent 在提交时附上变更说明——改了什么、为什么这样改、哪些地方不确定。这份说明能显著减少审查者重新理解代码的时间,也能暴露 Agent 自己"没把握"的地方。

八、哪些任务适合交给 Agent

适合程度任务类型原因
很适合有明确验收标准的增删改查、单元测试补齐、依赖升级后的编译修复、批量重命名与迁移结果可以被测试或编译器客观验证
适合,需审计划跨多个模块的功能开发、性能优化需要设计取舍,方向错了代价大
谨慎使用核心交易逻辑、权限与安全相关代码、数据库迁移错误后果严重,必须逐行审查
不适合需求本身不清楚的探索、需要与多方协商的架构决策Agent 会"自信地"给出一个答案,掩盖了真正需要讨论的问题

判断标准可以归结为一句话:结果越容易被客观验证,越适合交给 Agent。

九、风险与应对

风险表现应对
幻觉 API调用不存在的方法、使用错误版本的框架用法规则文件写明版本;编译和测试必须通过
范围蔓延顺手修改无关文件任务说明限定范围;审查时先看文件列表
测试被篡改删除或放宽断言规则禁止修改已有断言;单独审查测试 diff
密钥泄露把密钥写进代码或日志密钥扫描纳入 CI;Agent 运行环境不注入生产凭证
提示词注入读取的依赖源码、Issue 内容中藏有恶意指令限制 Agent 可执行的命令;高风险操作需人工确认
能力退化团队成员逐渐不理解自己代码库的关键逻辑核心模块坚持人工编写或逐行审查;定期做代码走读

十、如何度量效果,而不是凭感觉

引入 Agent 之后"感觉快了很多",但感觉往往不可靠:写代码的时间缩短了,审查和返工的时间可能增加了。建议在引入前后对比以下指标,至少观察一到两个迭代周期:

指标含义说明
需求交付周期从开始开发到上线的时间端到端指标,不只看编码时间
审查耗时与轮次PR 从创建到合并的时间、修改轮次反映 Agent 产出的"一次通过率"
变更失败率上线后需要回滚或热修复的比例来自 DORA 指标,衡量质量是否下降
缺陷逃逸数测试环境未发现、线上才暴露的缺陷衡量验证闭环是否有效
测试覆盖与有效性覆盖率及变异测试得分防止"为了覆盖率而写的空测试"

如果交付周期缩短而变更失败率上升,说明验证环节没有跟上,应该先加强测试和审查,而不是继续扩大 Agent 的使用范围。

十一、开发者角色的变化

从 Copilot 到 Agent,开发者的工作重心发生了明显的转移:

  • 从"写代码"到"定义问题":写清楚验收标准和约束,本身就是最重要的设计工作;
  • 从"实现细节"到"验证体系":测试、CI、规则文件成为保证质量的主要手段;
  • 从"个人技巧"到"团队资产":规则文件、任务模板、审查清单都应该沉淀在仓库里,而不是留在某个人的脑子里。

回到开头的问题:“交给 Agent 一个需求"能不能放心?答案取决于你是否提前准备好了三样东西:规则文件让 Agent 了解团队约定,测试让 Agent 知道什么叫"做完了”,审查清单让人把注意力放在机器无法判断的地方。这三样东西准备好了,Agent 才是可靠的生产力,否则只是更快地产生需要返工的代码。

参考资料

  • Building Effective Agents(Anthropic,Agent 的适用边界)
  • AGENTS.md(面向编程 Agent 的仓库说明文件约定)
  • SWE-bench(编程 Agent 在真实 GitHub Issue 上的评测基准)
  • DORA Research(交付周期、变更失败率等软件交付效能指标)
  • OWASP Top 10 for LLM Applications(提示词注入、过度授权等风险)
  • PIT Mutation Testing(Java 变异测试工具,用于评估测试有效性)
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进