Vibe coding 和 Agent 写代码已经很强:改界面、补脚本、在单卡训练里搜更优方案,Karpathy 的 autoresearch 甚至能把「改 train.py、跑固定时长、看 val_bpb」收成可循环的科研试错。这些场景往往目标单一、验收指标清楚,人只要把边界写进 program.md,Agent 就能整夜试错迭代。
业务工程系统不一样。人和 AI 各自掌握的上下文并不重合:线上约定、历史补丁里藏着的语义、别的系统还在依赖的字段口径,大量停在工程师脑子里或散落在旧对话里,并没有完整写进仓库。同一段需求,人对「算不算错、该不该跟旧版一样」常有判断;若把重构或迁移完全交给 AI 主动改代码,结果容易超出预期------不是多写了几个功能,而是按它理解的「正确」改了一版,旧坑没清掉又叠新坑,最后仍是一坨难维护的代码。
复杂系统重构尤其如此。做过的人多半都踩过这几类坑:
- 历史包袱重,补丁叠补丁,不少逻辑能跑但没人敢动
- 多人、多阶段、多风格混在同一套代码里,读一遍搞不清边界
- 文档长期跟不上,真实语义卡在几段 if/else 和线上实际输出里
- 需求还在变,改动却已是牵一发动全身
- 底层架构或范式要换:旧逻辑散在补丁和调用链里,没法按模块整包搬迁,只能先保证输出和以前一致,再逐步换实现
难的不在「新代码写不出来」,而在迁移后功能效果不能变差,同时还得把工程从不可维护拉回可持续------中间还要不断对齐「什么叫对」。
这篇讲 Semi-Autoresearch(半监督对齐迭代):自动化评估负责对照、归因和提案,人工卡点负责语义分歧和有效金标准;把迁移拆成可审计的小步,而不是通宵 Agent 或赌一把大 rewrite。下文用订单/成交文本解析举例------旧版 NER+规则迁到 Schema 多阶段 pipeline 时,解析结果仍要让路由和风控照常用,代码也不能再堆回一团;字段与机构名均为构造,不涉及真实业务数据。
Semi 同时管住这两头:双轨 eval、人工前缀裁定、回归守护与规则回写,不默认「旧版永远对」,也不默认「AI 刚改完就算对」。
真正开始重构前:先把分层和契约对齐
很多重构不是栽在实现能力,而是分层边界、业务语义和评估口径还没对齐就动手。动手前至少对齐四件事:
- 架构分层:抽取、标准化、业务决策、输出适配,各段边界写清楚,改 diff 时有落点。
- 业务语义:哪些字段是硬契约,哪些允许演进,哪些可能是 legacy 错数,先标出来。
- 评估契约:raw pass / effective pass 怎么定义,diff 比到字段还是比到条数,先冻结。
- CODEMAP:核心模块职责、依赖方向写清楚,别只靠口口相传。
缺这层对齐,后面的 Agent 自动修复很容易变成自动加债。
若用 Cursor 一类工具,关键不在某条 prompt,而是把规则沉淀成可执行约束(例如 Cursor Rules):
- 对齐规则:语义对齐优先,禁止照抄 legacy 调用链
- 评估规则:每轮必须产出 eval 摘要,不能只说「修好了」
- 文档规则:代码变更同步更新文档
- CODEMAP 规则:模块职责变化时同步 CODEMAP
- 测试规则:输出或逻辑变更时补测试或回归验证
人验收的是可追溯流程:谁改了什么、为什么改、影响了哪些 case,留痕即可,不必逐行盯 diff。
和 Karpathy autoresearch 不是一回事
Karpathy 的 autoresearch 是固定 GPU 时间、固定 val_bpb、代理只改 train.py------变量面极小,适合搜更优方案。
解析系统迁移变量面大得多:旧栈常是 NER + 规则 + 正则,新栈是 Schema 槽位 + 多阶段 pipeline。一边要保证解析输出和旧版一致、别让依赖方踩雷,一边工程上又不能复制 spaghetti;文档和实现还常不一致。全自动容易把旧版 bug 当金标准,或为个别 case 堆特例。
Semi-Autoresearch 拆成两半:Autoresearch 指自动化对照需求、旧实现和双轨 diff,梳理成因、试修复、出提案;Semi 指语义歧义、旧版疑错、方案分歧等卡点必须停,动代码前过回归守护,人用固定前缀指令确认或否决后,才冻结通过。
举例:订单/成交文本解析
某企业有「订单文本 → 结构化字段」服务,供路由与风控使用。旧版:模型只到 NER 槽位,后面靠规则补全;新版:结构化输出 + Schema 多阶段 pipeline。
输出字段(节选):order_id、side、bridge_party、clearing_speed、dialogue_send_to;一条输入可能拆多条成交,条数也要比。
语料示意:
"
编号 DEMO-20260615-001 买入 BOND-260205 04.28+0 甲银行出给乙农商 对话发丙证券
旧版基线:side=buy,bridge_party=乙农商,clearing_speed 空,dialogue_send_to 空,条数 1。
新版:clearing_speed=+0,dialogue_send_to=丙证券,其余同上。
双轨会标红 clearing_speed、dialogue_send_to。默认「以旧版为准」就逼新版改回空;默认「以文档为准」又可能和线上还在跑的约定冲突。Semi 要做的是:系统主动标出分叉,等人定,且禁止为凑通过率静默把新版改错。
流程长什么样
回归守护卡在动代码之前:先看已冻结和已通过 case 会不会被带崩,再让人选方案。正文把流程收成五类机制,可按下表对照读。
| 五类机制 | 机制要点 |
|---|---|
| 双层目标 + 双轨对比 | 功能效果与工程结构两层;双轨 eval 与 diff 报告 |
| 旧版疑似错误 + 有效金标准 | 主动判定、待确认、动态白名单 |
| 单差异修复 + 能力门禁 | 语义提炼、唯一阶段;Schema/兜底门禁 |
| 人工指令 + 分歧提案 | 六类前缀;结构化决策材料 |
| 回归守护 + 同步 + 回写 + 冻结 | 副作用对比;实现同步;规则沉淀 |
双轨对比:两种 pass,两个目标
同一请求进旧接口和新接口。diff 不止 pass/fail,还有字段名、旧值、新值、条数偏差,以及相对原始旧基线与相对有效金标准两种通过态。
一边看功能效果:关键字段在有效金标准下是否一致、依赖方能不能照常用;一边看工程结构:修复是否落在唯一处理阶段、有没有乱加全文兜底。两个目标一起进 eval,免得测过了功能却又把代码结构弄烂。
上面订单例里,报告会写清 clearing_speed、dialogue_send_to 旧空新非空,两种 pass 态都留着,后面走纠错或对齐分支。
旧版疑似错误:不能默认逼新版改回去
这和「出 diff」不是一步。clearing_speed 旧空、新版 +0,若业务规则写明应从 +N 解析,系统应主动标旧版疑似错误,写入纠错登记簿,状态待确认,把争议字段、旧值、新版值和依据推给人裁定。收到指令前:禁止为变绿静默改回空;待确认条目只接受 确认:、坚持旧版行为:、否决:、问题: 四类前缀,不能无指令自动定案。
发出 确认:clearing_speed 以新版解析为准,纳入有效金标准 后,字段和覆盖规则进动态白名单(不事先穷举全集)。有效通过用新标准;原始基线对比仍保留审计。已冻结 case 不会再逼新版改回空。
dialogue_send_to 若语义本身不清(这个字段到底要不要输出),走 问题: 出分歧提案,不要硬套「旧版疑似错误」。
单差异怎么修:先语义,再阶段,再过门禁
每个差异按固定顺序来,不复制旧版调用链:用业务语言说清旧版在该字段上的规则;映射到新版管线唯一阶段(抽取修正、标准化、业务逻辑、协议适配等);过模型能力门禁;修完再跑双轨。
订单例:clearing_speed 应在抽后修正/标准化阶段做 +N 归一,别在协议层加全局正则抄旧代码。dialogue_send_to 要先看 Schema 槽位和命中率,再谈兜底。
拟加「全文扫 +\d」前,门禁要问:槽位有没有、命中率如何、会不会误吸别的数字。只有槽位稳定失败才允许兜底,并记来源类型(模型/规则/混合)。结构化模型已在该槽位稳定命中时,不必再叠正则------RegexValidator 那类补刀也是这个意思:补格式,不替代 schema。
人工指令与分歧提案
规范冲突、条数分歧、旧版疑错、文档和实现各说各话时,人用固定前缀发指令,系统按约定路由:
| 前缀 | 含义 | 订单例 |
|---|---|---|
| 确认: | ① 按旧版语义修所列字段;或 ② 旧版疑似错误时,把新版正确输出纳入有效金标准 | ① 确认:dialogue_send_to 对齐旧版为空;② 确认:clearing_speed 以新版 +0 为准,入有效金标准 |
| 采纳建议: | 输出一致,但采用更优工程路径 | 采纳建议:+N 归一放标准化配置,不新增全局正则 |
| 坚持旧版行为: | 与旧版最大程度一致(纠错场景也可坚持对齐旧基线) | 坚持旧版行为:clearing_speed 保持空 |
| 补充: | 追加语料或用例 | 补充:追加 20 条含「对话发」话术 |
| 否决: | 允许该字段长期与旧版不同 | 否决:dialogue_send_to 允许与旧版不同 |
| 问题: | 语义不清 → 分歧提案 | 问题:dialogue_send_to 要不要作为输出字段? |
当 AI 裁决不了时,出分歧提案:标题、旧版业务语义、旧版实现问题、建议落点、坚持旧版的工程代价、待选选项。人裁定后流程才继续。
回归守护、同步、回写、冻结
改 dialogue_send_to 可能误伤已通过 clearing_speed 的 case。动刀前先拉守护集合:已冻结通过的进硬守护,其余 effective pass 的进观察集合。接着列出候选修法(只动标准化配置 vs 加全局正则),对每套方案估一遍会影响哪些 case、哪些字段可能翻车,再等人选方案------和改共享模块前先问「会不会割到别处」是一类习惯,只是这里由双轨 eval 把影响面摊开给你看。
改完后对守护集合重跑双轨。已冻结 case 从过变不过,阻断冻结并告警;字段静默偏移则列入 diff,等人确认或回滚。
即使 effective pass 已为真,仍要交实现同步报告(用例、目标输出、变更模块、规则/配置变更、前后 diff 计数)。人工确认或无异议等待期结束后,才可冻结。
冻结前还要把裁定写回业务规则文档(例如 dialogue_send_to 要不要输出、clearing_speed 口径)。否则知识只留在 diff 和聊天里,下轮还会踩同一坑。这是硬门禁,不是顺手补文档。
差异常成簇,不必一次清完。订单迁移可以按批推进:条数切分、清算速度标准化、对话发送归属,一批只攻一种模式。已冻结 case 默认不再动,除非走纠错流程重新裁定。
边界
订单解析纯属举例;细节以团队工程约束为准。Semi-Autoresearch 不替代单测或 code review,管的是迁移期功能效果对齐与知识沉淀。Karpathy autoresearch 在目标单一、文档完备时仍有用;Semi 用于文档不全、旧版不可信、字段有歧义的生产迁移。守护层级、等待期长度按项目定。
方法有开源示例:github.com/GuiminChen/semi-autoresearch。主仓是方法论加最小 demo(toy legacy/v2 parser、双轨 eval、回归守护、人工前缀指令);另附示例 case study。跑一遍应能看到 raw pass 与 effective pass,legacy 疑错经 human confirm 后 corrections 生效,guard 能拦住 pass→fail。
收束一下:迁移要同时管住功能效果和工程结构,双轨报告保留两种 pass,旧基线不是唯一真理。旧版疑似错误要主动标、待确认、禁止静默迁就;确认: 在纠错场景是入有效金标准,不等于永远跟旧版一样。改代码前先回归守护,改完后同步报告和规则回写,再冻结------Semi 不是通宵 Agent,是带门禁的协作迭代。
本文为复杂系统迁移场景下的工程实践整理;Karpathy autoresearch 见 github.com/karpathy/autoresearch;示例仓库 github.com/GuiminChen/semi-autoresearch。