
上周有朋友找我聊一件事他接手了一段被 AI 重构过的 Python 代码功能一切正常但问当初为什么用这种写法、为什么把某个模块拆成这样项目里没有任何一点记录。他自己翻聊天记录也没找到因为那是三个月前让 AI 改的对话窗口早就不在了。最后只能对着代码反推硬生生花了一个下午才理清思路。这不是个例。用 AI 改代码越来越顺手大多数人让 AI 输出完那一瞬就默认“代码就是最终成果”。但代码只是结果真正的依据往往存在于对话过程里——当时的约束条件、你提过的要求、AI 解释过的取舍、被否决的替换方案。这些信息如果没有被主动沉淀下来下一次面对这段代码的人很可能就是三个月后的你自己就只剩一个黑盒能看能跑但不知道它“为什么长这样”。这篇文章想聊的就是这件事AI 改完代码后怎么让下一次还能找到当时的依据。适合正在用 AI 编程的开发者适合需要在团队里接受别人 AI 改动的同学也适合那些已经意识到“AI 代码越来越多、越来越难维护”的团队。1. 为什么 AI 改出来的代码依据默认会丢先说结论不是 AI 不告诉你依据而是我们的工作流没有给“依据”留位置。1.1 代码能跑但没人能解释“为什么这么写”传统手写代码时人脑里有一整条决策链为什么选这个框架、为什么拆成三个函数、为什么这里用缓存。即使没写注释代码的形态本身就带着一部分思考痕迹——比如函数命名、异常处理、边界判断都留着人的逻辑。AI 生成代码则完全不同。你提一个需求它直接吐出一段完整实现。如果你只是简单 review 一下然后提交那么原本应该由“写代码的人在脑中完成并潜移默化传递下去”的决策过程就全部消失在了生成对话里。没有人知道 AI 为什么选择 dict 而不是 dataclass为什么在异常分支里 return 而不是 raise为什么不处理某个边界条件。代码的确能跑但后续想改它的人没有任何上下文。我自己的感受是AI 就像一个回答完问题就消失的临时顾问。它在线时说得头头是道一旦对话窗口关闭你手里就只剩它留下的几段代码跟没有解题过程的试卷答案差不多。1.2 对话里的约束条件没跟着代码一起落盘大部分 AI 改代码的典型场景是这样你把当前文件源码贴给它补一句“把登录接口的超时时间从 5 秒改成 3 秒同时加一个重试机制”。它会给你新版代码顺便说一句“这里用了指数退避避免重试风暴”。这句话其实就是关键依据——为什么不直接固定间隔重试因为它考虑到了网关限流。但如果没有人把这句话记录下来一个月后别人看到这段代码很可能觉得“这个退避逻辑太复杂了简化一下”然后出现线上问题。更常见的约束条件还包括某个公共函数不能被改动因为另一个服务也在调用某个配置只能启动时读取因为动态切换会引发缓存不一致某个工具函数不能改签名因为有老版本数据在跑。这些约束通常只存在于你当时给 AI 的提示词里或者 AI 在解释方案时顺口提过一句。代码里没有评论区没有文档里也没有。1.3 批量生成放大了“事后考古”的成本如果只是改三五行代码依据丢了影响还不大看看 diff 基本能猜个大概。但 AI 最擅长的是批量改造——一口气重构一个模块统一替换某个模式给十几个文件同时加统一异常处理。这种改动提交到版本库以后常规手段几乎没法追查依据。Git blame 只能看到“commit Info”如果 commit message 填的是“优化代码”这种话等于没有任何信息。你想知道这个函数为什么从同步改为异步得点开几个文件的 diff 拼凑线索想知道为什么这个算法复杂度明明更高却被采用只能去问当时在场的人然而那个人很可能自己也忘了。所以问题的实质是AI 让“产生代码”的成本急剧降低但“理解代码为什么长这样”的成本并没有同步下降反而因为决策链断裂变得更贵了。后者就是我们通常说的 AI 技术债。2. 改之前先把“依据契约”定下来既然问题出在工作流解法也必须在工作流里找。我现在的原则很简单任何一次让 AI 动手改代码之前先确定这次改动的“依据契约”是什么。2.1 把提示词当成第一份正式依据很多人写提示词是即兴的“你帮我优化一下这个函数”“把这个改成异步”“加个缓存”AI 改完对话结束。提示词没有被保存AI 的回复也没有被导出所有依据消失。正确做法是把提示词本身当成一份临时文档来对待。你可以建立一个习惯在项目根目录下放一个docs/AI_CHANGES.md或者直接在提交前把提示词里的需求背景整理进 commit message。关键不是这条提示词有多规范而是它必须被记录在某个可以检索的地方。我甚至建议一种更自动化的姿势让 AI 在动代码之前先输出一份简短方案内容包含改动文件清单、每个改动点的目的、影响范围、替代方案的取舍理由。等这份方案确认后再让它写代码。这样你手里就有了一份 AI 自己生成的“施工前勘察报告”它就是天然的依据。2.2 文件级的 AI 变更记录到底怎么组织我实践下来比较好用的是一个简单的模板放在docs/AI_CHANGES.md里每次新增一段而不是覆盖式地重写。内容不用多够用就行## [2025-06-02] 订单折扣接口重构 - 需求背景运营部门需要支持限时活动折扣原折扣计算逻辑无法动态调整。 - 提示词摘要在 preserve 现有订单历史数据的前提下将折扣规则从数据库读取改为启动时加载。 - 采用方案启动时一次性加载规则配置到内存避免每次请求都查库。 - 替代方案Redis 缓存带过期时间因当时 Redis 集群迁移未完成暂不采用。 - 影响范围只涉及 app/services/discount.py不影响 order 创建流程。 - 预期验证python -m pytest tests/test_discount.py -k active_rule - 实际验证8 passed, 1 skipped性能压测 QPS 从 200 提升到 350。 - 回滚方法git revert 8f1a3c2注意 revert 后需同步清理缓存。每次让 AI 改完代码顺手往这个文件里追加这些字段。听起来像额外工作但实际花不了两分钟。而且我直接让 AI 帮我填这个模板——我会把需求背景和聊天记录发回去让它按上面的结构整理出来我再核对一遍。这里有一个很重要的点在提交代码之前让 AI 在 PR 描述或者 commit message 里附上这份依据。多数 AI 编程工具都支持多轮对话你完全可以问它“我们刚才这个改动按这个模板整理成一节变更记录。”如果嫌项目根目录文件太乱也可以按模块归档例如docs/decisions/order-service/2025-06-02-discount-cache.md。小项目一个文件就够大项目按模块分开维护起来更容易定位。2.3 提交信息里塞进去三个要素文件级记录是“长期档案”commit message 则是“第一眼线索”。每次让 AI 改完代码后我至少要求提交信息包含三个要素改了什么一句话说清楚变更功能点为什么改可以是一句话也可以带 issue 编号验证方式用什么命令或测试验证过结果如何。举个例子不要写“feat: update discount”而是写feat: discount rules loaded at startup why: database queries per request caused high load under promotional campaigns; rules are rarely changed, startup loading reduces query count. verify: pytest tests/test_discount.py -k active_rule如果团队用的是飞书文档、Confluence 或者 GitHub Wiki也可以把 AI 对话记录里“确认方案”的那一段贴到对应的需求页面。这些外部缓存系统的好处是可检索、支持全文搜索比某个成员的聊天窗口可靠得多。3. 把“思考痕迹”钉在代码边上外部文档和 commit message 能解决“找得到”但解决不了“打开代码时一眼就能看到依据”。最好的位置永远是代码本身旁边。3.1 结构化的“意图块”注释写给下一个维护者以前我们写注释喜欢写“这里需要重试三次”这属于描述代码做什么信息量很有限。更好的注释应该解释“为什么这样做、不做什么、验证过什么”。我通常建议使用一种轻量级的固定格式# [AI-INTENT] 2025-06-02 # why: 网关在流量高峰期返回 429必须做指数退避重试而不是固定间隔重试 # scope: 仅影响 outbox_publisher 的发送逻辑不影响 receiver 消费 # verify: pytest tests/test_outbox.py -k retry --qos def publish_with_retry(message): ...这种格式有几点好处可以搜索、可以区分历史依据、可以在代码评审时被机器检查。团队甚至可以直接规定“凡是有 AI 介入的改动文件里必须包含一个 AI-INTENT 注释块没有则 MR 不允许合并。”我自己把AI-INTENT当成一种“依据锚点”。新接手的同学只要是看到这个标记就知道这段代码背后有故事值得去docs/AI_CHANGES.md里翻上下文。要是没有这个记号别人大概率默认这段代码是“从来没有过争议”的很容易误改。3.2 让 AI 在每次修改后强制输出变更影响说明过程中还有一个很好用的小技巧要求 AI 在每次生成代码后额外输出一个“变更影响说明”不需要它在代码里写完整文章但必须列出这几个条目改动涉及的文件每个文件的改动意图受影响的入口或调用方推荐的测试范围是否有不可逆的操作比如数据迁移或配置变更。你可以在自己的提示词模板里固定加上一句“请在你给出的代码之后附上一段变更影响说明使用 Markdown 列表格式”。之后这段说明可以复制到 PR 描述里或者直接作为docs/AI_CHANGES.md的一部分保存。如果你的团队用 GitHub 或 GitLab可以把这段说明作为模板字段。GitHub 的 PR 描述模板、GitLab 的 merge request 模板都支持预设字段## What ## Why ## How to verify ## AI assistance record这么一来AI 生成的内容和人工 review 的结论都有了固定缓存位置。尤其是“AI assistance record”这个字段很多人从来没用过但它恰好就是找依据时的第一个索引。3.3 约束与回归范围写在常量层或配置注释里有时候依据不是一段话能说清的而是被压在某个常量或配置项背后。比如RETRY_TIMES 3 # 不要调高上游网关限流策略是 5 秒窗口内最多 5 次调高会触发封禁这类直接在常量旁边写约束的注释价值比大段说明文字更高。因为它出现在未来最容易被人改动的位置同时也提示了“动这个值之前要了解什么内容”。AI 经常会生成一些“魔法数字”比如max_attempts 5、timeout 30。如果当时 AI 给过理由——比如“30 秒是因为批处理任务内部有 25 秒的响应超时上限”——那么这句解释必须作为常量注释留下来。这是普通文档替代不了的位置记忆。4. 用验证结果反向绑定依据前面说的都是“话”但光讲话不可靠像“我觉得这样可以”“应该没问题”这种依据太软。真正能让下一次信任的是验证过程的输出和数据。4.1 先定义“怎么说才算改好了”让 AI 改代码前最好先约定验收标准。这个标准不需要多全面至少得能回答四个问题改完之后跑什么命令能确认功能正常有没有需要回归的旧用例有没有性能或资源方面的验收指标出现什么信号时证明这次改动是失败的把验收标准记下来本质上就是在给“依据”钉一个终点。等测试跑过、压测通过再把结果存下来依据就从“你说可以”变成了“实测可以通过”。两者分量完全不同。4.2 把测试输出和性能数据存档作为证据链我在实际操作中的习惯是每次 AI 改动完成后把验证命令和关键输出复制到docs/AI_CHANGES.md的“实际验证”字段。如果涉及性能把压测摘要存下来不需要保留原始报告但至少要有 QPS、延迟、错误率这几个关键数字。这样做的目的是下一次有人质疑这段代码时你可以直接引用验证记录作为依据而不是重新跑一遍测试再猜。尤其是性能优化类的改动回滚决策经常会面临“之前那版性能到底差多少”的模糊印象这个数字一旦没有存档后面所有人都会靠感觉办事。如果团队有 CI最好让流水线自动生成测试报告并按分支归档。这样不只 AI 这一次改动的依据能找到整个项目的回归历史都有迹可循。4.3 回滚时用什么判断“回去”而不是“再猜一次”很多人忽略了一种特殊情况依据最大的考验不是顺风顺水时的维护而是出问题后回滚的时候。改坏了第一个念头就是“把这个改动撤掉”。但如果没有记录你根本不知道这个改动的完整边界是什么不知道它跟哪些文件有耦合不知道回滚后还有没有残留数据需要清理。所以在docs/AI_CHANGES.md模板里保留了一个“回滚方法”字段。哪怕只在里面写“git revert 对应 commit回滚后删除本地缓存文件”下次遇到事故时的决策成本也会瞬间降低。这是 AI 时代最值得补的一课AI 可以让回滚的代码瞬间消失但存储它的边界和清理条件只能由人来记。5. 一个真实案例同一段代码的两种结局这部分我拿一个具体场景说事大家可以对照自己的项目。5.1 没有记录的结局半天反推加一次误改订单系统里有一段calc_discount函数原始逻辑查数据库拿折扣规则。某天因为大促流量太大让 AI 把这个函数改成启动时加载规则配置减少高频查询。AI 改完压测确实提升了提交信息只写了“refactor: discount rule cache”。当时大家也没觉得有问题。一周后运营临时加了一个活动发现新规则配置不生效因为服务启动后根本不会重新读取配置。负责的同学第一反应就是让 AI 改成每次请求时直接查数据库改完重新上线性能指标立刻下降数据库连接池报警。接着另一个团队说他们有一个离线任务依赖这个函数缓存逻辑变了导致数据不一致连锁问题瞬间爆发。最后花了整整半天排查上面这些上下文回头才发现本地缓存是刻意的不生效是已知限制正确解法应该是给配置加一个手动刷新接口而不是把缓存整个拆掉。5.2 有记录的结局十分钟找到依据并修正如果当时在docs/AI_CHANGES.md里记录了那一段情况完全不一样。第二天发现配置不生效时第一个动作就是打开文档找到这一条- 采用方案启动时加载配置到进程内缓存降低数据库查询压力 - 替代方案Redis 缓存带过期时间因 Redis 集群迁移未完成暂不采用 - 预期验证./scripts/load_test.sh --ratio 0.8 - 实际验证QPS 从 200 提升到 350错误率低于 0.1% - 回滚方法git revert f3a9b21回滚后需清空进程内缓存通过这段记录很快就能判断本地缓存是需求前提临时活动不生效属于未覆盖到的场景。正确做法是在配置服务里加一个主动刷新接口调用后清掉进程内缓存重新加载。实现十分钟搞定而且不会误伤性能。整件事从“考古”变成了“查文档”。5.3 复盘依据链断了出现在哪一个环节这个案例里链条断裂的地方一共有三处第一原始提示词没有被保存AI 当时为什么提出“启动时加载”这个方案人已经记不完整第二commit message 只有一句“refactor”没有写为什么要重构第三代码常量旁边没有任何约束注释导致后来的人认为“缓存是临时的、可以随便拆”。每一处都是同一个问题我们只保存了 AI 输出的代码没有保存它得到结论的过程。哪怕把这三处中的任意一处补上后面那个同学都不至于犯将错就错的误改。所以我现在每次看到团队里有人让 AI 改代码第一句话都是改完后把“为什么”写到我能搜到的地方。6. 用脚本和协作约定让追依据变成顺手的事手动记录需要自觉但人恰恰是最不稳定的因素。与其靠自觉不如用一点自动化和团队约定把这件事固定下来。6.1 用 Git 钩子拦住空泛提交信息Git 的commit-msg钩子可以强制校验提交信息包含关键字段。比如这个简单的 bash 脚本会检查 message 里有没有why:这个前缀没有就拒绝提交#!/bin/sh MSG_FILE$1 if ! grep -qE ^(why|Why|WHY): $MSG_FILE; then echo 提交信息必须包含 why: 字段请补充改动依据后再提交。 exit 1 fi exit 0保存到.git/hooks/commit-msg并加上执行权限即可。团队内想强制的话可以把这个脚本放进项目仓库的hooks/目录通过工程化手段让成员统一安装。这是成本最低的兜底能最大程度避免零信息提交。6.2 把 AI 会话和决策快照放进仓库另一个做法是维护一个docs/ai_sessions/目录每次让 AI 参与代码修改时把关键对话导出成一个 Markdown 文件放进去。很多 AI 编程工具已经支持导出对话即使不支持你也可以直接把提问和回贴关键部分复制出来加到文件末尾。命名用日期加主题比如docs/ai_sessions/2025-06-02-discount-refactor.md。不需要整理得漂亮完全没有格式也能接受。重点是它存在仓库里、可被检索、和代码在同一个版本历史中。这样即使三个月后只要这个仓库还在依据就在。6.3 团队约定让 AI 自己也遵守记录规则如果你用的是支持 instructions 文件的 AI 编程工具比如 GitHub Copilot 的copilot-instructions.md或 Cursor 的.cursorrules可以在里面直接写一条规则当被要求修改代码时在提交代码之前必须输出以下内容 1. 本次修改涉及的文件列表 2. 每个文件改动的目的 3. 被否决掉的替换方案如果有 4. 推荐执行的验证命令和预期结果这样 AI 每次都会自动给你一份可复制的依据初稿你只需要检查、粘贴、存档。比自己从零写省力得多也让“记录依据”这个动作从人工习惯升级成流程约束。实际上这是把“让 AI 补文档”这个思路前置到了提示词约定里非常适合小团队复制。如果团队用飞书或企业微信做协作也可以专门开一个“AI 变更记录”的文档空间每人每次改动往里面丢一条摘要。不需要完整模板写上日期、文件、一句话原因、验证命令就行。它不能替代代码仓库里的依据但解决了“别人找不到聊天记录”的尴尬。说到底AI 不会替你记住它自己说过什么。想让下一次的自己找到当时的依据就得在每次对话结束前花两分钟逼它把思考过程吐出来然后放到一个能搜索的地方。这比任何高级 prompt 技巧都管用。