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 设计者
| 旧视角 | 新视角 |
|---|---|
| 提示词写得好 → 输出好 | Harness 搭得好 → 输出好 |
| 选择模型 | 设计模型 + 工具 + 上下文 + 权限 |
| 单次问答 | 持续循环运作 |
Claude Code 是体验这种视角转变的绝佳教材。不要只停留在使用它,要拆解它的机制并融入自己的 Agent。这是 2026 年以后 AI 工程师必备的姿态。
先把上面的迷你 harness 复制粘贴跑一遍。十分钟后,你就迈出了属于自己 Agent 的第一步。
相关文章
参考资料
免费 PDF:5 分钟看懂 Claude Code 速查表
只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。
我们会严格保护你的个人信息,绝不发送垃圾邮件。
本文作者
Masa
深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。
相关文章
Claude Code 安全最佳实践完全指南:API密钥管理、权限设置与生产环境保护
安全使用 Claude Code 的实战指南。从 API 密钥管理到权限配置、基于 Hooks 的自动化检查,再到生产环境保护——附带可直接运行的代码示例。
Claude Code 安全失败案例7选 | 真实事故与防范措施
介绍7个Claude Code中实际发生的安全事故:.env泄露、生产数据库误操作、计费爆炸等,逐案解析原因与防范代码。
Claude Code 权限配置完全指南 | settings.json·Hooks·allowlist 深度解析
全面解析 Claude Code 权限配置。allow/deny/ask 的使用场景、Hooks 自动化、环境专属 settings.json 以及实战配置模式,附完整可运行代码示例。