第10章:Harness Engineering —— AI Agent 的工程实践
"The decisive result came not from the model alone, but from the harness around it."
决定成败的不仅是模型本身,更是其配套的外围系统。
——Anthropic Harness Engineering Team
Skill 封装能力。Spec 写规格。Ralph Loop 自己跑到对。gstack 用角色覆盖质量。Goal Workflow 串成七步流水线。autoresearch 全自动从 Issue 到合入。
这些方法论都在 Claude Code 之上运行。但 Claude Code 自己怎么造出来的?它调工具、读文件、写代码、执行 Bash——每一步都可能出错、可能越权、可能陷入死循环。谁在管这些?
Harness Engineering 回答的就是这个问题。不是"怎么用 Agent",是"怎么造 Agent"。本章拆开 Claude Code 的引擎盖,看它的 hooks、settings.json、权限模型、沙箱、可观测性怎么拼在一起,把一个大语言模型变成可安全交付的产品。
10.1 Harness 这个词
Harness 的原意是马具。骑手不直接徒手控马——靠缰绳、挽具、马衔把力量引导到正确的方向。在 AI Agent 上,Harness 就是那一层控制基础设施:模型推理能力以外的、所有约束和引导它的系统代码。
把大语言模型直接暴露给文件系统和 Bash 的操作结果,在任何 Agent 产品中都是必须解决的安全问题。Anthropic 内部有一个专门的 Harness Engineering 团队,不写模型代码,不调 prompt——他们设计 hooks 机制、权限模型、settings.json 的继承逻辑、bash 沙箱。Claude Code 从"能对话的模型"变成"能安全交付的产品",Harness 是那条分界线。
到 2026 年 5 月,Claude Code 的 Harness 已经演化成一套完整系统——31 个生命周期事件、5 种 hook 类型、4 层配置继承、3 种权限模式、bash 沙箱。它们搭出了一套 Agent 的操作系统。
5月19日,DeepSeek 资深研究员陈德里在小红书上发了一条招聘贴,证实内部正在组建全新的"Harness"(代码智能体工程)团队,目标直指 Anthropic 旗下的明星产品 Claude Code。
10.2 Harness 的十二个组件
本节给出本书对 Harness Engineering 的归纳框架——一个生产级 Agent 需要处理的十二个维度。这不是 Anthropic 或任何组织的官方分类,而是从前述各 Agent 产品的实现中抽象出的共性组件。
- 编排循环 — Agent 什么时候做、什么时候停
- 工具系统 — Agent 能调什么、怎么调
- 记忆管理 — 跨会话状态存在哪
- 上下文管理 — 模型能看到什么
- Prompt 构造 — 系统指令怎么注入
- 输出解析 — 模型的响应怎么变成行动
- 状态管理 — 会话、任务、迭代的状态
- 错误处理 — 调失败了怎么办
- 安全护栏 — 什么绝对不能做
- 验证循环 — 完成的判断标准
- 子 Agent 编排 — 多 Agent 怎么协作
- 安全机制 — 沙箱、权限、审计
第 4 章 Ralph Loop 的双出口门——Agent 说"做完了",还得匹配 completion promise——对应这里的编排循环和验证循环。第 5 章 gstack 的 hooks 强制门控——代码审查没过不允许 commit——对应工具系统。
10.3 Claude Code:最完整的 Harness 落地
Claude Code 是目前 Harness Engineering 实现最完整的 Agent 产品。
10.3.1 编排循环:谁告诉 Agent 什么时候停
Claude Code 的编排循环通过 hooks 实现。31 个生命周期事件中,最核心的编排相关事件是 Stop、PostToolBatch、PreCompact。
第 4 章 Ralph Loop 的 Stop Hook 是最直观的例子:Agent 完成一轮工作后尝试退出 → Stop hook 触发 → 检查 completion promise 是否匹配 → 未匹配则返回 exit 2 阻止退出 → 相同的 prompt 重新注入 → Agent 继续下一轮。Stop Hook 不依赖 Agent 的自我判断——它在 Claude Code 运行时层面拦截退出行为,Agent 无法绕过。
PostToolBatch 在 Agent 完成一组并行工具调用后触发。如果 hook 返回 exit 2,整个 agentic loop 停止——相当于在工具执行层面提供了熔断。第 4 章的 max-iterations 安全阀是编排循环的补充:hook 层解决"什么时候继续",计数器层解决"什么时候必须停"。
对应 10.2 的编排循环和验证循环两个组件。
10.3.2 工具系统:Agent 能调什么、怎么调
Claude Code 的工具系统有两层控制。第一层是工具类型本身——Bash、Read、Write、Edit、Glob、Grep、WebFetch、WebSearch、Task 等——Claude Code 决定 Agent 能访问哪些工具。第二层是 hooks 对工具调用的拦截——PreToolUse、PostToolUse、PostToolUseFailure 三个事件覆盖了工具调用之前、成功之后、失败之后三个节点。
PreToolUse 是整个工具系统中最关键的机制。它在 Agent 执行任何工具之前截住调用。匹配语法支持精确匹配和正则:
{ "matcher": "Bash", "command": "..." } // 所有 Bash 命令
{ "matcher": "Bash(git:*)", "command": "..." } // 所有 git 命令
{ "matcher": "//Edit|Write//", "command": "..." } // 所有文件修改
Hook 脚本收到 stdin JSON(含 tool_name、tool_input、cwd、session_id)。exit 0 = 放行。exit 2 = 阻止。这个简单的 exit code 合约是 Harness 的控制边界——Agent 无法绕过,因为 hooks 在 Claude Code 运行时内执行,不在 Agent 的 prompt 上下文里。
第 5 章 gstack 的强制门控就是对 PreToolUse 的直接应用:匹配 git commit → 执行审查完整性检查 → 审查未通过返回 exit 2 → commit 被阻止。gstack 的三十余个 Skill 之所以能形成一条不可跳过的流水线,PreToolUse 是底层执行者。
五种 hook 实现方式覆盖不同场景:
| 类型 | 机制 | 典型用途 |
|---|---|---|
| Command | 执行本地脚本,stdin 接收 JSON 上下文,stdout 返回决策 | 安全检查、门控脚本 |
| HTTP | POST JSON 到远程服务 | 调用企业安全网关、审批服务 |
| MCP Tool | 调用 MCP 服务器的工具 | 集成已有的安全工具链 |
| Prompt | 用快速模型做单轮判断 | "这个命令安全吗?回答 yes/no" |
| Agent | 启动子 Agent 做完整分析 | 复杂审查需要多步推理 |
对应 10.2 的工具系统组件。
10.3.3 安全护栏:什么绝对不能做
Claude Code 的安全护栏由三层构成。最外层是权限模型,中间层是 Bash 沙箱,最内层是 hooks 的策略拦截。
权限模型提供三种模式:无权限控制(个人实验)、defaultMode: "ask"(日常交互,敏感操作弹窗)、精细化 Allow/Deny(生产环境,白名单预定义)。规则语法 Tool(specifier),匹配顺序 deny → ask → allow,先命中生效:
"allow": ["Bash(git *)", "Bash(npm run *)"]
"deny": ["Bash(rm *)", "Bash(sudo *)", "Read(./.env)", "Read(./secrets/**)"]
Bash 沙箱(v2.1+,macOS/Linux/WSL2)提供操作系统级隔离。filesystem.denyWrite 禁止写入指定路径,network.allowedDomains 限制出站域名,network.allowLocalBinding 控制本地端口绑定。沙箱可配置"网络完全不可用"——Agent 只能读写本地代码,不能联网。failIfUnavailable: true 时沙箱启动失败直接退出 Claude Code。
hooks 的策略拦截在权限模型和沙箱之上提供更灵活的控制。PreToolUse 匹配具体命令,通过五种方式执行任意检查逻辑,返回 exit 2 即可阻止。PermissionRequest hook 可以在权限弹窗出现的瞬间自动决定 allow/deny——不需要人工点"允许",对无人值守的 Agent 服务至关重要。
对应 10.2 的安全护栏和安全机制两个组件。
10.3.4 上下文管理与 Prompt 构造:Agent 能看到什么
Claude Code 的上下文注入通过 SessionStart hook 实现。会话启动时,hook 自动读取 CLAUDE.md、.claude/rules/*.md、项目级 settings.json 中定义的 env 变量,注入到 Agent 的系统上下文中。不需要每次手动贴项目背景。InstructionsLoaded hook 在每次 CLAUDE.md 或 rules 文件被加载时触发,提供可观测性。
PreCompact hook 在上下文窗口即将满时触发。hook 可以阻止压缩(exit 2),或通过 additionalContext 在压缩前保存关键信息的摘要,确保压缩后 Agent 不丢失重要上下文。第 4 章 Ralph Loop 的长循环场景对此高度依赖——几十轮迭代后上下文窗口被代码变更和测试结果填满,没有 PreCompact 的保护,Agent 可能在压缩后忘记最初的任务目标。
对应 10.2 的上下文管理、Prompt 构造、记忆管理三个组件。
10.3.5 配置管理与状态管理:四层继承
Claude Code 的配置通过 settings.json 管理,四层继承(优先级从高到低):
Managed ← 企业 IT 强制推送(MDM plist/Windows 组策略),不可覆盖
User ← ~/.claude/settings.json,个人全局
Project ← .claude/settings.json,团队共享,提交到 Git
Local ← .claude/settings.local.json,本地覆盖,不提交
权限规则跨层合并——逐层收紧,上层的 deny 不可被下层 override。其他设置跨层覆盖——高层优先。团队在 Project 层定义安全基线、注册 hooks、配置沙箱规则。个人在 Local 层只能追加限制。IT 在 Managed 层推送 deny: ["Bash(rm *)", "Bash(sudo *)"]——所有项目、所有个人配置自动继承,无法绕过。
状态管理通过 YAML frontmatter 文件实现(如 Ralph Loop 的 .claude/ralph-loop.local.md 存储 iteration、max_iterations、completion_promise)和 hooks 的 additionalContext 机制——hook 可以将任意文本注入 Agent 的上下文,相当于"跨会话的状态便签"。
对应 10.2 的配置管理、状态管理两个组件。
10.3.6 子 Agent 编排与错误处理
SubagentStart 和 SubagentStop 事件让 hooks 能监控和控制子 Agent 的执行。SubagentStop 返回 exit 2 可以阻止子 Agent 退出——和 Stop hook 的核心逻辑一致。
错误处理通过 PostToolUseFailure 实现。工具调用失败后 hook 触发,可以注入上下文帮助 Agent 理解失败原因,或记录到审计日志。StopFailure 在 API 错误导致 turn 结束时触发——即使 Agent 什么都没做,Harness 也知道"这一轮失败了"。Notification hook 在 token 消耗等系统事件发生时发出告警。
对应 10.2 的子 Agent 编排、错误处理、输出解析三个组件。
10.3.7 自动审查场景完整运转
以上十二个组件合在一起,看一次 PR 自动审查 Agent 的完整运转:
Agent 启动 → SessionStart 加载项目 settings.json(权限白名单 + hooks 注册)→ 收到 PR 审查任务 → 调 gh pr diff(权限模型:gh 在白名单,放行)→ 调 Edit 修改代码建议(权限检查:目标路径在 src/** 内)→ 想执行 npm test(PreToolUse 判断安全,exit 0)→ 发现 SQL 注入漏洞,尝试修改 .env 文件修复(权限模型:*.env 在 deny 列表,拒绝)→ 转而生成修复建议 → 审查完成,准备 git commit → PreToolUse 匹配 git commit,跑审查完整性检查 → 所有维度都有结论 → exit 0,放行 → Agent 提交代码 → PostToolUse 记录审计日志。
十二个组件映射:编排循环(Stop/PostToolBatch)→ 工具系统(PreToolUse + PostToolUse)→ 安全护栏 + 安全机制(权限模型 + 沙箱)→ 错误处理(PostToolUseFailure)→ 上下文管理 + Prompt 构造 + 记忆管理(SessionStart + CLAUDE.md)→ 配置管理(settings.json 四层继承)→ 状态管理(YAML frontmatter)→ 子 Agent 编排(SubagentStart/Stop 可选)→ 验证循环(审查完整性检查脚本)。
一次运转,十二个组件同时工作。
10.4 Codex CLI:Rust 内核的安全优先设计
OpenAI 的 Codex CLI 在 Harness 设计上走了不同的路。96% Rust 代码——安全约束编译进二进制文件,而非通过外部脚本注入。
10.4.1 编排循环:/goal 的生命周期管理
Codex 的编排循环基于 Rust 运行时的自主循环控制——Agent 每轮完成工作后,由 Rust 侧判断是否继续。和 Claude Code 的 Stop Hook(运行时拦截 Agent 退出)不同,Codex 的编排逻辑在进程内闭环——不需要外部 hook 脚本判断"该不该停",Rust 代码自己决定。--max-turns 限制单次会话的工具调用轮次上限。Codex 也支持目标恢复:会话中断后从上次状态继续,而不是从头开始。
2026 年 5 月 Codex 新增的 /goal 命令在这个编排循环之上加了一层"声明式目标定义"——本文第 8 章讨论过三个平台的 /goal 对比。但在 Codex 的早期版本(2025 年 4 月中就已存在)中,代码编排循环早已通过 Rust 运行时实现,/goal 是对已有编排循环的接口层封装。
对应 10.2:编排循环(Rust 运行时循环,成熟但不可编程扩展)、验证循环(Rust 侧任务完成判断 + /goal checkbox 对照,无外部 hook)。
10.4.2 工具系统与安全护栏:三级沙箱 + hooks
Codex 的沙箱通过 --sandbox 参数控制,支持三级模式:
read-only(默认):Agent 可以读文件、搜索代码,不能写入或执行有副作用操作workspace-write:允许写入工作区danger-full-access:完全访问
从 --dangerously-bypass-approvals-and-sandbox 和 --dangerously-bypass-hook-trust 两个标志的存在可以看出,Codex 有原生 hooks 机制和审批流程——和 Claude Code 的 PreToolUse / PermissionRequest 是同类概念,但通过 config.toml(而非外部 JSON + bash 脚本)管理。沙箱和 hooks 都编译在 Rust 侧,不依赖外部脚本。
和 Claude Code 对比:Claude Code 的权限是用户可编程的外挂策略(通过 hooks + settings.json),灵活性高但配置复杂度也高。Codex 的权限是编译进二进制文件的默认行为,修改成本高但安全性不依赖用户正确配置。
对应 10.2:工具系统(编译时注册 + 原生 hooks,灵活性不如 Claude Code 的 PreToolUse + 五种 hook 类型)、安全护栏(三级沙箱 + hooks + 审批流程,默认只读,安全性最保守)、安全机制(Rust 编译时保障 + 原生 hooks)。
10.4.3 错误处理:Rust 的类型系统作为 Harness
Rust 编译而非解释执行——这是 Codex Harness 和 Claude Code Harness 最底层的差异。Claude Code 的 Harness 是 TypeScript/Node.js 运行时 + bash hook 脚本的组合——灵活性高,但错误可能发生在 hook 脚本的 shell 层(jq 解析 JSON 失败、脚本找不到、PATH 没配好)。Codex 的 Harness 在编译期就排除了大量错误类别——所有权系统防止内存问题、Result 类型强制错误处理、没有运行时动态加载脚本的机制意味着没有"hook 脚本挂了 Agent 继续跑"的风险。
这个差异直接对应 10.2 的错误处理组件:Claude Code 的 hook 脚本出错时 Harness 需要处理 hook 自身的故障(脚本 crash → 非阻塞 error → Agent 继续);Codex 没有这个层级——Rust 侧的工具调用失败直接通过 Result 传播,Agent 收到的失败信息是确定性的。
10.4.4 十二组件对照:Codex 缺了什么
对比 10.2 的十二个组件:
| 组件 | Claude Code | Codex CLI | 差异关键 |
|---|---|---|---|
| 编排循环 | hooks | Rust 循环 | 可编程 vs 编译时 |
| 工具系统 | PreToolUse | 编译时注册 | 有 hook vs 无 hook |
| 安全护栏 | 权限+沙箱+hooks | 三级沙箱 | 可编程安全 vs 默认安全 |
| 错误恢复 | PostToolUseFailure | Rust Result | 脚本层 vs 类型系统 |
| 上下文管理 | SessionStart | 文件自动发现 | hook 注入 vs 静态发现 |
| Prompt 构造 | 多层 CLAUDE.md | 系统 prompt 模板 | 可编程 vs 固定 |
| 输出解析 | model + hook 提示 | 模型原生 | 可注入提示 vs 依赖模型 |
| 状态管理 | YAML + hooks | 会话内存 | 文件持久化 vs 内存 |
| 可观测性 | 审计日志 | Built-in | PostToolUse vs 内建统计 |
| 人工检查点 | PermissionRequest | CLI+hooks | hook 自动化 vs CLI/hooks 混合 |
| 子 Agent 编排 | Subagent hooks | 外部脚本 | 原生支持 vs bash 包装 |
| 配置管理 | 四层继承 | CLI flags + 文件 | settings.json vs CLI |
Codex 缺失的项目集中在 hooks 可编程性和多 Agent 编排上。它没有 PreToolUse hook——不能像 gstack 一样在 commit 前插审查检查。没有子 Agent hooks——多 Agent 协作靠外部脚本驱动(类似 autoresearch 的 bash 包装),而非 Harness 层原生编排。Codex 的设计哲学是:默认安全压倒可编程扩展。Claude Code 的设计哲学是:可编程的 Harness 压倒默认安全。
10.5 其他 Agent 平台的 Harness
10.5.1 Pi Coding Agent:最小化终端编码 Harness
Pi(pi.dev)是一个 MIT 许可的终端编码 Agent。它对自己的定位是"minimal terminal coding harness"——核心刻意做小,通过 TypeScript 扩展系统增强。Pi 的包管理器 pi packages 可以分发和共享扩展、Skills、prompts 和主题。
工具系统与扩展模型。 Pi 没有 Claude Code 式的 PreToolUse hook。它的工具扩展点是 TypeScript 模块,开发者通过实现扩展接口注册新工具、slash 命令、事件处理器和自定义 TUI 组件。工具调用走 TypeScript 运行时,而非外部脚本拦截。
安全护栏。 Pi 的公开文档没有描述独立的权限模型或沙箱机制。安全依赖操作系统权限和容器隔离,而非 Agent 自身的 Harness 层。对比 Claude Code 的三层安全体系(权限模型 + Bash 沙箱 + hooks 策略拦截)和 Codex 的三级沙箱(read-only / workspace-write / danger-full-access),Pi 选择了零 Harness 安全层的路线。
会话管理与状态持久化。 Pi 用 JSONL 格式持久化会话文件,提供 SessionManager API 做会话管理、分支和树形导航。和 Claude Code 的 YAML frontmatter + hooks 状态管理相比更结构化(类型化 JSONL entries),但没有 hooks 的 additionalContext 跨会话注入机制。
配置模型。 全局配置 + 项目级配置——和 Claude Code 的四层继承(Managed/User/Project/Local)相比简化了两层,没有企业 IT 强制推送的 Managed 层。
上下文管理。 支持上下文压缩和分支摘要——和 Claude Code 的 PreCompact hook 同类,但 Pi 的压缩逻辑是内建的,不可通过 hooks 定制。
SDK 与可嵌入性。 Pi 的一个独特优势是 SDK——可以嵌入 Node.js 应用,支持 stdin/stdout JSONL 的 RPC 模式、JSON 事件流打印模式。Claude Code 有 Agent SDK 但面向多 Agent 编排;Pi 的 SDK 面向将 Agent 嵌入已有应用。
十二组件对照:
| 组件 | Pi 实现 | 对比 Claude Code |
|---|---|---|
| 编排循环 | 交互式主循环 | 无 Stop Hook 机制 |
| 工具系统 | TypeScript 扩展接口 | 无 PreToolUse 拦截 |
| 安全护栏 | 依赖 OS/容器 | 无自有权限模型或沙箱 |
| 记忆管理 | JSONL 会话文件 | 无 auto-memory 机制 |
| 上下文管理 | 内建压缩 + 分支摘要 | 无 PreCompact hook 定制 |
| Prompt 构造 | slash 命令展开 + 模板 | 无多层 CLAUDE.md |
| 输出解析 | JSONL 类型化条目 | 结构化但不通过 hooks |
| 状态管理 | SessionManager API | JSONL 类型化持久化 |
| 错误恢复 | TypeScript 异常处理 | 无 PostToolUseFailure hook |
| 人工检查点 | CLI 确认提示 | 无 PermissionRequest hook |
| 子 Agent 编排 | Skill 按需调用 | 无 SubagentStart/Stop hooks |
| 配置管理 | 全局 + 项目两层 | 无 Managed 强制层 |
Pi 代表了一条和 Claude Code 完全相反的设计路线:最小化核心,通过 TypeScript 扩展。对需要嵌入已有 Node.js 应用的场景、实验性项目和原型开发,Pi 的 SDK 和扩展模型有独特优势。对需要审计日志的合规场景、生产环境无人值守 Agent、多 Agent 安全编排——它的 Harness 厚度不够。
10.5.2 DeepSeek Harness 团队:追赶者的信号
2026 年 5 月 19 日,DeepSeek 资深研究员陈德里在小红书上发了一条招聘贴——内部正在组建全新的"Harness"(代码智能体工程)团队,目标直指 Claude Code。
DeepSeek 选择把 Harness 作为独立团队方向——和 Anthropic 的内部组织结构一致——这说明 Harness 已经从"Claude Code 的附属工程"变成了"Agent 产品的核心基础设施"。招聘要求中强调候选人需同时熟悉模型推理和系统工程。DeepSeek 目前的 Agent 产品形态尚未公开详细文档,Harness 设计方向有待观察。
10.5.3 十二组件对照总表
| 组件 | Claude Code | Codex CLI | Pi (pi.dev) | DeepSeek | Cursor/Copilot |
|---|---|---|---|---|---|
| 编排循环 | hooks | Rust 循环 | 交互式 | 待公开 | 编辑器事件 |
| 工具系统 | PreToolUse | 编译+hooks | TS 扩展 | 待公开 | 扩展 API |
| 安全护栏 | 三层安全 | 三级沙箱 | 依赖 OS | 待公开 | 默认扩展边界 |
| 错误恢复 | PostToolUseFailure | Rust Result | TS 异常 | 待公开 | 扩展崩溃恢复 |
| 上下文管理 | SessionStart | 文件发现 | 内建压缩 | 待公开 | 编辑器上下文 |
| Prompt 构造 | 多层 CLAUDE.md | 模板 | slash+模板 | 待公开 | 扩展注入 |
| 输出解析 | model+hook | 模型原生 | JSONL 类型化 | 待公开 | 扩展处理 |
| 状态管理 | YAML+hooks | 会话内存 | SessionManager | 待公开 | 工作区状态 |
| 可观测性 | 审计日志 | Built-in | 日志 | 待公开 | 扩展诊断 |
| 人工检查点 | PermissionRequest | CLI+hooks | CLI 确认 | 待公开 | 编辑器对话框 |
| 子 Agent 编排 | Subagent hooks | 外部脚本 | Skill 调用 | 待公开 | — |
| 配置管理 | 四层继承 | CLI+toml | 全局+项目 | 待公开 | 扩展设置 JSON |
10.6 五个通用模式
前三节逐平台拆解了 Claude Code、Codex、Pi 的 Harness 设计。虽然它们的实现路线各不相同——外挂 hooks vs 编译时约束 vs TypeScript 扩展——但反复出现的几个设计模式越来越清晰。本节把这些跨平台的共性抽象出来。
阶段门模式。 在关键操作前设置检查点。Claude Code 通过 PreToolUse hook 实现——gstack 的 git commit 门控、Ralph Loop 的 Stop 门控都是实例。Codex 通过原生 hooks + Rust 侧循环检测实现类似效果。Pi 没有原生阶段门机制。
审计日志模式。 所有工具调用记录:时间、操作、输入、输出。Claude Code 的 PostToolUse hook 自动生成完整审计轨迹。Codex 的 Built-in 统计功能提供简化版。企业合规场景里,审计日志就是"Agent 做了什么"的唯一可追溯证据。
上下文注入模式。 Agent 启动时自动加载项目背景。Claude Code 的 SessionStart hook + CLAUDE.md。Codex 的系统 prompt 拼接 + 文件自动发现。第 2 章的 CONTEXT.md 和第 8 章的 program.md 都依赖这个机制。
熔断器模式。 连续失败 N 次 → 强制退出。第 4 章 Ralph Loop 的 max-iterations。第 7 章 autoresearch 的 MAX_CONSECUTIVE_FAILURES。熔断器不依赖 Agent 自我判断——系统层面计数器到了就停。
生成器-评估器模式。 一个 Agent 生成,另一个独立评估。第 7 章 autoresearch 的三个 Agent 交叉审查是这个模式的工程实现。Claude Code 的子 Agent 机制通过 SubagentStart/Stop hooks 原生支持。Pi 目前不支持——单 Agent 架构。
10.7 可观测性:AgentOps 与 DevOps 的共生
生产环境跑 Agent,传统 DevOps 监控(CPU、内存、网络)不够。Agent 可能在十分钟内静默消耗几十万 token,调了 47 次 Bash 执行同一探测命令。服务没挂,API 账单在安静膨胀。
AgentOps 补齐的维度:工具调用频率(每分钟多少次、什么类型)、token 消耗曲线(突发还是平稳)、Skill 激活链路(哪个 Skill 被触发、触发了什么子操作)、Hook 执行耗时(PreToolUse 脚本跑了多久、有没有超时)、错误率与重试(哪些调用频繁失败、Agent 怎么应对)、会话时长与完成率(正常完成 vs 死循环)。
两套监控并排——DevOps 告诉系统状态,AgentOps 告诉 Agent 行为——才能在"服务没挂但 budget 炸了"时定位根因。Claude Code 通过 PostToolUse hook 和 Notification hook 已有较完整的 AgentOps 基础。其余平台目前主要靠外部工具(LangSmith、Weave 等)补充 AgentOps 能力。
10.8 如何应用:构建 Agent 时的 Harness 决策清单
写第一行 Agent 代码之前,把 10.2 的十二个维度逐个过一遍。
编排循环——Agent 跑起来之后谁告诉它停?只用 max-turns 计数器吗,还是你也要像 Ralph Loop 一样加验证循环?
工具系统——Agent 能调哪些工具?有没有 PreToolUse 式的拦截点,还是硬编码在代码里?
安全护栏——Agent 的默认权限有多宽?至少需要一个工具白名单,别从"什么都能做"开始。
记忆管理——跨会话的状态存在哪?会话崩溃后 Agent 从零开始,还是从上个 checkpoint 继续?
上下文管理——Agent 能看到什么?会话启动时自动注入项目背景,还是每次手动贴?
Prompt 构造——系统指令怎么维护?写在代码里的魔字符串、还是像 CLAUDE.md 一样可独立编辑的配置文件?
输出解析——模型返回的东西怎么变成行动?JSON 解析崩了怎么办,有没有 fallback?
状态管理——迭代轮次、任务完成状态、子任务进度存在哪?内存里的字典够不够,还是需要文件持久化?
错误处理——工具调用失败了 Agent 怎么应对?重试几次?连续失败 N 次后熔断?
验证循环——Agent 怎么判断"做完了"?靠自己的判断,还是外部验证脚本?验收标准可自动化测试吗?
子 Agent 编排——需要多个 Agent 协作吗?如果需要,它们之间的权限怎么隔离?
安全机制——有没有沙箱?文件系统写入受控吗?网络出站有限制吗?
这十二个维度不需要都有满分答案。但每个维度你选择"不做"时,应该知道自己承担了什么风险。十二个组件不是目标,是决策清单。理解每个选择的安全后果之后,再决定什么时候加、什么时候先跳过。
10.8.1 第一步:选编排模型
Agent 跑起来之后,谁告诉它什么时候停?
- 简单任务用计数器。 最大轮次上限是最原始的熔断器,但也是最可靠的。Ralph Loop 的 max-iterations 之所以能跑通,不是因为上限值设得准,是因为它不需要准——只是防止 Agent 永远跑下去。任何 Agent 都应该有一个 max-turns。
- 复杂任务用验证循环。 如果你的 Agent 需要自主判断"做完了",第一时间不要信任 Agent 的自我判断。Ralph Loop 的 completion promise 模式——"Agent 声明完成 → Harness 拦截 → 验证 → 通过才放行"——比"Agent 说做完了就退出"要安全得多。Claude Code 的 Stop Hook 是现成方案。Codex 在
/goal中也有类似的 checkbox 对照。如果你在裸写 Agent 循环,至少加一层 post-completion 验证:Agent 声称完成之后,由另一个 Agent 或确定性脚本确认验收标准确实满足。 - 长任务用中断恢复。 持续数小时的自主任务随时可能被 API 错误或网络抖动打断。autoresearch 的
-c标志(从上次迭代继续)、Codex 的resume命令——状态持久化不是加分项,是必备项。每次迭代结束时保存状态(完成了哪些子任务、还剩哪些、当前迭代轮次),崩溃后可以从断点恢复而非从头开始。
10.8.2 第二步:定安全基线和人机检查点
Harness 安全有两条原则:默认最小权限,关键节点必须有人确认。
默认最小权限。 给 Agent 它完成任务所需的最少权限,而不是"所有它能做的事"。Codex 的三级沙箱(read-only / workspace-write / danger-full-access)是这个理念的极端体现——默认只读,需要写入时显式升级。Claude Code 的 Allow/Deny 列表是这个理念的可编程版——allow: ["Bash(git *)", "Bash(npm run *)"],其余拒绝或弹窗确认。如果你在裸写 Agent,至少实现一个工具白名单——Agent 能调哪些工具、每个工具能接受什么参数范围。别从"全部允许"开始,从"只允许你确认过的几个"开始。
关键节点设人工检查点。 全自动不等于没人看。gstack 的强制门控给出的启发是:不是所有步骤都需要人确认,但以下节点几乎总是需要:
- 执行破坏性操作前(
rm、drop table、DELETE FROM) - 向外部服务推送前(
git push、API deploy、数据库写) - 修改安全敏感文件前(
.env、密钥文件、权限配置)
Claude Code 的 PreToolUse + PermissionRequest hook 可以自动化这些检查点。Codex 的 --dangerously-bypass-approvals-and-sandbox 的存在本身就说明:正常情况下,这些操作应该有审批。如果你的 Agent 没有 hook 机制,至少在代码里硬编码这些检查点。
10.8.3 第三步:决定可观测性策略
Agent 出问题了,你怎么知道?怎么回溯?
至少记录全部工具调用。 Claude Code 的 PostToolUse hook 自动生成审计日志——时间、工具名、输入、输出。即使你没有 hook 系统,也应在每个工具调用之前和之后打印日志行:[timestamp] AGENT_TOOL: Bash("npm test") → exit 0。几行日志比事后靠回忆排查强。
Token 消耗单独监控。 Agent 最常见的问题不是崩溃,而是静默烧预算。一个陷入低效循环的 Agent 可能在几小时内消耗百万 token 而不触发任何错误。把每次 LLM 调用的 token 数记录到单独的指标里,设置预算告警。这不是事后优化——第一天就该有。
正常行为建立基线。 生产环境跑了两周的审查 Agent,平均每次审查用 12 轮工具调用、每轮约 3000 token。某天突然变成 47 轮——不是功能坏了,是 Agent 在处理一个新类型的问题时反复探测。有了基线,你才能区分"正常波动"和"需要看的异常"。没有基线,一切都是事后猜测。
10.8.4 第四步:选择平台
不是所有 Agent 都需要从零搭 Harness。现有平台已经覆盖了不同场景的 80% 需求:
- 企业合规 → Claude Code。 审计日志(PostToolUse)+ 企业 IT 强制推送(Managed settings)+ 四层配置继承 + Bash 沙箱。需要合规留痕的 Agent 产品直接跑在 Claude Code 上,不用重造权限系统。
- 安全敏感 → Codex。 默认只读沙箱 + Rust 编译时安全约束。Agent 面向外部用户、可能处理不受信的输入时,Codex 的三级沙箱比 prompt 里写"请勿"管用。
- 可嵌入 SDK → Pi。 需要把 Agent 嵌进已有 Node.js 应用——Pi 的 SDK + JSONL 会话持久化 + TypeScript 扩展系统是天然适配。
- 快速原型 → 最小化 Harness。 验证一个 Agent 想法时,不要先搭十二个组件。先用 Claude Code 或 Codex 的现有 Harness 跑通流程。确认 Agent 有价值之后,再考虑是否需要更完整的 Harness。
一个常见的误区:上来就搭完整的 Harness,Agent 本身还没验证清楚。十二个组件是目标,不是起点。先跑通 Agent 的核心循环(编排 + 工具系统),再逐层加安全护栏和可观测性。
10.9 与前后章节的关系
与第 2 章 Skills:Skill 是 Harness 之上的一层抽象——加载、匹配、权限继承都依赖 Harness 的运行时。Pocock 的"一个 Markdown 文件定义一种行为"能被 Claude Code 执行,是 Harness 在背后做 Skill 发现和加载。
与第 4 章 Ralph Loop:自主循环是 Harness 验证循环组件的最佳范例——Stop Hook + PreToolUse hook + completion promise 匹配 = 一个完整的控制结构。
与第 5 章 gstack:强制性阶段门控直接运行在 PreToolUse hooks 之上。没 hooks,gstack 的流程门控从"系统强制"降级为"引导指令"。gstack 和 Harness 是应用和 OS 的关系。
与第 7 章 autoresearch:多 Agent 轮转是生成器-评估器模式 + 多 Agent 编排的工程实现。外部 bash 脚本调多个 Agent 交叉审查——Harness 的 hook 机制让这可以安全地进行。
10.10 本章小结
Harness Engineering 不是通用方法论——写 Agent 产品时才需要它。但它贯穿全书——第 4 章的熔断器、第 5 章的强制门控、第 7 章的多 Agent 交叉审查——全部依赖 Harness 提供的底层机制。
不同 Agent 平台选择了不同的 Harness 设计。Claude Code 选可编程性——31 个生命周期事件、5 种 hook 类型、4 层配置继承、bash 沙箱,通过外挂 hooks 和 JSON 配置实现最大的灵活性。Codex 选安全优先——Rust 编译时安全约束、三级沙箱(read-only / workspace-write / danger-full-access)、原生 hooks。Pi 选最小化可嵌入路线——TypeScript 扩展系统、JSONL 会话持久化、SDK 支持嵌入 Node.js 应用。DeepSeek 正在组建专门的 Harness 团队,方向有待公开。
十二个组件对照每个平台的特点不同。编排循环靠 hooks vs 原生循环控制。工具系统靠运行时拦截 vs 编译时注册。安全护栏靠三层组合 vs 默认只读。理解每个平台的设计选择,就能预判它适合什么场景——企业合规选 Claude Code,安全敏感环境选 Codex,快速原型选 Pi。
Harness 的出发点是踩过很多坑后的一条认知:模型不可预测,所以系统必须可预测。靠 prompt 里的"请勿"不顶用——靠 hooks 的 exit 2 blocking error。把不可预测的组件嵌在一个可预测的框架里——这是 Harness Engineering 的全部。
留言板
欢迎在此分享你的想法!评论通过 GitHub Issues 存储,需要 GitHub 账号登录。