第十六章 综合案例:从零构建一个完整的 Harness 系统

核心命题:理论最终要落地。本章通过三个完整的端到端案例,展示如何将前面所有章节的知识整合到实际的 Harness 系统设计中。每个案例从需求出发,经过问题空间建模、Q/T/C 工作点选择、五个构件的逐一设计,到最终的系统集成——使读者看到完整的工程推理过程,而非孤立的构件配置。

阅读建议:三个案例的侧重点不同。案例一(代码生成)侧重 Hook Pipeline 的组合设计,是五个构件协作的标准范例;案例二(数据分析)侧重目标模糊性下的评估设计与人类介入点;案例三(客服邮件处理)侧重权限治理、不可逆操作防护与 Multi-Agent 协调。建议按顺序阅读,但如果只关心特定场景,可以直接跳转。


16.1 案例一:代码生成与审查 Agent

场景描述:一个接受自然语言需求描述,自主生成实现代码、运行测试、修复错误、最终提交 Pull Request 的完整 Agent 系统。该 Agent 运行在一个中等规模的 Python 代码库(约 50K 行代码,200+ 个模块)上,需要在现有代码风格和架构约定下工作。

问题空间建模

首先,按第四章的框架定义问题空间 P=S,O,s0,GP = \langle \mathcal{S}, \mathcal{O}, s_0, \mathcal{G} \rangle

  • 状态空间 S\mathcal{S}:{当前代码库快照(受影响文件内容), 测试结果集合, 已修改文件列表, Git 状态, 当前错误信息}。注意不包含全量代码库——只保留与当前任务相关的模块,这是状态表示的关键设计决策(§4.2)。
  • 操作集 O\mathcal{O}:{读取文件, 搜索代码, 编辑文件(apply_patch), 运行测试, 运行 linter, 创建分支, 提交变更, 创建 PR}。注意不包含 raw shell exec 或任意文件删除——操作集的边界是精心设计的(§4.4)。
  • 初始状态 s0s_0:{需求描述, 当前分支状态, 相关模块的文件内容, 现有测试结果, 代码风格指南}。Context Engineering 的任务是确保这些信息的新鲜度(§8.3)。
  • 目标状态集 G\mathcal{G}:{所有现有测试通过, 新增测试覆盖需求功能, linter 无新增警告, PR 描述符合模板}——大部分是可形式化验证的目标(§4.5),这是 Hook 自动化的基础。

Q/T/C 工作点选择

本场景的帕累托工作点偏向质量优先,时间次之,成本可控

  • Q 约束:测试通过率 100%(硬约束),linter 零新增警告(硬约束),代码风格一致性(软约束,由审查环节捕获)
  • T 约束:单个需求从接受到 PR 创建 < 30 分钟(软约束,超时触发人工介入)
  • C 约束:单任务 token 消耗 < 500K(软约束,超过 350K 触发成本预警)

System Prompt 设计

# 角色定义
你是一个高级 Python 工程师,专注于在现有代码库中实现新功能和修复缺陷。

# 能力边界
你可以:读取项目文件、搜索代码、编辑文件、运行测试和 linter
你不可以:删除文件、修改 CI/CD 配置、直接推送到 main 分支、安装新依赖

# 工作原则
1. 先理解现有代码结构,再动手修改——读取相关模块和测试,理解接口契约
2. 每次修改后立即运行相关测试,不要积累多个未验证的修改
3. 保持现有代码风格,不引入新的编码约定
4. 修改范围最小化——只改需要改的,不做"顺手"重构

# 输出规范
- 代码修改使用 apply_patch 格式
- PR 描述必须包含:变更摘要、测试计划、影响范围
- 遇到不确定的设计决策时,在 PR 描述中标注为待讨论项

合约条件验证(§7.2):

  • 可观察性:每条规则都有对应的验证机制——“不删除文件”由 Pre-Hook 权限检查验证,“运行测试”由 Post-Hook 验证执行记录,“保持代码风格”由 linter Post-Hook 验证
  • 激励相容性:“先理解再修改”与任务目标方向一致——不理解就修改会导致测试失败、增加迭代轮数

Context 策略

按第八章的分层设计:

层 0:系统指令(~1.5K token,不淘汰)
层 1:需求描述 + 代码风格指南(~3K token,任务内不淘汰)
层 2:当前正在编辑的文件内容 + 最近测试结果(~10K token,每步更新)
层 3:相关模块的接口签名(~4K token,按相关性淘汰)
层 4:历史修改摘要(~6K token,Compaction 时优先压缩)

Compaction 触发点:token 使用率 > 70%。Compaction 策略:保留层 0-1 完整内容,层 2 保留当前文件和最近一次测试结果,层 3 压缩为接口签名列表,层 4 压缩为“已完成 N 个步骤,关键决策:…”的摘要。

Plan Schema

{
  "goal": "实现用户需求:[需求描述摘要]",
  "steps": [
    {
      "id": 1,
      "action": "读取相关模块,理解现有接口和数据流",
      "expected_output": "识别需要修改的文件列表和修改策略",
      "fallback": "扩大搜索范围,读取上层模块",
      "estimated_cost": { "tokens": 30000, "time_sec": 60 }
    },
    {
      "id": 2,
      "action": "实现核心功能代码",
      "expected_output": "功能代码编写完成",
      "fallback": "如果涉及未预期的依赖,回到步骤 1 重新评估",
      "estimated_cost": { "tokens": 80000, "time_sec": 120 }
    },
    {
      "id": 3,
      "action": "编写测试用例",
      "expected_output": "覆盖正常路径和边界情况的测试",
      "fallback": "参考现有测试风格补充",
      "estimated_cost": { "tokens": 50000, "time_sec": 90 }
    },
    {
      "id": 4,
      "action": "运行测试套件,修复失败",
      "expected_output": "所有测试通过",
      "fallback": "如果修复超过 3 轮,重新评估实现方案",
      "estimated_cost": { "tokens": 100000, "time_sec": 180 }
    },
    {
      "id": 5,
      "action": "运行 linter,修复风格问题",
      "expected_output": "linter 零新增警告",
      "fallback": null,
      "estimated_cost": { "tokens": 20000, "time_sec": 30 }
    },
    {
      "id": 6,
      "action": "创建 PR,填写描述",
      "expected_output": "PR 创建成功,描述符合模板",
      "fallback": null,
      "estimated_cost": { "tokens": 15000, "time_sec": 20 }
    }
  ],
  "success_criteria": "所有测试通过 + linter 无新增警告 + PR 已创建",
  "abort_conditions": [
    "连续 5 轮测试修复无进展",
    "token 消耗超过 500K",
    "发现需求本身存在歧义需要人类澄清"
  ]
}

Tool 权限设计

工具风险级别策略
读取文件 / 搜索代码只读自动允许
运行测试 / 运行 linter只读(不改变状态)自动允许
编辑文件(apply_patch)非幂等写自动允许(由 Post-Hook 验证)
创建分支幂等写自动允许
提交变更非幂等写需要 Pre-Hook 确认(提交前检查)
创建 PR不可逆写(外部可见)Plan 步骤确认

注意:git push --force、文件删除、依赖安装不在操作集中——这不是遗漏,而是操作集边界设计(§10.1)。

Hook Pipeline 设计

这是本案例的核心设计——三层 Hook 的组合,是代码生成 Agent 可靠性的关键保障:

PreToolUse:
  1. 权限检查(计算型,< 1ms)
     → 阻断:文件删除、shell exec、CI 修改
  2. 修改范围检查(规则型,< 5ms)
     → 警告:修改超出 Plan 中声明的文件列表
  3. 提交前检查(计算型,< 10ms)
     → 阻断:提交包含未运行测试的文件修改

PostToolUse:
  1. 编辑格式验证(计算型,< 5ms)
     → 验证 apply_patch 语法正确性
  2. 测试结果解析(计算型,< 10ms)
     → 提取失败测试名称和错误信息,结构化回写 Context
  3. Linter 结果解析(计算型,< 5ms)
     → 提取新增警告,归类为风格/逻辑/安全三类
  4. 发散检测(规则型,< 10ms)
     → 检测:同一测试连续 3 次失败且错误信息相似
     → 触发:强制 Plan 重新评估,而非继续盲目修复

Stop:
  1. 完整测试套件运行(计算型,耗时视项目而定)
     → 硬约束:任何测试失败则不允许创建 PR
  2. Linter 全量检查(计算型)
     → 硬约束:新增警告数 > 0 则不允许创建 PR
  3. PR 描述质量评估(语义型,可选)
     → 软约束:评估描述是否清晰、完整、覆盖变更要点

关键设计决策记录

决策一:为什么 apply_patch 不需要人工确认? 因为文件编辑是可逆的(git 可以回退),且 Post-Hook 的测试和 linter 提供了即时的质量反馈。期望损失公式(§10.1):E[损失]=P(误编辑)×低不可逆性×有限影响范围E[\text{损失}] = P(\text{误编辑}) \times \text{低不可逆性} \times \text{有限影响范围},乘积足够低。

决策二:为什么发散检测设在 PostToolUse 而非 Stop? 因为发散的代价随时间累积——在第 3 轮测试修复就检测到发散,比在 Stop 阶段发现已经消耗了 200K token 后再处理,节省的成本是数量级的差异。这是“在靠近问题发生的地方处理问题”原则(§11)的直接体现。

决策三:PR 创建为什么设为“Plan 步骤确认”而非“人工确认”? 因为 PR 创建虽然外部可见,但还不是合入代码——它的不可逆性低于直接推送。Plan 步骤确认意味着 Agent 在创建 PR 前会在 Plan 中声明这个意图,如果人类配置了 Plan 审批(§15.3),这个节点自然被覆盖。

系统架构图

需求输入 → [System Prompt + Context 构建]

           [Plan 生成] ←── 人类审批(可选)

           ┌─ 执行循环 ─────────────────────────┐
           │  选择下一步 → PreHook 权限检查       │
           │       ↓                           │
           │  执行工具 → PostHook 测试/linter    │
           │       ↓                           │
           │  Context 更新 → 发散检测            │
           │       ↓                           │
           │  Plan 更新(成功→下一步 / 失败→修正) │
           └───────────────────────────────────┘

           [Stop Hook: 全量测试 + linter]

           [创建 PR] → 完成

16.2 案例二:数据分析与报告生成 Agent

场景描述:一个接受数据集(CSV/Parquet)和分析目标(自然语言描述),自主探索数据特征、选择分析方法、执行分析、生成包含可视化的分析报告的 Agent。关键差异在于:分析报告没有“测试通过”这样的客观正确性标准,输出质量的度量本身就是核心工程挑战。

与案例一的结构性差异

维度代码生成(案例一)数据分析(案例二)
目标定义大部分可形式化(测试通过)大部分不可形式化(“分析有洞察力”)
质量验证计算型 Hook 为主语义型评估为主 + 人类判断
搜索结构收敛型(测试引导方向)发散-收敛交替(先探索再聚焦)
人类介入模式关键节点确认阶段性方向引导

问题空间建模

  • 状态空间 S\mathcal{S}:{数据集元信息(列名、类型、缺失率、基本统计量), 已执行的分析步骤及结果, 当前分析假设列表, 已生成的可视化, 报告草稿}
  • 操作集 O\mathcal{O}:{加载数据, 数据清洗, 统计描述, 相关性分析, 假设检验, 回归建模, 生成可视化, 编写报告段落, 修改报告}。注意:不包含“修改原始数据”——分析 Agent 只能读取和转换数据,不能改写数据源。
  • 初始状态 s0s_0:{数据集路径, 分析目标描述, 数据字典(如有), 领域背景(如有)}
  • 目标状态集 G\mathcal{G}:这是核心难题——“好的分析报告”无法被形式化。按 §4.5 的目标模糊性对策,分解为:
    • 必要子条件(硬):数据加载无错误、统计方法使用正确(如不对非正态数据使用 t 检验)、可视化能正确渲染、报告结构完整
    • 偏好条件(软):分析有深度而非停留在描述性统计、可视化选择恰当、结论有数据支撑、语言清晰简洁

Q/T/C 工作点选择

本场景的帕累托工作点偏向质量优先,成本受控,时间宽松

  • Q 约束:统计方法正确性(硬约束),报告结构完整性(硬约束),分析深度(软约束,由人类评估)
  • T 约束:< 60 分钟(软约束,探索性任务允许较长时间)
  • C 约束:< 800K token(软约束,数据探索阶段 token 消耗较高是正常的)

System Prompt 设计

# 角色定义
你是一个资深数据分析师,擅长从数据中发现有价值的模式并用清晰的语言表达洞察。

# 能力边界
你可以:加载和转换数据、执行统计分析、生成可视化、编写报告
你不可以:修改原始数据文件、访问外部网络、安装新的分析库

# 工作原则
1. 先理解数据结构和分布,再选择分析方法——不要跳过探索性数据分析
2. 统计方法的选择必须与数据特征匹配(正态性、样本量、变量类型)
3. 每个结论必须有明确的数据支撑——不做无依据的推断
4. 可视化服务于洞察传达,不是装饰——选择最能传达关键信息的图表类型

# 输出规范
- 报告结构:执行摘要 → 数据概览 → 核心发现 → 方法论说明 → 局限性
- 每个核心发现配一张可视化
- 使用的统计方法须注明前提假设和检验结果

Plan 设计:两阶段结构

数据分析的 Plan 与代码生成不同——它需要一个探索阶段(发散)和一个分析阶段(收敛),两阶段之间有一个关键的人类介入点:

{
  "goal": "分析 [数据集] 以回答 [分析目标]",
  "phase_1_exploration": {
    "steps": [
      { "id": 1, "action": "加载数据,生成数据概览(维度、类型、缺失率、基本统计量)" },
      { "id": 2, "action": "探索性数据分析:分布、相关性、异常值检测" },
      { "id": 3, "action": "基于探索结果,提出 3-5 个分析假设" }
    ],
    "checkpoint": "将假设列表提交人类审核——方向确认后进入阶段二"
  },
  "phase_2_analysis": {
    "steps": [
      { "id": 4, "action": "对每个确认的假设执行统计分析" },
      { "id": 5, "action": "生成支撑性可视化" },
      { "id": 6, "action": "编写报告草稿" },
      { "id": 7, "action": "自检:结论是否有数据支撑,方法是否适当" }
    ]
  },
  "success_criteria": "报告结构完整 + 统计方法正确 + 人类确认分析方向",
  "abort_conditions": [
    "数据质量过低(缺失率 > 50% 或关键字段类型不匹配)",
    "token 消耗超过 800K",
    "人类否决所有分析假设"
  ]
}

阶段间的人类介入点设计

步骤 3 到步骤 4 之间的 checkpoint 是本案例最关键的设计决策。为什么在此处设置人类介入?

  • 杠杆原理(§15.3):分析方向一旦确定,后续所有步骤都是在这个方向上展开。方向性错误的纠正成本随执行深度指数增长——在假设审核阶段花 5 分钟审视,可能避免后续 40 分钟的无效分析。
  • 不可外包性(§15.4):“这些假设是否指向了客户真正关心的问题”是一个价值判断,不是 Agent 能自行裁决的。

Hook Pipeline 设计

PreToolUse:
  1. 数据操作权限检查(计算型)
     → 阻断:任何试图写入原始数据文件的操作
  2. 统计方法前提检查(规则型)
     → 警告:对非正态分布数据使用参数检验
     → 警告:样本量 < 30 时使用大样本方法

PostToolUse:
  1. 代码执行错误捕获(计算型)
     → 提取错误信息,结构化回写 Context
  2. 可视化渲染验证(计算型)
     → 验证图表是否成功生成、是否包含轴标签和标题
  3. 统计结果合理性检查(规则型)
     → 警告:p 值恰好为 0.000(可能是计算错误)
     → 警告:R² > 0.99(可能过拟合或数据泄露)

Stop:
  1. 报告结构完整性检查(计算型)
     → 硬约束:缺少必要章节(执行摘要、方法论说明、局限性)
  2. 结论-数据支撑性评估(语义型)
     → 软约束:每个结论是否有对应的统计结果或可视化支撑
  3. 整体质量评估(语义型,LLM-as-judge)
     → 评估维度:洞察深度、逻辑连贯性、表述清晰度
     → 评分 < 阈值时触发修改建议,而非直接拒绝

LLM-as-judge 在本场景的应用与局限

LLM-as-judge 在 Stop Hook 的语义评估中扮演关键角色,但必须认识到其局限性(§12.1 的 Wald 框架):

已知偏差

  • 长度偏差:倾向于给较长的报告更高评分——对策:在 judge prompt 中明确“简洁性是质量维度之一”
  • 表面流畅偏差:语法通顺但逻辑谬误的结论可能获得高分——对策:要求 judge 先识别每个结论的数据依据,再评估整体质量
  • 自我一致偏差:judge 可能偏好与自身生成风格相似的输出——对策:使用与生成 Agent 不同温度参数或不同 System Prompt 的 judge

人类审核的必要性:即便 LLM-as-judge 评分达标,最终报告仍应经过人类审核——因为“分析是否回答了正确的问题”是 judge 无法替代人类判断的维度。judge 的价值在于过滤明显的低质量输出,减少人类需要审核的数量,而非替代人类审核本身。


16.3 案例三:客服邮件处理 Multi-Agent 系统

场景描述:回顾导言中的失败案例——一个无 Harness 的 Agent 处理客服邮件,三天内:回复了不该回复的邮件、删除了未读邮件、在无限循环中消耗了 $2000 的 API 成本。现在用完整的 Multi-Agent Harness 重新设计这个系统。

失败案例的三流归因回顾(§2.5 章末案例):

失败根因流缺失的构件
回复不该回复的邮件数据流无明确的“回复/不回复”判断标准;无 Pre-Hook 拦截
删除未读邮件控制流Tool 权限未分级;“标记已读”与“删除”的不可逆性差异未被区分
成本爆炸($2000)反馈流无成本熔断 Hook;无限循环未被检测;无人类升级机制

Multi-Agent 架构选择

本场景选择 Orchestrator-Worker 模式(§14.5),原因:

  • 邮件处理可以被清晰分解为并行子任务(每封邮件独立处理)
  • 子任务之间无强状态依赖(不同邮件的处理互不影响)
  • 需要一个中心协调者来管理优先级队列和全局成本预算

Agent 角色分工

Agent职责操作集风险等级
Orchestrator邮件分类、优先级排序、任务分配、全局成本监控读取邮件列表、分配任务、监控状态只读
Classifier Agent判断邮件类型和处理策略读取邮件内容、输出分类结果只读
Draft Agent生成回复草稿读取邮件内容和客户历史、生成回复文本只读(生成但不发送)
Action Agent执行经批准的操作发送回复、标记状态、归档不可逆写

关键设计原则:读写分离

这是从失败案例中得到的最重要的架构教训:将“判断”和“执行”分配给不同的 Agent。Draft Agent 只能生成回复草稿,不能发送;Action Agent 只能执行经过审批的操作。这在操作集层面实现了“不可逆操作必须经过独立确认”的原则(§10.2)。

System Prompt 设计(Orchestrator)

# 角色定义
你是客服邮件处理系统的协调者,负责邮件分类、优先级管理和任务分配。

# 能力边界
你可以:读取邮件列表和元信息、分配处理任务、监控系统状态
你不可以:直接读取邮件正文内容、直接生成回复、执行任何写操作

# 工作原则
1. 按紧急程度排序:投诉 > 退款请求 > 咨询 > 通知
2. 可疑邮件(外部链接、异常发件人、非标准格式)标记为"需人工审核",不分配自动处理
3. 监控全局 token 消耗,超过预算 60% 时暂停新任务分配
4. 任何 Agent 报告不确定性 > 阈值时,升级至人工处理

# 分类规则
- 自动回复:标准咨询(FAQ 命中)、订单状态查询、自动确认类
- 需要草稿审核:退款请求、投诉、涉及金额变更的请求
- 直接转人工:法律相关、媒体相关、VIP 客户、Orchestrator 无法分类的

Tool 权限治理

这是本案例的核心设计——权限的精细分级是防止导言失败案例重演的关键:

操作不可逆性影响范围默认策略
读取邮件列表/元信息自动允许
读取邮件正文低(隐私相关)自动允许,记录访问日志
生成回复草稿无(不发送)自动允许
标记邮件为“已读”自动允许
归档邮件中(可恢复)需要 Classifier 确认
发送回复高(不可撤回)高(客户可见)需要人工确认 / 规则审批
删除邮件极高(不可恢复)禁止——不在操作集中

核心设计决策:“删除邮件”操作根本不在任何 Agent 的操作集中——这是从失败案例直接学到的。不是通过 Hook 拦截删除(事后约束),而是从操作集层面消除删除能力(事前约束)。按 §2.5 的设计原则:硬约束前置,软约束兜底

发送回复的审批流程

发送回复是最关键的不可逆操作,设计三级审批机制:

Level 1(自动审批):
  条件:FAQ 命中率 > 95% + 回复模板匹配 + 无金额涉及
  流程:Draft Agent 生成 → 模板匹配验证 → 自动发送
  适用:标准咨询回复(约占 60% 的邮件量)

Level 2(规则审批):
  条件:非模板回复 + 无金额涉及 + 客户情绪中性
  流程:Draft Agent 生成 → 语义型 Hook 评估回复质量 → 
        规则检查(无承诺、无敏感词、语气适当)→ 自动发送
  适用:一般性回复(约占 25%)

Level 3(人工审批):
  条件:涉及金额 / 涉及投诉 / 客户情绪负面 / Agent 不确定性高
  流程:Draft Agent 生成 → 人工审核界面展示(邮件原文 + 
        回复草稿 + Agent 的判断依据 + 建议操作)→ 人工确认/修改/拒绝
  适用:高风险回复(约占 15%)

Hook Pipeline 设计

PreToolUse(所有 Agent 共享):
  1. 全局权限检查(计算型,< 1ms)
     → 阻断:任何 Agent 试图执行超出其操作集的操作
  2. 成本熔断检查(计算型,< 1ms)
     → 阻断:全局 token 消耗超过硬预算上限
  3. 速率限制(计算型,< 1ms)
     → 阻断:单封邮件处理超过 5 次工具调用(防止循环)

PreToolUse(Action Agent 专有):
  4. 发送前审批级别判断(规则型,< 10ms)
     → 根据分类结果和回复内容,判断应走哪级审批
  5. 回复内容安全检查(规则型 + 语义型)
     → 阻断:包含不当承诺("我们保证...")、敏感信息泄露、语气不当

PostToolUse:
  1. 发送结果确认(计算型)
     → 记录发送状态,更新邮件处理状态
  2. 客户情绪变化检测(语义型,采样执行)
     → 对比回复前后的客户情绪指标,检测是否引发负面反应

Stop(任务批次级别):
  1. 批次统计汇总(计算型)
     → 输出:处理邮件数、自动处理率、人工介入率、总成本
  2. 异常模式检测(规则型)
     → 警告:同一客户被回复 > 3 次、相同回复模板连续使用 > 10 次
  3. 成本效率评估(计算型)
     → 与历史基准比较,偏差 > 50% 触发告警

成本控制:防止无限循环的熔断机制

导言案例中 $2000 的 API 成本爆炸,根因是系统缺乏成本感知的反馈回路。本设计引入三级熔断:

Level 1(单邮件熔断):
  触发:单封邮件处理 token 消耗 > 10K
  动作:中止该邮件处理,标记为"需人工处理",释放资源
  目的:防止单封复杂邮件消耗过多预算

Level 2(批次熔断):
  触发:当前批次总 token 消耗 > 预算的 80%
  动作:暂停新任务分配,完成进行中的任务后停止
  目的:防止持续处理导致预算超支

Level 3(系统熔断):
  触发:总成本超过硬预算上限 / 检测到循环模式
  动作:立即中止所有 Agent,发送告警通知给运维团队
  目的:最终防线,防止导言案例重演

发散检测的具体实现

失败案例中 Agent 陷入无限循环的根因是:没有机制检测“做了很多事但没有进展”这一模式。本设计在 Orchestrator 层实现发散检测:

  • 重复操作检测:同一操作(相同邮件 + 相同动作)在 5 分钟内出现 3 次 → 强制中止该邮件处理
  • 进展检测:每 10 次工具调用检查一次——已处理邮件数是否增加?如果 30 次调用内无新增已处理邮件 → 触发 Level 2 熔断
  • Token 加速度检测:如果最近 5 分钟的 token 消耗速率是前 5 分钟的 3 倍以上 → 触发告警并暂停新任务分配

Multi-Agent 可观测性设计

按 §14.6 的三层扩展:

全局因果追踪:每封邮件从进入系统到最终处理完成,携带一个 Trace ID。Orchestrator 分配 → Classifier 分类 → Draft 生成 → Action 执行的每一步都记录 Span ID 和 Parent Span ID。当客户投诉某封回复不当时,可以沿 Trace ID 追溯完整的决策链。

消息序列快照:记录 Orchestrator 与各 Worker 之间的消息时序。关键价值在于:当批量处理出现异常时(如多封邮件的回复被错位发送),时序图能揭示是哪个环节的消息交错导致了状态混乱。

Observer Agent:一个轻量的监控 Agent,不参与邮件处理,只读取全局状态并检测异常:成本加速、Agent 沉默超时、未处理邮件堆积、审批队列积压等。Observer Agent 的存在确保了即便所有处理 Agent 都陷入异常状态,仍有一个独立的“看门人”能发出告警。

与导言失败案例的对比

失败场景导言中的原因本设计的防护
回复不该回复的邮件无分类逻辑Classifier Agent 分类 + “可疑邮件”标记转人工
误删未读邮件删除权限无限制“删除”不在操作集中 + 读写分离架构
发送不当回复无回复审核三级审批机制 + 内容安全 Pre-Hook
$2000 成本爆炸无成本监控三级熔断 + 发散检测 + Observer Agent
无限循环无发散检测重复操作检测 + 进展检测 + Token 加速度检测

总投入的额外成本:Orchestrator + Classifier + Observer Agent 的运行成本约占总成本的 15-20%。但这 15-20% 的“管理成本”换来的是:系统在无人监督下可以安全运行数周,而非三天后灾难性失败。这是 Harness 成本——不是浪费,而是使系统从“不可部署”变为“可部署”的最小必要投资。


章末综合反思

三个案例不是独立的工程报告,而是同一套理论在不同问题空间投影的结果。回顾它们的差异与共性,可以将本章——乃至本书前十五章——的核心设计原则收束为一份可携带的工程结晶。本节数据为说明性示例,旨在量化呈现跨案例的结构性差异。

(一) 三案例 Q/T/C 工作点对照(横向)

维度案例一:代码生成案例二:数据分析案例三:客服邮件
Q 主导轴形式化质量(测试 + linter)半形式化质量(统计正确 + 语义评估)风险敏感质量(不可逆操作不出错)
Q 硬约束测试 100% 通过、linter 零新增统计方法正确、报告结构完整“删除”不存在、回复必经审批
Q 软约束代码风格一致分析深度、洞察力客户情绪、回复语气
T 上限< 30 分钟< 60 分钟单邮件 < 10K token(间接时间约束)
C 上限< 500K token< 800K token全局预算硬上限 + 三级熔断
帕累托倾向Q 优先 → T → CQ 优先 → C → TQ + 风险下界优先,C 严格管控,T 弹性
目标可形式化度高(≥ 80%)中(约 50%)低(≤ 30%——风险判断不可形式化)
搜索结构收敛型发散-收敛交替分类-生成-审批的并行流水
架构形态单 Agent + Hook Pipeline单 Agent + 阶段间 CheckpointMulti-Agent Orchestrator-Worker

读这张表的方式是:Q/T/C 的工作点不是任意选择,而是被问题空间的目标可形式化度倒推决定的。目标越可形式化,自动化覆盖越深、人类介入越稀;目标越不可形式化或越涉及不可逆风险,人类介入越密集、Hook 与权限分级越精细。

(二) 五构件在三案例中的印证矩阵(纵向)

构件案例一中的体现案例二中的体现案例三中的体现跨案例不变的设计原则
System Prompt角色 + 能力边界 + 4 条工作原则 + 输出规范角色 + 能力边界 + 4 条工作原则 + 报告结构规范Orchestrator 专属角色 + 分类规则 + 升级触发条件合约结构同构:角色 / 边界 / 原则 / 输出四段式(§7.2)
Context五层(系统/任务/活动/相关/历史)+ 70% Compaction探索阶段 token 高、分析阶段聚焦多 Agent 间消息传递 + Trace ID 全局序分层 + 压缩 + 新鲜度三位一体(§8.3-8.5)
Plan6 步线性 Plan + abort 条件两阶段 Plan + 阶段间人类 Checkpoint分类 → 生成 → 审批的分级 PlanPlan 即解空间的主动建模(§9.2),并以 abort 条件兜底
Tool工具按风险分级 + 操作集排除 force-push工具按“读 / 转换”分级 + 排除“修改原始数据”工具按不可逆性分级 + 排除“删除邮件”最小必要权限集 + 操作集层面的硬约束前置(§10.1-10.2)
Hook三层(Pre/Post/Stop)+ 发散检测 + 测试 / linter 解析三层 + 统计方法前提检查 + LLM-as-judge三层 + 三级审批 + 三级成本熔断 + 异常模式检测靠近问题发生的地方处理问题(§11);语义型 Hook 仅在形式化方法不足时启用

这张表的关键观察是:五构件的存在是不变的(结构必然),但每个构件的具体形态是问题特定的(参数可调)——这恰好印证 第二章 的论断:“任何独立构建的成熟 Harness 都不约而同地包含相同的五个核心构件”。

(三) 三类不确定性的收敛路径

第一章 提出 LLM 的三类结构性不确定性。三个案例分别展示了它们如何被针对性收敛:

不确定性案例一的收敛手段案例二的收敛手段案例三的收敛手段
分布外填充(OOD)读取相关模块、强制“先理解再修改”原则强制 EDA 阶段、要求“统计方法与数据特征匹配”Classifier Agent 拒识 + “可疑邮件转人工”
分布偏移测试套件 + linter 即时反馈阶段间 Checkpoint 重新校准方向Observer Agent 监控 + Token 加速度检测
自信度误校准连续 3 轮失败触发 Plan 重评估LLM-as-judge 多维评分 + 人类终审三级审批分流 + Agent 不确定性 > 阈值即升级

值得注意的是:三类不确定性在每个案例中都被触及,但收敛手段的“重心”不同——案例一以测试驱动的快反馈为主,案例二以阶段化的人类校准为主,案例三以架构层的隔离与监控为主。这反映了一个更深的原则:不确定性的收敛策略由其代价结构决定,而非由模型能力决定。

(四) 跨案例不变 vs 案例特定

整理三案例后,可以清晰区分哪些经验是 Harness Engineering 的结构不变量,哪些是问题特定的参数

跨案例不变的(结构性原则,可直接迁移到新场景)

  1. 操作集先于 Hook:在操作集层面消除不应存在的能力,比在 Hook 层面拦截更可靠——§2.5 的“硬约束前置”原则在每个案例中都是首要设计步骤。
  2. 目标可形式化度决定人类介入密度:案例一的目标可形式化(测试通过),人类主要在 Plan 审批和 PR 阶段介入;案例二的目标部分模糊(“有洞察力”),人类在分析方向确认阶段介入;案例三的高风险操作需要逐项人类确认。人类介入的密度与目标的可形式化程度成反比
  3. 成本意识贯穿全栈:每个案例的 Plan 中都包含每步骤的预估成本,Hook Pipeline 中都包含成本熔断,Compaction 策略都有明确的触发阈值。成本不是事后核算的指标,而是设计时的约束参数。
  4. 发散检测是生存机制:三个案例都设计了早期发散检测——代码修复循环检测、数据分析方向偏离检测、邮件处理循环检测。发散一旦开始,成本以指数速率增长;早期检测是将指数代价降为线性代价的唯一手段。
  5. 三流必须分离设计:数据流(Context 携带的事实)、控制流(Plan + Tool 决定的下一步)、反馈流(Hook 携带的纠错信号)在每个案例中都被显式区分——没有这层分离,就无法定位问题归因(参见案例三对失败案例的三流归因表)。

案例特定的(参数性选择,需按场景重新评估)

维度是否问题特定原因
Token 预算上限由任务复杂度决定(500K / 800K / 全局熔断)
单 Agent vs Multi-Agent由子任务可并行度与状态依赖度决定
Compaction 触发阈值由任务执行步数分布决定
发散检测阈值(连续失败次数)由问题搜索结构决定(收敛型 vs 发散-收敛型)
人类介入点的具体位置由“杠杆原理 + 不可外包性”双重判据决定(§15.3-15.4)
五构件本身是否存在否(结构不变量)这是 第二章 已论证的结构必然
操作集硬约束的存在否(结构不变量)不可逆操作的硬约束前置是普适原则

这份“不变 vs 特定”的区分,是迁移本章经验到新场景的指南:迁移结构不变量、重新评估参数性选择。

(五) Harness 成本的再认识

案例三末尾给出了一个数据:Orchestrator + Classifier + Observer Agent 的运行成本约占总成本的 15-20%(说明性示例)。这个 15-20% 的“管理成本”换来的是系统从“三天后崩溃”变为“无人监督下安全运行数周”。这印证了一个贯穿全书的论断:Harness 不是性能损耗,而是使系统从“不可部署”变为“可部署”的最小必要投资。三个案例分别从不同角度展示了这一点——案例一是“如果没有 Hook 测试,PR 质量无法保障”;案例二是“如果没有阶段间 Checkpoint,分析方向偏离无法被早期发现”;案例三是“如果没有架构层隔离,导言失败案例必将重演”。

承上启下:三个案例都来自代码工程或代码工程紧邻的领域(编程、数据分析、邮件自动化)。下一章将进一步追问:Harness Engineering 的结构性需求是代码任务的特殊性,还是 LLM 不确定性本质的普适后果? 如果是后者,那么本章提取的所有结构不变量——操作集先于 Hook、目标可形式化度决定人类介入密度、成本意识贯穿全栈、发散检测是生存机制、三流必须分离——应当在医疗、法律、教育、运营等任意高影响知识工作领域同样成立。第17章 将通过跨域案例验证这一假设。