教程概述
Verification Loops 确保 AI 的输出是可靠的,而不是"看起来对"。本教程将教你如何建立有效的验证循环和持续学习机制。
你将学到
- ✅ Verification Loop 的两种模式
- ✅ pass@k vs pass^k 评估指标
- ✅ Benchmarking 工作流
- ✅ continuous-learning skill 配置
- ✅ Stop Hook vs UserPromptSubmit 设计决策
为什么需要 Verification Loops?
问题:AI “声称完成”
flowchart LR
A[AI: "功能已完成"] --> B{实际验证}
B --> C[✅ 真的完成]
B --> D[❌ 有 bug]
B --> E[❌ 测试失败]
B --> F[❌ 需求理解错误]
style D fill:#ffcccc
style E fill:#ffcccc
style F fill:#ffcccc
解决方案:验证循环
flowchart TD
A[实现] --> B[验证]
B --> C{通过?}
C -->|是| D[完成]
C -->|否| E[修复]
E --> A
style B fill:#e8f5e9
Verification Loop 模式
模式 1:Checkpoint-Based Evals
在关键检查点设置验证:
sequenceDiagram
participant AI as Claude
participant C as Checkpoint
participant V as Verifier
AI->>C: 检查点 1: 功能实现
C->>V: 验证
V-->>C: ✅ 通过
AI->>C: 检查点 2: 测试通过
C->>V: 验证
V-->>C: ❌ 失败
C->>AI: 返回修复
AI->>C: 检查点 2: 重新验证
C->>V: 验证
V-->>C: ✅ 通过
AI->>C: 检查点 3: 代码审查
C->>V: 验证
V-->>C: ✅ 通过
配置示例:
checkpoints:
- name: 功能实现
criteria:
- 代码编译通过
- 无 TypeScript 错误
on_failure: 继续实现
- name: 测试通过
criteria:
- pytest 通过
- 覆盖率 ≥80%
on_failure: 修复测试
- name: 代码审查
criteria:
- 无安全漏洞
- 代码风格一致
on_failure: 修复问题
模式 2:Continuous Evals
定期运行完整验证:
flowchart TD
A[开发中] --> B{每 N 分钟}
B --> C[运行完整测试套件]
C --> D{全部通过?}
D -->|是| E[继续开发]
D -->|否| F[暂停并修复]
style C fill:#e3f2fd
配置示例:
continuous_evals:
interval: 10 # 分钟
commands:
- npm run test
- npm run lint
- npm run typecheck
on_failure:
- 通知用户
- 记录失败点
两种模式对比
| 特性 | Checkpoint-Based | Continuous |
|---|---|---|
| 验证时机 | 关键节点 | 定期 |
| 成本 | 较低 | 较高 |
| 反馈延迟 | 低 | 中 |
| 适用场景 | 需要确认的里程碑 | 长时间开发会话 |
| 推荐 | ✅ 主要使用 | 补充使用 |
pass@k vs pass^k 指标
定义
pass@k:k 次尝试中至少有一次成功
P(at least 1 success in k attempts)
示例:
k=1: 70% 成功率
k=3: 91% 成功率
k=5: 97% 成功率
pass^k:k 次尝试全部成功
P(all k attempts succeed)
示例:
k=1: 70% 成功率
k=3: 34% 成功率
k=5: 17% 成功率
计算公式
pass@k = 1 - (n - c choose k) / (n choose k)
其中:
n = 总测试数
c = 通过测试数
k = 尝试次数
简化版(当 n 很大时):
pass@k ≈ 1 - (1 - c/n)^k
选择建议
flowchart TD
A[选择评估指标] --> B{需求是什么?}
B -->|只要能成功一次| C[pass@k]
B -->|必须每次都成功| D[pass^k]
C --> E[适用场景:<br/>探索性任务<br/>创意性任务]
D --> F[适用场景:<br/>生产部署<br/>安全关键]
示例计算
场景:单个任务成功率 70%
# pass@k 计算
def pass_at_k(single_success_rate, k):
return 1 - (1 - single_success_rate) ** k
pass_at_k(0.70, 1) # 0.70 (70%)
pass_at_k(0.70, 3) # 0.973 (97.3%)
pass_at_k(0.70, 5) # 0.99757 (99.76%)
# pass^k 计算
def pass_k(single_success_rate, k):
return single_success_rate ** k
pass_k(0.70, 1) # 0.70 (70%)
pass_k(0.70, 3) # 0.343 (34.3%)
pass_k(0.70, 5) # 0.16807 (16.8%)
Benchmarking 工作流
流程图
flowchart TD
A[定义基准任务] --> B[创建测试集]
B --> C[运行基准]
C --> D[记录结果]
D --> E[应用优化]
E --> F[重新测试]
F --> G{改进?}
G -->|是| H[记录改进]
G -->|否| I[回滚/调整]
H --> J[更新基线]
I --> E
style A fill:#e3f2fd
style D fill:#e8f5e9
style H fill:#fff3e0
示例:比较有无 Skill 的效果
# 步骤 1:创建测试集
mkdir -p benchmarks/tasks
echo "实现一个链表" > benchmarks/tasks/01-linked-list.txt
echo "实现一个栈" > benchmarks/tasks/02-stack.txt
echo "实现一个二叉树" > benchmarks/tasks/03-binary-tree.txt
# 步骤 2:运行基准测试(无 Skill)
for task in benchmarks/tasks/*.txt; do
claude --no-skill "$(cat $task)" > benchmarks/results/no-skill-$(basename $task).txt
done
# 步骤 3:运行基准测试(有 Skill)
for task in benchmarks/tasks/*.txt; do
claude --skill tdd-workflow "$(cat $task)" > benchmarks/results/with-skill-$(basename $task).txt
done
# 步骤 4:比较结果
python scripts/compare_benchmarks.py
关键指标
| 指标 | 说明 | 目标 |
|---|---|---|
| 首次成功率 | 一次尝试就正确 | ≥85% |
| Token 效率 | 输出/输入比 | 优化 |
| 时间效率 | 任务完成时间 | 最小化 |
| 错误率 | 生成错误代码频率 | 最小化 |
continuous-learning Skill
工作原理
sequenceDiagram
participant S as Session
participant H as Stop Hook
participant L as Learner
participant K as Skills
S->>H: 会话结束
H->>L: 分析会话
alt 发现新模式
L->>K: 创建新 Skill
K-->>L: 保存成功
else 无新模式
L->>L: 记录日志
end
L-->>H: 学习完成
配置
在 ~/.claude/skills/continuous-learning/README.md:
# 持续学习 Skill
## 触发条件
通过 Stop Hook 在会话结束时触发
## 学习规则
### 保存条件
1. 同一问题出现 ≥2 次
2. 解决方案非显而易见
3. 可复用于其他项目
### 忽略条件
1. 一次性问题
2. 项目特定配置
3. 已有文档覆盖
## 输出位置
~/.claude/skills/learned/YYYY-MM-DD-topic.md
配套 Stop Hook
{
"Stop": [
{
"matcher": "true",
"hooks": [
{
"type": "command",
"command": "python ~/.claude/scripts/continuous_learner.py --session-log $SESSION_LOG"
}
]
}
]
}
Stop Hook vs UserPromptSubmit
设计决策
| 特性 | Stop Hook | UserPromptSubmit |
|---|---|---|
| 触发频率 | 每会话一次 | 每条消息 |
| 延迟影响 | 无(会话结束时) | 有(每条消息) |
| 上下文 | 完整会话 | 单条消息 |
| 推荐用途 | 持久化、学习 | 预处理、注入 |
推荐选择
✅ 使用 Stop Hook 用于:
- 会话摘要
- 持续学习
- 状态持久化
- 清理工作
❌ 避免 UserPromptSubmit 用于:
- 学习相关任务(增加每条消息延迟)
✅ 使用 UserPromptSubmit 用于:
- 上下文注入
- 预处理用户输入
验收标准验证
完整验证流程
flowchart TD
A[声称完成] --> B[检查文件存在]
B --> C[运行编译检查]
C --> D[运行测试套件]
D --> E[运行 Lint]
E --> F[检查覆盖率]
F --> G{全部通过?}
G -->|是| H[✅ 验证通过]
G -->|否| I[❌ 返回修复]
style H fill:#e8f5e9
style I fill:#ffcccc
验证脚本示例
#!/bin/bash
# verify_completion.sh
echo "=== 验证开始 ==="
# 1. 编译检查
npm run build || { echo "❌ 编译失败"; exit 1; }
echo "✅ 编译通过"
# 2. 类型检查
npm run typecheck || { echo "❌ 类型错误"; exit 1; }
echo "✅ 类型检查通过"
# 3. 测试
npm test || { echo "❌ 测试失败"; exit 1; }
echo "✅ 测试通过"
# 4. 覆盖率
COVERAGE=$(npm run test:coverage -- --silent | grep "All files" | awk '{print $4}' | tr -d '%')
if [ "$COVERAGE" -lt 80 ]; then
echo "❌ 覆盖率不足: $COVERAGE% < 80%"
exit 1
fi
echo "✅ 覆盖率: $COVERAGE%"
# 5. Lint
npm run lint || { echo "❌ Lint 失败"; exit 1; }
echo "✅ Lint 通过"
echo "=== ✅ 全部验证通过 ==="
小结
关键要点
- ✅ Checkpoint-Based Evals 在关键节点验证
- ✅ pass@k 用于"至少成功一次"场景
- ✅ pass^k 用于"必须每次成功"场景
- ✅ continuous-learning 通过 Stop Hook 实现
- ✅ Stop Hook 比 UserPromptSubmit 更适合学习任务
系列导航:
- ← 上一篇:教程 9:Token 优化与模型选择策略
- → 下一篇:教程 11:安全防护与 AgentShield
- 返回:教程系列索引