Harness Engineering

第21章 设计模式与架构决策:Harness Engineering 的 14 条原则

作者 杨艺韬 · 6,578 字

第21章 设计模式与架构决策:Harness Engineering 的 14 条原则

“Patterns are not invented — they are discovered.” — Christopher Alexander

本章要点

  • 回顾全书前 20 章,提炼 Harness Engineering 反复出现的 14 条可迁移原则
  • 每条原则配具体场景、Claude Code / LangGraph / OpenClaw 实现对照、跨领域类比
  • 理解架构决策的四个顶层判断:方法论优先于框架 / 安全默认 / 可观测优先 / 为失败而设计
  • 看懂 14 条原则之间的关联图——它们不是孤立清单,是一张有机工程网
  • 拿到一个可执行的”新 Agent 项目启动清单”,把原则落到实际动作

21.1 为什么要提炼原则

技术细节会过时。框架 API 会重构,模型能力排名会变化,Prompt Caching、tool calling、structured output 的协议也会随 provider 演进。

原则不会过时

  • 工具粒度按最小可撤销操作设计”——这条原则在 LangChain、AutoGPT、Claude Code、任何未来的 Agent 框架都成立
  • 不可逆操作默认审批”——不管用什么 HITL 机制,这条红线都要显式处理
  • 上下文即接口”——只要 LLM 的输入方式没变,这条就是真理

前 20 章讲的每一个工程决策,底下都站着某条原则。本章做的事——把原则从具体实现里抽出来,形成一张可迁移的工程地图

mindmap
  root((Harness 14 原则))
    架构层 4
      分层架构
      工具抽象
      OTA 循环
      可插拔接口
    控制层 3
      渐进式信任
      纵深防御
      预算约束
    信息层 3
      上下文即接口
      记忆分层
      数据契约最小化
    协作层 2
      失败即信号
      人在回路
    哲学层 2
      方法论优先于框架
      fail-fast 胜于半修复

21.2 架构层:系统骨架的四条原则

原则 1:分层架构(Layered Architecture)

问题:Agent 系统涉及 LLM 调用、工具执行、权限、上下文、记忆、观测——混在一起没法维护。

原则:分成职责单一的层次,层间通过清晰接口通信。

┌─ 交互层 ─┐  HTTP / CLI / SSE 输出
├─ 编排层 ─┤  Agent Loop、多 Agent 协调、任务调度
├─ 能力层 ─┤  工具系统、Tool Registry
├─ 控制层 ─┤  权限、沙箱、审计
├─ 模型层 ─┤  LLM 调用、Prompt 组装
└─ 基础层 ─┘  配置、存储、记忆、缓存

真实实现

  • Claude Code:CLI → Query Engine → Tool System → Permission → LLM Client
  • LangGraph:Studio UI → StateGraph → Node → LangChain → Provider
  • OpenClaw:Gateway → Router → Provider → LLM API

跨领域类比

  • OSI 网络 7 层协议栈
  • Linux 内核的用户态/内核态分离
  • Unix”everything is a file”让分层统一

适用边界:复杂 Agent 系统都需要分层。层数可以 3-7 不等,但”关注点分离”不可妥协。

原则 2:工具抽象(Tool Abstraction)

问题:Agent 能力来自工具,但每种工具底层实现可能完全不同(本地函数 / HTTP / gRPC / Shell / DB)。

原则:用统一接口描述工具 —— (name, description, params_schema, execute_fn) 四元组。

interface Tool {
  name: string
  description: string
  parameters: JSONSchema
  execute(params: any, ctx: Context): Promise<ToolResult>
}

真实实现

  • Claude Code:大量内置工具遵循统一 Tool 接口,新工具不应改动主循环
  • LangGraph:@tool 装饰器 + Pydantic schema
  • MCP 协议:跨进程跨生态的工具契约

跨领域类比

  • Unix 命令行 stdin/stdout/argv 协议
  • HTTP method+url+body+headers 四元组
  • REST 的统一资源模型

适用边界:要么全面采用统一抽象,要么直接放弃——“半统一”是最糟的状态(有些工具走抽象有些走 ad-hoc,调试地狱)。

原则 3:观察-思考-行动循环(OTA Loop)

问题:Agent 不是 one-shot 生成——它必须和环境持续交互。

原则:永远是 observe → think → act → observe … 的循环结构。

while not done:
    obs = collect_observations()
    thought = llm.think(obs)
    if thought.has_actions:
        results = execute(thought.actions)
    else:
        done = True

真实实现

  • Claude Code 的 main loop
  • LangGraph 的 Pregel engine(BSP 超步式 OTA)
  • ReAct / Reflexion / CodeAct 等 agent 范式,底下都是 OTA

跨领域类比

  • 控制论的反馈循环(negative feedback)
  • 游戏引擎的 game loop(input→update→render)
  • React 的单向数据流(action→state→view)

适用边界:大多数交互式 agent 系统。差异在”循环如何终止”、“循环中如何并发”、“是否允许人类中断”。

原则 4:可插拔接口(Pluggable Interfaces)

问题:Agent 生态演化快——新模型、新工具、新量化方案、新部署平台——每种都要支持。

原则:用稳定的抽象接口挡住变化;具体实现是后来可替换的插件。

graph TB
    Core[核心引擎不变]
    Core -->|LLMProvider 接口| Providers[OpenAI / Anthropic / Google / Local]
    Core -->|ToolBackend 接口| Tools[Local / MCP / HTTP / gRPC]
    Core -->|MemoryStore 接口| Memory[File / Postgres / Redis / Vector DB]
    Core -->|Checkpointer 接口| Checkpoint[SQLite / Postgres / Redis]
    Core -->|Observability 接口| Obs[LangSmith / LangFuse / Langtrace]

    style Core fill:#3b82f6,color:#fff,stroke:none

真实实现

  • OpenClaw 的 Provider 热切换(换 model 不重启)
  • vLLM 的 Executor / AttentionBackend / QuantizationConfig 三层插拔(见 vLLM 书 ch18)
  • LangChain 的 BaseChatModel / BaseTool / BaseMemory 抽象

跨领域类比

  • JVM 的 ClassLoader + 字节码接口
  • WordPress 的 hook/filter 系统
  • Linux kernel modules

适用边界:接口要先稳定再开放——一旦你说”这是稳定 API”,破坏兼容就代价极大。接口设计慢一点、冻结前多讨论几轮,值得。

21.3 控制层:让 Agent 可控的三条原则

原则 5:渐进式信任(Progressive Trust)

问题:Agent 能做危险操作;完全禁止就无用,完全放开就失控。

原则默认最严,用户主动放权。信任永远不自动升级。

Plan Mode → Default → Auto-Edit → Full Auto
(最严)                              (最宽)

真实实现(详见 ch17):

  • Claude Code 的四档权限模式
  • Cursor 的 Auto Mode 切换
  • Copilot 的”建议 vs 应用”双模式

跨领域类比

  • Linux sudo(显式提权)
  • iOS App 权限弹窗(位置、相册等逐项授权)
  • 金融软件的”单次授权 vs 免密支付”

适用边界:不可逆操作默认审批——与信任级别无关。信任升级主要针对可逆、可审计、可回滚操作。

原则 6:纵深防御(Defense in Depth)

问题:单一安全检查容易被绕过(prompt injection、规则漏洞、正则 bypass)。

原则多层独立安全机制叠加,每层独立失效不传染。

请求 → 权限检查 → 参数校验 → 命令白名单 → 路径沙箱 → OS seccomp → 执行

真实实现

  • Claude Code 的 Bash:模式 → 正则过滤 → 路径白名单 → Seatbelt 沙箱 → 超时
  • Kubernetes 的 Pod Security:RBAC + AdmissionController + SecurityContext + seccomp
  • 浏览器的 Same-Origin Policy + CSP + CORS + Sandbox iframe

跨领域类比

  • 军事分层防御(外围 / 前线 / 预备队)
  • 银行的多道门禁
  • 气坝 / 防洪堤的多层设计

适用边界:每加一层都有代价(复杂度、性能),但对”绝不能破”的关键路径值得。消费级 agent 通常 2-3 层够;企业级 agent 4-5 层起步。

原则 7:预算约束(Budget Constraints)

问题:Agent 可能因为 bug / 死循环 / prompt injection 消耗天量资源。

原则每个资源维度都有硬上限——token、时间、cost、循环轮次、并发数。

const BUDGETS = {
  maxTokensPerTask: 200_000,
  maxTurnsPerLoop: 50,
  maxWallTimeMs: 600_000,      // 10 min
  maxCostPerTask: 5.00,          // $5
  maxConcurrentSubAgents: 5,
}

超过立即硬停——不要”尽量控制”。

真实实现

  • Claude Code --max-turns 参数
  • OpenAI SDK 的 timeoutmax_tokens
  • 多级熔断器(ch20):per-task / per-user / per-tenant / global

跨领域类比

  • Unix ulimit
  • Docker 的 memory / cpu limit
  • Kubernetes 的 ResourceQuota

适用边界:生产系统都应有预算约束。没有预算约束,就是把成本、延迟和循环风险交给偶然性。

21.4 信息层:让 Agent “会思考”的三条原则

原则 8:上下文即接口(Context as Interface)

问题:模型无法调 API、无法读数据库——它只能”看”文本。

原则:把所有信息精心编码为模型能理解的上下文。context 设计本质上就是 UX 设计。

四类信息:

  • 稳定规则 → System Prompt
  • 环境状态 → 动态注入(session 开始时)
  • 按需信息 → 工具调用结果
  • 噪音 → 不放

真实实现

  • Claude Code 的 CLAUDE.md 自动注入机制
  • Cursor 的 .cursorrules
  • LangChain 的 ContextualCompressor

跨领域类比

  • 前端 React 的 props 设计(数据向下流)
  • 数据库的 schema(给应用的”窗口”)
  • 游戏的 HUD 设计(信息展示优先级)

适用边界:所有依赖上下文驱动模型行为的 Agent 系统。context 设计做得不好,再强的模型也发挥不出能力——“Garbage In, Garbage Out”。

原则 9:记忆分层(Layered Memory)

问题:Agent 既要记短期(当前对话)又要记长期(用户偏好、项目惯例)——两类记忆机制完全不同。

原则:至少三层记忆:工作记忆 + 会话记忆 + 长期记忆。

┌─ 工作记忆 ─┐  当前 context window,MB 级,TTL 秒
├─ 会话记忆 ─┤  当前 session 可压缩历史,几百 MB,TTL 天
└─ 长期记忆 ─┘  跨 session 持久,GB 级,TTL 永久

真实实现(详见 ch11-13):

  • Claude Code:messages[] + compact + ~/.claude/memories/
  • LangGraph:state channels + checkpointer + long-term store
  • Letta(前 MemGPT):core memory + archival memory + external memory

跨领域类比

  • CPU 的 L1/L2/L3 缓存 + RAM + 磁盘
  • 人类的短时记忆 + 工作记忆 + 长时记忆
  • 数据库的 buffer pool + 索引 + 磁盘文件

适用边界:任何多轮 agent。单轮工具用不着分层。

原则 10:数据契约最小化(Minimal Data Contracts)

问题:系统各层之间传递的数据结构臃肿,导致层间耦合、难演化。

原则每个跨边界 DTO 只含下游真正需要的字段,不多一个。

# ❌ 把 internal state 全抛出去
class BadRequest:
    ...20 个字段,其中 15 个下游用不到

# ✅ 最小化
class EngineCoreRequest:
    prompt_token_ids: list[int]
    sampling_params: SamplingParams
    request_id: str
    # ... 仅此而已

真实实现

  • vLLM 的 EngineCoreRequest(跨进程最小化)vs Request(进程内有状态)
  • LangGraph 的 Channel(每个 Node 只读自己关心的字段)
  • Kubernetes CRD 的 spec vs status 分离

跨领域类比

  • REST API 的 “Return the smallest useful response”
  • Protobuf 的 versioning(未知字段忽略)
  • Unix pipe 的纯文本流

适用边界:任何分布式/多进程系统。单进程应用也值得——未来拆分会轻松。

21.5 协作层:让 Agent 可靠的两条原则

原则 11:失败即信号(Failure as Signal)

问题:工具会失败、模型会幻觉、网络会抖——怎么处理?

原则把失败作为模型能消费的信号,让模型自己决定怎么调整。

// ❌ 把错误当异常抛
throw new ToolError('File not found')

// ✅ 把错误当结果返回
return {
  error: 'File not found',
  suggestion: 'Check path or use Glob to search',
  recovery_hints: ['Did you mean src/auth.ts?'],
}
// → 模型看到后会自动调整策略

真实实现

  • Claude Code 的所有工具错误都 formatted 成给模型看的文本
  • LangGraph 的 add_edge 可以从 error 节点 fork
  • ReAct 范式天然以”observation contains errors”为第一公民

跨领域类比

  • Erlang “Let it crash” + 监督树
  • HTTP 4xx 错误码(详细描述怎么错的)
  • Rust 的 Result<T, E> 强制处理错误

适用边界:错误是输入给模型的结果,不是 bug。真正的 bug(配置错、网络断)仍然要 crash + log。

原则 12:人在回路(Human in the Loop)

问题:Agent 不可能保证每一步都正确。什么时候让人介入?

原则风险驱动介入——不可逆 + 影响共享 + 跨信任边界 = 必须审批。

低风险 → 自动
中风险 → 默认问,可加白名单
高风险 → 默认问
不可逆 → 必须打字确认

真实实现(详见 ch17):

  • LangGraph interrupt() + Command(resume=...)
  • Claude Code 的 Plan / Default / Auto-Edit / Full Auto 四档

跨领域类比

  • 核电站的人工钥匙(双人同时转动)
  • 数据库 DDL 的 “dry run” + 人工 apply
  • 军方核武发射协议

适用边界:production agent 都要定义 HITL 边界。消费级 agent 可以宽松一点,企业级必须更严格。

21.6 哲学层:两条跨全书原则

原则 13:方法论优先于框架(Methodology Over Framework)

问题:framework 的版本、概念和最佳实践都会变——LangChain、Semantic Kernel、AutoGen、OpenAI Agents SDK 都会继续演进。框架对框架战争没完没了。

原则理解原则,而不是记住 API

❌ 把 LangChain 0.2 的 API 背熟
✅ 理解"Chain 是一种表达数据流的抽象"
   → 任何框架的类似概念你都秒懂

真实实现(本书的每一章):

  • 讲 Claude Code 时不讲它的具体 TypeScript 接口——讲它为什么这样做
  • 讲 LangGraph 时不教 add_edge 语法——讲 BSP 超步的设计动因
  • 讲 MCP 时不背 JSON-RPC 字段——理解”跨进程工具契约”为什么有价值

跨领域类比

  • 学数学不是记公式——是理解公式怎么推导出来
  • 学设计模式不是抄 Gang of Four——是看懂模式背后的意图
  • 学 Linux 不是背命令——是理解”一切皆文件”

适用边界:长期学习和架构设计场景。API 记忆会过期,方法论迁移得更久。

原则 14:fail-fast 胜于半修复(Fail-fast over Partial Recovery)

问题:Agent 运行中某个组件坏了——尝试修复还是整个重启?

原则大多数时候应该让整个进程退出,让外部编排重建。“半修复”状态复杂、难测试、容易埋 bug。

// ❌ 复杂的半修复
async function handleWorkerCrash(worker) {
  try {
    await restartWorker(worker)
    await reloadState(worker)
    await reconnectToLLM(worker)
    // ... 10 种可能的边界情况
  } catch {
    // 这里还是崩了
  }
}

// ✅ fail-fast
async function handleWorkerCrash(worker) {
  logger.error('Worker crashed, exiting engine')
  process.exit(1)
  // K8s 会重建
}

真实实现

  • vLLM 的 Worker 崩溃处理(见 vLLM 书 ch06)
  • Erlang 的 “Let it crash”
  • Kubernetes pod 的 restartPolicy: Always

跨领域类比

  • 分布式系统的”幂等 + 重试”设计
  • 数据库事务的”all or nothing”
  • 飞行控制系统的”冗余 + fail-safe”

适用边界:有外部编排(K8s、systemd、PM2)的环境。没有编排的纯 CLI 工具要保守一些(用户看到 crash 很不爽)。

21.7 原则之间的关联图

14 条原则不是孤立清单,而是一张有机工程网

graph TB
    L[分层架构] --> T[工具抽象]
    L --> O[OTA 循环]
    L --> P[可插拔接口]

    T --> O
    O --> PT[渐进信任]
    PT --> DD[纵深防御]
    DD --> BC[预算约束]

    O --> CI[上下文即接口]
    CI --> LM[记忆分层]
    CI --> DCM[数据契约最小化]

    O --> FS[失败即信号]
    FS --> HIL[人在回路]
    HIL --> PT

    MO[方法论优先] -.贯穿.- L & T & O & P & PT & DD & BC & CI & LM & DCM & FS & HIL
    FF[fail-fast] -.贯穿.- DD & BC & HIL

    style L fill:#3b82f6,color:#fff,stroke:none
    style O fill:#8b5cf6,color:#fff,stroke:none
    style DD fill:#ef4444,color:#fff,stroke:none
    style HIL fill:#10b981,color:#fff,stroke:none
    style MO fill:#f59e0b,color:#fff,stroke:none
    style FF fill:#f59e0b,color:#fff,stroke:none

三条核心链路

  1. 系统骨架链:分层 → OTA 循环 → 工具抽象 → 可插拔接口
  2. 控制链:渐进信任 → 纵深防御 → 预算约束(“安全”的三大支柱)
  3. 信息链:上下文即接口 → 记忆分层 → 数据契约最小化(“能思考”的三大支柱)

两条贯穿原则(方法论优先、fail-fast)像底色一样贯穿所有具体决策。

21.8 架构决策的四个顶层判断

14 条原则之上,还有 4 个更高层的架构判断。它们决定你整个产品的气质:

判断 1:方法论优先于框架

体现:选型时问”它背后的设计思想是什么”,而不是”它有哪些 API”。

反例:项目深度绑定某个框架的内部 API,等框架升级或概念重构时,业务逻辑、工具协议和状态模型被迫一起迁移。

判断 2:安全默认,能力可选

体现:默认拒绝一切,再选择性开放。不是默认允许一切,再试图堵漏洞。

反例:早期 AutoGPT 默认可以执行任意 Shell 命令——结果被用户吐槽”一不小心就 rm -rf”。

判断 3:可观测优先于可控制

体现:你无法控制模型的内部推理,但可以观测所有外部行为。先做到看得见,再谈控得住

没有 trace 的 agent 系统调试起来就是猜谜游戏。可观测性不是 nice-to-have,是 must-have

判断 4:为失败而设计

体现:Agent 一定会犯错——工具失败、模型幻觉、用户矛盾。好的 Harness 不是消除失败,而是让失败可恢复、可诊断、损害可控

三条具体落实:

  • 失败作为信号(原则 11)
  • fail-fast(原则 14)
  • 可观测性(第 19 章)

21.8.5 14 条原则 vs 常见失效模式

把全书前 20 章重新组织,可以看到一个规律:Agent 事故很少只违反一条原则。真正的生产故障通常是两三条原则同时失效,比如预算没有硬上限、错误没有变成可恢复信号、trace 又不够完整,最后才表现成”模型乱跑”或”账单异常”。下面这张表不是事故复盘,而是失效模式索引:看到症状时,先回到对应原则检查。

失效模式常见症状相关原则修复抓手
循环失控Agent 反复调用同一工具、重复修改同一文件预算约束 / fail-fastmax turns、单工具频率限制、连续失败熔断
权限膨胀一次授权后后续危险操作也被放行渐进式信任 / 人在回路权限按操作类型拆分,不可逆操作重新确认
沙箱穿透工具参数绕过路径边界或命令过滤纵深防御canonical path、denylist+allowlist、OS 级隔离
上下文污染旧工具结果、旧记忆或错误摘要误导模型上下文即接口 / 记忆分层freshness 标记、引用校验、压缩后抽样审计
缓存串台语义缓存或 prompt cache 被不同用户复用数据契约最小化 / 纵深防御cache key 加 tenant/user/scope,敏感操作不进语义缓存
评估脱钩eval 分数高,真实用户仍然失败失败即信号 / 可观测优先从生产 trace 反哺 eval,按失败类别扩充用例
成本漂移单次任务可控,总体账单不可控预算约束 / 可观测优先per-task、per-user、per-tenant、global 四级预算
恢复过度系统试图半修复,状态越修越乱fail-fast / 数据契约幂等边界、外部编排重启、恢复流程最小化
多 Agent 互相踩踏子 Agent 重复做同一件事或写同一文件分层架构 / 数据契约明确 ownership、任务租约、共享状态只读化
工具抽象泄漏新工具要求主循环加特例工具抽象 / 可插拔接口把差异放进 adapter,不把特例推回核心循环

这张表的价值在于定位顺序。不要一看到事故就先调 prompt;先问四个问题:

  1. 有没有硬边界:预算、权限、路径、时间有没有真正的上限?
  2. 有没有可见证据:trace 能不能还原模型看到了什么、调用了什么、工具返回了什么?
  3. 有没有最小契约:跨层传递的数据是不是多带了不该带的权限、身份或缓存上下文?
  4. 有没有恢复策略:失败是作为 observation 返回、让模型调整,还是应该直接 fail-fast 交给外部编排?

如果这四个问题答不上来,继续堆 prompt 只是在掩盖架构债。Harness Engineering 的核心纪律是:先让系统边界清楚,再让模型在边界内变聪明

21.8.6 原则冲突时怎么取舍

14 条原则并不总是同向。工程难点恰恰在于冲突:

冲突典型场景取舍原则
安全默认 vs 用户效率每次文件写入都询问会打断开发流可逆操作可记忆授权,不可逆操作仍审批
上下文完整 vs 上下文噪音全量塞入 trace 会挤掉当前任务信息稳定规则常驻,长尾信息按需工具化
fail-fast vs 可用性服务端 worker 崩溃是否尝试热恢复有外部编排时 fail-fast;本地 CLI 要给用户可理解错误
插件开放 vs 接口稳定第三方工具想扩展主循环事件先提供窄 hook,不承诺内部状态结构
多 Agent 并行 vs 状态一致多个子任务同时编辑同一模块并行只分配不重叠 ownership,共享资源需要锁或审阅

一个好 Harness 的标志不是”每条原则都做到极致”,而是知道哪条原则在当前场景拥有更高优先级。企业内网自动化和个人本地编程助手的取舍不同;离线批处理 agent 和实时对话 agent 的取舍也不同。原则给的是判断框架,不是机械规则。

21.8.7 把原则落成 ADR

原则如果只停留在口号层,过几周就会被需求压力冲掉。真正可执行的做法,是把关键取舍写成 ADR(Architecture Decision Record)。每一条 ADR 不需要长,但必须回答五个问题:

字段要回答的问题例子
Context为什么现在要做这个决策工具开始支持文件写入,风险等级上升
Decision最终采用什么规则写入 workspace 内文件可自动,删除和跨目录写入需审批
Alternatives放弃了哪些方案全自动、全询问、只按工具名授权
Consequences代价是什么用户会多看到审批弹窗,但误删风险下降
Revisit什么时候重新评估当 undo 覆盖率达到目标后,放宽部分写入

把 14 条原则翻译成 ADR 时,可以按下面的顺序走:

  1. 先写控制层 ADR:权限模式、不可逆操作、预算熔断、沙箱边界。这些决定系统能不能安全上线。
  2. 再写信息层 ADR:上下文注入规则、记忆 freshness、cache key、DTO 字段。这些决定模型看到什么。
  3. 最后写扩展层 ADR:provider 插件、tool adapter、memory store、eval backend。这些决定未来换实现时要不要动核心。

一个常见错误是反过来:先讨论用哪个框架、哪个向量库、哪个 tracing vendor,然后才补安全和数据契约。这样项目很容易形成”库驱动架构”:框架给了什么 hook,系统就长成什么样。Harness Engineering 的顺序应该相反:先定义安全和上下文边界,再让框架实现这些边界。

ADR 还有一个隐藏价值:它让团队能区分”原则变了”和”参数变了”。比如 max_turns 从 20 调到 30,只是参数变化;“不可逆操作从审批改成自动执行”,这是原则变化,必须重新审查风险、eval、审计和回滚策略。没有 ADR,这两类变化很容易混在同一个 PR 里,reviewer 只能凭直觉判断。

21.8.8 反模式清单

最后列一组反模式,作为 code review 和架构评审时的快速嗅探器:

反模式表面表现背后违反的原则
Prompt 补丁越写越长每次事故都往 system prompt 加一句禁令纵深防御、上下文即接口
Tool 直接读全局状态工具执行依赖隐式 singleton数据契约最小化、分层架构
权限按工具名授权bash 一放行就什么都能做渐进式信任、人在回路
Memory 永不失效旧项目事实长期影响新任务记忆分层、失败即信号
Eval 只测 happy path上线后失败全来自边界输入可观测优先、为失败而设计
插件可以改核心对象第三方扩展能破坏主循环不变式可插拔接口、fail-fast

看到这些反模式,不要急着写修复代码。先回到本章 14 条原则,判断这是架构边界问题、信息编码问题,还是控制策略问题。分类准确,修复才不会越修越散。

21.9 新 Agent 项目启动清单

把 14 条原则落到具体动作,一个可执行的启动清单:

第 1 周:架构与骨架

  • 画清楚分层架构图(至少 5 层)
  • 定义所有工具的统一接口(原则 2)
  • 搭建最简 OTA 循环 + 一个 demo 工具
  • 确定可插拔接口:LLM Provider / Tool Backend / Memory Store

第 2 周:控制层

  • 定义权限模式(Plan / Default / Auto-Edit / Full Auto 或等价物)
  • 为每个风险级别的操作定规则
  • 加预算熔断:per-task / per-user / per-day

第 3 周:信息层

  • 设计 System Prompt 分层(稳定 / 动态 / 注入)
  • 搭建短期 + 长期记忆(至少文件级别)
  • 定义所有跨层 DTO 的最小字段集

第 4 周:协作层

  • 实现所有工具的 error-as-signal 处理
  • 搭 HITL 审批机制(LangGraph interrupt 或等价)
  • 给所有操作加 undo 或 audit log

第 5 周:可观测性

  • 接 OpenTelemetry 或 LangFuse
  • 加 Trace 展示 UI
  • 搭 Prometheus metrics

第 6 周:评估与迭代

  • 构建初版评估集(50-100 任务,覆盖 typical + edge + adversarial)
  • 跑 LLM-as-Judge pipeline
  • CI/CD 质量门禁

持续迭代

  • 每周 review 用户反馈 → 转成评估任务
  • 每月跑 Pareto 分析(quality / cost / latency)
  • 每季度 review 14 条原则落实情况

21.10 全书回顾

graph LR
    subgraph "第一篇 · 开篇"
        P0[ch00 前言]
        P1[ch01 全景]
    end
    subgraph "第二篇 · 架构"
        P2[ch02-04]
    end
    subgraph "第三篇 · 工具"
        P3[ch05-07]
    end
    subgraph "第四篇 · 提示词"
        P4[ch08-10]
    end
    subgraph "第五篇 · 记忆"
        P5[ch11-13]
    end
    subgraph "第六篇 · 安全"
        P6[ch14-15]
    end
    subgraph "第七篇 · 协作"
        P7[ch16-17]
    end
    subgraph "第八篇 · 生产化"
        P8[ch18-21]
    end

    P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8

    style P0 fill:#3b82f6,color:#fff,stroke:none
    style P8 fill:#10b981,color:#fff,stroke:none

从”什么是 Harness”到”怎么评估 / 观测 / 控制成本”,再到本章的”设计原则”——这本书画的地图完整了。

21.11 核心信念

全书浓缩成一句话:

模型是引擎,Harness 是缰绳。好的缰绳不限制马的力量,而是让力量指向正确的方向。

每一条原则、每一个设计决策、每一段代码,都是在服务这个信念。

Agent 工程是年轻的学科。本书写的是当前工程实践中相对稳定的一组判断——几年后回头看,某些章节可能会显得幼稚。那没关系——好的方法论书是阶梯,你踩上去看到下一层风景后,把它拆掉就好。

感谢你读完这本书。现在,去构建你自己的 Harness 吧——用这 14 条原则作为起点,但不要被它们限制。


全书完。

“If you think good architecture is expensive, try bad architecture.” — Brian Foote & Joseph Yoder

愿你造的每一匹马,都有配得上它的缰绳。


延伸阅读

  • Christopher Alexander《A Pattern Language》—— 设计模式思想的源头
  • Gang of Four《Design Patterns》—— 软件设计模式经典
  • Erlang 的 “Let it crash” 哲学:Joe Armstrong 的博士论文
  • Unix Philosophy —— man 7 unix 或 Eric Raymond 《The Art of Unix Programming》
  • 杨艺韬讲堂全系列源码书:https://yangyitao.com/