Harness Engineering, Skills, and Loop Engineering — 从信任模型到验证系统 / From Trusting Models to Verifying Systems
📅 创建时间:2026-07-29 🏷️ 标签:#HarnessEngineering #LoopEngineering #Skills #AgentArchitecture #ContextEngineering 📚 前置知识:[[14-context-engineering]] [[14a-agent-caching]] [[05-agent-workflow]] [[11-agent-architecture-patterns]]
📋 本章目标
- 理解 Harness Engineering 的核心定义:从"信任模型"到"验证模型"的工程范式转变
- 掌握 Harness 五组件架构(Guides / Sensors / Enforcement / Context Pipeline / Observability)
- 理解 Skills 作为 Harness 中的可复用能力单元,与 Tool 的本质区别
- 理解 Loop Engineering:自动化 Agent 的触发、验证和迭代,不再需要人类逐轮驱动
- 掌握四层嵌套模型(Prompt → Context → Harness → Loop)及其诊断框架
- 理解"外层不取消内层"的核心原则:Loop 放大 Harness 和 Context 的错误而不是修复它们
- 建立从 2023 到 2026 的工程演化时间线直觉
第0部分:回顾——你已经有 Context Engineering,然后呢?
0.1 Context Engineering 管好了什么?
Context Engineering 管好了模型的输入。你精心设计了哪些信息进入上下文窗口、以什么格式、在什么时候检索。你已经做到了:
- 把 RAG 检索到的相关文档精确地注入 prompt
- 通过 CLAUDE.md 或项目 spec 设置模型的行为基调
- 用 system prompt 分层结构管理指令优先级
- 在上下文窗口里放上了所有"应该"知道的信息
┌─────────────────────────────────────────────────────────────┐
│ Context Engineering 做了什么 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入侧(已解决): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ System Prompt → Project Specs → RAG Docs → History │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM(模型推理) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 输出侧(仍然失控): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ?模型可能调用错误的工具 │ │
│ │ ?模型可能忽略安全规则 │ │
│ │ ?模型可能陷入死循环 │ │
│ │ ?模型可能在同一类错误上反复失败 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘0.2 问题不在输入,在输出
即使你给了模型完美的上下文,模型仍然可能:
- 调用错误的工具:问"北京天气",它调了
send_email——上下文再完美也没用 - 忽略安全规则:prompt 里写了 3 遍"禁止删除生产数据库",模型依然生成
DROP TABLE - 陷入死循环:工具调用失败 → 重试 → 同样的参数 → 又失败 → 又重试
- 在同类错误上反复失败:上一次出错的模式,下一次照犯
Context 是信任模型做对的事。但我们需要验证模型确实做对了。 这就是 Harness Engineering 要解决的问题。
┌─────────────────────────────────────────────────────────────┐
│ Context vs Harness —— 两种不同的工程哲学 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Context Engineering: │
│ "我把所有必要信息都放进去了,模型应该能做好。" │
│ 哲学基础:信任(Trust-based) │
│ │
│ Harness Engineering: │
│ "我不关心模型想做什么,我只关心 —— │
│ 生成的代码通过了 lint 吗? │
│ 工具调用的参数合法吗? │
│ 权限检查过了吗? │
│ 如果没通过,拦住别让它进下一步。" │
│ 哲学基础:验证(Verification-based) │
│ │
└─────────────────────────────────────────────────────────────┘0.3 核心公式
Vivek Trivedy(LangChain, 2026.3)提出了一个简洁的公式:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Agent = Model + Harness │
│ │
│ Model → 推理能力(概率性的、不可预测的) │
│ Harness → 约束系统(确定性的、可验证的) │
│ │
│ 只有两者结合,才能把概率性推理变成确定性执行。 │
│ │
└─────────────────────────────────────────────────────────────┘这是一个深刻的工程洞察:如果你只有一个裸模型加上漂亮的 prompt,你没有 Agent。你有的只是一个会说话的黑箱。 Agent 之所以叫 Agent,是因为它被 Harness 约束在了一个可预期的行为边界内。
第1部分:什么是 Harness Engineering
1.1 Harness = 模型外面的"一切"
Harness 是包裹在模型外层的所有非模型组件的统称。它的职责是:无论模型输出什么,系统都不会做出不可接受的事。
┌─────────────────────────────────────────────────────────────┐
│ Harness 五组件架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Guides │ │ Sensors │ │ Enforcement │ │
│ │ (引导) │ │ (感知) │ │ (执行) │ │
│ └─────┬────┘ └─────┬────┘ └──────┬───────┘ │
│ │ │ │ │
│ 生成前提供上下文 生成后检测问题 确定性约束 │
│ │ │ │ │
│ ┌─────┴─────────────┴──────────────┴───────┐ │
│ │ Context Pipeline(上下文管道) │ │
│ │ 编排 L2 Context Engineering 的流入流出 │ │
│ └────────────────────┬──────────────────────┘ │
│ │ │
│ ┌────────────────────┴──────────────────────┐ │
│ │ Observability(可观测性) │ │
│ │ trace: 输入 → 输出 → 工具调用 → 延迟 → 决策 │ │
│ └────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1.2 五组件详解
Guides(引导):在模型生成之前注入约束和知识。
┌─────────────────────────────────────────────────────────────┐
│ Guides 举例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ • CLAUDE.md / AGENTS.md — 项目级行为约定 │
│ • .cursor/rules — IDE 级 AI 编码规范 │
│ • specs/*.md — 功能规格,告诉 Agent 要达成什么目标 │
│ • templates/ — 代码模板,约束输出格式 │
│ • prompts/*.md — 可复用的 prompt 片段 │
│ │
│ 一句话:Guides = 在模型开口之前,先把"规矩"摆在它面前 │
│ │
└─────────────────────────────────────────────────────────────┘Sensors(感知):在模型生成之后检测问题。
# Sensors 的典型实现模式
class SensorPipeline:
"""在 Agent 的每一步输出之后运行的检测器链"""
def __init__(self):
self.sensors = [
LintSensor(), # ESLint / Ruff —— 代码质量
TypeCheckSensor(), # mypy / tsc —— 类型安全
SecurityScanSensor(), # bandit / semgrep —— 安全漏洞
TestSensor(), # pytest / jest —— 行为正确性
DriftSensor(), # 输出与 spec 的偏离度检测
]
def evaluate(self, agent_output: str) -> SensorReport:
results = {}
for sensor in self.sensors:
results[sensor.name] = sensor.check(agent_output)
return SensorReport(results)┌─────────────────────────────────────────────────────────────┐
│ Sensor 检测 → 反馈循环 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Agent 生成代码 │
│ ↓ │
│ ┌──────────────┐ │
│ │ LintSensor │── 通过 → 继续 │
│ │ │── 失败 → 把 lint 错误注入 Agent 的 │
│ └──────────────┘ 下一条消息,让它自己修 │
│ ↓ │
│ ┌──────────────┐ │
│ │ TypeCheck │── 通过 → 继续 │
│ │ Sensor │── 失败 → 注入类型错误,让 Agent 修 │
│ └──────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ TestSensor │── 通过 → 输出合格 │
│ │ │── 失败 → 注入测试失败信息,让 Agent 修 │
│ └──────────────┘ │
│ │
│ 关键:Sensors 不自己做修复,它们只是检测 + 反馈。 │
│ 修复由 Agent 在下一轮迭代中完成。 │
│ │
└─────────────────────────────────────────────────────────────┘Enforcement(执行):确定性的硬约束——不通过就拦住,没有商量余地。
┌─────────────────────────────────────────────────────────────┐
│ Enforcement —— 没有商量的硬约束 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 层级 │ 机制 │ 失败后果 │
│ ────────────────┼───────────────────────┼──────────────────│
│ CI Gate │ GitHub Actions Check │ PR 不能合入 │
│ Permission │ allowlist / denylist │ 操作被拒绝 │
│ Sandbox │ Docker / Firecracker │ 命令无法逃逸 │
│ Rate Limit │ Token bucket │ 请求被限流 │
│ Budget Limit │ $ cap per session │ 会话被终止 │
│ │
│ 核心原则:Enforcement 是确定性的,不是概率性的。 │
│ 不是 "请遵守规则"(prompt 里的请求), │
│ 而是 "不遵守就过不去"(系统级的硬拦截)。 │
│ │
└─────────────────────────────────────────────────────────────┘Context Pipeline(上下文管道):Harness 中负责编排 Context Engineering 的部分。
// Context Pipeline —— 决定每一步把什么放进上下文窗口
interface ContextPipeline {
// 每一轮对话开始前,Pipeline 决定注入什么
async assembleContext(turn: Turn): Promise<ContextWindow> {
const systemPrompt = await this.loadSystemPrompt(); // 基础指令
const projectSpecs = await this.loadProjectSpecs(); // 项目规范
const relevantDocs = await this.rag.search(turn.query); // RAG 检索
const recentHistory = await this.memory.recent(20); // 最近对话
const skillFiles = await this.loadRelevantSkills(turn); // 相关 Skills
const sensorFeedback = turn.previousSensorResults; // 上一轮的 Sensor 反馈
return new ContextWindow({
systemPrompt,
projectSpecs,
relevantDocs,
recentHistory,
skillFiles,
sensorFeedback,
});
}
}Observability(可观测性):完整的 trace,覆盖每一次决策。
┌─────────────────────────────────────────────────────────────┐
│ Observability —— 每一次决策都有迹可循 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Trace 记录的内容: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 输入(user message + assembled context) │ │
│ │ • 输出(model response text) │ │
│ │ • 工具调用(name, arguments, result, latency) │ │
│ │ • 决策路径(why did it choose tool A over tool B?) │ │
│ │ • Sensor 结果(which checks passed/failed) │ │
│ │ • Enforcement 事件(was anything blocked?) │ │
│ │ • Token 消耗(per turn, cumulative) │ │
│ │ • 延迟(TTFT, inter-token latency, tool round-trip) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 没有可观测性 = 黑箱。出了问题你只知道"Agent 做错了", │
│ 但不知道为什么错、在哪一步错、以后怎么防止。 │
│ │
└─────────────────────────────────────────────────────────────┘1.3 关键哲学转变:Mitchell Hashimoto 的观点
Mitchell Hashimoto(Terraform / Ghostty 创始人,2025 年末)提出了一个清晰的对比框架:
┌─────────────────────────────────────────────────────────────┐
│ L1/L2(信任模型) vs L3(验证输出) │
├─────────────────────────────────────────────────────────────┤
│ │
│ L1/L2 做法(Prompt / Context): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 在 prompt 里写: │ │
│ │ "请遵守 PEP 8 编码规范" │ │
│ │ "请确保所有函数都有类型注解" │ │
│ │ "请使用 async/await 而非原始 Promise" │ │
│ │ │ │
│ │ 问题:模型可能忽略、忘记、或被其他指令覆盖。 │ │
│ │ 你只能"相信"它遵守了。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ L3 做法(Harness / Enforcement): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 在 CI 里接一个 ruff lint + mypy type check: │ │
│ │ │ │
│ │ - name: Lint & Type Check │ │
│ │ run: | │ │
│ │ ruff check . │ │
│ │ mypy src/ │ │
│ │ │ │
│ │ 不通过?PR 合不进去。模型自己想办法修。 │ │
│ │ 你不需要"相信"它遵守了——你"验证"了它遵守了。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 这就是 Harness Engineering 的本质: │
│ 把"请遵守规则"的请求,变成"不遵守就过不去"的系统。 │
│ │
└─────────────────────────────────────────────────────────────┘1.4 Harness 和 Context 的分工
┌─────────────────────────────────────────────────────────────┐
│ Harness vs Context —— 谁管什么? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 层 │ 管什么 │ 怎么管 │
│ ─────────────────┼─────────────────────┼───────────────────│
│ Context (L2) │ 信息完整性 │ 注入正确的上下文 │
│ │ 模型"应该"知道什么 │ │
│ ─────────────────┼─────────────────────┼───────────────────│
│ Harness (L3) │ 行为正确性 │ 验证 + 拦截 + 反馈 │
│ │ 模型"实际"做了什么 │ │
│ │
│ 例子: │
│ Context 确保 Agent 看到了项目的 ESLint 配置文件。 │
│ Harness 确保 Agent 生成的代码通过 ESLint—— │
│ 不通过就把 lint 错误喂回去让它修。 │
│ │
│ Context 提供信息,Harness 强制执行。 │
│ │
└─────────────────────────────────────────────────────────────┘第2部分:Skills——Harness 中的可复用能力单元
2.1 Tool 太细,Agent 需要操作手册
Tool 是 Agent 的最小可调用单元:
// Tool —— 一个原子操作
{
"name": "get_weather",
"description": "获取指定城市的实时天气",
"parameters": {
"city": { "type": "string" }
}
}但真实场景中,用户的需求往往比单个 Tool 复杂得多。用户说"帮我查一下天气",隐含的期望是:
- 先调用
get_weather查询 - 如果主 API 挂了,自动尝试
backup_weather_api - 用中文回复,附带穿衣建议
- 如果是雨天,提醒带伞
- 把结果格式化成一段友好的话,不要直接 dump JSON
这些Tool 的组合逻辑 + 行为约定——Agent 不能每次都靠 LLM 从零推理出来。这就是 Skills 要解决的问题。
┌─────────────────────────────────────────────────────────────┐
│ Tool vs Skill —— 层级对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tool = "你可以调用 get_weather" │
│ │
│ Skill = "当用户问天气时: │
│ 1. 先查 get_weather │
│ 2. 如果失败,尝试 backup_weather_api │
│ 3. 用中文回复 │
│ 4. 附带穿衣建议 │
│ 5. 雨天提醒带伞 │
│ 6. 格式化输出,不 dump 原始 JSON" │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tool = 原子操作(一把螺丝刀) │ │
│ │ Skill = 操作手册("怎么用这把螺丝刀修好这张桌子") │ │
│ │ = prompt + tools + workflow 的打包 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2.2 Skill 的物理形态
一个 Skill 是一个目录或文件,包含:
┌─────────────────────────────────────────────────────────────┐
│ Skill 的目录结构(以 Claude Code 为参考) │
├─────────────────────────────────────────────────────────────┤
│ │
│ .claude/skills/ │
│ └── weather-advice/ │
│ ├── SKILL.md ← 核心:行为约定 + prompt │
│ ├── tools/ ← 这个 Skill 专属的工具函数 │
│ │ └── get_weather.py │
│ └── examples/ ← 示例:帮助模型理解预期 │
│ ├── sunny_day.md │
│ └── rainy_day.md │
│ │
└─────────────────────────────────────────────────────────────┘一个典型的 SKILL.md 文件内容:
# Weather Advice Skill
## 触发条件
- 用户询问任何城市的天气
- 用户问"今天出门穿什么"
- 用户提到"明天会下雨吗"
## 行为约定
1. 先调用 get_weather(city) 获取天气数据
2. 如果 get_weather 返回错误,自动尝试 backup_weather_api(city)
3. 解析天气数据,提取:温度、天气状况、湿度、风力
4. 根据温度给出穿衣建议:
- <10°C: 厚外套
- 10-20°C: 薄外套
- >20°C: 短袖
5. 如果天气状况包含"雨",追加带伞提醒
6. 输出格式:📍 {城市}今天天气:{状况},{温度}°C 👔 穿衣建议:{建议} ☂️ 雨具提醒:
## 错误处理
- get_weather 失败 → 尝试 backup_weather_api
- backup_weather_api 也失败 → 诚实告知用户,不要编造天气数据
## 历史教训
- 2026-03: 用户投诉 Agent 编造天气数据。原因:backup API 也失败时,
LLM 自行发挥了。修复:在 SKILL.md 中明确要求"不要编造"。
- 2026-06: 穿衣建议不考虑湿度。南方 25°C 高湿和北方 25°C 低湿体感
完全不同。修复:增加湿度判断逻辑。2.3 Skills 解决了什么问题
┌─────────────────────────────────────────────────────────────┐
│ Skills 解决的三个核心问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题1:Tool 太细粒度,LLM 需要自己去组合它们 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 没有 Skill: │ │
│ │ LLM 看到 tools: [get_weather, backup_weather, │ │
│ │ send_email, format_message] │ │
│ │ 它需要在这 4 个 tool 中推理出正确的组合方式。 │ │
│ │ 每次推理都可能不同——不稳定。 │ │
│ │ │ │
│ │ 有 Skill: │ │
│ │ LLM 读到 SKILL.md,知道: │ │
│ │ "先调 get_weather → 失败则 backup → 格式化输出" │ │
│ │ 组合逻辑已经编码好了——稳定可预期。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 问题2:消除"意图债务"(Intent Debt) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 意图债务 = Agent 在你没明说时猜测的东西。 │ │
│ │ │ │
│ │ 你问"北京天气",Agent 猜你要穿衣建议 → 猜对了还好。 │ │
│ │ Agent 猜你要旅游建议 → 猜错了,你需要纠正。 │ │
│ │ │ │
│ │ Skill 把"隐含意图"变成"显式约定": │ │
│ │ "当用户问天气时,默认给穿衣建议,除非用户另有说明。" │ │
│ │ 不再需要猜——意图已经被编码进 Skill。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 问题3:让 Agent 从历史教训中学习 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 每次 Agent 犯错误,你把教训写进 SKILL.md。 │ │
│ │ 下一次运行时,Agent 读到这些教训,不再犯同样的错误。 │ │
│ │ │ │
│ │ 这比 fine-tuning 快得多,比修改 system prompt │ │
│ │ 更模块化——教训和技能绑定,互不污染。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2.4 Skill 的运行时加载流程
┌─────────────────────────────────────────────────────────────┐
│ Skill 运行时加载流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户输入:"北京今天天气怎么样?" │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Context Pipeline │── 检测到"天气"关键词 │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Skill Registry │── 查找匹配的 Skill │
│ │ │ → weather-advice/SKILL.md │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Assemble Context │── 把 SKILL.md 内容注入 context window │
│ │ │ + 注册 skill 专属的 tools │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ LLM 推理 │── 现在 LLM 知道: │
│ │ │ "遇到天气问题要按 weather-advice │
│ │ │ skill 的约定行事" │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 执行 + 验证 │── 按 SKILL.md 定义的流程执行 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2.5 再谈"Tool vs Skill"——给工程师的直觉
# Tool:你给 Agent 一个工具清单
TOOLS = [
"get_weather",
"backup_weather_api",
"send_email",
"search_web",
]
# Skill:你给 Agent 一份操作手册
SKILL = """
When user asks about weather:
1. Call get_weather(city)
2. If error → call backup_weather_api(city)
3. If both fail → tell user honestly, DO NOT fabricate
4. Format response: temp + advice + umbrella reminder
5. Use Chinese for all responses
6. Do NOT call send_email unless user explicitly asks
"""┌─────────────────────────────────────────────────────────────┐
│ 给开发者的类比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tool = 标准库函数(open(), requests.get(), json.loads()) │
│ Skill = 设计模式(Factory, Observer, Strategy) │
│ │
│ 标准库给你原子操作。 │
│ 设计模式告诉你"遇到这类问题时,这样组织代码最有效"。 │
│ │
│ Agent 同理: │
│ Tool 给你原子能力。 │
│ Skill 告诉你"遇到这类任务时,这样组合工具最稳定"。 │
│ │
└─────────────────────────────────────────────────────────────┘第3部分:Loop Engineering——自动化 Agent 的触发和迭代
3.1 命名来源——当"prompt Claude"变成"写好 loop 让代码去 prompt Claude"
Boris Cherny(Claude Code 的创建者,Anthropic)在 2026 年说过一句后来被反复引用的话:
"I don't prompt Claude anymore. I write loops that prompt Claude."
这句话的精髓是:高级 Agent 用户不再手动输入 prompt 等结果了。他们写代码——循环、条件判断、重试逻辑——让代码自动驱动 Agent。
Addy Osmani(Google Chrome 工程总监,2026.6)将这个实践命名为 Loop Engineering:
┌─────────────────────────────────────────────────────────────┐
│ Loop Engineering —— 从"人驱动"到"代码驱动" │
├─────────────────────────────────────────────────────────────┤
│ │
│ 过去(Prompt Engineering 时代): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 人类写 prompt → LLM 处理 → 人类看结果 → │ │
│ │ 人类觉得不对 → 人类改 prompt → LLM 再处理 → ... │ │
│ │ │ │
│ │ 瓶颈:人类在循环里。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 现在(Loop Engineering 时代): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 代码写 loop: │ │
│ │ for each PR: │ │
│ │ Agent reviews code │ │
│ │ if lint fails → Agent fixes │ │
│ │ if tests fail → Agent fixes │ │
│ │ if review passed → auto-merge │ │
│ │ │ │
│ │ 人类在循环外面。只在 Agent 卡住时才介入。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘3.2 Loop Engineering 的定义
Loop Engineering = 设计反复触发 Agent、验证结果、自我迭代的系统,不需要人类逐轮驱动。
┌─────────────────────────────────────────────────────────────┐
│ Loop Engineering 的形式化定义 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Loop = 一个持续运行的进程,它: │
│ │
│ 1. 检测触发条件(定时/事件/状态变化) │
│ 2. 组装上下文(Context Pipeline) │
│ 3. 启动 Agent(Model + Harness) │
│ 4. 评估结果(Sensors) │
│ 5. 决定: │
│ ├── 通过?→ 提交结果,等待下一次触发 │
│ ├── 失败但可修复?→ 反馈给 Agent,回到步骤 3 │
│ └── 失败且不可修复?→ 升级给人类 │
│ │
│ 关键:步骤 1-5 全部由代码驱动。人类只在"不可修复"分支介入。 │
│ │
└─────────────────────────────────────────────────────────────┘3.3 Loop 的六个构建块
┌─────────────────────────────────────────────────────────────┐
│ Loop Engineering 六构建块架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Loop Orchestrator │ │
│ │ (编排器:决定何时启动、何时停止) │ │
│ └────────────┬────────────────────────────┬────────────┘ │
│ │ │ │
│ ┌────────────┴───────────┐ ┌──────────────┴─────────────┐ │
│ │ 1. Automations │ │ 4. Plugins/Connectors │ │
│ │ cron, CI hooks, │ │ MCP → issue tracker, │ │
│ │ event-driven triggers │ │ database, Slack, Linear │ │
│ └────────────────────────┘ └────────────────────────────┘ │
│ │ │ │
│ ┌────────────┴───────────┐ ┌──────────────┴─────────────┐ │
│ │ 2. Worktrees │ │ 5. Sub-agents │ │
│ │ Git worktree isolation│ │ Maker-Checker 分离 │ │
│ │ 并行 Agent 互不干扰 │ │ 不同 prompt 做不同事 │ │
│ └────────────────────────┘ └────────────────────────────┘ │
│ │ │ │
│ ┌────────────┴───────────┐ ┌──────────────┴─────────────┐ │
│ │ 3. Skills │ │ 6. State │ │
│ │ 项目知识编码 │ │ progress.md, Linear board │ │
│ │ 每次运行读取的能力文档 │ │ 跨运行持久化 │ │
│ └────────────────────────┘ └────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘构建块 1:Automations(自动化触发)
# Loop 的三种触发模式
# 模式 A:定时触发(Cron)
@schedule(cron="0 9 * * 1-5") # 每个工作日早上 9 点
async def daily_standup_agent():
"""自动生成昨日工作总结"""
agent = Agent(skill="daily-standup")
result = await agent.run("总结昨天提交的所有 PR 和 issue 更新")
await slack.post("#team-daily", result)
# 模式 B:事件触发(Webhook / CI hook)
@webhook("github.pull_request.opened")
async def on_new_pr(event: PullRequestEvent):
"""新 PR 自动触发代码审查 Agent"""
agent = Agent(skill="code-review")
result = await agent.run(f"Review PR #{event.pr_number}: {event.title}")
await github.post_review(event.pr_number, result)
# 模式 C:状态触发(State change)
@state_watch("linear.project.progress < 80%")
async def check_blockers():
"""项目进度低于 80% 时自动检查阻塞项"""
agent = Agent(skill="blocker-detection")
result = await agent.run("分析所有标记为 blocked 的 issue,总结阻塞模式")
await linear.post_comment(project_id, result)构建块 2:Worktrees(隔离工作区)
┌─────────────────────────────────────────────────────────────┐
│ Worktree —— 并行 Agent 的隔离机制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 主分支 (main) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ .claude/worktrees/ │ │
│ │ ├── agent-1-fix-bug/ ← Agent 1: 修 bug │ │
│ │ │ └── src/buggy.py (独立修改) │ │
│ │ │ │ │
│ │ ├── agent-2-add-feature/ ← Agent 2: 加功能 │ │
│ │ │ └── src/feature.py (独立修改) │ │
│ │ │ │ │
│ │ └── agent-3-refactor/ ← Agent 3: 重构 │ │
│ │ └── src/legacy.py (独立修改) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 每个 Agent 在自己的 worktree 里工作: │
│ • 文件系统隔离 —— Agent A 改的文件不影响 Agent B │
│ • 并行安全 —— 不需要锁,不需要协调 │
│ • 干净清理 —— worktree 完成使命后可以删除 │
│ │
└─────────────────────────────────────────────────────────────┘构建块 3:Skills(项目知识编码)
(详见第2部分——Skills 是 Loop 每次运行时读取的能力文档,确保每次迭代遵循相同的约定。)
构建块 4:Plugins/Connectors(连接器)
┌─────────────────────────────────────────────────────────────┐
│ Plugins —— 让 Agent 接入真实世界 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 通过 MCP (Model Context Protocol) 连接外部系统: │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Linear │ │ GitHub │ │ Slack │ │
│ │ Issue │ │ API │ │ API │ │
│ │ Tracker │ │ │ │ │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ┌───────┴───────┐ │
│ │ MCP Server │ │
│ │ (统一接口) │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────┴───────┐ │
│ │ Agent (Loop) │ │
│ └───────────────┘ │
│ │
│ Agent 不需要知道每个 API 的细节。 │
│ 它通过 MCP 统一接口与外部系统交互。 │
│ │
└─────────────────────────────────────────────────────────────┘构建块 5:Sub-agents(子 Agent —— Maker-Checker 分离)
┌─────────────────────────────────────────────────────────────┐
│ Sub-agents —— 不同的 Agent 做不同的事 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Maker-Checker 模式: │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Maker Agent │────→│ Checker Agent │ │
│ │ (写代码) │ │ (审查代码) │ │
│ │ │ │ │ │
│ │ Prompt: │ │ Prompt: │ │
│ │ "你是资深工程师 │ │ "你是严格的 │ │
│ │ 写高质量的代码"│ │ 代码审查者 │ │
│ │ │ │ 找出所有问题" │ │
│ └─────────────────┘ └────────┬────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ 审查结果 │ │
│ │ ├── 通过 → merge│ │
│ │ └── 不通过 → │ │
│ │ 反馈给 Maker│ │
│ └─────────────────┘ │
│ │
│ 为什么不能用同一个 Agent? │
│ • LLM 写代码时处于"生成模式"——关注实现,忽略缺陷 │
│ • LLM 审查代码时需要"批判模式"——关注缺陷,严格挑剔 │
│ • 同一个 prompt 里让 LLM 同时做两件事 → 两者都做不好 │
│ │
└─────────────────────────────────────────────────────────────┘构建块 6:State(外部状态 —— 跨运行持久化)
# State —— 让 Agent 的"记忆"跨运行存活
class AgentState:
"""Agent 的外部状态存储,跨 Loop 迭代持久化"""
def __init__(self, project_path: str):
self.progress_file = f"{project_path}/.claude/progress.md"
self.checkpoint_db = f"{project_path}/.claude/checkpoints.json"
async def load_last_checkpoint(self) -> dict:
"""加载上一次运行的中断点"""
return json.loads(await read_file(self.checkpoint_db))
async def save_checkpoint(self, step: str, result: dict):
"""保存当前进度"""
current = await self.load_last_checkpoint()
current[step] = {"timestamp": now(), "result": result}
await write_file(self.checkpoint_db, json.dumps(current))
async def get_pending_tasks(self) -> list[str]:
"""从 progress.md 中提取未完成的任务"""
content = await read_file(self.progress_file)
return [line for line in content.split("\n")
if line.startswith("- [ ]")]┌─────────────────────────────────────────────────────────────┐
│ State —— 让 Loop 拥有"记忆" │
├─────────────────────────────────────────────────────────────┤
│ │
│ Loop 迭代 1: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Agent 处理 issue #42 │ │
│ │ 完成步骤 1/3: 分析问题 │ │
│ │ 完成步骤 2/3: 修复代码 │ │
│ │ → 保存 checkpoint: {"step": "2/3", "next": "写测试"}│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Loop 迭代 2: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Agent 读取 checkpoint → 继续从步骤 3/3 开始 │ │
│ │ 完成步骤 3/3: 写测试 │ │
│ │ → 更新 progress.md: "- [x] Fix issue #42" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 没有 State = 每次运行都是失忆的 Agent。 │
│ 有 State = Agent 可以跨多次运行持续推进一个复杂任务。 │
│ │
└─────────────────────────────────────────────────────────────┘3.4 一个完整的 Loop 代码示例
"""
一个完整的 Loop Engineering 示例:
自动化 GitHub Issue 分类 + 初筛 + 分配
"""
import asyncio
from datetime import datetime
from typing import Optional
class IssueTriageLoop:
"""GitHub Issue 自动分类 Loop"""
def __init__(self):
self.maker = Agent(
skill="issue-triage",
system_prompt="你是 issue 分类专家,快速判断 issue 类型和严重程度。"
)
self.checker = Agent(
skill="issue-review",
system_prompt="你是严格的 issue 审核者,检查分类是否正确。"
)
self.state = AgentState(".claude")
async def run_once(self) -> LoopResult:
"""Loop 的一次迭代"""
# Step 1: 检测触发条件
new_issues = await github.get_issues(
repo="org/repo",
since=self.state.get_last_run_time(),
labels=["needs-triage"]
)
if not new_issues:
return LoopResult.NO_WORK
for issue in new_issues:
# Step 2: Maker Agent 分类
maker_result = await self.maker.run(f"""
分析这个 issue 并给出分类:
Title: {issue.title}
Body: {issue.body}
输出格式:
- type: bug | feature | question | docs
- severity: critical | high | medium | low
- suggested_assignee: @username
- reason: 分类理由(一句话)
""")
# Step 3: Checker Agent 审核
check_result = await self.checker.run(f"""
审核以下 issue 分类是否合理:
Issue: {issue.title}
Maker 分类: {maker_result}
如果分类合理,回复 "APPROVED"。
如果分类有问题,回复 "REJECTED: <原因>"。
""")
# Step 4: 根据审查结果执行
if "APPROVED" in check_result:
await github.update_issue(issue.id, {
"labels": maker_result.labels,
"assignee": maker_result.assignee,
})
self.state.record_success(issue.id)
else:
# 反馈给 Maker,再试一次
retry_result = await self.maker.run(f"""
你的分类被拒绝了。拒绝理由:{check_result}
原始 issue: {issue.title}
你的原始分类: {maker_result}
请修正后重新输出分类。
""")
if "APPROVED" in await self.checker.run(
f"审核修正后的分类: {retry_result}"
):
await github.update_issue(issue.id, retry_result)
else:
# 两次都失败 → 升级给人类
await slack.post("#triage-escalation",
f"@human Issue #{issue.id} 分类失败,请手动处理。"
)
return LoopResult.SUCCESS
async def run_loop(self, interval_seconds: int = 300):
"""持续运行 Loop"""
while True:
try:
result = await self.run_once()
if result == LoopResult.NO_WORK:
await asyncio.sleep(interval_seconds)
else:
await asyncio.sleep(10) # 有任务时快速轮询
except Exception as e:
await self.observability.log_error(e)
await asyncio.sleep(interval_seconds)第4部分:四层嵌套模型
4.1 四层架构总览
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────── Loop Engineering ──────────────────┐ │
│ │ (when to call, when to stop, │ │
│ │ how to iterate, how to escalate) │ │
│ │ │ │
│ │ ┌─────────── Harness Engineering ──────────────┐ │ │
│ │ │ (tools, execution, retries, │ │ │
│ │ │ safety, hooks, observability, │ │ │
│ │ │ permissions, sandbox) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────── Context Engineering ─────────┐ │ │ │
│ │ │ │ (what fills the window: │ │ │ │
│ │ │ │ RAG, memory, specs, skills, │ │ │ │
│ │ │ │ system prompts, project files) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ ┌──── Prompt Engineering ────┐ │ │ │ │
│ │ │ │ │ (wording of the request: │ │ │ │ │
│ │ │ │ │ instructions, examples, │ │ │ │ │
│ │ │ │ │ tone, format, role-play) │ │ │ │ │
│ │ │ │ └────────────────────────────┘ │ │ │ │
│ │ │ └──────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ L1: Prompt — "我说了什么" │
│ L2: Context — "模型看到了什么" │
│ L3: Harness — "系统允许/阻止了什么" │
│ L4: Loop — "系统何时启动、何时停止、何时升级" │
│ │
└─────────────────────────────────────────────────────────────┘4.2 四层的职责边界
┌─────────────────────────────────────────────────────────────┐
│ 四层职责 —— 谁管什么 │
├─────────────────────────────────────────────────────────────┤
│ │
│ L1 Prompt Engineering: │
│ ├── 同一句话,怎么说效果更好? │
│ ├── Few-shot examples 放几个?放什么样的? │
│ ├── 角色设定("你是资深工程师" vs "你是一个助手") │
│ └── 不在本文讨论范围,但它是地基。 │
│ │
│ L2 Context Engineering: │
│ ├── 模型"应该"知道什么信息? │
│ ├── 哪些文件放进上下文窗口? │
│ ├── RAG 检索策略、记忆管理 │
│ └── 前置文章 [[14-context-engineering]] 覆盖。 │
│ │
│ L3 Harness Engineering: │
│ ├── 模型"实际"做了什么?是否合规? │
│ ├── Tools 的定义、注册、调用、结果验证 │
│ ├── Sensors(lint, test, type check)的编排 │
│ ├── Enforcement(permission, sandbox, CI gate) │
│ ├── Skills(可复用的 prompt + tools + workflow 单元) │
│ └── 本文第1-2部分覆盖。 │
│ │
│ L4 Loop Engineering: │
│ ├── 什么时候启动 Agent? │
│ ├── 怎么判断 Agent 该停了? │
│ ├── 失败了怎么重试?重试到什么时候升级给人类? │
│ ├── 怎么让多个 Agent 并行工作? │
│ └── 本文第3部分覆盖。 │
│ │
└─────────────────────────────────────────────────────────────┘4.3 核心原则:外层不取消内层
┌─────────────────────────────────────────────────────────────┐
│ 最关键的原则:外层不取消内层的错误 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ❌ 常见误解: │
│ "我上了 Loop,prompt 写烂一点没关系,Loop 会让 Agent 重试" │
│ "我有 Harness 的 lint check,Context 不用包含编码规范" │
│ │
│ ✅ 实际后果: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Loop 里 prompt 写烂了 → │ │
│ │ Agent 每次都产出烂代码 → │ │
│ │ Harness 每次拦截 → │ │
│ │ Loop 触发重试 → │ │
│ │ Agent 又产出烂代码 → │ │
│ │ ... │ │
│ │ │ │
│ │ 结果:没修好任何东西,只是用更快的速度反复失败。 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 外层放大内层的错误,而不是修复它们。 │
│ │
│ Loop 以 100x 速度运行一个烂 prompt → 100x 速度产出烂结果。 │
│ Harness 只能拦住明显错误,拦不住"合法但低质量"的输出。 │
│ Context 缺失导致的信息不全,Harness 和 Loop 都无法弥补。 │
│ │
└─────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────┐
│ 每层继承下层的弱点 —— 具体例子 │
├─────────────────────────────────────────────────────────────┤
│ │
│ L1 问题(Prompt 烂): │
│ "写一个排序函数" ← 没说要考虑边界情况 │
│ ↓ │
│ L2 没补救(Context 没包含编码规范): │
│ Agent 看不到项目要求"所有函数必须有 docstring" │
│ ↓ │
│ L3 能拦住部分(Harness lint): │
│ Lint 能拦住"缺少 docstring",拦不住"算法选了 O(n^2)" │
│ ↓ │
│ L4 放大问题(Loop 反复重试): │
│ Agent 加上了 docstring,但算法还是 O(n^2) │
│ Lint 通过 → 合入 → 生产性能问题 │
│ │
│ 每一层都在前一层的假设上工作。 │
│ 底层假设错了,上层做得再好也只是高效地执行错误假设。 │
│ │
└─────────────────────────────────────────────────────────────┘4.4 Kirby 效应:模型会"吃掉"为弥补其弱点而建的 Harness
2026 年出现了一个被社区称为 Kirby 效应 的现象,以任天堂角色卡比(Kirby)命名——它会吃掉敌人并获得其能力。类比到 Agent:
┌─────────────────────────────────────────────────────────────┐
│ Kirby 效应 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 现象: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. 你为模型弱点 A 建了 Harness 组件 H │ │
│ │ (比如:模型不会做类型检查 → 你在 CI 里加 mypy) │ │
│ │ │ │
│ │ 2. 模型升级后,弱点 A 消失了 │ │
│ │ (新模型原生就能输出类型正确的代码) │ │
│ │ │ │
│ │ 3. 但 Harness 组件 H 没有被移除 │ │
│ │ ("万一哪天又出问题呢?") │ │
│ │ │ │
│ │ 4. 模型暴露了新弱点 B、C、D │ │
│ │ 你为 B、C、D 又建了更多 Harness 组件 │ │
│ │ │ │
│ │ 5. 最终:模型已经不同了,但 Harness 里堆满了 │ │
│ │ 为旧弱点建的冗余组件 + 为新弱点建的必须组件 │ │
│ │ 变为一个谁也不敢删的"技术债坟场" │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 教训:Harness 需要定期审计和瘦身。 │
│ 每个组件都应该有"失效日期"假设—— │
│ "这个组件是为模型版本 X 的弱点建的,版本 Y 发布后应该重新 │
│ 评估是否需要保留。" │
│ │
└─────────────────────────────────────────────────────────────┘第5部分:四层怎么诊断问题
5.1 诊断矩阵
┌─────────────────────────────────────────────────────────────┐
│ 四层诊断矩阵 —— 症状 → 该修的层 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 症状 │ 该修的层 │
│ ───────────────────────────────────────┼──────────────────│
│ 模型读错了一条格式清晰的指令 │ Prompt (L1) │
│ Agent 有相关信息但没用到 │ Context (L2) │
│ 工具调用跑了但失败没被发现 │ Harness (L3) │
│ Agent 不知道何时重试、何时停 │ Loop (L4) │
│ ───────────────────────────────────────┼──────────────────│
│ Agent 反复犯同一个错,每次都重试 │ Harness (L3) │
│ (Sensors 没检测到这个错误模式) │ │
│ ───────────────────────────────────────┼──────────────────│
│ Agent 正确完成了任务但用了太长时间 │ Loop (L4) │
│ (该加超时/预算上限/early-stop) │ │
│ ───────────────────────────────────────┼──────────────────│
│ Agent 输出质量波动很大 │ Context (L2) │
│ (不同运行看到不同的上下文) │ │
│ ───────────────────────────────────────┼──────────────────│
│ Agent 在并行运行时互相覆盖文件 │ Loop (L4) │
│ (没做 worktree 隔离) │ │
│ ───────────────────────────────────────┼──────────────────│
│ 安全事件:Agent 删了不该删的东西 │ Harness (L3) │
│ (Permission 系统没拦住) │ │
│ │
└─────────────────────────────────────────────────────────────┘5.2 2025-2026 实战共识:最容易被误诊的故障
┌─────────────────────────────────────────────────────────────┐
│ 实战共识:大多数生产故障是 Harness 层问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 来自 2025-2026 的生产 Agent 运维经验: │
│ │
│ 故障分类统计(非正式,社区共识): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Harness 层问题(工具参数校验失败、权限缺口、 │ │
│ │ 错误吞没、重试逻辑失效) ~50% │ │
│ │ │ │
│ │ Context 层问题(缺失关键上下文、RAG 召回失败、 │ │
│ │ 过时信息、prompt 污染) ~30% │ │
│ │ │ │
│ │ Prompt 层问题(指令模糊、格式要求不明确) ~15% │ │
│ │ │ │
│ │ Loop 层问题(过早停止、过度重试、 │ │
│ │ 死循环、资源泄漏) ~5% │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 但实际中: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 被误诊为 Prompt 问题的 Harness 故障:~80% │ │
│ │ │ │
│ │ 典型场景: │ │
│ │ → Agent 生成了不安全的代码 │ │
│ │ → 团队反应:"prompt 里再强调一遍安全规则" │ │
│ │ → 真实原因:CI 里的安全扫描器没配置对, │ │
│ │ 根本没在跑。 │ │
│ │ │ │
│ │ 修 prompt 是"感觉上最快"的修复, │ │
│ │ 但修 harness 才是"实际上有效"的修复。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘5.3 最危险的常见反模式:跳过 L3 直接上 L4
┌─────────────────────────────────────────────────────────────┐
│ 危险反模式:没搞定单次可靠性就搞自主循环 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 团队做 Agent 的典型流程(反模式版): │ │
│ │ │ │
│ │ Step 1: 写个 prompt,Agent 基本能跑 → 激动 │ │
│ │ Step 2: Agent 有时出错 → "没关系,加个重试 Loop" │ │
│ │ Step 3: Agent 反复出错 → "没关系,加个 fallback" │ │
│ │ Step 4: Agent 在 Loop 里反复失败 │ │
│ │ → "Agent 技术不成熟!" │ │
│ │ │ │
│ │ 真相:不是 Agent 不成熟,是跳过了 L3。 │ │
│ │ 单次调用都不可靠,用 Loop 重复调用只会放大不可靠。 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 正确顺序: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Step 1: 把单次调用的可靠性做到 >95%(L1+L2+L3) │ │
│ │ Step 2: 确定哪些失败模式是可重试的(L3 Sensor) │ │
│ │ Step 3: 为可重试的失败模式加上 Loop(L4) │ │
│ │ Step 4: 为不可重试的失败模式加上升级路径(L4) │ │
│ │ │ │
│ │ 原则:让你重试的不是"Agent 又失败了"这个事实, │ │
│ │ 而是"这个具体失败模式已知是可重试的"这个判断。 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘5.4 诊断流程
def diagnose_agent_failure(failure: AgentFailure) -> Layer:
"""
从症状反推该修哪一层。
决策树:
1. 模型理解了指令吗?→ 没有 → L1 Prompt
2. 模型有足够的信息吗?→ 没有 → L2 Context
3. 错误输出被检测到了吗?→ 没有 → L3 Harness (Sensors)
4. 检测到了但没拦住吗?→ 是 → L3 Harness (Enforcement)
5. 拦住了但重试策略有问题吗?→ 是 → L4 Loop
"""
# Step 1: 模型理解问题了吗?
if not failure.model_understood_instruction:
return Layer.PROMPT # L1
# 例:你把格式要求写在了代码块外面,模型没看到
# Step 2: 模型有足够的信息吗?
if not failure.model_had_sufficient_context:
return Layer.CONTEXT # L2
# 例:RAG 检索到的文档版本过时了
# Step 3: 错误输出被检测到了吗?
if not failure.error_was_detected_by_sensors:
return Layer.HARNESS_SENSORS # L3
# 例:你把 lint 接到了 CI 但没接到 Agent 的 Sensor pipeline
# Step 4: 检测到了但被拦截了吗?
if not failure.error_was_blocked_by_enforcement:
return Layer.HARNESS_ENFORCEMENT # L3
# 例:lint 报 warning 而不是 error,Agent 忽略了
# Step 5: 拦住了但重试/升级策略有问题?
if failure.retry_or_escalation_failed:
return Layer.LOOP # L4
# 例:Agent 用完全相同的参数重试了 10 次
return Layer.UNKNOWN第6部分:2023-2026 时间线
6.1 工程焦点的四年迁移
┌─────────────────────────────────────────────────────────────┐
│ Agent Engineering 工程焦点迁移 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 2023 ───────────────────────────────────────────→ 2026 │
│ │
│ ┌────────────┐ │
│ │ 2023-2024 │ Prompt Engineering 主导 │
│ │ │ "怎么说才能让 LLM 输出我想要的结果?" │
│ │ │ Few-shot, Chain-of-Thought, Role-play │
│ └─────┬──────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │ 2025 中 │ Context Engineering 出现 │
│ │ │ "LLM 应该看到什么信息才能做对判断?" │
│ │ │ RAG, Memory, System Prompts, Specs │
│ │ │ 关键人物:Karpathy + Anthropic │
│ └─────┬──────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │ 2025 末 │ Harness Engineering 浮现 │
│ │ │ "我们怎么验证 LLM 确实做对了?" │
│ │ │ Sensors, Enforcement, Permissions, Skills │
│ │ │ 关键事件:Anthropic harness blog, │
│ │ │ OpenAI Codex harness 设计文档 │
│ └─────┬──────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │ 2026 │ Loop Engineering 结晶 │
│ │ │ "我们怎么让 Agent 不需要人类逐轮驱动?" │
│ │ │ Automations, Worktrees, Sub-agents, State │
│ │ │ 关键人物:Osmani, Cherny, Kirby effect │
│ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘6.2 关键事件时间线
┌─────────────────────────────────────────────────────────────┐
│ 2023-2026 Agent Engineering 关键事件 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 2023 Q1-Q4 │
│ ├── ChatGPT 发布 → Prompt Engineering 成为热门技能 │
│ ├── LangChain 发布 → Agent 框架概念进入主流 │
│ └── 社区聚焦:"怎么写更好的 prompt?" │
│ │
│ 2024 Q1-Q4 │
│ ├── RAG 架构成熟 → Context Engineering 的前身 │
│ ├── Anthropic "Building effective agents" → 架构模式 │
│ └── OpenAI Function Calling 成熟 → Tool 使用标准化 │
│ │
│ 2025 Q1-Q2 │
│ ├── Karpathy 提出 Context Engineering 概念 │
│ ├── Anthropic 发布 Claude Code → Harness 的工程化标杆 │
│ └── 社区开始区分 "prompt 问题" 和 "context 问题" │
│ │
│ 2025 Q3-Q4 │
│ ├── Anthropic: "Effective harnesses for long-running agents"│
│ ├── Mitchell Hashimoto 提出 trust-based vs verification 框架│
│ ├── Vivek Trivedy: Agent = Model + Harness │
│ └── Skills 概念开始在 Claude Code 生态中成形 │
│ │
│ 2026 Q1-Q2 │
│ ├── Boris Cherny: "I don't prompt Claude, I write loops" │
│ ├── Addy Osmani: Loop Engineering 命名和系统化 │
│ ├── MCP 协议广泛采用 → Plugin/Connector 标准化 │
│ ├── Kirby 效应被社区识别和命名 │
│ └── Worktree 隔离成为并行 Agent 的标准实践 │
│ │
│ 2026 Q3+(当前) │
│ ├── 四层嵌套模型成为主流框架 │
│ ├── "先 L1-L3 再 L4" 成为公认的最佳实践 │
│ └── Harness 审计和 Kirby 清理成为运维常态 │
│ │
└─────────────────────────────────────────────────────────────┘6.3 工程重心的转移
┌─────────────────────────────────────────────────────────────┐
│ 工程重心转移:从"怎么说"到"怎么管" │
├─────────────────────────────────────────────────────────────┤
│ │
│ 2023: "这句话怎么说才能让 LLM 输出正确结果?" │
│ → Prompt Engineering │
│ → 技能:写好 prompt │
│ │
│ 2025: "LLM 需要看到哪些信息?怎么组织这些信息?" │
│ → Context Engineering │
│ → 技能:信息架构 + RAG + Memory 管理 │
│ │
│ 2026: "我怎么确保 LLM 的行为在边界内?我怎么让 Agent │
│ 在没有人盯着的情况下安全地自主运行?" │
│ → Harness + Loop Engineering │
│ → 技能:系统设计 + 可靠性工程 + 安全工程 │
│ │
│ 趋势:离"语言学"越来越远,离"软件工程"越来越近。 │
│ │
│ 2023 的 Agent 工程师主要在研究怎么和模型说话。 │
│ 2026 的 Agent 工程师主要在研究怎么设计分布式系统。 │
│ │
└─────────────────────────────────────────────────────────────┘核心总结
总结1:Harness Engineering 的本质
Harness = 模型外面的所有非模型组件
五组件:
Guides(引导) → 生成前提供约束和知识
Sensors(感知) → 生成后检测问题
Enforcement(执行)→ 确定性的硬拦截
Context Pipeline → 编排 L2
Observability → 完整的 trace
核心哲学转变:
从"信任模型做对的事"(Context)
到"验证模型确实做对了"(Harness)总结2:Skills —— Tool 和 Skill 的本质区别
Tool = 原子操作(你能调这个函数)
LLM 需要自己去想怎么组合多个 tool
Skill = 操作手册(遇到这类任务时,这样组合工具最稳定)
= prompt + tools + workflow 的打包
= 消除意图债务 + 编码历史教训 + 保证行为一致性
Tool 是给 Agent 的"工具清单"
Skill 是给 Agent 的"操作手册"总结3:Loop Engineering 的六个构建块
1. Automations —— 定时/事件/状态触发
2. Worktrees —— Git worktree 隔离并行 Agent
3. Skills —— 项目知识编码,每次运行读取
4. Plugins/Connectors—— MCP 接入外部系统
5. Sub-agents —— Maker-Checker 分离
6. State —— 跨运行持久化(progress.md, checkpoints)总结4:四层嵌套模型
L1: Prompt Engineering —— "我说了什么"
L2: Context Engineering —— "模型看到了什么"
L3: Harness Engineering —— "系统允许/阻止了什么"
L4: Loop Engineering —— "系统何时启动、何时停止、何时升级"
核心原则:
1. 外层不取消内层。Loop 放大 Harness 和 Context 的错误。
2. 大多数生产故障是 Harness 层问题,但被误诊为 Prompt/Context 问题。
3. 没搞定 L1-L3 就上 L4 是最危险的常见反模式。
4. Kirby 效应:Harness 需要定期审计,清理为旧模型弱点建的冗余组件。总结5:2023-2026 趋势
2023-2024: Prompt Engineering 主导("怎么说")
2025 中: Context Engineering 出现("看到什么")
2025 末: Harness Engineering 浮现("验证什么")
2026: Loop Engineering 结晶("自动运行")
趋势:从语言学技能到软件工程技能。
2026 的 Agent 工程师 ≈ 分布式系统工程师。章节测试
测试1:概念辨析
Harness Engineering 和 Context Engineering 的根本区别是什么?
测试2:架构理解
请列出 Harness 的五组件,并各举一个具体的例子。
测试3:Skill vs Tool
下面哪一个描述是 Skill 而非 Tool? A. get_weather(city: str) -> dict B. send_email(to: str, subject: str, body: str) -> dict C. "当用户问天气时,先查 get_weather,如果失败用 backup API,用中文回复并附带穿衣建议" D. search_web(query: str) -> list[SearchResult]
测试4:Loop 构建块
一个 Agent 在多次 Loop 运行之间需要记住上次处理到哪个 issue 了。这应该使用 Loop 六个构建块中的哪一个?
测试5:诊断框架
Agent 反复调用同一个工具、使用完全相同的参数、每次结果都失败、但一直不停。这个故障应该在哪一层诊断和修复?
测试6:反模式识别
以下哪个是文中描述的最危险的常见反模式? A. 在 prompt 里写太多规则导致 token 浪费 B. 没有做 RAG 就直接让 Agent 回答专业问题 C. 单次调用可靠性还没搞定就上自主重试 Loop D. 使用多个 sub-agent 而不是一个大 agent
测试7:Kirby 效应
什么是 Kirby 效应?为什么它对你的 Harness 维护策略有影响?
参考答案
测试1答案
答案:Context Engineering 管模型的输入——确保模型看到正确的信息。Harness Engineering 管模型的输出——验证模型实际做了什么,并在行为越界时拦截。Context 基于"信任",Harness 基于"验证"。
测试2答案
答案:
- Guides(引导):CLAUDE.md / .cursor/rules —— 在生成前为模型提供项目级行为约定
- Sensors(感知):ESLint / mypy / pytest —— 生成后检测代码是否合规
- Enforcement(执行):GitHub Actions CI gate —— 不通过就不让合入
- Context Pipeline(上下文管道):RAG + Memory —— 每一步决定往上下文窗口注入什么
- Observability(可观测性):Trace 系统 —— 记录每次调用的输入、输出、工具调用、延迟
测试3答案
答案:C
解析:
A、B、D 都是单个原子操作——Tool。
C 包含了一组 tool 的组合逻辑 + 行为约定 + 错误处理策略——Skill。
Skill 的核心特征:
• 组合了多个 tool
• 定义了调用顺序和条件
• 包含了错误处理策略
• 规定了输出格式
• 可能包含历史教训测试4答案
答案:State(外部状态,构建块 6)
解析:State 负责跨运行持久化 Agent 的进度。典型实现包括 progress.md(自然语言进度追踪)和 checkpoints.json(结构化状态快照)。没有 State,Agent 每次启动都是"失忆"状态,无法持续推进复杂任务。
测试5答案
答案:L3(Harness 层,具体是 Sensors)+ L4(Loop 层)
解析:
这个问题需要两层同时修复:
L3 Harness(Sensors):
加一个"重复调用检测 Sensor"——
如果检测到同一个 tool 用同样的参数被调用了 >3 次,
标记为"疑似死循环",不继续喂给 Agent。
L4 Loop:
在重试逻辑里加"重试上限"和"参数变化要求"——
重试时不能使用完全相同的参数,或者最多重试 N 次
后升级给人类。
只修 L4(加个重试上限)也能止损,但不治本——
为什么 Agent 没意识到同样的参数会得到同样的错误结果?
这可能是 Context 层没有把"上一次失败"的信息
正确地注入下一轮上下文。测试6答案
答案:C(单次调用可靠性还没搞定就上自主重试 Loop)
解析:
A 是效率问题,不是危险反模式。
B 是 Context 层的缺失,会导致结果不准但通常不会造成级联故障。
C 是危险反模式:如果单次调用的可靠性没搞定,Loop 只是用更快的速度
反复失败,可能造成:
• Token 浪费(每次重试都在烧钱)
• 级联故障(如果 Loop 里的操作有副作用,每次重试都在做破坏)
• 虚假的安全感("有重试机制,应该没事")
D 不是反模式——用多个 sub-agent 做 Maker-Checker 分离恰恰是推荐做法。测试7答案
答案:
Kirby 效应 = 模型升级后,"吃掉"了原本为弥补其弱点而建的 Harness
组件的能力,但那些组件仍然留在 Harness 里,导致 Harness 膨胀为
"技术债坟场"。
对维护策略的影响:
1. Harness 的每个组件都需要记录"为什么存在"和"为哪个模型版本的
哪个弱点服务"
2. 定期审计(比如每个模型大版本发布后),评估哪些 Harness 组件
可以退役
3. Harness 组件应该有"失效日期"假设,到期后主动重新评估
4. 避免"万一哪天又出问题呢?"的心态——
如果模型已经修复了那个弱点,组件就该删除相关笔记
- [[14-context-engineering]] - Context Engineering 详解(本文的前置文章)
- [[14a-agent-caching]] - Agent 缓存工程:稳定上下文、前缀复用与缓存安全
- [[05-agent-workflow]] - Agent 工作流:从单次调用到多步执行
- [[11-agent-architecture-patterns]] - Agent 架构模式:Prompt Chaining 到 Multi-Agent
- [[20-agent-evaluation]] - Agent 评估:Sensors 的设计基础
- [[21-tools-design]] - 工具设计:Tool 的定义、注册与最佳实践
- [[16-claude-code-leak-analysis]] - Claude Code 泄露分析:Harness 优先的工程实践
- [[09-安全沙箱]] - 安全沙箱:Enforcement 层的具体实现
- [[10-权限与门卫]] - 权限系统:Enforcement 层的另一面
下一步学习
- [ ] 深入阅读 [[14-context-engineering]] 理解 L2 层的完整设计
- [ ] 阅读 [[14a-agent-caching]],检查你的 System Prompt 与 Tool Schema 是否破坏前缀缓存
- [ ] 实践:为你当前项目的 Agent 写至少一个 SKILL.md
- [ ] 实践:在你的 Agent 系统中加入至少一个 Sensor(lint / type check / test)
- [ ] 实践:设计一个简单的 Loop(例如:PR 自动审查 → lint 失败 → 自动修复 → 重新审查)
- [ ] 阅读 Anthropic: "Effective harnesses for long-running agents"(2025)
- [ ] 阅读 Addy Osmani: Loop Engineering(2026.6)
学习状态:🟡 开始学习