Harness Engineering:架构与核心组件
📍 位置:Harness Engineering / 技术架构
📌 核心发现:一个完整的 harness 由五层组成:上下文管理层、约束执行层、反馈回路层、编排调度层、自愈维护层
📥 输入:OpenAI(Ryan Lopopolo)、Anthropic、Stripe、Cursor、Philipp Schmid、arXiv OPENDEV
📤 流向:→ findings.md [架构模式部分]、→ 4-开发者采纳指南.md
一、五层架构模型总览
在深入每一层的细节之前,先建立整体的架构视图。这个五层模型是从 OpenAI、Stripe、Cursor、Anthropic 四个最具代表性的工业案例中提炼的综合模型——没有任何一家公司完整地发表了这五层,但每家公司的实践都覆盖了其中的核心部分。
┌─────────────────────────────────────────────────────────────────────┐
│ Layer 5: 自愈维护层 │
│ doc-gardening agent / 技术债追踪 / 质量评分系统 │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 4: 编排调度层 │
│ 递归规划者 + 专职工作者 / 隔离沙箱 / 任务路由 │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 3: 反馈回路层 │
│ 左移验证 / Chrome DevTools MCP / 可观测性堆栈 │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 2: 约束执行层 │
│ 分层领域架构 / 自定义 Linter / 条件规则系统 │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 1: 上下文管理层 │
│ AGENTS.md 目录 / 渐进式披露 / 代码仓库作为记录系统 │
├─────────────────────────────────────────────────────────────────────┤
│ [AI 模型] │
│ GPT-5 / Claude / Codex │
└─────────────────────────────────────────────────────────────────────┘
设计哲学:这五层不是孤立的模块,而是形成一个强化回路——上下文层为 Agent 提供知识,约束层将知识转化为可执行规则,反馈层检测规则执行的效果,编排层协调多个 Agent 并行工作,自愈层持续修复整个系统的退化。每层的输出都成为其他层的输入。
为什么需要五层而不是三层或两层:
最简单的 harness 只有两个组件:一个 prompt 文件(上下文)+ 一个测试脚本(反馈)。这在小规模、短生命周期的项目中足够了。但随着代码库增大、Agent 数量增多、时间拉长,会依次出现新的失控点:
- 代码库增大 → 上下文管理变复杂,需要 Layer 1 的精细设计
- 多 Agent 并行 → 输出质量参差不齐,需要 Layer 2 的约束
- 任务周期变长 → 反馈延迟导致迭代效率低,需要 Layer 3 的左移设计
- Agent 数量增多 → 协调成本指数级上升,需要 Layer 4 的编排
- 时间拉长 → 代码库自然退化,需要 Layer 5 的自愈机制
二、Layer 1:上下文管理层(Context Layer)
2.1 核心挑战:上下文是稀缺资源
上下文窗口是 AI Agent 系统中最稀缺、最容易被低估的资源。OpenAI 团队在 100 万行代码的实践中发现了一个反直觉的规律:
“情境是一种稀缺资源。一个巨大的指令文件会挤掉任务、代码和相关文档——因此智能体要么会错过关键约束条件,要么开始针对错误的约束条件进行优化。“(来源:OpenAI,Ryan Lopopolo)
这揭示了一个”多即是少”的悖论:向 Agent 提供越多信息,Agent 的注意力越分散,真正重要的约束反而被淹没。从计算机科学的角度看,这类似于缓存污染(cache pollution)——把低优先级数据推入缓存,导致高优先级数据被驱逐。
具体的失败模式(来自 OpenAI 实践):
当团队最初尝试”一个大型 AGENTS.md”方法时,观察到了三种系统性失败:
- 语义性遗忘:Agent 在模式匹配,而不是有意识地导航。它识别出类似的模式就复现,不会主动查阅规则。
- 快速腐烂:单一大文件无法追踪新鲜度,陈旧规则和有效规则混杂,Agent 无法区分。
- 可验证性差:单一 blob 不适合进行覆盖率检查、所有权追踪和交叉链接验证。
2.2 AGENTS.md:目录而非百科全书
错误模式(O(n²) 复杂度的文档):
# AGENTS.md(反例)
## 代码风格
- 所有变量必须使用 camelCase
- 函数长度不超过 50 行
- 不得使用魔法数字
- 日志必须使用结构化格式
- ... (500 行各类规则)
## 架构约束
- Services 不得直接访问数据库
- 所有外部 API 调用必须经过 Gateway
- ... (另外 300 行)
## 测试规范
- 单元测试覆盖率不低于 80%
- ... (另外 200 行)
问题:这个文件什么都说了,等于什么都没说。Agent 无法判断哪条规则在当前任务中最重要。
正确模式(O(1) 入口 + O(n) 索引):
# AGENTS.md(来源:OpenAI 实践,约 100 行)
你正在处理一个 [产品名称] 代码库。
## 快速导航
- 架构概述 → ARCHITECTURE.md
- 当前活跃计划 → docs/exec-plans/active/
- 产品规格 → docs/product-specs/index.md
- 设计规范 → docs/references/design-system-reference-llms.txt
- 已知技术债务 → docs/exec-plans/tech-debt-tracker.md
## 核心原则(不可违反)
1. 在边界处解析数据(parse don't validate)
2. 分层架构:Types → Config → Repo → Service → Runtime → UI
3. 所有日志必须结构化
## 工作流程
1. 理解任务前,先阅读相关的产品规格文档
2. 修改架构时,先查阅 ARCHITECTURE.md
3. 完成后,更新 exec-plans 中的进度日志
## 我看不到的内容
- Slack 讨论 → 已提炼为 docs/design-docs/
- 历史决策 → docs/design-docs/core-beliefs.md
为什么这样设计有效:
这个模式实现了”渐进式披露”(Progressive Disclosure)原则,这一原则原本是 UX 设计中的概念——只在用户需要时才展示信息,而不是一次性倾倒所有信息。
对 Agent 来说,“渐进式披露”意味着:
- AGENTS.md 提供入口点,不提供所有细节
- Agent 根据任务类型,主动导航到相关文档
- 每次只有与当前任务相关的上下文进入工作窗口
这种设计的效率优势可以量化:假设一个任务需要 20 条相关规则(来自 1000 条总规则),“百科全书”方案会把全部 1000 条都塞进上下文;“目录”方案只会加载相关的 20 条。前者的信噪比是 2%,后者是 100%。
2.3 代码仓库作为记录系统
关键洞察(来源:OpenAI):
“从智能体的角度来看,它在运行时无法在情境中访问的任何内容都是不存在的。存储在 Google Docs、聊天记录或人们头脑中的知识都无法被系统访问。代码仓库本地的、已版本化的工件就是它所能看到的全部。”
这句话建立了一个重要的设计约束:所有 Agent 需要知道的信息,必须以 Markdown 文件的形式存在于代码仓库中。
这是 Harness Engineering 中一个看似简单却极其深刻的原则,其影响范围远超技术层面:
组织影响:这意味着所有重要的架构决策、设计理念、团队规范,都必须被文档化并提交到代码仓库。不能口耳相传,不能存在 Confluence,不能停留在 Slack 消息里。这实际上倒逼团队建立了更好的知识管理实践。
版本控制的好处:文档和代码一起版本控制,意味着可以追溯”3 个月前 Agent 在这里做决策时,它能看到什么信息”——这对调试 Agent 行为极其有价值。
OpenAI 的知识仓库结构(实际案例):
docs/
├── design-docs/
│ ├── index.md # 所有设计文档的索引,含验证状态
│ ├── core-beliefs.md # 智能体优先的操作原则
│ └── ...
├── exec-plans/
│ ├── active/ # 当前活跃的执行计划(含进度日志)
│ ├── completed/ # 已完成的执行计划(历史记录)
│ └── tech-debt-tracker.md # 已知技术债务
├── generated/
│ └── db-schema.md # 自动生成的数据库 schema 文档
├── product-specs/
│ ├── index.md
│ ├── new-user-onboarding.md
│ └── ...
└── references/
├── design-system-reference-llms.txt # 设计系统参考(专为 LLM 优化)
├── nixpacks-llms.txt
└── uv-llms.txt
AGENTS.md # 顶层目录(约 100 行)
ARCHITECTURE.md # 架构顶层地图
DESIGN.md # 设计系统顶层
FRONTEND.md # 前端规范
PLANS.md # 计划索引
PRODUCT_SENSE.md # 产品感知文档
QUALITY_SCORE.md # 质量评分追踪
RELIABILITY.md # 可靠性要求
SECURITY.md # 安全约束
几个值得注意的细节:
references/ 下的文件命名为 *-llms.txt,明确标注这些是为 LLM 优化的参考文档(不是给人读的普通文档)
exec-plans/ 同时包含活跃计划和已完成计划——已完成计划不删除,作为历史决策记录保留
QUALITY_SCORE.md 是一个质量追踪文件,记录每个产品领域和架构层的文档质量评分,随时间追踪差距
2.4 自适应上下文压缩
来源:arXiv OPENDEV(2024-2025 学术研究)
当上下文窗口接近限制时,朴素的处理方法是”截断”——扔掉最旧的内容。但这会导致 Agent 失去重要的早期上下文(例如,任务的最初定义、关键的架构决策)。
更好的方案是自适应上下文压缩(Adaptive Context Compression):
原始上下文(1M tokens)
↓
内容重要性评分
┌──────────────────────────────────┐
│ 任务定义 → 100%保留 │
│ 最近的工具调用结果 → 100%保留 │
│ 中间推理步骤 → 压缩为摘要 │
│ 已完成的子任务详情 → 只保留结论 │
│ 背景上下文 → 关键词索引 │
└──────────────────────────────────┘
↓
压缩后上下文(200K tokens)
核心思想:不同类型的信息有不同的”衰减速率”。任务定义在整个执行过程中始终重要(零衰减),而中间推理步骤一旦完成就可以压缩为摘要(高衰减)。
OPENDEV 的双代理方案:该研究提出了一个具体的架构:用一个专职的”摘要 Agent”监控主 Agent 的上下文,在接近窗口限制时自动生成摘要并替换细节。这使得理论上无限长的任务成为可能,只是随着时间推移,早期信息被逐步压缩为摘要。
2.5 上下文重置 vs 上下文压缩
来源:Anthropic
Anthropic 提出了一个重要的设计抉择:当上下文累积了大量”污染”信息(错误的尝试、被推翻的方向、过时的中间状态)时,是否应该重置上下文?
上下文压缩的适用场景:任务是连续的,中间状态有价值,压缩损失是可接受的
- 例:长时间运行的代码重构任务,每次迭代都在前一次基础上改进
上下文重置的适用场景:上下文已经被大量错误信息”污染”,Agent 陷入了错误的局部最优
- 例:Agent 尝试了多种错误方案后仍未成功,重新开始往往比继续更高效
Anthropic 的建议:将重置设计为一等公民,而不是失败的最后手段。预先定义”重置触发条件”(例如,连续 3 次工具调用失败,或任务执行超过预设时间上限),将其纳入 Harness 的标准流程。
OpenAI 的实践印证:OpenAI 团队的 Codex 任务”在人类睡眠时间持续工作超过六个小时”——这意味着单次任务的上下文可以非常长。但他们同时为每个任务提供独立的 devbox(参见 Layer 4),使得任务结束后的上下文完全销毁,下次任务从干净状态开始,实际上是在”任务级别”做了上下文重置。
三、Layer 2:约束执行层(Constraint Layer)
3.1 设计哲学:强制执行不变量,而非微观管理
OpenAI 对约束执行层给出了最精炼的设计哲学:
“通过强制执行不变量,而非对实施过程进行微观管理,我们令智能体能够快速交付,而且不会削弱基础。”
这句话区分了两种约束策略:
微观管理式约束(反模式):
# 在 AGENTS.md 中
- 所有函数必须使用这个命名模式:[动词][名词],例如 getUserById
- 所有错误必须这样处理:try { ... } catch (e) { logger.error(e); throw e; }
- API 响应必须包含这些字段:{ data, error, metadata }
- 分页必须使用 cursor-based,不允许 offset-based
问题:这类约束无法被机械验证,Agent 只能”尽力遵守”,而”尽力”意味着偶尔遗漏。更深的问题是,这些约束会随着代码库演进而过时,而 Agent 无法判断哪条规则还有效。
不变量式约束(正确模式):
// 在代码仓库的 linter 配置中(由 Codex 生成的自定义 ESLint 规则)
// 规则 1:在边界处解析数据(parse don't validate)
// 触发条件:在 service 层直接使用 any 类型接收外部数据
// 错误信息(专为 Agent 优化):
// "Error: Raw external data used without parsing at boundary.
// Fix: Use Zod schema to parse at the API boundary.
// See: docs/references/parse-dont-validate.md"
// 规则 2:分层依赖(层间依赖方向不可逆)
// Config 层不得 import Service 层
// 触发条件:config/* 中有 import from service/*
// 错误信息:
// "Error: Layer violation: Config cannot depend on Service.
// Allowed dependency direction: Types→Config→Repo→Service→Runtime→UI
// See: ARCHITECTURE.md#layer-dependencies"
关键差异:
- 不变量约束是机械可验证的:linter 在每次 commit 时运行,100% 覆盖
- 错误信息面向 Agent设计:不只是”发生了什么错误”,还包括”如何修复”和”为什么有这条规则”
- 约束编码在代码中,不在文档中:不会过时,因为代码和规则一起演进
3.2 分层领域架构
来源:OpenAI
这是 OpenAI 实践中最具代表性的约束实例,也是理解 Harness 约束设计的最佳案例。
架构图:
┌─────────────────────────────────────────────────────┐
│ Business Domain A │
│ │
│ Types → Config → Repo → Service → Runtime → UI │
│ │
│ ↑ 依赖方向只能向右,不能向左 │
│ │
│ 横切关注点通过唯一入口进入: │
│ Utils ──────────────────────┐ │
│ ↓ │
│ Providers │
│ ┌────────┤ │
│ │Authentication │
│ │Connectors │
│ │Telemetry │
│ │Feature Flags │
│ └────────┘ │
└─────────────────────────────────────────────────────┘
每层的职责:
| 层 | 职责 | 示例内容 |
|---|
| Types | 数据类型定义 | interface User { id: string; email: string; } |
| Config | 配置和常量 | 数据库连接参数、功能开关 |
| Repo | 数据访问(数据库/外部 API) | getUserById(id: string): Promise<User> |
| Service | 业务逻辑 | createUser(data: CreateUserInput): Promise<User> |
| Runtime | 运行时绑定(HTTP 处理器等) | Express route handlers |
| UI | 用户界面 | React 组件 |
为什么这个设计对 Agent 特别重要(而不只是对人类工程师):
对人类工程师来说,分层架构是一个”好习惯”,人类可以在需要时主动检查是否违反了层间规则。但 Agent 没有这种元认知——它在解决当前任务时不会主动检查是否违反了架构约束,除非约束被编码为自动执行的规则。
“这种架构通常要等到你拥有数百名工程师时才会推迟。对于编码智能体来说,这是一个早期的先决条件:有了约束,速度才不会下降,架构才不会漂移。“(来源:OpenAI)
这句话揭示了一个关键洞察:对人类团队来说是”中期需要”的架构纪律,对 Agent 团队来说是”起步就必须”的基础设施。这是因为 Agent 没有”我好像违反了一个隐性规范,让我检查一下”的直觉——它会在每次任务中独立地做出架构决策,在没有约束的情况下,这些决策会随机漂移。
3.3 条件规则系统
来源:Stripe
Stripe 实践中的一个重要创新是按子目录条件应用规则——不同代码区域有不同的约束集,通过目录结构自动路由。
# .agent-rules.yaml(Stripe 模式,非官方公开规范,基于描述重建)
rules:
# 全局规则(对所有路径生效)
global:
- id: structured-logging
description: "所有日志调用必须使用结构化格式"
enforcement: hard # hard = 违反则 CI 失败;soft = 仅警告
- id: no-direct-db-access
description: "Service 层不得直接访问数据库,必须通过 Repo 层"
enforcement: hard
# 条件规则(仅对特定路径生效)
conditional:
- path: "src/payments/**"
rules:
- id: payment-idempotency
description: "所有支付操作必须有幂等性键"
enforcement: hard
- id: pci-compliant-logging
description: "支付相关日志不得包含原始卡号"
enforcement: hard
- id: max-retry-limit
description: "重试次数不超过 3 次,防止 Stripe API 触发限速"
enforcement: hard
- path: "src/webhooks/**"
rules:
- id: webhook-signature-validation
description: "所有入站 webhook 必须验证签名"
enforcement: hard
- id: idempotent-handler
description: "Webhook 处理器必须是幂等的"
enforcement: soft # 软约束:提示但不阻塞
- path: "src/internal/**"
rules:
- id: no-external-calls
description: "内部工具不得直接调用外部 API"
enforcement: soft
这种设计的深层逻辑:
支付系统、用户认证系统、内部工具,它们有不同的安全要求和合规约束。如果用一套全局规则覆盖所有代码,要么过于严格(阻碍非关键代码的开发速度)要么过于宽松(关键代码的安全约束不足)。
条件规则系统允许”最小权限原则”(Principle of Least Constraint)——每个代码区域只应用它实际需要的约束,不多也不少。
对 Agent 来说,这种设计还有一个额外好处:Agent 在处理 src/payments/ 下的任务时,只需要关注支付相关的约束,不需要在全局的 1000 条规则中搜索”哪些规则与支付相关”——规则系统本身承担了这种上下文过滤。
3.4 自定义 Linter 的设计要点
Linter 在 Harness Engineering 中扮演的角色远超”代码风格检查”——它是约束执行层的核心工具。
面向 Agent 设计的错误信息(与面向人类的错误信息的关键区别):
# 面向人类的错误信息(传统 ESLint):
Error: 'import/no-cycle' - Dependency cycle detected.
# 面向 Agent 的错误信息(Harness Engineering 设计):
Error: Layer violation detected in 'src/config/database.ts'
You're importing from 'src/service/auth.ts' in the Config layer.
Allowed dependency direction: Types → Config → Repo → Service → Runtime → UI
Config layer CANNOT depend on Service layer.
How to fix:
1. Move the dependency to a higher layer (Service or Runtime)
2. OR extract the shared logic to Types layer
3. OR use Providers interface if this is a cross-cutting concern
See: ARCHITECTURE.md#layer-dependencies for the full rules.
Context: This rule exists because Config is loaded at app startup,
and Service depends on Config. A cycle here would cause circular
initialization errors at runtime.
关键差异:
- 上下文化:告诉 Agent 为什么这条规则存在(防止循环初始化),而不只是说”你违反了规则”
- 操作化:提供 2-3 种具体的修复方案,Agent 可以直接选择执行
- 导航化:提供相关文档的链接(
ARCHITECTURE.md#layer-dependencies),Agent 可以获取更多上下文
这种设计将 linter 从”报告问题”转变为”指导修复”——对 Agent 来说,这意味着更少的反复尝试,更高的首次修复成功率。
3.5 Parse Don’t Validate 原则
来源:Lexi Lambda 博客,被 OpenAI 明确采用
这一原则是 Harness 约束层的重要设计思想,值得专门分析。
Validate(验证)模式(不推荐):
function processUser(data: any) {
if (typeof data.id !== 'string') throw new Error('id must be string');
if (typeof data.email !== 'string') throw new Error('email must be string');
if (!isValidEmail(data.email)) throw new Error('email is invalid');
// 此处 data 仍然是 any 类型,类型系统无法保证后续代码的安全性
return doSomethingWith(data.id, data.email);
}
Parse(解析)模式(推荐):
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
});
function processUser(rawData: unknown): Result<User, ParseError> {
const parsed = UserSchema.safeParse(rawData);
if (!parsed.success) return { ok: false, error: parsed.error };
// parsed.data 的类型是 User,类型系统完全保证后续的安全性
return { ok: true, data: doSomethingWith(parsed.data.id, parsed.data.email) };
}
为什么这对 Agent 生成的代码特别重要:
Agent 在生成代码时,往往倾向于”乐观地”假设数据的结构——它会根据上下文推断”这里应该是字符串”,然后直接使用,不做防御性检查。这在大多数情况下可以工作,但会在边界条件下产生难以调试的运行时错误。
Parse Don’t Validate 原则通过在边界处强制解析,将这类错误从运行时提前到编译时或 API 边界处,使得 Agent 生成的代码即使在乐观假设方面存在问题,也会在边界处被安全地捕获,而不是在系统深处爆炸。
OpenAI 的实践是:要求 Codex 在边界处解析数据形状,但不规定具体实现方式(“模型似乎偏好 Zod,但我们没有指定特定库”)。这正是”强制执行不变量、而非微观管理”原则的体现——约束”做什么”(在边界解析),不约束”怎么做”(用哪个库)。
四、Layer 3:反馈回路层(Feedback Layer)
4.1 核心原则:尽可能将反馈左移
来源:Stripe,在 OpenAI 实践中也有对应
“反馈左移”(Shift Left)是 DevOps 领域的经典原则,在 Harness Engineering 中被极端化执行。
传统反馈链条(从右到左,延迟递增):
代码生成 → 本地测试 → CI → 代码审查 → 集成测试 → 部署到测试环境 → 人工QA → 上线
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
立即 分钟 分钟 小时 小时 天 天 周
左移后的反馈链条(Stripe 实践):
代码生成 → 本地 Linting → 本地测试 → CI(最多两轮)→ 智能体审查 → 合并
↑ ↑ ↑ ↑ ↑ ↑
立即 <5秒 分钟 分钟 分钟 分钟
Stripe 的关键约束:最多两轮 CI。如果一个 PR 在两轮 CI 后仍未通过,这被视为需要人工介入的信号,而不是让 Agent 无限重试。这个约束防止了 Agent 陷入”针对 CI 指标优化而非解决真实问题”的陷阱。
为什么反馈速度对 Agent 特别重要:
对人类工程师来说,等待 30 分钟的 CI 是恼人的,但可以利用这段时间做其他事。对 Agent 来说,等待期间的上下文可能已经老化,模型可能已经”忘记”了之前的某些决策细节。更重要的是,在高吞吐量的场景(Stripe 每周 1000+ PR),如果每个 PR 的反馈周期很长,整个系统的吞吐量会被严重限制。
左移的具体实现技术:
# 在 Agent 工作流中,本地 pre-check 脚本(<5秒目标)
#!/bin/bash
echo "Running local harness checks..."
# 1. 类型检查(最快,约 1-2 秒)
npx tsc --noEmit --incremental
# 2. 层间依赖检查(自定义 linter,约 0.5 秒)
npx eslint src --rule 'layer-dependencies: error' --cache
# 3. 关键路径单元测试(约 2 秒)
npx jest --testPathPattern='src/(types|repo)' --bail
echo "Local checks passed in ${SECONDS}s"
目标:在代码提交前,本地完成 80% 的关键检查,CI 仅作为最后防线。
来源:OpenAI
这是 OpenAI 实践中最有创意的 Harness 组件之一:将 Chrome DevTools 接入 Agent 运行时,使 Codex 可以像人类工程师一样通过浏览器验证 UI。
系统架构:
Codex Agent
│
│ 通过 MCP(Model Context Protocol)
↓
Chrome DevTools MCP Server
│
├── take_snapshot() # DOM 快照
├── take_screenshot() # 视觉截图
├── navigate_page(url) # 页面导航
├── evaluate_script(js) # 执行 JS
├── list_network_requests() # 网络请求
└── get_console_messages() # 控制台日志
│
↓
运行在 git worktree 上的应用实例
(每个 Codex 任务有独立的应用实例)
具体工作流程(来自 OpenAI 博文描述):
1. Codex 接收任务:"修复用户头像上传后页面不刷新的 bug"
2. 通过 Chrome DevTools MCP:
a. 导航到头像上传页面
b. 触发上传操作
c. 在触发前后分别获取 DOM 快照
d. 观察 console 错误和网络请求
3. 根据观察结果定位问题(例如:上传成功但 state 未更新)
4. 实施修复
5. 重新运行应用,再次通过 Chrome DevTools 验证:
a. 触发上传
b. 验证 DOM 是否正确更新
c. 检查无新增 console 错误
6. 只有验证通过,才打开 PR
这为什么重要:
在没有 Chrome DevTools 集成之前,UI 验证是 Agent 的盲区——Agent 可以看代码,可以运行测试,但无法”看到”真实的界面效果。这意味着 UI bug 只能在人工 QA 阶段被发现。
Chrome DevTools MCP 将 UI 验证从”人工 QA”转移到了”Agent 自验证”,完成了反馈回路的最后一块拼图。
4.3 可观测性堆栈:让 Agent 看到运行时状态
来源:OpenAI
OpenAI 为 Codex 配备了完整的可观测性堆栈:
应用实例
│
├── 日志 ──────────────→ Vector(日志路由)──→ Victoria Logs
├── 指标 ──────────────→ Vector ──────────→ Victoria Metrics
└── 链路追踪 ──────────→ Vector ──────────→ Victoria Traces
│
Codex 可以查询:
├── LogQL 查日志
├── PromQL 查指标
└── TraceQL 查链路
隔离性设计:每个 Agent 任务运行在独立的 devbox 上,该 devbox 有自己独立的可观测性实例。任务完成后,devbox 和所有遥测数据一起销毁。
这种设计解决了一个重要问题:Agent 的诊断不能依赖全局状态。如果多个 Agent 共享一个日志系统,Agent A 很难区分哪些日志是自己任务产生的,哪些是 Agent B 同时产生的。独立的可观测性实例消除了这种干扰。
可观测性使能的新型提示范式:
有了可观测性,可以给 Agent 更精确的性能约束:
"确保服务启动在 800ms 内完成"
"这四个关键用户旅程中的任何 span 都不得超过两秒"
这些约束在没有可观测性的情况下是无法验证的——Agent 只能猜测性能是否达标。有了 PromQL/TraceQL 查询能力,Agent 可以精确测量并迭代优化。
4.4 GAN 式生成-评估分离
来源:Anthropic
这是 Anthropic 提出的一种反馈架构,借鉴了 GAN(生成对抗网络)的对抗性设计思想:
┌─────────────────┐ 提交代码 ┌─────────────────┐
│ Generator │ ──────────────→ │ Discriminator │
│ Agent │ │ Agent │
│ │ ←────────────── │ │
└─────────────────┘ 拒绝/改进建议 └─────────────────┘
↑
任务提示 + 上下文
Generator Agent:负责生成代码变更,目标是实现功能
Discriminator Agent:独立评审代码变更,从以下角度评估:
- 是否符合架构约束?
- 是否有安全问题?
- 是否引入了回归?
- 代码质量是否达标?
两个 Agent 使用相同的基础模型,但上下文是分离的——Discriminator 不知道 Generator 的生成过程,只看最终的代码 diff。这种信息隔离防止了”自我评估偏差”(self-evaluation bias)——知道自己如何生成某段代码的 Generator,在评审时会无意识地偏向认为这段代码是正确的。
与 OpenAI 实践的对应:
OpenAI 的实践中,人类审查 + 智能体审查是并行的,而且”随着时间推移,我们已将几乎所有的审核工作调整为用智能体对智能体的方式来处理”。这与 GAN 式架构高度一致——最终形成了由专职 Discriminator Agent 负责代码审查的流程。
4.5 Playwright 自动化测试的集成
来源:行业通用实践,OpenAI 案例中有端到端 UI 测试的描述
在 Harness Engineering 语境中,Playwright 的角色不只是”自动化测试工具”,而是反馈回路的最后一道防线。
// Agent 生成的 Playwright 测试(面向 Agent 可读的结构)
// 注意:测试描述精确到 "为什么" 而非仅有 "是什么"
import { test, expect } from '@playwright/test';
test.describe('用户头像上传', () => {
test('上传后应立即在 UI 中反映,无需刷新页面', async ({ page }) => {
// 背景:此测试验证 ticket #1234 的修复
// 失败原因追溯:Agent 之前的版本在上传后不更新 state
await page.goto('/settings/profile');
const initialAvatarSrc = await page.locator('[data-testid="user-avatar"]').getAttribute('src');
// 模拟文件上传
await page.locator('[data-testid="avatar-upload-input"]').setInputFiles('test-fixtures/avatar.jpg');
// 验证:不刷新页面,头像应在 2 秒内更新
await expect(page.locator('[data-testid="user-avatar"]')).not.toHaveAttribute('src', initialAvatarSrc, { timeout: 2000 });
// 验证:没有 console 错误
const errors = await page.evaluate(() => window.__consoleErrors || []);
expect(errors).toHaveLength(0);
});
});
关键设计思路:在 Harness Engineering 中,测试文件本身也是上下文的一部分——测试中的注释(// 失败原因追溯:Agent 之前的版本在上传后不更新 state)会被未来的 Agent 读取,帮助其理解为什么要保留这个测试,而不是在修改相关代码时删除它。
五、Layer 4:编排调度层(Orchestration Layer)
5.1 Cursor 的四代架构演进
来源:Cursor 技术团队(2026年3月)
Cursor 的架构演进是 Orchestration 层设计思想最清晰的展示:
第一代:单 Agent,工具调用
用户 → Agent → [工具: 读文件/写文件/运行测试] → 结果
问题:Agent 需要同时处理”理解任务”和”执行操作”,上下文快速被执行细节淹没
第二代:子任务分解
用户 → Planner Agent → 子任务列表 → [多次工具调用] → 结果
改进:引入了规划层,但规划和执行仍由同一个 Agent 完成
第三代:并行工作者
用户 → Orchestrator → [Worker 1] [Worker 2] [Worker 3] → 结果汇总
改进:引入并行,但 Orchestrator 容易成为瓶颈,且 Worker 间的依赖关系处理复杂
第四代(当前):递归规划者 + 专职工作者
用户
↓
Recursive Planner(递归规划者)
│ 分解任务
├── Plan A: [子计划 A1, A2]
│ ├── A1 → Specialist Worker(专职工作者:前端变更)
│ └── A2 → Specialist Worker(专职工作者:测试更新)
└── Plan B: [子计划 B1]
└── B1 → Specialist Worker(专职工作者:文档更新)
↑
反馈回路(Workers 完成后回报)
↓
重新规划或完成
关键创新:
- 递归规划:复杂任务可以递归地分解为子计划,每个子计划又可以进一步分解,直到达到可由单个 Worker 完成的粒度
- 专职化 Workers:不同的 Worker 针对不同类型的工作进行优化(前端、后端、测试、文档),而不是使用同质化的通用 Agent
- 反脆弱性:任何单个 Worker 失败不会导致整个任务失败,Planner 可以重新规划
Cursor 用这套架构在一周内协调数千个 Agent,完成约 1000 次提交。
5.2 OPENDEV 的规划-执行分离架构
来源:arXiv OPENDEV(2024-2025)
OPENDEV 提出了一个与 Cursor 相似但更学术化的双代理架构:
规划代理(Planning Agent) 执行代理(Execution Agent)
───────────────────────── ─────────────────────────
读取高层需求 接收具体操作指令
分解为操作序列 执行代码变更
生成执行计划 报告执行结果
处理约束冲突 不做规划决策
← 通过结构化消息通信 →
分离规划与执行的理由:
从认知负荷的角度:规划需要宏观视角(“哪个顺序最优?”、“有没有依赖约束?”),执行需要微观精度(“这个函数签名是否正确?”)。同一个 Agent 同时承担两种任务,会导致两种任务都做得不够好。
从上下文管理的角度:执行过程产生大量细节性上下文(工具调用结果、中间状态),这些细节对规划决策几乎没有价值,却会占用大量上下文窗口。分离后,规划代理的上下文保持干净,执行代理的上下文可以更自由地扩展。
与 Cursor 方案的比较:OPENDEV 的方案是”水平分离”(规划 vs 执行),Cursor 的方案是”垂直分层”(递归规划者 + 专职工作者)。实践中,两种方案都有效,区别在于任务的复杂度结构——OPENDEV 方案更适合明确的顺序任务,Cursor 方案更适合有递归分解需求的复杂任务。
5.3 隔离沙箱:Stripe 的 10 秒 devbox
来源:Stripe
Stripe 的实践中,每个 Agent 任务运行在一个独立的隔离 devbox(开发环境容器)中,启动时间约 10 秒。
为什么要隔离:
没有隔离的情况:
─────────────────
Agent A 正在修改 auth.ts
Agent B 正在修改 auth.ts(另一个任务)
↓
文件系统冲突、git 状态混乱、测试结果相互干扰
有隔离的情况:
─────────────────
Agent A → devbox-a(独立的代码仓库副本)→ 修改 auth.ts
Agent B → devbox-b(另一个独立副本)→ 修改 auth.ts
↓
两个任务完全独立,最终通过 git merge 合并
技术实现要点:
10 秒的启动目标是一个严格的工程约束。传统 VM 启动需要分钟级别,Docker 容器可以做到秒级,但需要仔细优化:
- 预构建基础镜像(避免在每次启动时安装依赖)
- 使用 overlay filesystem(写时复制,快速 fork 代码仓库状态)
- 增量同步(只同步当前任务需要的代码,而不是整个 repo)
- 内存映射(对大型代码仓库,使用内存映射文件加速访问)
Stripe 的”10 秒”目标意味着:即使每天运行数千个 Agent 任务,总启动开销也控制在可接受范围内。
OpenAI 的对应实践:OpenAI 使用 git worktree 实现轻量级隔离——每个 Codex 任务在一个独立的 worktree 上工作,共享底层的 git 对象存储,但有独立的工作目录和应用实例。这比 Stripe 的 devbox 方案更轻量,适合在同一台机器上并发运行多个任务。
5.4 工作负载特化模型路由
在 Orchestration 层,当系统涉及多个专职 Worker 时,可以引入模型路由(Model Routing)——根据任务类型选择最合适的模型。
# 模型路由示例(概念性代码)
def route_to_model(task: Task) -> ModelConfig:
if task.type == "code_generation":
return ModelConfig(
model="gpt-5-3-codex", # 专为代码优化
temperature=0.1, # 低温度,追求确定性
context_budget=200_000 # 大上下文
)
elif task.type == "documentation":
return ModelConfig(
model="gpt-5", # 通用强模型
temperature=0.3, # 稍高温度,允许创意
context_budget=50_000
)
elif task.type == "code_review":
return ModelConfig(
model="claude-opus-4", # 擅长分析
temperature=0.0, # 零温度,追求一致性
context_budget=100_000
)
elif task.type == "test_generation":
return ModelConfig(
model="gpt-5-3-codex",
temperature=0.2,
context_budget=150_000,
system_prompt=SPECIALIZED_TEST_PROMPT
)
为什么不总是用最强的模型:
- 成本:强模型的 API 成本通常是弱模型的 10-100 倍,文档生成任务不需要最强的代码模型
- 延迟:强模型通常也更慢,简单任务用快速模型可以提高吞吐量
- 专业性:某些任务(如安全审查)可能有专门训练的模型表现更好
六、Layer 5:自愈维护层(Self-Healing Layer)
6.1 “垃圾回收”:doc-gardening Agent
来源:OpenAI
这是 Harness Engineering 中最富创意的概念之一:把软件工程中的垃圾回收(GC)隐喻应用于文档和代码质量管理。
背景问题:随着 Agent 持续生成代码,代码库会自然地积累”AI 残渣”(AI residue)——不一致的模式、过时的文档、次优的实现。OpenAI 的经验是:
“Codex 会复现代码仓库中已存在的模式——甚至包括那些不均衡或不够理想的模式。随着时间的推移,这不可避免地导致漂移。”
不可扩展的初始方案:OpenAI 团队曾经每周花费工作时间的 20%(每周五)人工清理这些”AI 残渣”。这显然无法扩展——当 Agent 吞吐量翻倍时,清理工作量也会翻倍,人工维护很快变成瓶颈。
可扩展的解决方案:doc-gardening Agent
┌─────────────────────────────────────────────────────┐
│ doc-gardening Agent(定期运行) │
│ │
│ 扫描任务: │
│ 1. 文档新鲜度检查 │
│ - 最后修改时间 vs 对应代码的最后修改时间 │
│ - 代码行为是否与文档描述一致? │
│ - 文档引用的 API 是否仍存在? │
│ │
│ 2. 质量评分更新 │
│ - 每个模块的测试覆盖率 │
│ - 每个文档页面的完整性评分 │
│ - 架构约束的遵循率 │
│ │
│ 3. 漂移检测 │
│ - 识别不符合"黄金原则"的代码模式 │
│ - 检测重复实现(应使用共享工具但却自行实现) │
│ - 发现违反 parse don't validate 的新代码 │
│ │
│ 产出:针对性重构 PR(大多数 < 1 分钟审查,自动合并) │
└─────────────────────────────────────────────────────┘
技术债像高息贷款的隐喻(来源:OpenAI):
“技术债就像一笔高息贷款:不断地以小额贷款的方式偿还债务,总比让债务不断累积,再痛苦地一次解决要好得多。”
这个比喻精确地描述了 doc-gardening Agent 的工作方式:每天自动产出大量小型重构 PR,每个 PR 的范围小到”大多数可以在一分钟内完成审查并自动合并”,而不是积累到需要重大重构的程度。
6.2 质量评分追踪系统
来源:OpenAI(QUALITY_SCORE.md)
OpenAI 维护了一个明确的质量评分文件,追踪代码库各个部分的质量状态:
# QUALITY_SCORE.md(概念性示例,基于 OpenAI 描述重建)
## 当前质量快照(2026-03-15 自动更新)
### 产品领域质量
| 领域 | 测试覆盖率 | 文档完整度 | 架构合规率 | 总分 |
|------|-----------|-----------|-----------|------|
| 用户认证 | 89% | A | 97% | 🟢 |
| 支付处理 | 93% | A+ | 99% | 🟢 |
| 通知系统 | 67% | B | 88% | 🟡 |
| 内部工具 | 45% | C | 76% | 🔴 |
### 架构层质量
| 层 | 约束遵循率 | 最近违规数 | 状态 |
|----|-----------|-----------|------|
| Types | 100% | 0 | 🟢 |
| Service | 98% | 2 | 🟢 |
| Runtime | 91% | 8 | 🟡 |
| UI | 87% | 15 | 🟡 |
### 已知差距(doc-gardening Agent 待处理)
1. 通知系统文档落后代码 6 周 → 计划:2026-03-20 前修复
2. 内部工具测试覆盖率低 → 计划:2026-04 Q 修复
这个文件的作用:
- 对 Agent 的价值:Agent 在处理某个领域的任务时,可以查看该领域的质量分,了解当前状态的”基线”,并有意识地改善而不只是维持
- 对人类的价值:提供一个持续更新的质量仪表板,无需人工检查就能了解系统健康状态
- 对 doc-gardening Agent 的价值:作为优先级排序依据,分数低的区域优先处理
6.3 “黄金原则”编码
来源:OpenAI
“黄金原则”(Golden Rules)是 OpenAI 团队对”我们希望代码库始终保持的不可妥协属性”的称呼。与 Layer 2 中的架构约束不同,黄金原则更关注于代码库的长期健康而非即时正确性。
OpenAI 公开的两个黄金原则案例:
原则 1:使用共享工具,而非自行实现
问题描述:Agent 在解决新问题时,往往倾向于就地实现一个辅助函数,
而不去查找是否已有共享工具可以使用。这导致同一功能在代码库中
出现多个实现版本,维护成本倍增。
黄金原则:我们更倾向于使用共享的实用程序包,而不是手工编写的辅助工具,
以便将不变式集中管理。
检测方式:doc-gardening Agent 定期扫描
- 是否有与现有工具功能相似的新实现?
- 是否有内联 helper 函数应该被提取到共享包?
处理方式:生成 PR,将孤立实现替换为共享工具的调用
原则 2:不使用 YOLO 式探测数据
问题描述:Agent 在不确定外部 API 响应结构时,会猜测性地访问
字段(如 response.data?.user?.id),形成"YOLO 式"的数据探测。
这些代码在大多数情况下工作,但在边界情况下会出现难以调试的问题。
黄金原则:我们不会使用"YOLO 式"探测数据——我们会验证边界,
或依赖类型化的 SDK。
检测方式:识别代码中的可选链式访问(?.)和非空断言(!.)模式,
区分"合理的防御性编程"和"掩盖类型不确定性的探测"
处理方式:生成 PR,添加 Zod 解析或将探测替换为类型化的访问
6.4 自愈层的工程实现:系统性 vs 一次性
自愈层有两种截然不同的实现方式,理解其区别对设计有重要意义:
一次性补丁(反模式):
问题:Agent 错误地使用了 response.user.id(没有解析)
修复:找到所有这种用法,手动替换为 UserSchema.parse(response).id
结果:这次修好了,但下次 Agent 仍会生成同样的错误用法
系统性防护(正确模式):
问题:Agent 错误地使用了 response.user.id(没有解析)
步骤 1:分析为什么 Agent 会产生这种模式
→ 因为类似的旧代码在训练时被看到过
→ 因为 AGENTS.md 没有明确说明边界解析要求
步骤 2:系统性解决
→ 在 linter 中添加规则:检测 response.* 模式(外部响应直接访问)
→ 更新 AGENTS.md:明确"所有外部响应必须先经过 Zod 解析"
→ 添加一个示例到 docs/references/:如何正确解析外部 API 响应
步骤 3:验证解决
→ 运行 linter,确认所有现有违规被标记
→ 生成修复 PR(由 doc-gardening Agent 处理)
→ 测试:给 Agent 一个涉及外部 API 的新任务,验证它会主动使用 Zod 解析
Mitchell Hashimoto 的操作定义——“每当发现代理犯错时,花时间工程化解决方案防止再次出现”——正是描述了这种系统性思维方式。每个错误都不只是需要修复的 bug,而是 harness 中需要填补的漏洞。
七、设计原则总结
7.1 确定性与 LLM 的混合
来源:Martin Fowler
这是 Harness Engineering 最基础的设计原则之一:系统不应该是”纯 LLM 驱动”的,而应该是确定性组件和概率性 LLM 的混合体。
系统的确定性层次(由高到低):
100% 确定性:
- Linter 规则(代码是否违反约束)
- 单元测试(函数是否返回预期结果)
- 类型检查(接口是否匹配)
高度确定性:
- 集成测试(功能是否端到端工作)
- 性能基准(响应时间是否在阈值内)
概率性:
- LLM 代码生成
- LLM 代码审查
- LLM 文档生成
设计原则:
- 用确定性层验证概率性层的输出
- 越接近"用户可见"的层次,确定性要求越高
- 概率性层的错误应被确定性层捕获,而不是传递给用户
在实践中,这意味着:
- 不要让 LLM 判断”这段代码是否遵循了架构约束”——用确定性的 linter 来判断
- 不要让 LLM 判断”这个 PR 是否引入了性能回归”——用确定性的 benchmark 来判断
- LLM 负责生成,确定性系统负责验证
7.2 约束优于指令
来源:Cursor 技术文章
这一原则区分了两种控制 Agent 行为的方式:
指令(Instructions):
# 在 AGENTS.md 中
- 请不要在 Config 层 import Service 层的内容
- 记住使用结构化日志
- 在写数据库操作时要考虑事务安全
约束(Constraints):
// 在代码中自动执行
// rule: no-layer-violation
// 触发时:自动报错并阻止 commit
if (currentLayer === 'Config' && importedLayer === 'Service') {
throw new LintError('Layer violation: Config cannot depend on Service');
}
为什么约束优于指令:
| 维度 | 指令 | 约束 |
|---|
| 执行可靠性 | Agent”尽力遵守”(约 80-90%) | 100% 强制执行 |
| 覆盖范围 | 只影响读过指令的 Agent | 影响所有通过 CI 的代码 |
| 随时间退化 | 文档过时后指令失效 | 代码约束持续有效 |
| 可测试性 | 无法自动测试遵循率 | 可以精确测量遵循率 |
| 认知负担 | 增加 Agent 的上下文负担 | 无需 Agent 记忆,自动执行 |
这并不意味着指令没有用——在无法用代码表达的情况下,指令是必要的(例如产品决策原则、设计美学偏好)。但每当一条指令可以被转化为约束时,应该果断地转化。
7.3 最小可行 Harness,按需增加复杂性
来源:Anthropic
Anthropic 的建议是反对”过度工程化”的:不要在项目开始时就建造全部五层,而是从最小可行 harness 开始,按需增加复杂性。
最小可行 Harness(MVP 阶段):
├── AGENTS.md(50 行,基本导航)
├── .lintrc(5 条关键规则)
└── 一套基础测试
中期 Harness(团队阶段):
├── AGENTS.md(100 行,完整导航)
├── docs/(结构化文档体系)
├── 自定义 linter(20+ 规则)
├── 分层架构约束
└── CI with feedback shift
完整 Harness(规模阶段):
├── 完整的上下文管理层
├── 条件约束系统
├── Chrome DevTools MCP
├── 完整可观测性堆栈
├── 双代理编排
├── doc-gardening Agent
└── 质量评分追踪
增加复杂性的触发条件:
| 触发信号 | 应添加的 Harness 组件 |
|---|
| Agent 频繁违反同一类错误 | 添加对应的 linter 规则 |
| 上下文窗口经常溢出 | 实施渐进式披露和文档结构化 |
| UI bug 在人工 QA 才被发现 | 添加 Chrome DevTools MCP |
| 多个 Agent 工作互相冲突 | 添加隔离沙箱 |
| 代码库出现大量重复实现 | 添加 doc-gardening Agent |
| 文档频繁过时 | 添加文档新鲜度 linter |
7.4 模块化以应对模型更替
来源:Philipp Schmid
这一原则反映了 AI 领域的一个独特挑战:底层模型会不断被更新和替换,而 harness 需要能够跨模型版本保持有效。
不可移植的 Harness(反模式):
# AGENTS.md
"你是 GPT-5,你的能力包括 200K context window 和 function calling v3..."
"使用 GPT-5 特有的 system prompt 格式..."
"当你遇到代码问题,使用 GPT-5 的 reasoning token 功能..."
这类 harness 在模型升级(或降级)时需要大规模重写。
可移植的 Harness(正确模式):
上下文管理层:不依赖特定模型的 context window 大小,用渐进式披露适配任何大小
约束执行层:用代码(linter)而非 prompt 实现约束,完全模型无关
反馈回路层:测试和 CI 脚本是模型无关的
编排调度层:接口化设计,模型切换只需更改配置
# model-config.yaml(模型配置集中管理)
default_model: claude-opus-4
fallback_model: gpt-5
code_specialist: gpt-5-3-codex
review_specialist: claude-opus-4
实际效益:Cursor 在其四代架构演进中,每次更换或升级底层模型时,harness 层基本无需改动——因为 harness 的核心组件(上下文管理、约束系统、反馈回路)都是模型无关的。
7.5 反脆弱性:容忍单个 Agent 失败
来源:Cursor
在高吞吐量的 Agent 系统中,部分任务失败是不可避免的。脆弱的系统设计是:任何 Agent 失败都会阻塞整个流水线。反脆弱的设计是:单个 Agent 的失败是正常事件,系统有内建的恢复机制。
脆弱设计(串行依赖):
Task A → Task B → Task C → Task D → 完成
↓
Task C 失败 → 整个流程停止
反脆弱设计(并行 + 容错):
Task A ─┬→ Task B1 → ···
├→ Task B2 → ··· → 汇总 → 完成
└→ Task B3 → ···
↓
Task B2 失败 → 重新分配给备用 Agent,或降级处理,其他任务继续
Cursor 实践中,递归规划者(Recursive Planner)负责监控各个 Worker 的状态,当某个 Worker 失败时,Planner 有几种处理策略:
- 重试:重新创建一个相同的 Worker 任务
- 重规划:如果重试也失败,修改计划,绕过该子任务
- 上报:如果任务是关键路径,通知人类介入
- 降级:采用简化版本的实现,在后续 PR 中完善
八、核心权衡分析
8.1 过度约束 vs 欠约束
这是 Harness Engineering 中最常见的失衡问题。
欠约束的症状:
- Agent 频繁产生架构违规
- 代码库风格不一致(每个 Agent 有自己的”风格”)
- 文档快速过时
- 小错误在代码库中传播并被复现
过度约束的症状(来源:OpenAI 的明确警告):
- Agent 的”有效自由度”接近零,创造性解决方案被阻塞
- 大量时间花在修复 lint 错误而非解决实际问题
- 边缘情况频繁触发规则误报
- “围绕规则优化”而非”解决问题”
OpenAI 提供了一个有效的校准标准:
“我们明确指出了哪些地方需要限制,哪些地方不需要限制。这类似于领导一个大型工程平台组织:在中央层面强制执行边界,在本地层面允许自主权。”
实践中的校准方法:
将代码库关注点分为两类:
- 必须强制执行:安全边界、架构层间依赖、数据类型解析——这些一旦违反,影响是系统性的
- 允许 Agent 自由决策:函数命名(只要语义清晰)、注释风格、算法实现细节——这些即使”不理想”,影响也是局部的
对”必须强制执行”的点,用 linter + CI 硬约束;对”允许自由”的点,用文档建议而非规则强制。
8.2 速度 vs 可靠性
来源:Stripe(最多两轮 CI 的约束)
在高吞吐量系统中,速度和可靠性之间存在基本张力:
追求极致可靠性的代价:
- 更多的测试层次 → 更长的反馈周期
- 更多的人工审核 → 人力成本增加、成为瓶颈
- 更严格的约束 → Agent 的自由度降低、速度下降
追求极致速度的代价:
- 更少的验证 → 更高的错误率进入主干
- 自动合并 → 问题传播更快
- 宽松的约束 → 代码质量退化
Stripe 的”最多两轮 CI”约束是一个在速度和可靠性之间找到平衡点的具体例子:
- 一轮 CI:可能存在测试不稳定(flaky tests)导致的误报,值得重试
- 两轮 CI:如果两次都失败,很可能是真实问题,需要 Agent 修改或人工介入
- 超过两轮:收益递减,且占用 CI 资源,影响其他 PR 的速度
OpenAI 对此有一个更宏观的观察:
“在一个智能体吞吐量远超人类注意力的系统中,纠错成本低,而等待成本高。”
这句话是整个速度/可靠性权衡的指导原则:系统吞吐量越高,等待(慢反馈、多审核层次)的相对成本越高,因为等待期间 Agent 是空闲的。
8.3 通用 Harness vs 定制 Harness
来源:Martin Fowler,Philipp Schmid
通用 Harness 指可以跨多个项目或团队复用的 harness 组件(例如 LangChain、LlamaIndex 等框架)
定制 Harness 指针对特定代码库、特定领域设计的 harness 组件(例如 OpenAI 为其 Codex 项目设计的一套)
通用 Harness 的优势:
- 快速上手,已经解决了常见问题
- 社区支持,有文档和示例
- 不需要维护
通用 Harness 的劣势(来源:OpenAI 的经验):
“在某些情况下,让智能体重新实现部分功能子集比绕过公共库中不透明的上游行为更便宜。”
OpenAI 的具体案例:他们没有使用 p-limit 这样的通用并发控制包,而是实现了自己的带并发的 map 辅助函数,原因是:
- 与 OpenTelemetry 仪表的紧密集成(通用包无法做到)
- 100% 的测试覆盖率(通用包的行为有不确定性)
- 完全符合运行时预期(不受上游版本升级影响)
实践中的判断标准:
| 使用通用 Harness | 使用定制 Harness |
|---|
| 标准问题(RAG、记忆管理等) | 领域特定约束(支付合规、安全要求等) |
| 早期阶段,快速验证 | 成熟阶段,追求极致可靠性 |
| 团队 AI/ML 经验有限 | 团队有深入的 Harness 工程能力 |
| 代码库规模小(<10 万行) | 代码库规模大(>100 万行) |
8.4 人工审核 vs 全自动
来源:OpenAI
OpenAI 团队的实践记录了从”大量人工审核”到”基本全自动”的演进路径:
初期:每个 PR 都需要人工审查,确保质量
- 优点:质量控制严格,可以捕获细微问题
- 缺点:成为吞吐量的瓶颈,人类注意力是稀缺资源
中期:建立 Agent 审核系统,逐步替代人工审核
- 实现:Discriminator Agent 负责代码审查
- 标准:只有涉及”判断性决策”(而非技术正确性)时才交给人类
成熟期(OpenAI 当前状态):
“我们已将几乎所有的审核工作调整为用智能体对智能体的方式来处理。”
“人类可以审核 Pull Request,但并非必须这样做。”
什么情况下人工审核是不可替代的:
- 价值判断:这个功能是否值得构建?是否符合产品方向?
- 安全敏感:涉及认证、权限、数据隐私的重大变更
- 架构决策:影响整个系统结构的设计选择
- 边界情况:Agent 自己标记为”不确定如何处理”的情况
METR 研究的警示:
METR 发现 SWE-bench 自动评分存在 24 个百分点的系统性偏差,这提醒我们:
即使系统完全自动化,也需要定期对自动化验证机制本身进行人工校准——检查 Discriminator Agent 是否存在系统性偏差,检查 linter 规则是否误报,检查测试是否测的是”真正重要的事情”。
“全自动”不意味着”人类完全退出”,而意味着人类的工作从”逐个审核”转变为”设计和校准自动化系统”。
九、架构实现的完整示例
以一个中等规模的 Web 应用(约 10 万行代码,5 名工程师)为例,展示五层架构的具体实现:
project/
├── AGENTS.md # Layer 1: 上下文入口(100 行)
├── ARCHITECTURE.md # Layer 1: 架构地图
│
├── docs/
│ ├── design-docs/ # Layer 1: 设计文档
│ ├── exec-plans/ # Layer 1: 执行计划
│ │ ├── active/
│ │ ├── completed/
│ │ └── tech-debt-tracker.md
│ └── references/ # Layer 1: LLM 专用参考文档
│ ├── architecture-llms.txt
│ └── api-conventions-llms.txt
│
├── .eslintrc.js # Layer 2: 标准 + 自定义规则
├── eslint-rules/ # Layer 2: 自定义层间约束 linter
│ ├── layer-dependencies.js
│ ├── parse-at-boundary.js
│ └── structured-logging.js
├── .agent-rules.yaml # Layer 2: 条件规则系统
│
├── scripts/
│ ├── local-check.sh # Layer 3: 本地快速验证(<5秒)
│ ├── playwright/ # Layer 3: E2E 测试
│ └── observability/ # Layer 3: 本地可观测性堆栈配置
│ ├── vector.yaml
│ └── grafana-datasources.yaml
│
├── .devcontainer/ # Layer 4: 隔离沙箱配置
│ └── devcontainer.json # 10秒启动目标
├── agents/
│ ├── planner.yaml # Layer 4: 递归规划者配置
│ └── workers/
│ ├── frontend-worker.yaml # Layer 4: 专职前端 Worker
│ ├── backend-worker.yaml # Layer 4: 专职后端 Worker
│ └── test-worker.yaml # Layer 4: 专职测试 Worker
│
├── maintenance/
│ ├── doc-gardening-agent.yaml # Layer 5: 文档维护 Agent
│ ├── golden-rules.md # Layer 5: 黄金原则
│ └── QUALITY_SCORE.md # Layer 5: 质量评分(自动更新)
│
└── .github/
└── workflows/
├── ci.yaml # Layer 3: CI(最多两轮)
└── maintenance.yaml # Layer 5: 定期维护任务
这个结构是上述五层架构的直接映射,每一层都有明确的文件和目录对应。在实际实施中,建议按照”从 Layer 1 开始,逐层添加”的顺序推进,而不是一次性建立全部五层。