案例 001:用 Agent 改造电商订单系统的缺陷修复流程
电商订单系统的 Bug 修复有三大痛点:状态机复杂(订单 × 支付 × 退款 × 物流交叉影响)、数据敏感(涉及金额和支付信息)、时效要求高(线上故障必须分钟级响应)。本文以订单、支付、退款三个核心场景为例,展示如何把 Agent 接入现有 Bugfix 流程,从 Issue 创建到 PR 合并全程自动化,同时保留关键节点的人工审批。
一、业务背景
这是一个日均 50 万单的电商平台,技术栈为 Python(Django)+ PostgreSQL + Redis + RabbitMQ。订单系统有 120 个 API、45 张表、380 个单元测试。团队 15 人,每天新增 3-5 个 Bug Issue。
核心痛点:
| 问题 | 具体表现 | 影响 |
|---|---|---|
| 状态机复杂 | 一个"订单金额计算错误"的 Bug 可能涉及 6 个模块 | 人工定位平均 2 小时 |
| 回归风险高 | 改了支付逻辑,可能破坏退款流程 | 每次修复需要跑 380 个测试 |
| 修复时效差 | 从 Issue 到 PR 平均 3.5 天 | 严重 Bug 积压影响 GMV |
引入 Agent 的目标:把简单 Bug 的修复时间从 3.5 天降到 4 小时,同时保证不引入新 Bug。
二、系统架构与 Agent 接入点
┌─────────────────────────────────────────────────────────────────┐
│ 电商平台订单系统 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 订单服务 │──▶│ 支付服务 │──▶│ 退款服务 │──▶│ 物流服务 │ │
│ │ (order) │ │ (pay) │ │ (refund) │ │ (ship) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ ┌────▼──────────────▼──────────────▼──────────────▼─────┐ │
│ │ 状态机引擎 (StateMachine) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Agent 工作台接入层 │ │
│ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ │ │
│ │ │Issue 解析│→│上下文包生成│→│Agent 修复│→│质量门禁 │ │ │
│ │ └─────────┘ └──────────┘ └─────────┘ └────┬─────┘ │ │
│ └────────────────────────────────────────────────│────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────▼────────┐ │
│ │ 审批门禁:涉及金额/支付/退款的修改 → 必须人工审批 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘Agent 不是替代开发者,而是作为一个"初级开发者"参与流程:自动完成日志分析、代码定位、补丁编写和测试验证,但涉及核心业务逻辑(金额、支付)的修改必须人工审批。
三、三个核心场景
3.1 场景一:订单金额计算错误
Issue 描述:#BUG-2341 使用满减券后订单金额显示为负数
# 订单金额计算 Bug 的 Agent 上下文包
bugfix_context:
issue: "#BUG-2341"
error_log: |
AssertionError: order.total_amount = -15.00, expected >= 0
at OrderService.calculate_total (app/order/service.py:287)
# Agent 可以读取的文件
allowed_files:
- app/order/service.py # 订单服务核心逻辑
- app/order/models.py # 订单数据模型
- app/coupon/calculator.py # 优惠券计算
- app/order/tests/test_total.py # 已有测试
# 业务规则(注入到 Prompt)
business_rules:
- "订单金额不能小于 0"
- "满减券抵扣不能超过订单原价的 80%"
- "运费不参与满减计算"
# 约束
constraints:
max_patch_lines: 15
must_add_test: true
approval_required: true # 涉及金额,需审批Agent 修复过程:
- 读取报错文件
service.py:287,发现total_amount = subtotal - coupon_discount - shipping - 读取
calculator.py,发现满减券的discount没有上限检查 - 修复:在优惠券计算中增加
min(discount, subtotal * 0.8)约束 - 添加测试:
test_coupon_exceeds_subtotal验证边界条件 - 运行 380 个测试 → 全部通过
3.2 场景二:支付回调幂等性问题
Issue 描述:#BUG-2356 微信支付回调重复处理导致订单状态异常
# 支付幂等性 Bug 上下文包
bugfix_context:
issue: "#BUG-2356"
error_log: |
OrderStatusError: Cannot transition from 'paid' to 'paid'
at OrderStateMachine.transition (app/common/state.py:45)
triggered by PayCallbackHandler.handle (app/pay/callback.py:89)
allowed_files:
- app/pay/callback.py # 支付回调处理
- app/common/state.py # 状态机
- app/order/models.py # 订单模型
- app/pay/tests/test_callback.py
business_rules:
- "支付回调必须幂等:相同交易号重复回调不改变状态"
- "回调处理使用数据库事务 + 行锁"
constraints:
max_patch_lines: 20
must_add_test: true
approval_required: true # 涉及支付,需审批Agent 修复:在 callback.py 中增加幂等检查——用 Redis 分布式锁 + 数据库唯一索引双重保障:
# app/pay/callback.py 修复后
class PayCallbackHandler:
def handle(self, callback_data):
transaction_id = callback_data["transaction_id"]
# 幂等检查 1:Redis 分布式锁(快速路径)
lock_key = f"pay_callback:{transaction_id}"
if not self.redis.set(lock_key, "1", nx=True, ex=3600):
# 已有处理中的锁,可能是重复回调
existing_order = self.order_repo.get_by_transaction(transaction_id)
if existing_order and existing_order.status == "paid":
return {"status": "already_processed"} # 幂等返回
# 幂等检查 2:数据库事务 + 行锁
with db.atomic():
order = Order.select().where(
Order.transaction_id == transaction_id
).for_update().first()
if order.status == "paid":
return {"status": "already_processed"}
order.status = "paid"
order.paid_at = datetime.now()
order.save()
return {"status": "success"}3.3 场景三:退款金额计算与库存回滚
Issue 描述:#BUG-2378 部分退款后库存未正确回滚
bugfix_context:
issue: "#BUG-2378"
error_log: |
InventoryWarning: SKU-001 stock mismatch after partial refund
Expected: 95, Actual: 97
at InventoryService.rollback (app/inventory/service.py:112)
allowed_files:
- app/inventory/service.py # 库存服务
- app/refund/service.py # 退款服务
- app/order/models.py
- app/inventory/tests/
- app/refund/tests/
business_rules:
- "部分退款只回滚被退商品的库存"
- "已发货的商品不能退库存(只能退货后入库)"
- "退款金额 = 商品单价 × 退款数量 - 分摊优惠"
constraints:
max_patch_lines: 25
must_add_test: true
approval_required: true
run_tests: ["test_inventory", "test_refund", "test_order"] # 跨模块回归这个场景的难点在于"部分退款"需要精确计算哪些商品被退了、库存该回滚多少。Agent 通过分析 refund/service.py 发现退款时没有区分"已发货"和"未发货"的商品,导致多回滚了 2 件库存。
四、审批与质量门禁
4.1 审批策略
# approval-policy.yaml
approval_rules:
- condition: "patch touches files matching 'app/pay/**'"
action: "require_approval"
approvers: ["@tech-lead", "@payment-owner"]
reason: "支付相关修改必须技术负责人和支付模块 owner 双重审批"
- condition: "patch modifies 'order.total_amount' or 'refund.amount'"
action: "require_approval"
approvers: ["@tech-lead"]
reason: "金额计算修改必须技术负责人审批"
- condition: "patch.lines_changed > 20"
action: "require_review"
reviewers: ["@senior-dev"]
reason: "修改超过 20 行,需要高级开发者 Review"
- condition: "all tests pass AND patch.lines_changed <= 10 AND no sensitive files"
action: "auto_merge"
reason: "小修改 + 测试通过 = 自动合并"4.2 质量门禁配置
# quality-gate.yaml
gates:
- name: "编译检查"
command: "python -m py_compile app/**/*.py"
blocking: true
- name: "单元测试"
command: "pytest app/{affected_module}/tests/ -v --tb=short"
blocking: true
timeout: 120
- name: "回归测试"
command: "pytest app/order/tests app/pay/tests app/refund/tests -v"
blocking: true
timeout: 300
- name: "Lint 检查"
command: "ruff check app/ --fix"
blocking: false
- name: "安全扫描"
command: "semgrep --config=auto app/"
blocking: true
fail_on: ["ERROR", "HIGH"]五、验收命令与效果
# 验收:运行全部相关测试
pytest app/order/tests app/pay/tests app/refund/tests app/inventory/tests \
-v --tb=short --cov=app --cov-report=term-missing
# 验收:检查 Agent 修复的 PR
gh pr list --author "agent-bot" --state open
gh pr checks <pr-number>
# 验收:审计日志
cat .agent-audit/$(date +%Y-%m-%d).json | jq '.[] | select(.action == "patch")'接入 Agent 后的效果(运行 30 天):
| 指标 | 接入前 | 接入后 | 变化 |
|---|---|---|---|
| 简单 Bug 修复时间 | 3.5 天 | 4 小时 | -85% |
| 复杂 Bug 修复时间 | 7 天 | 5 天 | -29% |
| 回归引入率 | 8% | 3% | -62% |
| 测试覆盖率 | 72% | 81% | +9% |
| 人工审批次数 | N/A | 日均 4.2 次 | — |
六、真实经验与踩坑
6.1 状态机是 Agent 最难理解的部分
场景:订单有 12 个状态、47 种转换路径。Agent 第一次修 Bug 时,把"待支付"直接转到了"已退款",跳过了中间的"支付失败"状态。
问题:Agent 不理解状态机的约束——它只看到"需要退款",不知道必须经过合法的状态转换路径。
解决方案:在上下文包中注入状态机定义文件(state_machine.py 的完整内容),并在 Prompt 中明确约束:"所有状态修改必须通过 OrderStateMachine.transition() 方法,不能直接修改 order.status 字段"。
6.2 金额计算必须用 Decimal,不能用 float
场景:Agent 写的补丁用了 float 做金额计算,在测试中通过了(测试数据凑巧没有精度问题),上线后出现 0.1 + 0.2 != 0.3 的问题。
问题:项目规范中写了"金额使用 Decimal",但 Agent 没有在上下文中看到这个规范。
解决方案:把 .cursorrules 中关于数据类型的规则提取成独立的 rules/money-rules.md,在涉及金额文件的上下文包中强制注入。同时增加 Lint 规则:禁止对 Decimal 字段使用 float() 转换。
6.3 跨模块回归不能只跑"改了的模块"
场景:Agent 修了 refund/service.py 的一个 Bug,回归测试只跑了 test_refund,通过了。合并后发现 inventory 模块挂了——因为退款逻辑改了,库存回滚的入参变了。
问题:Agent 不知道 refund 和 inventory 之间有隐式依赖。
解决方案:维护一张"模块依赖图"(module_deps.yaml),每次修复后根据依赖图扩展测试范围。refund 修改 → 同时跑 refund + inventory + order 的测试。
七、参数说明表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
issue_id |
string | 必填 | Bug Issue 编号 |
allowed_files |
list | 必填 | Agent 可读写的文件范围 |
business_rules |
list | 必填 | 业务规则(注入 Prompt) |
max_patch_lines |
int | 20 |
补丁最大修改行数 |
must_add_test |
bool | true |
是否必须添加回归测试 |
approval_required |
bool | false |
是否需要人工审批 |
approvers |
list | [] |
审批人列表 |
run_tests |
list | ["affected"] |
测试范围:affected / full / 指定模块列表 |
test_timeout |
int | 300 |
测试超时时间(秒) |
auto_merge_threshold |
int | 10 |
修改行数低于此值且测试通过时自动合并 |
八、落地检查清单
- 订单状态机定义文件在 Agent 上下文中可访问
- 涉及金额计算的 Prompt 中注入了 Decimal 规则
- 支付回调的幂等性有专项测试
- 退款库存回滚有跨模块回归测试
- 审批策略配置正确(金额、支付、退款必须审批)
- 模块依赖图已维护,回归测试范围能自动扩展
- 审计日志记录了每次 Agent 修复的完整过程
- 上线前做了人工 Review,不是全自动合并
九、系列导航
上一篇:Lab 005:上下文包越大越好吗?Token 成本与修复成功率实验 下一篇:案例 002:用 Agent 维护 SaaS 后台的 CRUD 与权限模块