title: 第 28 讲 · Agent 工程范式:Harness Engineering 到底革新了什么?

课程导航: « 第 27 讲 · 第 29 讲 »

第 28 讲 · Agent 工程范式:Harness Engineering 到底革新了什么?

热点加餐
第 28 / 33 讲
Claude Code 工程化实战
范式 3.0
Harness

📌 本讲摘要 · Harness Engineering 是 2025-2026 年 AI 工程领域最被低估的范式跃迁——它把 Agent 从调一个模型重新定义为运行一个系统。本讲从范式演进史(Prompt Engineering → Agent Engineering → Harness Engineering)出发、系统讲清楚 Harness 是什么、7 层架构长什么样、5 大设计原则如何在工程里落地、并用 200 行最小 Harness 演示让你跑通从想法到运行的完整闭环。学完本讲、你应该能用 Harness 思维重新审视 Claude Code / 任何 Agent 产品的设计选择、而不是只把它当高级 prompt 工具。

1. 范式演进史:从 Prompt 到 Harness

AI 工程 5 年走完了 3 个范式跃迁、每个范式回答的核心问题不一样:

范式 时间 核心问题 代表工作 范式局限
1.0 · Prompt Engineering 2022-2023 怎么让模型听懂我? few-shot、CoT、ReAct 换个模型就失效
2.0 · Agent Engineering 2024 怎么让模型能动手? Tool use、Function calling 行为边界不清晰、容易失控
3.0 · Harness Engineering 2025-2026 怎么让系统在边界内跑得稳? Claude Code、OpenClaw 工程复杂度高、需要团队

关键洞察:3 个范式不是取代、而是叠加。Prompt 仍然是 Harness 的一部分、Agent 仍然是 Harness 的一部分。Harness 是把它们装进一个可治理系统的元范式。

⚠️ 坑 1 · 以为 3.0 取代 1.0 / 2.0——不,3.0 是在 1.0 + 2.0 之上加了一个治理层。写好 Harness 里的 system prompt、仍然要懂 Prompt Engineering;选好 Harness 里的 tool schema、仍然要懂 Agent Engineering。

2. Harness 是什么:包裹模型的工程骨架

**Harness Engineering(驾驭工程)**是把模型装进可治理系统的工程范式——管理上下文、调度工具、设边界、做可观测——让 Agent 在可控、可复现、可演化的方式下稳定运行。它把 Agent 当作完整的产品来对待、而不是一个裸露的模型调用。该概念在 2025 年随着 Claude Code 的工程化实践被业界广泛接受。

用一个比喻:Harness 是 Agent 的骨架 + 神经系统 + 边界护栏、模型是心脏。没有 Harness、模型就是一堆漂浮的智能;有了 Harness、模型才能在工程里稳定工作。

具体说、Harness 包含 6 个组件:

  1. System Prompt:定义 Agent 的人格和工作守则
  2. Tool Schema:定义 Agent 能调的工具集(白盒化)
  3. Memory:短期上下文 + 长期记忆 + 项目记忆
  4. Control Loop:主循环——读消息 → 思考 → 调工具 → 写回复
  5. Boundary:权限 / 风险 / PII 等边界护栏
  6. Observability:日志 / 监控 / 审计 / 计费

Claude Code 之所以工程化、就是因为这 6 个组件都做齐了。对比之下、直接调用 client.messages.create() 那种裸模型路线、缺了 5 个组件、只在 demo 阶段能用。

3. 7 层架构图:从模型到用户的完整抽象

把 Harness 拆成 7 层、从底向上是:
在这里插入图片描述
7 层各自独立可替换——换模型(Claude → Gemini)只改 L1;换通道(CLI → Telegram)只改 L6;换 Orchestrator(单 Agent → 多 Agent)只改 L5。这正是 Harness 范式的可演化价值:不绑死在某一家、某一端。

4. 5 大设计原则:可观测 / 可控制 / 可演化 / 可分享 / 可测试

做 Harness 容易、做好 Harness 难。5 大设计原则是判断 Harness 质量的尺子:

原则 含义 反例 正例
可观测 每次行为都能被记录、回放 Agent 调了什么我也不知道 PostToolUse 落 JSON Lines、可重放
可控制 用户能随时介入、终止、回滚 Agent 跑了 30 分钟我只能等 支持 /rewind、中断、Checkpointing
可演化 Skills / Tools / Hooks 可热加载 加个 Skill 要重启 3 个服务 Skill 描述加进 CLAUDE.md 即生效
可分享 团队成员能复用你的 Harness 资产 我配的 Skills 你那边跑不通 Plugin 打包 + Marketplace 共享
可测试 Agent 行为有自动化测试 Agent 改坏了文件我才知道 评估集(eval) + 快照测试

用这 5 条对照 Claude Code、你会发现它每条都做了 60-80 分——这正是它能工程化的原因。把这 5 条做到 90+ 分、就是企业级 Harness 的标准。

⚠️ 坑 2 · 只追功能多、不追可观测与可控制——Harness 的价值不是多能干、而是出问题时能不能 5 分钟内定位 + 1 分钟内止血。事故驱动的设计比功能驱动的设计重要 10 倍。

5. 200 行最小 Harness 演示

理论再多不如一行代码。下面是一个最小可运行的 Harness(约 200 行 TypeScript):

实战代码块 1 — 最小 Harness 完整实现。能跑通用户输入 → 调模型 → 调工具 → 返回结果 完整闭环。

// minimal-harness.ts (200 行)
import Anthropic from "@anthropic-ai/sdk";
import * as readline from "readline";
import { execSync } from "child_process";
import { readFileSync, writeFileSync } from "fs";

// ============ L1: Model Adapter ============
const client = new Anthropic();

// ============ L3: Tool Layer (白盒化) ============
const TOOLS = [
  {
    name: "Bash",
    description: "执行 shell 命令,返回 stdout/stderr。危险命令会被拦截。",
    input_schema: {
      type: "object",
      properties: { command: { type: "string" } },
      required: ["command"]
    }
  },
  {
    name: "Read",
    description: "读取文件内容,返回带行号的文本。",
    input_schema: {
      type: "object",
      properties: { file_path: { type: "string" } },
      required: ["file_path"]
    }
  },
  {
    name: "Edit",
    description: "编辑文件,old_text 必须唯一匹配。",
    input_schema: {
      type: "object",
      properties: {
        file_path: { type: "string" },
        old_text: { type: "string" },
        new_text: { type: "string" }
      },
      required: ["file_path", "old_text", "new_text"]
    }
  }
];

// ============ L3: Tool Implementation ============
function executeTool(name: string, input: any): string {
  // L2: Boundary - 黑名单
  if (name === "Bash" && /rm -rf|sudo|dd |mkfs/.test(input.command) {
    return "❌ 拒绝执行危险命令";
  }
  // L2: Boundary - PII 脱敏
  if (/\b1[3-9][0-9]{9}\b/.test(JSON.stringify(input) {
    return "❌ 检测到 PII,已拦截";
  }

  if (name === "Bash") {
    try { return execSync(input.command, { encoding: "utf-8", timeout: 10000 }); }
    catch (e: any) { return `Error: ${e.message}`; }
  }
  if (name === "Read") { return readFileSync(input.file_path, "utf-8"); }
  if (name === "Edit") {
    const content = readFileSync(input.file_path, "utf-8");
    if (!content.includes(input.old_text) return "❌ old_text 不存在";
    writeFileSync(input.file_path, content.replace(input.old_text, input.new_text);
    return "✓ 已修改";
  }
  return "Unknown tool";
}

// ============ L4: Memory Store (短期) ============
const messages: any[] = [];
const SYSTEM_PROMPT = `你是 Claude Code 风格的 Agent,可以用工具帮用户工作。回答简洁,先想再动手。`;

// ============ L5: Orchestrator (主循环) ============
async function runTurn(userMessage: string) {
  messages.push({ role: "user", content: userMessage });
  for (let i = 0; i < 10; i++) {
    const response = await client.messages.create({
      model: "claude-sonnet-4-7",
      max_tokens: 4096,
      system: SYSTEM_PROMPT,
      tools: TOOLS,
      messages
    });
    messages.push({ role: "assistant", content: response.content });
    if (response.stop_reason === "end_turn") {
      return response.content.filter(b: any) => b.type === "text").map(b: any) => b.text).join("\n");
    }
    // 工具调用
    const toolUse = response.content.find(b: any) => b.type === "tool_use");
    if (toolUse) {
      const result = executeTool(toolUse.name, toolUse.input);
      messages.push({ role: "user", content: [{ type: "tool_result", tool_use_id: toolUse.id, content: result }] });
    }
  }
  return "达到最大轮数,停止";
}

// ============ L6 & L7: Channel + UI (CLI) ============
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const ask = (q: string) => new Promise<string>(r) => rl.question(q, r);
(async () => {
  console.log("Minimal Harness ready. 输入 exit 退出。");
  while (true) {
    const input = await ask("\n> ");
    if (input === "exit") break;
    const result = await runTurn(input);
    console.log("\n" + result);
  }
})();

这个 Harness 包含 L1-L7 中的 6 层(L6 channel 简化成 stdin)、约 200 行。把它跑起来、就是一个能调 Claude + 调工具 + 有边界护栏的最小 Agent。

想学 Harness 演进、从这里开始改:

  • L4 长期记忆:把对话摘要写进 .claude/memory.md、下次启动读回来
  • L5 子代理:把调工具改成 Task tool 调子代理
  • L6 Channel:用 Telegraf 库挂 Telegram bot
  • Skills 注册:从 .claude/skills/ 读 SKILL.md 描述、塞进 system prompt
  • L2 Hooks:PreToolUse 调 PII 脱敏 hook

每加一层、Harness 就向 Claude Code 近一步。

⚠️ 坑 3 · 抄完 200 行就跑——没有改、没有测、没有部署。Harness 的学习不是读懂、而是改 3 个版本后你才真的懂。

6. Harness 思维如何反哺日常开发

学完 Harness、回过头来用它审视日常开发、会看到一些以前觉得理所当然的事其实有更好的解法:

实战代码块 2 — 用 Harness 思维重构一个传统 CLI 工具。比如一个传统代码生成器工具、以前是参数驱动;Harness 化后是 Agent 驱动:

# 传统 CLI 工具(参数驱动)
$ gen-component --name Button --type functional --style css
# 输出:一堆文件,需要人工看是不是想要的

# Harness 化后(Agent 驱动)
$ gen-component 我要一个 React Button 组件,支持 primary/secondary 两种风格
# Agent 自己决定:用什么库、什么 props、什么样式、要不要 test
# 还能追问:Button 要不要带 icon?要不要支持 disabled 状态?

对比之下、Harness 化的工具更像在跟一个同事对话、而不是在用一台机器。这正是 Harness 范式想带给大家的东西:从工具升级为伙伴。

反哺日常开发的 3 个具体动作

  1. 把脚本改成 Agent:你常用的 5 个 shell 脚本、挑 1 个改成 Agent 能调的工具、放进 .claude/skills/
  2. 把 CLI 工具加 Harness 化:你写的 1 个 CLI 工具、加一个简单的对话模式——输入一句话、工具解读后执行
  3. 把团队约定写进 CLAUDE.md:把团队的代码规范 / Git 流程 / 部署命令写进 CLAUDE.md、让 Agent 替你守规矩

⚠️ 坑 4 · 觉得 Harness 离自己很远——我又不是做 Agent 产品的。其实,任何接受输入、产出结果的工具、都可以 Harness 化。哪怕是一个部署脚本、加了 Agent 化后都能省 50% 的手工操作。

7. 范式还在演进:Loop Engineering 正在兴起

本讲写于 2026 年初——彼时 Harness Engineering 已随着 Claude Code 的普及成为业界共识,但 Agent 工程范式的演进并未就此停步。2026 年上半年,**Loop Engineering(循环工程)**正在成为新的方向:把 Harness 的主循环升级成多 Agent 协作 + 长时记忆 + 自动评估的闭环,让 Agent 具备自我迭代能力。

几个值得关注的方向:

  • 长时记忆:突破单次会话边界、让 Agent 跨任务记住上次学到的教训
  • 自动评估:Agent 每次跑完、自动生成评估报告、驱动下一步改进
  • 多 Agent 协作:从一个 Agent 一杆枪到多个 Agent 分工 + 互相校验
  • 自我修复:检测到行为漂移后、自动回滚 + 重试 + 报警

这意味着 Agent 工程范式仍在快速演进——Harness Engineering 是当下、Loop Engineering 是下一个浪尖。保持对范式跃迁的敏感度、才能不被技术更替甩在身后。

8. 一句话备忘

🔨 未来不是更大的模型、而是更聪明的 Harness;下一步不是更长的上下文、而是更聪明的循环。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐