1. 一次事故现场:说好的碳硅协同,变成了AI挖坑、人填坑
2024年下半年,我给自己定了一个很高调的开发关键词:碳硅协同。当时的想法很简单——我是碳基工程师,大模型是硅基队友,两边各干各自擅长的事,软件的产出效率就能翻倍。这种论调在圈子里已经流行过一阵,不过我自己真正动手跑过几个小项目之后,发现事情远没有那么美好。其中一个叫“会员积分模块重构”的小项目,几乎让我把“协同”两个字从词典里划掉。
事故发生在周日晚上的线上环境。运营那边启动了一个秒杀活动,活动结束后大量用户反馈积分不对:有的人秒杀订单该到账的积分没到,有的人却被加了两份。我们紧急翻查代码,最后定位到的问题让人哭笑不得——积分明细和账户余额之间的一致性被新逻辑打破了。
旧系统的设计其实非常朴素:积分账户表永远只有一个来源,就是积分变动流水表。新增积分的操作必须同时做两件事,先插入一条流水,再更新账户余额,这两步在同一个数据库事务里完成。可那天晚上同事为了赶需求,让AI助手直接改了代码生成逻辑。AI生成的实现看起来每一步都合理:检查订单状态、生成积分变动记录、更新账户余额。但它在生成过程中漏掉了系统一个极其重要的隐藏契约:下游有个定时对账任务,每天会扫描账户余额表,如果发现累计流水和余额不一致,就会触发自动修正,修正逻辑是按照最近N天的流水重新计算余额。
结果就是,流水先写入、余额还没更新,对账任务跑过来发现账不平,主动重算并加了一次分;随后业务回调里原来的逻辑又正常执行了一次,又加了一次分。用户拿到的积分就这么翻倍了。只看AI生成的那段代码,逻辑是自洽的、局部正确、甚至测试都写得挺规范,但它根本不知道这个老系统里还存在一个“自动修正”的队友。
那次事故之后我专门在项目笔记里写了一句话:如果我们不能让AI在动手写逻辑前先理解系统的契约,那它产出的代码就像实习生在一家老店后厨乱炒菜——每道菜看起来都会做,但不知道灶台的脾气、老板的规矩、高峰期谁先谁后。
这句话最终演化成了“碳硅协同开发篇”整个系列的基础。我在复盘那次积分事故时正式决定:不能让AI在完全自由的状态下直接产出可上线的业务代码。于是有了这篇文章的主角——ELR。ELR一开始并不是产品名,也不是什么开源框架,它只是一个内部代号,代表一套我反复迭代出来的人机协作闭环。项目名字取的是三个英文单词的首字母:Explain、Learn、Review。这套东西诞生的过程很平淡,没有华丽的顶层设计,全是靠一次一次填坑填出来的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ELR不是框架,是一套被真实项目逼出来的协作闭环
很多团队都把AI辅助开发理解成“给AI一个需求,让它自己写完代码,人等着验收”。但我自己的实践和经验是,这种理解在简单页面或一次性脚本上确实成立,一旦碰到有历史包袱的核心业务模块,几乎必然翻车。原因非常简单:**企业级业务系统的真实约束,绝大部分不在代码里,而在流程、历史数据、上下游约定、补偿机制和对账规则里。**这些东西不会出现在需求文档中,AI光凭阅读代码库很难完整推断出来。
ELR之所以被我反复打磨,就是因为它试图解决这个核心矛盾:既要让AI快速产出大量实现代码,又要让它不破坏人类工程师脑中的“隐性契约”。整个闭环分成三段,每一段的职责、输入输出以及评审主体都不一样。
2.1 Explain:动手写代码之前,AI先交一份设计说明
过去我们习惯让AI直接生成代码,然后人再去看代码有没有问题。ELR把顺序改了:第一步不写代码,只要求AI产出一份设计说明,内容包括它准备怎么拆模块、改哪些文件、动哪些接口、可能影响哪些上下游、存在哪些风险点。这份说明要基于任务卡片给出的行为范围来写,不能自己随意扩大边界。
你可能会觉得多此一举,但我实测之后的感受是,这一步能把后期返工率降低一半以上。原因是当AI不写代码的时候,它可以更冷静地把整个方案的逻辑链条梳理一遍;而人在评审这份说明时,看到的不是一段段代码,而是它的思考路径,也就更容易发现AI在哪些地方对系统的理解是错的。
2.2 Learn:用预期行为作为判据,让AI在闭环里自我修正
Learn环节是ELR的发动机。AI根据通过评审的设计说明,小步实现代码,然后立刻喂给一组“预期行为验证”。这组验证不是单纯的单元测试,它更强调行为描述,也就是对某个输入,系统的可观测输出应该是什么。失败之后,AI会拿到具体的失败信息,自己分析原因,生成修复补丁,然后再次验证。
这个环节能跑通的前提,是人类必须事先把预期行为写得足够精确。哪些场景允许重复执行、哪些字段必须保持特定枚举值、失败时抛什么异常、错误码的命名规范是什么——这些契约越明确,AI的自我迭代就越精准。
2.3 Review:任何AI产出都不能绕过碳基的判断
ELR的最后一环是Review,由人类工程师对AI交上来的完整改动做评审。重点不是逐行读代码,而是逐个确认每个改动是否能回溯到任务卡片中的某一条预期行为。凡是不能回溯的,都属于越界改动,一律打回。
用做菜来打比方,Explain是让AI先报一遍菜谱,Learn是试吃后调整咸淡,Review则是正式上桌前主厨亲自把关。区别在于,过去主厨把食材丢给AI就看电视去了,ELR强制主厨必须待在后厨,只是不用亲自颠勺切菜了。
碳硅协同和之前流行的全自动AI编程最大的差别就在这里。全自动模式强调AI自主跑完整个开发流程,人只在最后看结果;而ELR的流程里,人始终掌握契约的定义权和最终验收权。硅基负责执行和效率,碳基负责边界和裁决,谁也不要越位。
3. 一次完整实战:用ELR驱动会员积分模块的“保鲜重构”
理论讲完,我用一个真实干过的项目串一遍流程。这个项目的代码基础就是开篇提到的会员积分模块,大概1200行PHP老代码,三块业务逻辑散落在接口文件、定时任务和SQL视图里。我们不对外的目标是“保鲜重构”:外部行为不允许有任何变化,内部结构重新整理。
重构这类老模块,最怕的不是代码写不出来,而是改完之后业务表现变了。所以我把任务卡片作为整个ELR流程的锚点。任务卡片是这么设计的:
| 项目 | 内容 |
|---|---|
| 任务编号 | M6-2024-002 |
| 行为范围 | 积分新增流程(订单支付回调到积分账户落库) |
| 输入 | userId、orderId、paidAt、实付金额(分)、商品类目 |
| 期望行为1 | 同一订单重复回调不得重复加分 |
| 期望行为2 | 积分按实付金额向下取整,比例按商品类目读取,默认1:1 |
| 期望行为3 | 每次增分必须写流水,流水包含bizId,枚举值按字段口径附录校验 |
| 期望行为4 | 失败时抛出带特定错误码的可重试异常 |
| 不改动范围 | 积分过期逻辑、账户冻结逻辑、定时对账任务 |
| 验证标准 | 对重构前抓取的历史流量回放,账户最终余额、流水条数、流水内容完全一致 |
这张卡片最大的特点,就是有明确的“不改动范围”。AI非常容易在追求代码整洁时顺手扩大改动,把不相关的逻辑也重构了。任务卡片相当于画了一条红线:禁区就是禁区,不管你觉得它有多“该改”。
然后走ELR流程。
Explain阶段,AI给出的方案是把散落在三处的积分计算规则收敛到一个积分服务类中,接口文件只做参数校验和调用,定时任务保留但改为只依赖账户表。方案能看出AI确实理解了部分问题,但它漏了一个我在任务卡片里没有写清楚的历史坑:老订单表里没有商品类目字段,需要从商品快照表反查类目。它在设计说明里默认从订单表直接取类目,如果在真实改造中按这个方案走,老订单的积分比例就会算错。
好在这只是Explain,不是代码。我在评审时直接把这个坑补进了行为范围里,同时要求AI在最终方案中写明类目回退策略。这就是碳硅协同开发的价值——AI负责把大方案铺开,人负责把AI看不见的历史细节钉在方案里。
Learn阶段,我们要求AI按一次一个小步的方式实现,每完成一步立刻跑预先抓好的历史流量回放。回放数据选了活动月开始到月底的全量积分消息样本,新代码在测试环境重放后,对比旧系统的积分账户余额、流水条数和流水字段内容。第一次回放跑完,结果三项全绿。说实话当时我很兴奋,差点直接把代码合入主干。但Review环节拦住了我——我在人工审查diff时发现,AI把流水表bizId字段的枚举值写错了。旧系统约定是“bizType_order_paid”,AI生成的代码里变成了“order_paid”,虽然回放测试用的是新代码对老消息的回放,字段细节这种问题未必会被主流程测试覆盖,但若上线,数据仓库的统计口径就废了。
这直接印证了一个原则:自动验证通过,不代表所有契约都对。ELR的人工Review不是走形式,而是专门盯着AI最容易无意识“合理化”的地方——字段命名、枚举取值、错误码、日志格式。这些内容太琐碎,AI往往凭语感自行发挥,但恰恰是企业系统的硬约束。
重构完成之后的效果也很直观:模块代码从1200行降到820行,三个文件拆成七个职责单一的文件,接口文件瘦身明显;基于回放测试和人工评审,整个改动没有发生一例线上行为回归。时间上更有说服力——按老办法,这种重构我保守估计要40人天;用ELR之后,实际消耗是12人天,其中人类工程师实际投入大约4.5人天,剩下的时间由AI在沙箱里跑实现和验证。
我没法把这个数字当成普遍标准,但它至少说明一件事:碳硅协同如果能控制好契约边界,效率是实打实的,不是营销话术。
4. 三个最容易翻车的节点,和我当时的处置方式
项目做完当然不能直接收工。这类协作流程最怕的不是AI能力不行,而是流程里的某个节点出现隐患时,你没有及时发现。这里把三个最容易翻车的节点单独拿出来讲,每个都是实际踩过的。
4.1 幻觉问题:AI自主脑补的字段口径
最典型的翻车点就是字段口径的幻觉。大模型训练语料里积累了海量代码片段,它对常见命名有一种“统计惯性”。你让它写“订单支付成功广播”,它大概率会给你写出一个类似order_paid的事件名;但你的老系统里可能约定的是bizType_order_paid。AI写代码很快,这种口径错误会均匀地铺在几百行代码里,光靠人眼review容易看走眼。
我当时的处置方法是把“字段口径附录”做成了任务卡片的强制组成部分。任何涉及数据库字段、事件名、枚举值、错误码的任务,AI在Explain阶段必须先抄一遍相关字段的口径定义,并逐项做自检确认。这里的逻辑是,让AI在开始写代码前就把脑子里最可能产生幻觉的部分暴露到纸面上,人类评审设计说明时第一眼看到的就是它,大大降低了后期兜底的成本。
4.2 上下文漂移:越改越“顺手”的越界重构
第二个坑更隐蔽:上下文漂移。AI如果一次性接触太多信息,它会逐渐淡忘最初的任务边界,尤其在代码审查中看到“丑陋”的老代码时,会忍不住顺手“优化”掉。我们曾经遇到过一次,AI在实现积分服务类的时候,看不上旧代码里一个独立的积分计算函数,直接把这个函数收编成类内部方法。单看这个改动确实更合理、更整洁,服务类的封装更完整,可它破坏了其他模块对旧函数的直接引用关系,导致三个不在本次重构范围内的页面在联调时崩溃。
处理方式很干脆:凡是任务卡片“不改动范围”里列出的内容,出现的任何改动都直接打回,不管改动本身多么合理。同时在每一轮AI的提示词里都重复一遍这条边界,防止长对话让模型把约束忘掉。这个规则听起来很死板,但做重构项目的朋友都知道,边界的价值就是让人觉得它死板。
4.3 验证失真:回放全绿,不代表覆盖全绿
第三个坑来自测试数据失真。第一次回放测试通过时,我几乎以为ELR已经大功告成了。但一个做测试的老同事提醒我:回放用的真实流量样本里,几乎没有“重复回调”这种异常数据。因为真实线上环境里,正常的成功消息不会重复发,重复消息往往出现在故障恢复、队列堆积、重试风暴这些特殊时间段里,常规时间段的流量样本天然缺失。
后来我们专门构造了异常样本,包括重复消息、乱序消息、金额为0的订单、老订单缺失商品类目等用例,重新跑了一遍。果然,重复消息的并发用例直接暴露了一个竞态问题:AI实现里没有对流水表bizId加唯一约束,而是靠业务代码先行判断,两线程同时到达时就出现了重复积分。这个问题如果没测出来,上线之后可能引发比开篇事故更严重的资损。
这次之后,我把验证标准改成了一个明确的检查项:真实流量回放通过只是“正向回归”通过,还必须补齐故障注入和边界样本。ELR的验证环节不再只是验证AI写的代码对不对,更是在验证人类的测试用例全不全。
另外还有一个发生过的小型翻车也值得一提:AI在处理并发积分写入时,主动引入了Redis分布式锁,说“防止高并发下超卖积分”。方案很主流,但它完全没考虑这个模块原本跑在单数据库事务里,行锁和唯一索引已经足够,引入Redis只会增加一个基础设施依赖和一个新的故障点。过度设计是AI代码中的常见病,由于AI不具备业务体量和基础设施的感知能力,这种“技术上更高级但工程上更脆弱”的决策只能靠人类Review来过滤。
ELR走到这一步,它作为一个“诞生记”的故事基本完整了:从一次生产事故触发,到形成Explain-Learn-Review的闭环,再经过一次贯穿重构项目的实战检验,最后在几个关键节点上把可能翻车的漏洞一点点堵上。
5. ELR沉淀下来的,不是工具,而是协作习惯
项目结束之后,我并没有把ELR做成一个完整的平台或框架,它至今仍然是一张任务卡片模板、一套Diff审查规则和一组回放验证脚本的组合。但ELR这个代号并没有被丢弃,它现在已经变成我们团队做碳硅协同开发时的默认工作语言。
有一个特别直观的变化:过去给AI派活儿,大家默认“AI把代码写完”就算完成;现在默认“AI把代码写完并跑通全部验证”才叫完成,而且必须通过人工Review才算真正结束。这个习惯的改变,让团队里所有人对AI产出的审慎程度高了一个级别。
另一个变化是评审节奏。以前代码评审集中在一个迭代结束时做,一次性要看完几百上千行diff,既痛苦又容易漏。ELR把评审变成每4小时一个循环,AI完成一个步骤就到人工那里过一遍,过完再放它跑下一步。人盯的东西没变多,但每一轮盯的范围小了很多,漏网概率直线下降。
如果要把这套习惯总结成几条能让别人直接用的规则,大概是这样的:
- 永远先要设计说明,再要代码。AI写代码之前先写方案,人能看懂方案再让它动手。
- 任务卡片里必须写“不改动范围”。让AI知道边界,比让它知道目标更重要。
- 契约类信息(字段名、枚举值、错误码)强制让AI在开工前自检一遍,不能依赖代码审查阶段人工发现。
- 自动验证通过不等于可以上线。回放测试全绿只能说明正向场景过了,故障注入和异常样本是另一道饭,少了不能上桌。
- Review环节不能只看diff本身,还要看每个改动能不能回溯到任务卡片上的某一条预期行为,不能回溯的改动就是越界。
说到底,ELR教给我最重要的一件事,不是某个具体的技术工具怎么用,而是碳硅协同的真正含义。我一直觉得,很多人对协同的最大误解,是以为协同等于让AI干得更多、人干得更少。但项目做下来我才清楚,协同应该是让机器在高速迭代时始终不越过边界。边界感是人类给的,效率感是AI给的,两者加起来才配叫协同。
现在我又开始把ELR流程应用到一个新的数据同步模块上,预期行为比会员积分还要复杂不少。我没有把握这次会不会又冒出新的坑,但至少流程已经比上次稳多了。如果有新的进展,我应该还会在“碳硅协同开发篇”里继续写下去,等真正跑完那套更复杂的闭环之后,再来分享那些还没踩过的坑。
