Tips & Tricks

Harness 工程完全指南|从 Claude Code 学会构建 AI Agent

光靠提示词驾驭不了 LLM。本文用可运行的代码与 Claude Code 的真实架构,手把手拆解串联工具、上下文与控制循环的 harness。

“随手扔个提示词给 ChatGPT”的时代已经结束。 进入 2025 年后,AI 工程的重心迅速转向 Harness 工程(harness engineering)。无论是 Anthropic 的内部博客,还是 OpenAI 的 Agent 研究,这都是出现频率最高的关键词之一。

然而问一句”harness 是什么?“,能立刻答得清楚的人其实不多。本文将以 可运行的代码Claude Code 自身的设计 为素材,完整拆解 harness 工程。读完之后,你就能从零搭出自己的 Agent。

Harness 就是 AI 的”脚手架”

“harness”原本指马具或安全带。软件工程里则有”test harness”这种说法,泛指 让某个东西得以运作的外层结构

在 AI 场景下,harness 就是 包裹 LLM 的那一层封装。具体来说,它把 LLM 处理真实任务所需的以下要素捆绑起来:

  • 工具组:读文件、执行命令、调用 API……
  • 上下文管理:记住什么、遗忘什么、压缩什么
  • 控制循环:何时调用、何时停止、何时重试
  • 权限与安全装置:防止破坏性操作被自动执行
  • 记忆:跨会话保留下来的知识

提示词只是投入这架 harness 的 一个输入如果 harness 薄弱,再巧妙的提示词都会撞上性能天花板。 这也是近年”光靠提示工程已经不够”这种说法出现的原因。

为什么 harness 关键:用 OODA 循环来理解

LLM 本身只能”续写下一个 token”。要完成现实任务,必须运转起军事战略里的 OODA 循环(Observe → Orient → Decide → Act)。

阶段内容负责人
Observe(观察)获取环境状态(读文件、查数据库)Harness
Orient(研判)整理信息并交给 LLMHarness
Decide(决策)决定下一步动作LLM
Act(执行)落地执行(执行命令、调 API)Harness

可以看到,四个阶段里有三个归 harness 负责。LLM 擅长的只有 Decide。撑起其余部分的脚手架质量,直接决定整个 Agent 的水平。

用实例理解”三档 harness”

下面用”请生成一篇博客文章”这一相同任务,分三档 harness 演示。

第 1 档:裸 API 调用(几乎没有 harness)

import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();

const res = await client.messages.create({
  model: "claude-opus-4-6",
  max_tokens: 4096,
  messages: [{ role: "user", content: "写一篇博客" }],
});
console.log(res.content[0].text);

结果:得到一段泛泛而空洞的文字,每次的主题和结构都对不上号。

第 2 档:引入工具(中等 harness)

const tools = [
  {
    name: "read_existing_posts",
    description: "获取现有博客文章列表及标题",
    input_schema: { type: "object", properties: {} },
  },
  {
    name: "write_post",
    description: "写出一个 MDX 文件",
    input_schema: {
      type: "object",
      properties: {
        slug: { type: "string" },
        frontmatter: { type: "object" },
        body: { type: "string" },
      },
      required: ["slug", "frontmatter", "body"],
    },
  },
];

async function runAgent(userGoal: string) {
  let messages = [{ role: "user", content: userGoal }];
  while (true) {
    const res = await client.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools,
      messages,
    });
    if (res.stop_reason === "end_turn") break;

    // 由 harness 执行工具调用
    const toolUse = res.content.find((c) => c.type === "tool_use");
    const result = await executeTool(toolUse.name, toolUse.input);
    messages.push({ role: "assistant", content: res.content });
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolUse.id, content: result }],
    });
  }
}

结果:挑选出与现有文章不重复的主题,并生成格式正确的 MDX。仅仅加上工具,质量就天翻地覆。

第 3 档:达到 Claude Code 水准的完整 harness

  • 自动循环(用户确认、错误重试)
  • 上下文压缩(把长对话摘要以节省 token)
  • 子 Agent 委派(翻译放到独立上下文去)
  • Prompt Caching(固定部分不重复发送)
  • Hooks(提交前自动 lint)

要把这些都手搓出来是一项大工程。正因如此,把 Claude Code 当作”实现范本”来研究就格外有价值。

拆解 Claude Code 的 harness 结构

Claude Code 是 Anthropic 内部打磨最精良的 Agent harness,可以按以下五层来理解。

第 1 层:工具设计

出厂就带 Read / Edit / Write / Bash / Glob / Grep / Agent 等工具。值得关注的是 工具粒度:

  • Grep 不是裸 grep,而是 ripgrep 的封装,精准又快速
  • Edit 不是整文件重写,而是 定位字符串替换,diff 最小
  • Agent 会派生子 Agent,隔离其上下文

工具质量直接决定 Agent 质量。“能跑就行”远远不够,必须以 幂等性、清晰的错误信息、单一职责 为准则来设计。

第 2 层:上下文的分层

~/.claude/CLAUDE.md         ← 全局规范
./CLAUDE.md                 ← 项目规范(自动加载)
~/.claude/memory/           ← 长期记忆(跨会话)
  ├── user_profile.md
  ├── feedback_xxx.md
  └── project_xxx.md
对话历史                     ← 最近的来回
任务/计划                    ← 当前会话进度

每一层寿命与角色各异,写错位置会让信息转瞬即逝,或反过来让陈旧数据长久滞留。“只用这一轮”放在任务里,“以后还会用”放进记忆,要区分清楚。

第 3 层:子 Agent 委派

借助 Agent 工具,可以派生出拥有独立上下文的 Agent。

# 主 Agent 只下指令,重活交给子 Agent
Agent(
  subagent_type: "general-purpose",
  prompt: "将 blog/harness.mdx 翻译成英文与另外 8 种语言,
           分别保存到 blog-{lang}/ 下并回报"
)

这样 主上下文就不会被冗长日志污染。长时间的构建日志、翻译中间结果、搜索结果,这些”只想要最终产物”的活儿都能整体甩出去。

第 4 层:Hooks(确定性处理)

.claude/settings.json 可在工具调用前后插入 shell 命令。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "npx tsc --noEmit" }
        ]
      }
    ]
  }
}

这样每次编辑文件后自动跑类型检查。“与其每次请求 LLM,不如让它确定性完成的事” 交给 hook 处理是惯例。

第 5 层:权限模式

{
  "permissions": {
    "allow": ["Read", "Grep", "Glob"],
    "deny": ["Bash(rm -rf*)", "Bash(git push --force*)"],
    "ask": ["Write", "Edit", "Bash"]
  }
}

显式拒绝破坏性命令,写操作则要求确认。事故往往发生在”自动跑了一下”的瞬间,因此这层设计决定运维安全。

五大常见陷阱

1. 工具给得太多 一口气塞 30 个工具,模型在选择上犹豫,精度下降。经验值是 5–15 个;不够的功能放到子 Agent 侧。

2. 没能利用 Prompt Cache 不用 Claude API 的 cache_control,冗长的 system prompt 每次都全量发送,费用暴涨。要留意 5 分钟 TTL,把不变的部分放进缓存。

messages: [{
  role: "system",
  content: [
    { type: "text", text: longStaticInstructions,
      cache_control: { type: "ephemeral" } },   // ← 就是这里
    { type: "text", text: dynamicContext },
  ],
}]

3. 错误信息 LLM 看不懂 工具只返回 Error: undefined,模型就无法自我修复。要写清楚”哪里错了、怎么修”

throw new Error(
  `文件 '${path}' 不存在。` +
  `scripts/ 目录下当前文件为: ${list.join(", ")}`
);

4. 省略人工确认 把破坏性操作(删除、force push、数据库更新)设为自动通过,总有一天会出事。默认应该”写操作询问,删除拒绝”。

5. 记忆从不整理 久未更新的旧信息会让 Agent 在错误前提下动作。记忆也需要 定期断舍离(Claude Code 里用 /compact 或手动编辑)。

亲自跑一跑自制迷你 harness

最后附上一个本地即可运行的最小 harness,Node.js + TypeScript。

// mini-harness.ts
import Anthropic from "@anthropic-ai/sdk";
import { readFileSync, writeFileSync } from "fs";

const client = new Anthropic();

const tools = [
  { name: "read_file",
    description: "读取文本文件",
    input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
  { name: "write_file",
    description: "写入文本文件",
    input_schema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } },
];

const executors = {
  read_file: ({ path }) => readFileSync(path, "utf-8"),
  write_file: ({ path, content }) => { writeFileSync(path, content); return `written ${path}`; },
};

async function loop(goal: string, maxSteps = 10) {
  const messages: any[] = [{ role: "user", content: goal }];
  for (let i = 0; i < maxSteps; i++) {
    const res = await client.messages.create({
      model: "claude-opus-4-6", max_tokens: 4096, tools, messages,
    });
    messages.push({ role: "assistant", content: res.content });
    if (res.stop_reason === "end_turn") return res.content;

    const toolUse = res.content.find((c: any) => c.type === "tool_use") as any;
    if (!toolUse) return res.content;
    const result = executors[toolUse.name](toolUse.input);
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolUse.id, content: String(result) }],
    });
  }
}

await loop("读取 README.md,用三行概括并保存到 TL;DR.md");

光这点代码就得到一个能 “读取既有文件、写出新文件” 的迷你 Agent。再接入 Grep、Bash、Agent 等工具,就能组装出 Claude Code 的缩小版。

总结:从提示词写手,进阶为 harness 设计者

旧视角新视角
提示词写得好 → 输出好Harness 搭得好 → 输出好
选择模型设计模型 + 工具 + 上下文 + 权限
单次问答持续循环运作

Claude Code 是体验这种视角转变的绝佳教材。不要只停留在使用它,要拆解它的机制并融入自己的 Agent。这是 2026 年以后 AI 工程师必备的姿态。

先把上面的迷你 harness 复制粘贴跑一遍。十分钟后,你就迈出了属于自己 Agent 的第一步。

相关文章

参考资料

#claude-code #agent-sdk #harness #prompt-engineering #llm #anthropic

让你的 Claude Code 工作流更上一层楼

50 个经过实战检验的提示词模板,现在就能复制粘贴到 Claude Code 中使用。

免费

免费 PDF:5 分钟看懂 Claude Code 速查表

只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。

我们会严格保护你的个人信息,绝不发送垃圾邮件。

Masa

本文作者

Masa

深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。