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(研判) | 整理信息并交给 LLM | Harness |
| 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 工程最常见的失败,不是工具太少,而是第一天就想做一个”什么都能干”的万能 Agent。真实项目里更稳的做法是反过来:先做一个范围很窄、能验证、能回滚的小循环。
在让 Claude Code 进入固定工作流之前,建议先把四条边界写清楚。
| 边界 | 先决定什么 | 不决定会发生什么 |
|---|---|---|
| 输入 | Agent 可以读取哪些文件、日志、URL 或 issue | 上下文太多,真正任务被淹没 |
| 输出 | 成果物是 MDX、PR、报告还是补丁 | 只得到漂亮说明,没有可交付结果 |
| 验证 | 用什么命令、截图、公开 URL 或 diff 证明完成 | 生成看似成功,实际页面或代码已坏 |
| 权限 | 哪些动作可自动执行,哪些必须询问,哪些禁止 | deploy、删除、计费、密钥相关操作失控 |
以内容运营为例,好的 harness 不是”帮我写一篇文章”,而是”读取既有文章 → 选择不重复主题 → 生成 MDX → 检查代码块 → build → deploy → 查看线上 URL”。如果是开发团队,还要加上 git diff、测试结果、review 规则和 rollback note。做到这里,Claude Code 才从聊天工具变成工作流的执行层。
下一步怎么走
如果只是自己试用,先把上面的 mini harness 放在临时目录跑通。然后阅读 Claude Code 权限设置指南,把安全操作、需要确认的操作和禁止操作分开。如果你的问题更偏知识管理,下一篇更适合看 Claude Code x Obsidian 联动指南。
需要随手查命令时,可以先领取免费的 Claude Code Quick Reference Cheatsheet。如果你已经准备把 Claude Code 用在真实团队、内容运营或开发流程里,可以先看 中文产品导航 选择自学资料。若真正难点是权限、review、验证证据、团队分工或业务转化路径,再从 中文咨询页 发需求会更合适。
本文内容的实际验证结果
这个网站自己的发布流程,现在也在用一个小型 harness:生成文章、检查多语言内容、检查代码块、build、deploy、再查看公开 URL。早期只靠提示词时,确实出现过代码块崩掉、部署失败却没有及时发现的问题。后来把检查步骤拆出来之后,写作并没有变得神奇,但失败模式变得可见。这就是 harness 工程在实际运营里的价值。
总结:从提示词写手,进阶为 harness 设计者
| 旧视角 | 新视角 |
|---|---|
| 提示词写得好 → 输出好 | Harness 搭得好 → 输出好 |
| 选择模型 | 设计模型 + 工具 + 上下文 + 权限 |
| 单次问答 | 持续循环运作 |
Claude Code 是体验这种视角转变的绝佳教材。不要只停留在使用它,要拆解它的机制并融入自己的 Agent。这是 2026 年以后 AI 工程师必备的姿态。
先把上面的迷你 harness 复制粘贴跑一遍。十分钟后,你就迈出了属于自己 Agent 的第一步。
如果你想先把常用命令和安全习惯放在手边,可以先领取免费的 Claude Code Quick Reference Cheatsheet。如果你已经准备把 Claude Code 用到真实项目里,下一步可以看 中文产品导航页 选择更合适的英文教材。若你需要按团队流程、内容运营或业务目标来落地,也可以从 中文咨询页 直接发需求。
相关文章
参考资料
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code 验证回执工作流:用 build、公开 URL、CTA 和截图证明 AI 修改
Claude Code 修改后的验证回执流程:记录差异、build、公开 URL、CTA、截图和收入路径检查。
Claude Code Permission Budget Loop: 不必每条命令都审批,也能安全推进
为 Claude Code 设计 permission budget,让安全工作快速运行,同时保护 secrets、deploy、billing 与数据。
Claude Code 提示词库维护:把一次性指令变成资产
为 Claude Code 提示词命名、测试、复用,让它从免费 PDF 学习自然连接到付费模板包。