迭代篇
目录¶
15. 迭代方法论¶
高质量 skill 不是一次就构建出来的,而是在真实实践中不断暴露问题、定位根因、修复验证后逐步打磨出来的。本章建立系统性改进 skill 的方法论框架。
15.1 两种迭代驱动力¶
迭代的触发点来自两个渠道,两者互补而非替代:
| 驱动力 | 来源 | 典型触发条件 |
|---|---|---|
| 量化评估驱动 | 第 10 章的三维度 A/B 对照 | 评估分数低于阈值、with-skill vs without-skill 差距不足 10% |
| 实践观察驱动 | 日常使用中的漏报和质量投诉 | 人工复核发现 AI 未捕获的缺陷、开发者反馈误报过多 |
评估篇(§10-11)解决的是"如何度量 skill 的当前质量"——建立基线、发现方向。迭代篇解决的是"拿到数据或遭遇漏报之后怎么系统性改进"——根因分类、精准修复、效果验证。两者形成闭环:评估指向方向,迭代完成改进,再评估校验效果。
实践观察驱动是量化评估的重要补充。A/B 测试需要提前设计场景,而真实代码千变万化,总有测试场景未覆盖的遗漏模式。当开发者在代码审查中发现 AI skill 未报出的缺陷时,这是一次高质量的"免费评估"——这个漏报既揭示了 skill 的盲区,也提供了一个可验证改进效果的具体案例。
15.2 迭代循环的四个阶段¶
每次迭代遵循固定的四阶段结构,跳过任何一步都会降低改进的可信度:
① 实战暴露
↓ 人工复核发现漏报 / 量化评估低于阈值
② 根因分类
↓ 判断:checklist 缺失 / 模型执行遗漏 / 领域知识盲区
③ 针对性修复
↓ 补 checklist / 强化执行规则 / 新增 reference
④ 清上下文验证
↓ 全新对话,无历史残留,重跑相同案例
回到 ①
阶段① 的关键是"保留现场":把漏报案例连同被审查代码一起记录下来,构成后续验证的测试输入。
阶段② 是最容易被跳过但最关键的步骤。不做根因分类就直接修改 skill,修改方向往往是错的:把一个"模型执行遗漏"的问题当成"checklist 缺失"处理,只会让 SKILL.md 越来越长,但遗漏率不会下降(见 §15.3 和 §15.4)。
阶段③ 的改进措施必须与根因类型对应,三种根因各有不同的修复路径(见 §15.4)。
阶段④ 是验证的核心约束:必须清除对话上下文,在全新对话中重跑相同案例。在同一对话中验证会携带历史上下文,模型可能"记住"之前的讨论而非真正依赖改进后的 skill。
15.3 根因分类框架¶
这是迭代方法论的核心。每一处遗漏都必须归入以下三类之一,不同根因的可修复性和修复措施截然不同:
| 根因类型 | 定义 | 可修复性 | 改进措施 | 观测修复率 |
|---|---|---|---|---|
| checklist 缺失 | SKILL.md 的 checklist 未覆盖该检查项,模型"不知道要查" | 确定性 | 补全 checklist | 100%(3/3) |
| 模型执行遗漏 | checklist 已覆盖但模型在执行时跳过,"知道但没查" | 概率性 | 强化执行规则 + 措辞加强 | 67%(2/3) |
| 领域知识盲区 | 超出通用 checklist 范围的业务场景特定知识 | 有限 | 新增 reference | 0%(优化建议类) |
上表数据来自 go-code-reviewer skill 的 3 个实战案例、8 处遗漏的归因分析(详见 §16.2)。
三类根因的诊断方法:
诊断时先回答一个问题:SKILL.md 的 checklist 里是否明确列出了这项检查?
- 否 → checklist 缺失。在当前 skill 中补全 checklist 即可确定性修复。
- 是,但模型未执行 → 模型执行遗漏。需要分析遗漏的触发条件(如高缺陷密度、执行规则措辞模糊),然后针对性强化规则。
- 无法列入通用 checklist(如业务场景特定的性能优化) → 领域知识盲区。可在 reference 文件中补充知识,但效果有限。
一个遗漏可能同时涉及多个根因(见案例二 §16.1),此时按照最高优先级根因处理,同时记录复合因素。
15.4 三类根因的改进策略¶
15.4.1 checklist 缺失:补全的工程方法¶
checklist 缺失是三类根因中唯一具有确定性修复效果的。补全 checklist 后,模型在下次执行时会被强制检查该项,修复率在观测案例中达到 100%。
补全 checklist 的工程方法:
第一步:定位插入点。新 checklist 项应插入到逻辑上相关的类别(如"goroutine panic recover"归入 Concurrency & Lifecycle,"指针切片 nil guard"归入 Code Quality),而非统一堆放在末尾。
第二步:控制描述粒度。checklist 项只需描述"查什么",不需要解释"为什么"和"怎么查"——后者放到对应的 reference 文件里。精简前后对比:
| 项目 | 精简前(冗长) | 精简后(推荐) |
|---|---|---|
| goroutine recover | "panics inside goroutines are not propagated to the parent goroutine; any unrecovered panic crashes..." (2 行) | Goroutine missing defer recover() — unrecovered panic crashes entire process (1 行) |
| goroutine 数量控制 | "spawning one goroutine per item in an unbounded loop causes resource exhaustion..." (2 行) | Goroutines created per loop element without concurrency limit (semaphore / errgroup.SetLimit / worker pool) (1 行) |
第三步:同步更新 reference 文件。新增的 checklist 项通常需要在对应的 reference 文件中补充 BAD/GOOD 示例和触发条件,帮助模型在执行时正确识别模式。
第四步:监控 SKILL.md 体积。每次补全 checklist 后检查总行数,保持 SKILL.md ≤ 500 行。超出时优先考虑将 anti-examples 或详细说明迁移至 reference 文件(见 §16.1 案例三的瘦身实践)。
15.4.2 模型执行遗漏:规则约束的边界¶
模型执行遗漏是最棘手的根因类型。checklist 明确列出了检查项,模型却在执行时跳过了——这不是知识问题,而是注意力分配问题。
常见触发条件:
- 高缺陷密度:存在多个 High 级别缺陷时,模型注意力集中在"能让代码停止运行的错误",系统性忽略较低优先级的检查项。
- 执行规则措辞模糊:checklist 项只写了模式名称(如
Missing error wrapping),没有明确指向具体的检查动作(如inspect every return err path),模型执行时缺乏操作锚点。 - 后置合理化幻觉:模型遗漏某项检查后,遭遇追问时会产生"听起来合理的"后置解释,而非承认遗漏。这是 LLM 推理的固有特性,不能通过措辞改进消除。
改进措施:
-
措辞加强:将被动描述改为主动动作指令。例如:
-
增加执行完整性规则:在 Review Discipline 节明确写入"无论已发现多少 High finding,必须继续执行全部 checklist 类别",针对高缺陷密度场景下的注意力竞争。
-
前置条件引证规则:应用任何 anti-example 抑制之前,必须在代码中找到具体证据证明前置条件已满足。仅凭"类别匹配"不构成抑制理由。
执行遗漏在规则层只能缓解,无法根本消除。这是一个架构层问题,解法见架构篇(§17-18)。
15.4.3 领域知识盲区:reference 的有限性¶
领域知识盲区指的是业务场景驱动的、无法列入通用 checklist 的优化建议。典型例子:当函数同时包含 Count + Find 且未分页时,如果业务中大多数用户的记录为空,先执行 Count 并在结果为 0 时提前返回能显著减少 SQL 请求——这是一个需要了解业务访问模式才能提出的建议。
改进措施是在 reference 文件中新增相关知识节(含 BAD/GOOD 示例和适用条件)。但这种改进的效果是有限的:
- Reference 提供的是"知识储备"而非"执行指令"——模型需要自主完成从代码特征到优化建议的推理链。
- 推理链越长、需要的业务上下文越多,reference 补充的效果越差。
- 在观测案例中,count-first 优化建议在补充 reference 后仍未被模型自主发现(修复率 0%)。
对于领域知识盲区,更务实的应对策略是: - 将该类建议从"AI 应自动发现"的期望调整为"人工复核重点关注"的范围。 - 如果业务特定模式足够重要且出现频率高,考虑为其单独构建一个专项 skill,而非在通用 skill 中处理。
15.5 验证方法论¶
验证是迭代循环的闭环环节,设计不当会使整个改进过程失去意义。
核心约束:清除对话上下文,在全新对话中重跑改进前的遗漏案例。不满足这个条件的验证结果不可信。
验证条件检查表:
□ 对话上下文已清空(clear context 或新开对话)
□ 模型版本与首次审查一致
□ 审查模式与首次一致(Lite / Standard / Strict)
□ 被审查代码与首次完全相同(原始版本,非修复后版本)
□ 未提供任何提示或引导(不额外告知 "注意检查 X")
量化记录:
验证结果应以表格形式记录,逐项对比首次审查和改进后审查,区分"保持"、"修复"、"未修复"、"新发现"四种状态。这份记录既是改进效果的证明,也是后续继续迭代的起点数据。
对部分修复结果的处理:如果某项遗漏在改进后仍未修复,需要重新分类根因——可能初始归因有误,或者根因属于"执行遗漏"而执行规则仍不够强。残留遗漏进入下一轮迭代,而非被忽略。
16. 迭代案例集与迭代边界¶
理论框架在具体案例中才能被真正理解。本章以两类 skill 的迭代实践为例,展示根因分类框架在真实场景中的应用——方法论本身(分类→修复→验证)不局限于代码审查场景,适用于任何遭遇漏报或质量问题的 Skill。
16.1 三类根因在实践中的应用(以代码审查 Skill 为例)¶
以下案例均来自 go-code-reviewer 在实际使用中的遗漏记录,作为三类根因分类框架的实证演示。每个案例对应一种(或多种)根因类型,展示不同根因下改进措施的选择逻辑和实际效果。
这是迭代方法论最完整的实证记录:3 个案例、8 处遗漏、系统性归因、针对性改进、清上下文验证。
案例一:ListLayout — 模型执行遗漏 + 后置合理化幻觉¶
被审查代码(节选):
func (s *LayoutService) ListLayout() (layouts []*db.Layout, total int64, err error) {
whereClause := fmt.Sprintf("uid = %v and corp_id = %v", s.uid, s.corpId)
if err := s.orm.Model(&db.Layout{}).Where(whereClause).Order("updated_at desc").Find(&layouts).Error; err != nil {
return nil, 0, err // 未包装
}
if err := s.orm.Model(&db.Layout{}).Where(whereClause).Count(&total).Error; err != nil {
return nil, 0, err // 未包装
}
return layouts, total, nil
}
AI 报出:SQL 注入(High)、缺少 ctx(Medium)、Find 无分页(Medium)。共 3 项。
人工复核发现的遗漏:
- 遗漏一:两处
return nil, 0, err将 GORM 原始错误直接透传,调用方无法区分是哪个查询失败,且原始错误可能携带表名、列名等敏感信息。正确做法:return nil, 0, fmt.Errorf("ListLayout: find layouts: %w", err)。 - 遗漏二:count-first 优化——当
total == 0时跳过代价更高的 Find 查询,在空结果率高的业务场景下可显著减少 SQL 请求。
根因分析:
| 遗漏 | 根因类型 | 分析 |
|---|---|---|
| 错误未包装 | 模型执行遗漏 | SKILL.md Error Handling checklist 明确列出 Missing error wrapping context (%w),但模型未逐条检查每个 return err 路径 |
| count-first 优化 | 领域知识盲区 | 业务场景驱动的性能优化,标准 checklist 无法覆盖 |
追问模型为何遗漏错误包装时,模型声称"误套了 anti-example 里的抑制规则"——但代码里完全没有包装,anti-example 的前置条件显然不成立。这是典型的后置合理化幻觉:模型在遗漏某项检查后,遭遇追问时构造了一个听起来合理的解释,而非承认遗漏。这个现象本身也驱动了改进措施——在执行规则中加入"应用任何 anti-example 抑制之前,必须先引用代码中的具体证据"。
案例二:getBatchUser 串行版本 — checklist 缺失 + 分析视角不对称¶
被审查代码(节选):
func getBatchUser(ctx context.Context, userKeys []*UserKey) (users []*User, error) {
userList := make([]*User, len(userKeys))
for i, u := range userKeys {
user, err := redis.GetGuest(ctx, u.Id) // u 可能为 nil → panic
if err != nil {
log.WarnContextf(ctx, "no found guest user: %v", u)
continue
}
userList[i] = user
}
return userList, nil
}
AI 报出:错误被静默吞噬(High)、结果切片含隐式 nil 条目(Medium)、N+1 Redis 调用(Medium)。共 3 项。
人工复核发现的遗漏:userKeys []*UserKey 是指针切片,元素可能为 nil。代码在 u.Id 处直接解引用,若任一元素为 nil 则触发 panic。正确做法:循环内先检查 if u == nil { continue }。
根因分析:
| 遗漏 | 根因类型 | 分析 |
|---|---|---|
| 指针切片 nil 防御 | checklist 缺失 + 分析视角不对称 | SKILL.md Code Quality checklist 无此检查项;且模型在同一循环中发现了输出侧 nil(userList[i] 可能为 nil),却未对输入侧(u 可能为 nil)应用同样的分析——分析视角不对称 |
这是一个复合根因案例:即使补全 checklist,输入/输出分析视角不对称的问题仍可能导致遗漏。改进时需要同时补 checklist 并在执行规则中明确"对指针切片需同时检查输入侧和输出侧"。
案例三:getBatchUser 并发版本 — 高缺陷密度下的注意力竞争¶
被审查代码(节选):
func getBatchUser(ctx context.Context, userKeys []*UserKey) (users []*User, error) {
userList := make([]*User, 0) // 未预分配容量
var wg sync.WaitGroup
for i, u := range userKeys {
if u == nil { continue }
wg.Add(1)
go func() {
defer wg.Done()
// 无 recover,无并发限制
user, err := redis.GetGuest(ctx, u.Id)
if err != nil { log.WarnContextf(ctx, "no found guest user: %v", u); continue }
userList = append(userList, user) // 数据竞争
}()
}
return userList, nil // 未等待 goroutine 完成
}
AI 报出(Strict 模式):goroutine 内 continue 编译错误(High)、缺少 wg.Wait()(High)、并发写数据竞争(High)、循环变量捕获(High)、错误静默吞噬(Medium)。共 5 项,其中 4 项 High。
人工复核发现的遗漏:
- 切片未预分配容量:
make([]*User, 0)初始容量为 0,len(userKeys)是已知上界,应预分配:make([]*User, 0, len(userKeys))。 - goroutine 缺少 panic recover:Go 的 panic 不跨 goroutine 边界传播,任意 goroutine 内 panic 导致整个进程崩溃,
wg.Done()也不会执行。 - 未限制 goroutine 数量:
len(userKeys)无上限约束,瞬间启动大量 goroutine 可能导致调度器压力激增、Redis 连接池打满。
根因分析:
| 遗漏 | 根因类型 | 分析 |
|---|---|---|
| 切片容量预分配 | 模型执行遗漏 | SKILL.md Performance checklist 明确列出该项,但 4 个 High 并发缺陷占据注意力,Performance 类 checklist 被跳过 |
| goroutine panic recover | checklist 缺失 | Concurrency checklist 覆盖了并发"正确性",缺少并发"安全性"(未覆盖 recover) |
| goroutine 数量控制 | checklist 缺失 | Concurrency checklist 缺少并发"规模安全性"(资源耗尽、调度压力) |
三处遗漏的共同模式:在存在多个 High 缺陷时,注意力集中在"能让代码停止运行的错误",系统性漏检了"代码能跑但在生产规模下会崩"的类别。
16.2 遗漏归因汇总¶
3 个案例共 8 处遗漏(含 7 项硬性缺陷、1 项优化建议),归因分布如下:
| 根因类型 | 数量 | 对应遗漏 |
|---|---|---|
| checklist 缺失 | 3 | goroutine panic recover、goroutine 数量控制、指针切片 nil guard |
| 模型执行遗漏 | 3 | 错误未包装、切片容量预分配、高缺陷密度下注意力分散 |
| 领域知识盲区 | 1 | count-first 优化(特定场景优化建议) |
| checklist 缺失 + 模型执行遗漏(复合) | 1 | 指针切片 nil guard(checklist 未覆盖 + 输入/输出分析视角不对称) |
改进措施:补充 4 项 checklist 条目、新增 1 个 reference 文件(go-review-anti-examples.md)、强化 2 条执行规则;同时将 SKILL.md 从 502 行优化至 470 行(通过将 anti-examples 迁移至独立 reference 文件)。
验证结果(清上下文后重跑相同案例):
| 根因类型 | 改进措施 | 修复数 | 未修复数 | 观测修复率 |
|---|---|---|---|---|
| checklist 缺失 | 补全 checklist | 3/3 | 0 | 100% |
| 模型执行遗漏 | 强化执行规则 | 2/3 | 1 | 67% |
| 领域知识盲区 | 新增 reference | 0/1 | 1 | 0% |
| checklist 缺失 + 执行遗漏(复合) | 补全 + 强化规则 | 1/1 | 0 | 100% |
总遗漏从 8 项降至 2 项,整体修复率 75%。同时新增发现 6 项此前未报出的问题(如 named return shadowing、日志消息误导性)。
16.3 git-commit Skill 的迭代案例(补充视角)¶
go-code-reviewer 展示的是"漏报驱动"的迭代路径——由实战中发现的遗漏触发。git-commit skill 则展示了另外两种同样常见的迭代触发类型:误报过多(工具噪音破坏信任)和诚实降级缺失(样本不足时仍输出看似确定的建议)。两种 skill 类型不同,触发点不同,但根因分类和改进路径的逻辑完全相同。
迭代案例一:secret 检测误报控制¶
发现的问题:secret 检测在实际使用中误报严重。password=、token= 等通用模式频繁命中测试文件、文档和示例配置(如 config.example.yml 中的 password=changeme)。初版"命中就阻断"的设计导致开发者频繁被打断。
根因分析:初版把所有 secret 模式平等对待——AKIA0EXAMPLE1234(结构化 token,假阳性极低)和 password=changeme(通用键名,假阳性极高)触发相同的阻断逻辑。这是典型的精度梯度缺失问题。
改进方案:
| 改动 | 解决的问题 |
|---|---|
| SECRET_PATTERNS 拆为 Tier1(结构化 Token)和 Tier2(通用键名) | 区分高低置信模式,分级处置 |
新增行级内联抑制(# nosec / # git-commit-allow) | 已确认安全的误报有标准出口 |
路径过滤扩展至 /examples/、.template.、.dist 等 | 覆盖示例配置的常见存放位置 |
| Tier2 命中增加值分析三档处置(忽略/软警告/硬阻断) | password=changeme 自动忽略,高熵值才阻断 |
验证:对示例配置文件的误报从 ~60% 降至 <5%,敏感扫描场景通过率维持 100%。
迭代案例二:scope 发现的诚实降级¶
发现的问题:skill 通过分析 git log 最近 30 条记录决定 scope 写法。在新仓库或刚切换分支时,历史记录不足,scope 建议变得不稳定——有时加 scope,有时不加,同一模块的 scope 名称也不一致。
根因分析:算法对历史数据的依赖没有设置下限。当样本不足时,统计结果不可靠,但 skill 仍输出了看似确定的建议。这是诚实降级缺失的问题(见进阶篇 §6.7)。
改进方案:当 git log 记录 < 10 条时,scope 改为可选,并提示"历史记录不足,建议手动指定 scope";同时添加 scope 一致性检查,若最近 10 条同模块提交写法不统一,输出警告。
核心原则:Skill 可以拒绝给出不可靠的建议——比"总是给建议但有时是错的"更值得信赖。
16.4 迭代停止信号¶
不是所有 skill 都需要无限迭代。以下四个信号表明迭代可以暂停或停止:
| 信号 | 含义 |
|---|---|
| A/B 测试中 with-skill 与 without-skill 差异 < 10% | Skill 的差异化价值不足,考虑简化或合并 |
| 连续 3 次迭代未改变评估分数 | 已接近当前架构的天花板,需要根本性重构或接受现状 |
| 实际使用中被跳过的频率 > 30% | Skill 与开发者工作流存在摩擦,需要审视定位 |
| Token 消耗持续增长但 ROI 下降 | 过度复杂化,考虑拆分为多个轻量 skill |
停止迭代不等于放弃维护。以下事件应触发重新评估:模型版本升级、业务场景发生重大变化、团队代码规范更新、新案例揭示此前未见的遗漏模式。
Skill 的迭代应遵循与代码相同的版本管理实践:每次改进提交到 Git,附带清晰的 commit message 说明改了什么、为什么改。团队成员可以通过 Git 历史追溯 skill 的演进脉络,理解每个设计决策的背景。每次改动后重跑 scripts/run_regression.sh,确保新的修改没有破坏已有的合约测试和黄金场景测试。
16.5 Skill 完整生命周期与多层防御¶
结合评估篇(§10-11)的量化框架与本章的迭代实践,skill 的完整生命周期如下:
┌─────────────────────────────────────────────────────────────────┐
│ Skill 完整生命周期 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ① 构建 │
│ ├── 设计模式(门禁 + 反例 + 渐进式披露 + 结构化输出) │
│ ├── 合约测试 + 黄金场景测试 │
│ └── 回归测试通过 │
│ ↓ │
│ ② 量化评估(评估篇 §10) │
│ ├── 触发准确率: 正例/负例查询集 → Recall + Precision │
│ ├── 实际任务表现: with-skill vs without-skill 对照实验 │
│ └── Token 效费比: 额外成本 vs 开发者时间 ROI │
│ ↓ │
│ ③ 数据驱动迭代(本章 §15-16) │
│ ├── 实战漏报 / 评估数据 → 根因分类(§15.3) │
│ ├── 针对性修复(§15.4)→ 清上下文验证(§15.5) │
│ └── 重复直至达标或触发停止信号(§16.4) │
│ ↓ │
│ ④ 融入开发流程(集成篇 §12-14) │
│ ├── 本地: Makefile 驱动的质量门禁 │
│ ├── CI: Skill 驱动的自动化门禁 │
│ └── PR: AI 驱动的代码审查 │
│ ↓ │
│ ⑤ 持续监控与再评估 │
│ ├── 模型更新 / 新场景 / 团队规范变化 → 触发复评 │
│ └── 合约测试 + 回归测试守护 skill 不退化 │
│ ↓ │
│ 回到 ② 或 ③ │
│ │
└─────────────────────────────────────────────────────────────────┘
阶段③(本章)在闭环中的位置:以前 skill 从①直接跳到④,中间缺乏验证——好比写了代码不跑测试直接上线。评估篇(②)建立了量化基线,迭代篇(③)建立了系统性改进路径,两者合力让 skill 开发具备与软件开发同等的工程严谨度。
多层防御体系
迭代改进的目标是最大化 AI skill 的效用,但 AI 代码审查存在两个固有边界,不能通过迭代消除:
- 注意力竞争边界:高缺陷密度场景下,模型会系统性地忽略较低优先级的检查项。规则约束只能降低概率,无法消除。
- 推理链长度边界:需要跨多个信号组合推理的优化模式,即使 reference 提供完整知识,模型也可能无法自主完成推理链。
因此,高质量代码保障体系应当是多层防御的:
┌─────────────────────────────────────────────────────────────────┐
│ 代码质量保障体系 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 第一层:自动化测试 │
│ ├── 单元测试(逻辑正确性、边界条件) │
│ ├── API 集成测试(接口契约、端到端数据流) │
│ └── E2E 端到端测试(用户流程、跨服务交互) │
│ │
│ 第二层:AI 代码审查 │
│ ├── 静态分析工具(go vet、staticcheck、golangci-lint) │
│ └── AI Agent skill 审查(模式匹配、缺陷检测) │
│ │
│ 第三层:人工代码审查 │
│ ├── 业务逻辑合理性(AI 难以判断的领域知识) │
│ ├── 架构设计评审(跨模块影响、长期可维护性) │
│ └── AI 审查结果复核(发现 AI 遗漏、校准 AI 输出) │
│ │
└─────────────────────────────────────────────────────────────────┘
三层之间的关系不是替代,而是互补:
- 自动化测试捕获回归和行为偏差——代码"能不能跑对"。
- AI 代码审查捕获模式化缺陷和已知反模式——代码"有没有常见坑"。
- 人工代码审查捕获业务语义、架构合理性和 AI 盲区——代码"该不该这么写"。
任何单一层都不足以保证代码质量。Skill 迭代的价值在于持续提升第二层的覆盖率和准确率,而非试图用 AI 完全替代第三层。
下一步阅读:集成篇(§12-14)介绍如何将经过迭代验证的 skill 融入 Makefile、CI 流水线和 PR 流程,形成完整的质量管线。