背景

维度 详情
业务历程 2008年上海首次上线缴电费,发展至今为亿级用户数字生活服务,系统迭代16年
技术负债 累计60万行代码,存在三大核心问题:
1. 业务异构:对接数千家机构,欠费/创单/支付/销账/对账/结算全流程逻辑定制泛滥
2. 治理缺失:500+系统开关、迭代无管控、历史兼容代码堆积、下线业务代码未清理
3. 维护困难:时间久远无人敢改动,仅能通过业务背景理解逻辑

核心挑战与整体策略

挑战维度 具体问题 质量要求
业务场景覆盖 2B侧600+技术接口、千家机构异构;2C侧百余项服务;单接口分支超8000个,多维度组合场景无法穷举 可枚举、可生成、可覆盖
生产切流稳定 日活千万级、日资金流水亿级,民生基建级应用,容错窗口极窄(1%差异影响十万级用户,单笔资损即事故) 可监控、可灰度、可回滚
持续交付能力 AI仅能看到代码文本,无法感知团队约定/历史故障/口头共识等隐性知识;传统验证环境AI适配性差,验证成功率仅~60%,有效验证耗时2-3小时 构建AI友好的工程体系(Harness Engineering)

AI重构质量体系建设实践

业务场景层:从「人工用例」到「业务场景」驱动

核心思路:定义-挖掘-验证GoldenSet场景集,基于PERS模型(入参P+环境E→返回R+副作用S)落地

环节 方法
场景建模 将被测接口视为函数(P+E)→(R+S),单个测试用例对应决策路径上的一次PERS采样,要求新老系统同路径下R、S完全一致
场景挖掘 1.骨架生成:解析被测代码决策树,叶子节点对应独立业务场景,标注因果维度+等价类
2. 路径展开:按因果维度等价类组合筛选路径(非笛卡尔积,避免无效场景)
3. 指纹锁定:用SHA256(P+E因果组合)生成场景唯一指纹,同指纹去重、失败可追溯,指纹由骨架锁定AI不可修改
场景验证 CI环境循环校验:外层保障100%场景指纹覆盖,内层保障单指纹下新老系统行为一致

生产切流层:多级灰度+影子流量校验

切流阶段 策略
L1组内切流 新老机器物理隔离,控制爆炸半径
L2机器内分流 新老逻辑同机共存,按场景组粒度灰度
前置校验 线上录制老系统流量→新系统重放→DIFF(R+S),通过后放行L2分流
准出标准 1. 影子流量阶段:新老DIFF完全匹配后启动L2灰度
2. 灰度阶段:新系统监控+核对无异常后逐步扩大比例

持续交付层:AI友好工程生态建设

(1)知识工程:补全AI隐性知识缺口

梳理全流程关键知识类型,覆盖4类核心环节:

环节 知识类型示例
spec生成&审查 业务流程图、规则、时序图、领域模型、接口契约
用例生成&审查 用例schema、逻辑分支、测试数据、历史缺陷、准出标准
研发测试反馈 编码规范、架构约束、异常排查手册
质量准出验收 验证报告、发布计划、监控核对规则、应急预案
(2)架构约束:三层兜底保障Spec质量
层级 校验方式 作用
L1 Spec Linter 机器静态质检,秒级反馈 保障Spec规范可运行,避免矛盾/无效内容
L2 消融实验 A/B对照(有Spec vs 无Spec)验证信息量 避免Spec形式主义,保障内容可执行、完备
L3 人工审查 核心场景覆盖&业务合理性校验 兜底业务正确性,解决AI无法判断的“应该是什么”问题
(3)反馈闭环:独立确定性门禁

设置4道Harness门禁,LLM不参与裁判,仅作为执行方:

门禁项 校验规则 级别
需求覆盖度(G1) 场景是否覆盖全部需求点 -
用例落地率(G2) 生成的用例是否全部可执行 -
变更一致性(G3) 代码变更与用例是否匹配 -
执行完整性(G4) 用例总数=0、失败数>0、报告超7天均阻断;跳过用例数>0告警 BLOCK/WARN
(4)长程任务观测

类比微服务Tracing,记录AI全流程行为,作为Harness自我进化的反向燃料,推动AI从“能跑”到“敢用”。

实战PERS

定义

PERS是把接口/函数行为标准化的建模公式,本质是用「输入+环境→输出+副作用」的因果关系,定义「新老系统行为一致」的判断标准:

字母 全称 含义 示例(生活缴费户号添加接口)
P​ Parameters 接口入参 户号、用户UID、机构ID、缴费金额
E​ Environment 运行时环境 系统开关状态、下游依赖返回值、数据库配置、灰度标签
R​ Return 接口直接返回 添加成功/失败状态码、返回报文
S​ Side-effects 间接副作用 数据库写入记录、消息队列投递、缓存更新、调用下游接口

核心判断规则相同P+E下,新老系统的R和S必须完全一致,否则判定为重构不一致。

为什么要做PERS建模?

针对60万行巨石应用的三大痛点:

痛点 PERS的解法
场景无法穷举 把复杂业务拆解为「P+E因果组合」,避免拍脑袋写用例
AI理解偏差 用结构化公式定义「什么是一致」,不给AI留模糊空间
新老对比困难 统一PERS标准,新老系统跑同一套输入,直接DIFF输出

简单实操

假设这是一个迭代了5年的老系统,正在做AI重构,我们要确保新老系统行为一致。

Step 1:抽象为 PERS 函数

订单创建接口 = f(P, E) → (R, S)

Step 2:绘制决策树骨架(简化版)

Step 3:标注因果维度 & 等价类
因果维度 等价类划分 说明
用户状态(P)​ {未登录, 已登录} 来自请求Header的Token
库存状态(E)​ {充足(>=购买数量), 不足(<购买数量)} 来自库存服务返回值
优惠券状态(E)​ {未使用, 已使用(有效), 已使用(无效)} 来自优惠券服务返回值
商品类型(P)​ {实物商品, 虚拟商品} 影响是否需要物流
Step 4:路径展开生成 P+E 组合(非笛卡尔积)

我们只选真实存在的业务路径,排除无效组合:

场景ID P:用户状态 P:商品类型 E:库存状态 E:优惠券状态 预期决策路径
S001 未登录 实物 充足 未使用 返回未登录错误
S002 已登录 实物 不足 未使用 返回库存不足
S003 已登录 实物 充足 已使用(无效) 返回优惠券无效
S004 已登录 实物 充足 已使用(有效) 创建优惠订单
S005 已登录 实物 充足 未使用 创建原价订单
S006 已登录 虚拟 充足 已使用(有效) 创建虚拟优惠订单

排除的无效组合未登录+库存不足(未登录时不会查库存)、虚拟商品+优惠券无效(虚拟商品不允许用券)

Step 5:生成场景指纹(SHA256示例)

# P+E因果组合字符串 pe_combo = "user_status=logged_in|product_type=physical|stock=adequate|coupon=used_valid" # 生成指纹(取前16位) fingerprint = hashlib.sha256(pe_combo.encode()).hexdigest()[:16] # 结果示例: a3f5c8d2e7b9a1f0

Step 6:定义 R+S 断言规则(新老系统必须一致)
场景 R(返回值)必须一致 S(副作用)必须一致
S004 HTTP 200, order_id非空, total_amount=原价-优惠 1. DB插入order记录(status=待支付)
2. DB插入coupon_used记录
3. MQ发送order_created事件
4. 库存服务扣减库存接口调用1次
S003 HTTP 400, error_code=COUPON_INVALID 无任何DB写入,无MQ发送,库存不扣减
S001 HTTP 401, error_code=UNAUTHORIZED 无副作用

关键避坑点(结合订单场景)

坑点 正确做法(订单场景)
忘记校验副作用S 必须校验「优惠券是否被锁定」「库存是否预扣」,否则会出现超卖
指纹粒度太粗 不要把「优惠券有效/无效」合并为一个维度,否则无法区分S003/S004
忽略幂等性 同一个P+E组合(相同请求ID),新老系统都必须返回相同order_id,不能重复创建

简化版落地清单(中小团队可用)


1. 选核心接口:订单创建 2. 列3个以内因果维度:用户登录态、库存状态、优惠券状态 3. 写5-10条P+E组合(覆盖正常/异常路径) 4. 定义每条组合的R+S断言 5. 新老系统跑一遍,对比DIFF

实战影子流量

定义

影子流量(Shadow Traffic) 是一种无侵入的生产环境验证技术:将线上真实用户流量复制一份,悄悄发送给待上线的新系统,对比新老系统的处理结果(R+S),但不影响真实用户的正常请求和业务流程。

核心特征 说明
无感知 真实用户只和老系统交互,完全不知道新系统在并行运行
零风险 新系统的返回值和副作用全部丢弃,不写入生产库、不发送真实通知
真实性 用的是线上真实请求和数据,比Mock测试更接近真实场景

影子流量在AI重构中的核心价值

针对60万行巨石应用+AI重构的特殊性,解决三大痛点:

痛点 影子流量的解法
测试环境数据失真 直接用线上真实请求,避免测试数据覆盖不全导致的漏测
长尾场景无法枚举 线上流量天然包含所有边缘场景(比如某小众机构的特殊逻辑)
切流前无法验证 重构上线前就能验证新系统处理能力,提前发现AI重构引入的bug

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 线上用户请求 │────▶│ 流量复制层 │────▶│ 老系统(现网) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 流量脱敏/改写 │────▶│ 新系统(影子) │ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ │ DIFF比对引擎 │ └─────────────────┘ │ ▼ ┌─────────────────┐ │ 准出决策系统 │ └─────────────────┘

坑点 解法
影子流量写脏生产数据 必须在应用层或中间件层做写操作拦截,所有写请求路由到影子表/影子队列
新系统调用下游影响第三方 必须做下游依赖隔离,要么Mock下游,要么转发到影子下游环境
DIFF结果噪音太大 配置忽略规则,排除日志ID、时间戳、非核心统计字段等无关差异

简单实操

步骤1:流量录制(Record)
  • 录制对象:生产环境老系统的入口流量(HTTP/RPC请求)

  • 录制方式

    • 方式1:负载均衡层镜像流量(推荐,无侵入)

    • 方式2:应用层埋点复制请求(需要改代码)

  • 录制范围:按场景组灰度录制,比如先录「电费缴费」场景,再扩到「水费」「燃气费」

步骤2:流量清洗与脱敏(Sanitize)

这是最关键的一步,防止影子流量污染生产环境:

处理项 操作 示例
请求ID替换 生成新的影子请求ID,避免和真实请求冲突 原请求ID=123 → 影子ID=shadow_123
敏感字段脱敏 替换手机号、身份证号等隐私数据 手机号=138****1234
写操作改写 拦截新系统的所有写操作 SQL INSERT→INSERT INTO shadow_table
下游调用隔离 将新系统的下游调用转发到影子环境 调用支付接口→调用影子支付环境
步骤3:流量重放(Replay)
  • 将清洗后的流量发送给同机部署的新系统(文档中L2阶段策略)

  • 保持请求的原始时序和并发度,模拟真实流量压力

  • 新系统的返回结果全部丢弃,不返回给用户

步骤4:DIFF比对(Compare)

核心比对PERS模型中的R和S

比对项 具体内容 比对规则
R(返回值) HTTP状态码、响应体JSON结构、字段值 完全一致,允许配置忽略字段(如timestamp)
S(副作用) DB写入记录、MQ消息、Redis Key、下游调用参数 影子表和影子队列的数据与老系统预期一致
步骤5:异常分析与收敛
  • 对DIFF结果进行分类:

    • 预期差异:比如新系统日志格式变化、非核心字段调整

    • 非预期差异:业务逻辑错误、资损风险、数据不一致

  • 优先解决高优先级差异,直到DIFF通过率达到准出标准

步骤6:准出决策(Gate)

只有满足以下条件,才允许进入L2机器内分流阶段:

  1. 影子流量DIFF通过率=100%(文档要求0差异)

  2. 无资损类、数据不一致类异常

  3. 性能指标(RT/QPS)符合预期

AI工程化

先把目标定义成可执行的:什么叫“生成质量受控”?

建议你把“质量”拆成 4 件事(这也是后面门禁的来源):

维度 说明 典型体现
可运行​ 编译/类型检查/静态分析通过、依赖齐全 build/lint/typecheck 红就是红线
契约一致​ 不改不该改的接口、DB schema、消息格式、配置语义 对外 API / SDK / 事件 schema 被悄悄改掉
约束合规​ 业务规则/安全/性能/可观测性不被绕过 裸 SQL、无鉴权、吞异常、N+1、超长锁、敏感日志
需求对齐​ 真正覆盖需求点,不是只把 happy path 写出来 缺异常分支、缺幂等、缺边界、缺指标/告警

总体架构:知识前置 → 生成 → 约束校验 → 独立门禁 → 反馈闭环

用一张“流水线视角”描述(它不一定是某个平台功能,更多是你们的组织机制):


需求/工单 │ ▼ [知识前置] ← spec模板 / 架构约束 / 历史缺陷/规则库 / 依赖拓扑 │ ▼ [AI生成] PR描述 + 代码 + tests + migration + 影响声明(diff scope) │ ▼ [约束校验] L1 lint/类型/build → L2 架构&契约检查 → L3 行为证据(API/集成/影子) │ ▼ [独立门禁] Coverage/契约漂移/安全/影响面/证据齐备 → Pass / WARN / BLOCK │ ▼ Review & Merge → 观测 → 把“翻车点”回填进知识库/规则库

关键词:AI 的输出要变成“可审计物”(spec + diff scope + 证据),而不只是 diff。


知识前置:别让 AI “猜业务”,要把上下文喂成结构化材料

这一步的价值是:降低歧义 → 减少后续校验要抓的“隐形bug”数量

至少前置 5 类“可长期维护”的知识

类别 放什么 怎么供给给 AI
接口/数据契约​ OpenAPI/Proto/JSON Schema、消息 event schema、DB 公共表字段注释 作为 pinned context / 检索语料;强制生成物引用 schema
架构约束(Do/Don’t)​ 分层规则、依赖方向、哪些包不能直连 DB、必须用 DAO/Repository、事务边界在哪里 规则文件 .archguard/, ARCH.md,并在 review prompt 里显式引用
业务规则片段​ 状态机(订单/审核/退款等)、枚举合法值、幂等与重试语义、结算口径 用 DSL/表格/状态图而不是长段散文;最好绑定到 module
安全&合规基线​ 敏感字段清单、日志脱敏规则、鉴权切面、SQL 注入/SSRF 禁止模式 作为 lint/正则规则 + 例子;AI 看到“会被扫描”就会收敛
历史事故/反例库​ 过去踩过的坑:某接口不能加字段、某表不能随便建索引、某种写法会引发慢查询 用“反例 + 修正写法”喂给 AI,生成时直接压住高频复发 bug

实践中最有效的不是“一股脑塞 system prompt”,而是:

把知识绑到路径/模块module/orders/**自动带订单状态机;/pay/自带资金安全规则。


约束校验:三层(L1/L2/L3)把 80% 问题挡在 cheap 的地方

这套分层本质上和前面说的“Spec三层兜底”一脉相承,只是对象从“重构规格”变成了“PR/MR”。

L1:机器静态质检(秒级,必须过)

目的只有一个:不让“低级错误”进人工眼裡

  • 编译 / typecheck / 格式化

  • 语言 lint + 安全 lint(secrets、硬编码 token、危险 eval/exec)

  • 测试是否可执行:tests 不为空至少一个 assert没只 print 不 assert

  • 生成物必须附带:受影响的对外契约清单(哪怕只写“无”)

原则:L1 失败 = 直接 BLOCK,不等 review。 AI 可以自动 retry fix(见闭环),但不能 bypass。

L2:架构 & 契约漂移检查(便宜但很关键)

这一层防的是 AI 最容易犯的“偷偷改边界”:

  • 契约漂移检测:API response schema 新增/删除/改类型;消息字段语义变化;错误码新增未登记

  • 分层/依赖违规:service 直接 import dao 包、跨域 import、domain 依赖 infra 细节

  • 数据库变更风险:migration 是否 drop column、是否加锁表、是否有 missing index 风险(可用规则库近似判)

  • 横切合规:新增接口是否有鉴权注解/切面;敏感字段是否走脱敏;是否出现裸 SQL 在不该出现的地方

落地方式通常是:

  • ArchUnit / custom rules / 依赖图 diff / schema diff 工具

  • 输出一份 Impact Statement(影响声明):这次改动可能影响哪些接口、哪些 topic、哪些表

L2 的核心不是“全自动化证明正确”,而是把可疑的结构性变化标红让人/二次校验聚焦。

L3:行为证据(更贵,但对关键路径值得做)

对资金/订单/支付这类“错一点就资损”的路径,最好再要一层:

  • 单测/集成测试必须 cover 的关键路径(见后面门禁 G1/G2)

  • 影子重放/对比(如果你有流量):老逻辑结果 vs 新逻辑结果,diff 规则可配置

  • 或者至少在 staging 跑一遍:端到端 happy path + 关键异常 path(幂等重复、并发、超时)

注意:L3 不是要求“全系统走影子”。而是 “标红模块(支付/结算/履约)→ 必须出示行为证据”


独立门禁:让 LLM 不当裁判,做成硬指标

把质量准出写成 不依赖 AI 主观打分 的规则,CI 可算、可阻断。

推荐的 5 个门禁(可按模块分级)

门禁 算什么 动作
G1 需求覆盖证据​ PR 描述/checklist 里是否声明覆盖了哪些 case(happy/异常/边界/幂等) 缺声明 → WARN;关键模块缺 → BLOCK
G2 用例落地率​ tests 存在、可执行、不是假通过;coverage diff 不下降太多(例如 -2% 阈值) 用例为空 / 明显假绿 → BLOCK
G3 契约一致性​ 对外 API/消息 schema 是否漂移;是否有“未审批的新错误码/新字段” 漂移且无注解 → BLOCK
G4 安全&合规​ secret、脱敏、鉴权、SQL injection pattern、权限切面 命中规则 → BLOCK
G5 变更可回溯​ 每个非 trivial PR 必须带:影响面声明 + 回滚方案/开关策略(哪怕一句) 关键模块缺 → WARN/BLOCK

关键点:门禁是分级的

  • module=payment开 full(G1–G5)

  • module=admin_tool只开 G2/G4

  • docs-only走 fast path


反馈闭环:让“生成→校验→翻车→改进”可积累

否则你会永远停留在“AI 写得快,人修得更累”。要把三样东西持续回流:

  1. 规则库进化:每次 production incident / PR 翻车 → 提炼成一条 L1/L2 规则或 lint 或 prompt 约束

  2. 反例语料(bad_patch, why_bad, fixed_patch)进检索库,让后续生成更少复发

例如:“上次在 orders 里直接改 status 没走状态机,导致补偿重复发货 → 加规则:status 变更必须走 state machine”

  1. 生成 harness 自身观测:记录每个 PR 的 AI 贡献占比、retry 次数、BLOCK 原因分布,用它决定哪里该强化知识前置、哪里该放宽噪音规则


一个最小闭环 MVP(你下周就能试的那种)

如果你想先“低成本验证这套思路行不行”,别上来搞平台,先做这条线:

  • 约定:所有 AI 辅助提交必须走 PR 模板


## 影响面:API / DB / MQ / 无## 覆盖声明:happy / 异常(哪类) / 幂等 / 边界## 回滚:feature switch / migration reversible?

  • CI 里加:

    • lint + typecheck(BLOCK)

    • 契约 drift diff(WARN 先,逐步 BLOCK)

    • 覆盖率 diff 不掉太多(WARN)

    • G4 security-scan(BLOCK)

  • Review 时只问一个问题:“如果这段在 prod 跑错,你怎么知道、怎么停、怎么回滚?”(这句话能把 90% 危险 PR 逼出真信息)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐