Harness Engineering
问题
什么是 AI "Harness"?它和 LLM、Context Engineering 是什么关系?如何设计一个健壮的 Agent 运行时——包括工具注册、权限控制、沙箱隔离、错误恢复、可观测性、Human-in-the-loop?
什么是 AI "Harness"?它和 LLM、Context Engineering 是什么关系? Harness = 把 LLM 包装成真正能干活的 Agent 的「运行时壳层」:
- LLM 本身只是一个文本输入 → 文本输出的纯函数,没记忆、没工具、不能动手。
- Harness 在外面套上 Agent Loop、工具注册、权限、Sandbox、记忆、Hooks、Human Gate,才能真正做事。
- 三者分工:LLM 是大脑,Context Engineering 决定它能看到什么,Harness 决定它能做什么、做到哪步。Claude Code、Cursor、Copilot Agent Mode、Devin 都是 Harness 产品。
如何设计一个健壮的 Agent 运行时? 按 7 个核心模块对号入座:
- Agent Loop:Think → Act → Observe 循环,控制最大轮次、超时、Token 预算。
- Tool Registry:工具要原子、idempotent、参数明确、错误信息清晰,否则 LLM 用不好。
- 权限模型:read-only / sandboxed-write / privileged 三档,写操作默认拒绝、白名单准入。
- Sandbox:危险操作走 Docker / Firecracker microVM 或浏览器 WebContainer,杜绝逃逸。
- 错误恢复:工具失败把报错原文给 LLM,让它自己重试或换路径,但要限重试次数。
- 可观测性:每步记 trace(prompt / tool / 输入输出 / token / 耗时),方便回放和复盘。
- Human-in-the-loop:发邮件、删文件、上线部署等高风险操作必须走人工 Gate。
答案
一、什么是 Harness
LLM 本身只是一个"文本输入 → 文本输出"的函数。它没有记忆、没有工具、不能执行代码、不能读文件、不能持久化。把 LLM 变成一个真正能做事的 Agent,需要在它外面套一层「运行时壳层」——这就是 Harness。
知名的 Harness 产品:
- Claude Code — Anthropic 官方 CLI Harness
- Cursor / Windsurf — IDE 内嵌 Harness
- GitHub Copilot Agent Mode — VS Code Chat Harness
- Devin / Cognition — 云端 Agent Harness
- 开源:Aider、Continue、OpenHands
和 Context Engineering 的关系:
- Context Engineering 决定 AI "能看到什么"(上下文的构成)
- Harness Engineering 决定 AI "能做什么"(工具、权限、沙箱)
- 两者正交,共同决定 Agent 的能力边界
二、Agent Loop:Think-Act-Observe
现代 Agent 的核心都是一个循环:
关键点:
- 一次 LLM 调用只做"一步"(思考+最多一个工具调用),不是一次完成所有事
- Harness 决定循环何时停止(通常看 LLM 是否返回 tool_use 还是 text)
- 每轮都要把工具结果喂回 LLM,形成 ReAct 模式(Reason → Act → Observe)
最小 Harness 伪代码:
async function runAgent(task: string) {
const messages: Message[] = [
{ role: 'user', content: task },
];
while (true) {
const response = await llm.complete({
messages,
system: SYSTEM_PROMPT,
tools: TOOL_SCHEMAS,
});
messages.push({ role: 'assistant', content: response.content });
// 检查是否有工具调用
const toolCalls = response.content.filter(c => c.type === 'tool_use');
if (toolCalls.length === 0) {
// 没有工具调用,Agent 认为任务完成
return response.text;
}
// 执行每个工具调用
for (const call of toolCalls) {
// 1. 权限检查
const permission = await checkPermission(call);
if (permission === 'deny') {
messages.push({ role: 'tool_result', tool_use_id: call.id, content: 'Permission denied' });
continue;
}
// 2. 运行 Hook
await runHook('PreToolUse', call);
// 3. 执行工具(在沙箱内)
const result = await sandbox.execute(call);
// 4. 运行 Hook
await runHook('PostToolUse', call, result);
// 5. 附加结果
messages.push({ role: 'tool_result', tool_use_id: call.id, content: result });
}
}
}
三、工具设计(Tool Design)
工具是 Agent 的"手"。工具设计决定 Agent 能力的上限。
好工具的 5 个特征
| 特征 | 说明 | 反例 |
|---|---|---|
| 单一职责 | 一个工具做一件事 | ❌ do_everything(action, args) |
| 结构化参数 | JSON Schema 清晰定义 | ❌ 一个 prompt string 塞所有意图 |
| 可预测结果 | 输入相同输出相同(幂等) | ❌ 每次返回随机字段 |
| 错误即返回 | 用返回值表达错误,而非抛异常 | ❌ 抛异常中断 Agent |
| 返回简洁 | 只返回必要信息 | ❌ 返回整个原始 JSON |
Claude Code 内置工具精华分析
Claude Code 的内置工具集经过精心打磨,可以作为教科书参考:
// 1. Read — 读文件
{
name: 'Read',
parameters: {
file_path: 'absolute path',
offset: 'start line', // 大文件分段读
limit: 'lines to read',
}
}
// 2. Edit — 精确编辑(不是全量覆盖)
{
name: 'Edit',
parameters: {
file_path: '...',
old_string: '...', // 必须精确匹配
new_string: '...',
replace_all: false,
}
}
// 设计亮点:强制先 Read 再 Edit,old_string 必须唯一匹配
// 3. Grep — 基于 ripgrep
// 支持 glob 过滤、context 行、多种输出模式
// 设计亮点:绕过 shell,参数结构化不会被注入
// 4. Bash — 执行命令
// 设计亮点:有 timeout、支持后台运行、有黑名单
// 5. Task — 委派子任务(Subagent)
// 设计亮点:子 Agent 独立上下文,不污染主 Agent
共同特征:参数都是结构化 JSON,不让模型自己构造字符串。
反例:让模型"裸写"命令
// ❌ 差的设计
{
name: 'run_command',
parameters: { command: 'string' },
}
// 问题:
// - 命令注入风险(模型拼接字符串)
// - 无法做精确权限控制(白名单只能做粗粒度)
// - 错误处理困难
// ✅ 好的设计
{
name: 'git_commit',
parameters: {
message: 'string',
files: 'array of paths',
amend: 'boolean',
},
}
// - 参数明确,易校验
// - 权限精确(按工具授权)
// - 错误可回传具体原因
MCP:工具标准化协议
MCP(Model Context Protocol) 是 Anthropic 主推的工具标准。它让工具和 Harness 解耦:
- 工具开发者实现一个 MCP Server(Node/Python/任意语言)
- Harness(Claude Code、Cursor 等)作为 MCP Client 通过 stdio/HTTP 连接
- 任何 MCP-compatible Harness 都能复用这套工具
企业内部的领域工具都应该做成 MCP Server——前端、后端、移动端的 AI 工具就能共享。详见 MCP 协议。
四、权限模型(Permission Layer)
Agent 能做什么由权限决定。好的权限模型要在"够用"和"安全"之间平衡。
三种典型权限模式
| 模式 | 说明 | 适用 |
|---|---|---|
| Strict(严格) | 每个工具调用都要用户确认 | 生产环境、涉外操作、新手 |
| Auto(自动) | 白名单内自动执行,黑名单拦截 | 日常编码、沙箱环境 |
| Plan(计划) | 只能读不能写,先出计划给用户确认 | 大型重构、探索未知代码库 |
Claude Code 的权限模式示例:
{
"permissions": {
"allow": [
"Read(**)",
"Grep(**)",
"Glob(**)",
"Bash(pnpm install)",
"Bash(pnpm lint*)",
"Bash(pnpm test*)",
"Bash(git status)",
"Bash(git diff:*)"
],
"ask": [
"Edit(**)",
"Write(**)",
"Bash(git commit:*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(sudo *)",
"Read(.env*)",
"Read(**/*secret*)"
]
}
}
设计原则:
- 默认最小权限:能不给就不给,用的时候再加
- 读多写少:Read/Grep 自动,Edit/Write 问一下
- 破坏性操作全黑名单:
rm -rf、--force、sudo - 敏感路径黑名单:
.env、secrets/、.ssh/
Plan Mode(计划模式)
2025 年下半年出现的新模式:Agent 先只读探索,给出行动计划,用户确认后才开始写。
适用场景:
- 大型重构(影响超过 10 个文件)
- 探索未知代码库
- 高风险改动(数据库迁移、破坏性 API 变更)
YOLO 模式(Auto-Accept)
和 Plan 相反——全自动,不问任何确认。只在以下场景用:
- 沙箱环境(Docker、worktree)
- 一次性脚本任务
- CI/CD 中的 headless 跑批
- 已知安全的重复操作(如
/gen-tests为 100 个文件生成测试)
YOLO 必须配合沙箱。在你的主工作区开 YOLO 等于把方向盘交给 AI,出任何问题都没有 undo。
五、沙箱(Sandbox)
沙箱是 Harness 的安全护栏。核心目标是:就算 Agent 做错了,也不会损坏主工作环境。
沙箱技术栈
| 技术 | 隔离级别 | 开销 | 适用 |
|---|---|---|---|
| Git Worktree | 文件系统 | 极低 | 独立分支并行开发 |
| Docker 容器 | 文件系统+进程+网络 | 中 | 一次性任务、CI |
| Firecracker VM | 完整 VM | 高 | 云端 Agent(Devin 等) |
| macOS Sandbox (sandbox-exec) | 文件系统+网络 | 低 | 本地实验(Mac) |
| Linux namespaces | 文件系统+进程 | 低 | 本地实验(Linux) |
Git Worktree:最实用的轻量沙箱
# 给 Agent 开一个独立的工作目录
git worktree add ../my-project-agent-task agent/feature-xyz
# Agent 在这个目录里跑,改错了直接删除目录即可
cd ../my-project-agent-task
claude "实现 xxx 功能"
# 如果结果好,合并回来
git worktree remove ../my-project-agent-task
git merge agent/feature-xyz
优点:
- 主工作区完全隔离
- 多个 Agent 可以并行在不同 worktree 工作
- 改错了丢弃整个 worktree 即可
Claude Code 的 Agent 工具支持 isolation: "worktree" 参数,自动在独立 worktree 跑子任务。
Docker 沙箱
对于更高隔离需求,用 Docker:
FROM node:22-slim
RUN apt-get update && apt-get install -y git ripgrep
WORKDIR /workspace
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
# 限制网络(可选)
# RUN echo "127.0.0.1 example.com" >> /etc/hosts
CMD ["bash"]
// 把 Agent 放到 Docker 里跑
const container = await docker.run({
image: 'my-claude-sandbox',
volumes: {
[process.cwd()]: '/workspace',
},
networkMode: 'bridge',
memoryLimit: '2gb',
readOnly: ['/etc', '/usr'],
});
await container.exec(['claude', '-p', task]);
典型约束:
- 限制 CPU/内存
- 限制网络访问(只允许 npm registry、GitHub)
- 只读系统目录
- 定时清理
六、Hooks:生命周期钩子
Hooks 允许在 Agent 的关键节点挂钩子脚本,实现拦截、修改、记录等自动化。
Claude Code 的核心 Hook 事件
| 事件 | 触发时机 | 可以做什么 |
|---|---|---|
SessionStart | 会话开始 | 注入动态上下文(当前分支、未完成任务) |
UserPromptSubmit | 用户发送消息时 | 敏感词过滤、补充上下文 |
PreToolUse | 工具调用前 | 拦截危险命令、审计日志、需要时阻断 |
PostToolUse | 工具调用后 | 自动 lint、type check、截图 |
PreCompact | 压缩上下文前 | 把关键决策持久化 |
Stop | Agent 停止时 | 通知、自动 commit、更新看板 |
SubagentStop | Subagent 结束时 | 聚合结果 |
Notification | Agent 想通知用户时 | 自定义通知渠道 |
实战案例
案例 1:PostToolUse 自动 lint/tsc
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "pnpm lint --fix $CLAUDE_FILE_PATHS 2>&1 | head -20"
},
{
"type": "command",
"command": "pnpm tsc --noEmit 2>&1 | head -20"
}
]
}
]
}
}
效果:AI 每次改完文件立即看到 lint/type error,下一轮就能自己修。
案例 2:PreToolUse 拦截危险命令
#!/bin/bash
# 读取 stdin 中 Claude 要执行的命令
CMD=$(cat)
# 黑名单检查
if echo "$CMD" | grep -qE "(rm -rf /|git push --force|sudo)"; then
echo "DANGER: blocked" >&2
exit 1 # 非零退出码会阻断工具调用
fi
# 审计日志
echo "$(date -Iseconds) | $CMD" >> .claude/audit.log
exit 0
案例 3:SessionStart 注入动态上下文
#!/bin/bash
cat <<EOF
<current_state>
Git 分支:$(git branch --show-current)
未提交改动:$(git status --short | wc -l) 个文件
近期错误日志:
$(tail -20 logs/error.log)
</current_state>
EOF
案例 4:Stop Hook 自动通知
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Agent 完成\" with title \"Claude Code\"'" }]}
]
}
}
Hook 的坑
- 无限循环:Hook 里调用了会触发同类事件的操作
- 阻塞主流程:Hook 执行慢(如跑全量 test),Agent 被 block
- 安全风险:Hook 能执行任意 shell,写错就是漏洞
- 错误处理:Hook 失败时是否应该阻断 Agent?默认通常是不阻断
原则:Hook 只做"快速、幂等、低风险"的事。重活交给 CI。
七、Memory:Agent 的记忆系统
LLM 本身无状态。要让 Agent 有"记忆"(跨会话持久化),需要 Memory 系统。
三级记忆
文件系统式 Memory
最简单也最有效的实现:
.claude/memory/
├── MEMORY.md # 索引
├── user_preferences.md # 用户偏好
├── project_decisions.md # 项目决策记录
├── known_bugs.md # 已知问题
└── current_sprint.md # 当前迭代目标
Harness 设计:
SessionStart时加载MEMORY.md的目录- Agent 主动用
Read工具按需读取具体 memory 文件 - Agent 更新 memory 时用
Edit/Write工具 - 所有 memory 提交到 Git,团队共享
详见 Claude Code 的 auto memory 系统设计(Claude Code 内置能力)。
进阶:结构化 Memory
对于更复杂场景(用户数据、任务状态),用数据库:
// 向量数据库 + 关键词索引
interface MemoryEntry {
id: string;
type: 'fact' | 'preference' | 'decision' | 'incident';
content: string;
embedding: number[];
tags: string[];
createdAt: Date;
source: 'user' | 'agent' | 'system';
}
// 注入时:按相关性 top-K 召回
async function loadRelevantMemory(currentTask: string): Promise<MemoryEntry[]> {
const queryEmbedding = await embed(currentTask);
return vectorDB.search(queryEmbedding, { topK: 10 });
}
本质上这是 RAG,只不过知识库是 "Agent 自己过去的经验"。
八、错误恢复(Error Recovery)
Agent 跑长任务时,错误是常态:
- 工具调用超时
- API 限流
- 编译报错
- 测试失败
- 用户 Ctrl-C 中断
重试策略
async function executeTool(call: ToolCall, attempt = 1): Promise<ToolResult> {
try {
return await runTool(call);
} catch (err) {
if (attempt < 3 && isRetryable(err)) {
// 指数退避
await sleep(1000 * 2 ** attempt);
return executeTool(call, attempt + 1);
}
// 把错误作为工具结果返回给 LLM,让它决定下一步
return {
isError: true,
content: `工具调用失败:${err.message}。请考虑替代方案或通知用户。`,
};
}
}
关键原则:
- Retryable 错误(网络、限流、超时):自动重试
- Non-retryable 错误(语法错误、权限拒绝):把错误当 tool result 喂给 LLM,让它自己决定
- 不要 throw:异常会打断 Agent Loop,失去自我修复机会
Checkpointing(检查点)
长任务运行时定期保存状态,失败时能恢复:
interface AgentState {
messages: Message[];
toolCallHistory: ToolCall[];
memory: Memory;
step: number;
}
// 每 N 步保存一次
if (step % 10 === 0) {
await fs.writeFile(`.claude/checkpoints/session-${sessionId}-step-${step}.json`, JSON.stringify(state));
}
// 恢复
const latestCheckpoint = await findLatestCheckpoint(sessionId);
const state = JSON.parse(await fs.readFile(latestCheckpoint));
Cursor 的 "Checkpoint" 功能、Claude Code 的 /resume 都是这个机制。
九、Human-in-the-Loop(HITL)
让人类在关键决策点介入。设计要点:
何时请求人工介入
异步 HITL:通知而非阻塞
对于长任务,不要让 Agent 停在那里等人,用通知:
// 需要用户确认时,发通知 + 进入"暂停"状态
await agent.pause({
reason: '需要用户确认生产部署',
notifyChannels: ['slack', 'email'],
});
// 用户回复后恢复
await agent.resume({ decision: 'approved' });
Claude Code 的 AskUserQuestion 工具和 Cursor Background Agent 的通知机制就是这种模式。
十、可观测性(Observability)
Agent 是黑盒。没有可观测性就没办法优化和定位问题。
核心指标
| 类别 | 指标 | 用途 |
|---|---|---|
| 成本 | Input/Output tokens、美元花费 | 成本优化 |
| 性能 | TTFT、工具调用耗时、总耗时 | 性能调优 |
| 质量 | 任务完成率、错误率、重试次数 | 质量评估 |
| 安全 | 权限拒绝次数、危险命令尝试 | 安全审计 |
| 行为 | 工具调用频次分布、Skill 命中率 | 理解 Agent 行为 |
日志规范
interface AgentLog {
sessionId: string;
step: number;
timestamp: string;
type: 'llm_call' | 'tool_call' | 'hook' | 'error';
// LLM 相关
model?: string;
inputTokens?: number;
outputTokens?: number;
latencyMs?: number;
// 工具相关
toolName?: string;
toolArgs?: unknown;
toolResult?: { isError: boolean; size: number };
// 错误
error?: { message: string; stack?: string };
}
建议工具:
- Langfuse — 专为 LLM Agent 设计的 observability
- Helicone — LLM API 监控
- OpenTelemetry — 通用分布式追踪
- 自建:结构化 JSON 日志 + Grafana
十一、多 Agent 协作
当任务复杂到单 Agent 搞不定,就需要多 Agent 协作。
模式 1:主从(Orchestrator-Worker)
Main Agent (Orchestrator)
├── 派发子任务 1 → Subagent A
├── 派发子任务 2 → Subagent B
└── 聚合结果
Claude Code 的 Task 工具 就是这种模式。详见 AI 辅助开发 的 Subagents 部分。
模式 2:流水线(Pipeline)
Input → Agent A (分析) → Agent B (生成) → Agent C (审查) → Output
例如:
- Agent A:从需求文档提取需求点
- Agent B:根据需求生成代码
- Agent C:审查生成的代码
模式 3:辩论(Debate)
让多个 Agent 独立得出方案,再对比:
- 对同一个任务派 3 个 Agent 独立工作
- 收集 3 份方案
- 让第 4 个 "仲裁 Agent" 对比择优
适用场景:高风险决策(架构选型、安全审查)。成本高但质量也更高。
模式 4:A2A 协议(跨团队 Agent)
Google Agent-to-Agent Protocol 等协议让不同团队/公司的 Agent 能互相调用。前端 Agent 可以调用后端 Agent 的服务,像调 API 一样。2026 年仍在早期。
十二、Harness 能力清单(设计 Checklist)
设计/评估一个 Harness 时的完整清单:
| 维度 | 核心能力 | 检查项 |
|---|---|---|
| Agent Loop | 支持多轮工具调用 | ✅ 循环终止条件清晰 |
| Tool Registry | 工具注册 + Schema | ✅ 支持 MCP |
| Permission | 多模式(strict/auto/plan) | ✅ 白名单+黑名单+通配符 |
| Sandbox | 文件/进程/网络隔离 | ✅ 支持 worktree / docker |
| Hooks | 生命周期事件钩子 | ✅ PreTool / PostTool / Stop |
| Memory | 跨会话持久化 | ✅ 文件系统或向量库 |
| Context | 自动压缩、动态注入 | ✅ Compaction + SessionStart 注入 |
| Error Recovery | 重试、Checkpoint | ✅ Retryable 错误自动恢复 |
| HITL | 人工确认、异步通知 | ✅ 破坏性操作强制确认 |
| Observability | 日志、指标、追踪 | ✅ 结构化日志 + 成本追踪 |
| Multi-Agent | 子代理、流水线 | ✅ 至少支持 Subagent |
| Extensibility | 插件化、可配置 | ✅ Skills / Custom Commands |
常见面试问题
Q1: Harness 和 LLM 是什么关系?为什么需要 Harness?
答案:
LLM 本身只是"文本输入 → 文本输出"的函数。它:
- 没有记忆(每次调用都是独立的)
- 不能执行代码
- 不能读写文件
- 不能调用外部 API
- 不能持久化任何状态
要把 LLM 变成真正能干活的 Agent,必须在它外面套一层运行时壳层——Harness。Harness 提供:
- Agent Loop:反复调用 LLM 形成"思考-行动-观察"循环
- 工具注册和执行:让 LLM 能"用手"
- 权限控制:决定 Agent 能做什么
- 沙箱隔离:防止 Agent 误操作损坏环境
- 记忆系统:跨会话的持久化
- 可观测性:日志、指标、成本追踪
Claude Code、Cursor、Copilot、Devin 本质上都是 Harness 的不同实现,底层都调用相同的 LLM。Harness 的质量往往比 LLM 的版本更决定 Agent 的能力上限。
Q2: Agent Loop 是什么?怎么实现最小版本?
答案:
Agent Loop 是 Think-Act-Observe 循环:
LLM 输出 → 有工具调用?
├─ 是 → 执行工具 → 结果喂回 LLM → 继续循环
└─ 否 → 返回最终回复给用户
最小实现大概 30 行代码:
async function runAgent(task: string) {
const messages = [{ role: 'user', content: task }];
while (true) {
const response = await llm.complete({ messages, tools: TOOL_SCHEMAS });
messages.push({ role: 'assistant', content: response.content });
const toolCalls = response.content.filter(c => c.type === 'tool_use');
if (toolCalls.length === 0) return response.text;
for (const call of toolCalls) {
const result = await executeToolWithPermission(call);
messages.push({ role: 'tool_result', tool_use_id: call.id, content: result });
}
}
}
关键点:
- 一次 LLM 调用只推进一步(不要强行一次完成所有)
- 错误要转成 tool result 喂回 LLM(不要 throw 打断 Loop)
- Loop 终止看 LLM 是否还在调用工具
Q3: 好工具和差工具的区别?
答案:
好工具 5 个特征:
| 特征 | 好例子 | 坏例子 |
|---|---|---|
| 单一职责 | git_commit(message, files) | do_git(action, args) |
| 结构化参数 | 明确 JSON Schema | 一个 prompt string 塞所有意图 |
| 可预测 | 输入相同输出相同 | 每次返回格式不一致 |
| 错误即返回 | { isError: true, content: "..." } | 直接抛异常中断 Agent |
| 返回简洁 | 只返回关键字段 | 返回整个原始 JSON 几万 token |
反例:很多早期 Harness 提供 run_command(cmd: string) 这种"裸写命令"工具——参数是一个自由字符串,结果:
- 模型要自己拼接命令(易错)
- 难做精确权限控制
- 容易被注入
正解:用结构化参数把意图明确化,如 git_commit({ message, files, amend })。
Q4: 权限模型应该怎么设计?
答案:
三种模式组合使用:
| 模式 | 说明 | 适用 |
|---|---|---|
| Strict | 每个工具都问 | 新手、生产、涉外操作 |
| Auto | 白名单自动、黑名单拒绝 | 日常编码 |
| Plan | 只读探索、出计划、人审、再执行 | 大型重构 |
核心原则:
- 默认最小权限:能不给就不给
- 读多写少:Read/Grep 自动,Edit 问一下
- 破坏性操作全黑名单:
rm -rf、--force、sudo - 敏感路径黑名单:
.env、secrets/
Claude Code 的 settings.json 是参考实现:
{
"permissions": {
"allow": ["Read(**)", "Grep(**)", "Bash(pnpm test*)"],
"ask": ["Edit(**)", "Write(**)"],
"deny": ["Bash(rm -rf *)", "Bash(sudo *)", "Read(.env*)"]
}
}
Q5: 什么是 Plan Mode?什么时候用?
答案:
Plan Mode 是 2025 年下半年出现的新模式:Agent 先只读探索,出一份详细的行动计划,人审批后才切换到"可写模式"开始执行。
流程:
用户给任务 → Agent 进入 Plan Mode(只读)
→ Agent 扫描代码、设计方案
→ 输出 N 步执行计划
→ 用户审核/修改
→ 切换到执行模式
→ 按计划执行
适用场景:
- 大型重构(影响超过 10 个文件)
- 迁移/升级(Pages Router → App Router)
- 探索未知代码库
- 高风险改动(数据库迁移、破坏性 API)
为什么好:避免 "Agent 乱写一通,改了 50 个文件结果方向错误" 这种灾难。计划阶段发现方向错误,代价几乎为零。
Q6: Sandbox 有哪些实现?什么场景用什么?
答案:
| 技术 | 隔离程度 | 开销 | 适用 |
|---|---|---|---|
| Git Worktree | 文件系统 | 极低 | 本地多 Agent 并行 |
| Docker 容器 | 文件+进程+网络 | 中 | 一次性任务、CI |
| Firecracker VM | 完整 VM | 高 | 云端 Agent(Devin 级) |
| sandbox-exec (macOS) | 文件+网络 | 低 | 本地 macOS 实验 |
| Linux namespaces | 文件+进程 | 低 | 本地 Linux 实验 |
日常开发最推荐 Git Worktree:
git worktree add ../project-task-xyz agent/feature
cd ../project-task-xyz
claude "实现 xxx"
# 做完合并或删除 worktree
优点:主工作区 100% 隔离、Agent 改错直接丢弃、支持多个 Agent 并行。
YOLO 模式必须配沙箱:没有沙箱就开 YOLO,等于把方向盘交给 AI,一个 rm -rf 就能删光代码。
Q7: Hooks 能做什么?有哪些常见陷阱?
答案:
Hooks 是 Agent 生命周期事件的回调,允许拦截、修改、记录 AI 行为。
主要 Hook 事件:
| 事件 | 用途 |
|---|---|
SessionStart | 注入动态上下文(Git 状态、任务看板) |
UserPromptSubmit | 敏感词过滤、补充上下文 |
PreToolUse | 拦截危险命令、审计 |
PostToolUse | 自动 lint、type check |
Stop | 通知、自动 commit |
实战案例:
PostToolUse对Edit|Write自动跑pnpm lint --fix && pnpm tsc --noEmit,让 AI 立即看到错误PreToolUse对 Bash 命令做黑名单检查,拦截rm -rf /、git push --forceSessionStart注入当前分支、最近错误日志
常见陷阱:
- 无限循环:Hook 触发的操作又触发同类事件
- 阻塞主流程:Hook 慢,Agent 被 block(比如跑全量 test)
- 安全风险:Hook 能执行任意 shell,写错就是漏洞
原则:Hook 只做快速、幂等、低风险的事。重活交给 CI。
Q8: Agent 的 Memory 怎么设计?
答案:
三级记忆:
- 短期:当前上下文窗口(会话内)
- 中期:通过 Compaction 压缩保留的会话摘要
- 长期:跨会话持久化
最简实现:文件系统
.claude/memory/
├── MEMORY.md # 索引
├── user_preferences.md # 用户偏好
├── project_decisions.md # 项目决策
└── known_bugs.md # 已知问题
SessionStartHook 加载 MEMORY.md 目录- Agent 按需用 Read 工具读具体文件
- Agent 用 Edit/Write 工具更新
- 全部 Git 版本管理,团队共享
进阶:向量数据库
当 Memory 条目太多(几百条以上),用 embedding + 向量检索:
const queryEmbedding = await embed(currentTask);
const relevant = await vectorDB.search(queryEmbedding, { topK: 10 });
// 把相关的 memory 塞进上下文
本质上这是 RAG,知识库是 Agent 自己过去的经验。
Q9: Agent 长任务跑崩了怎么恢复?
答案:
两个关键机制:
1. 错误容忍(不 throw)
工具错误应该作为 tool_result 喂回给 LLM,让 AI 自己决定怎么办:
try {
return await runTool(call);
} catch (err) {
// 不要 throw,转成 tool result
return { isError: true, content: `失败:${err.message}` };
}
- Retryable 错误(网络、限流):自动重试 + 指数退避
- Non-retryable 错误(语法错、权限拒绝):喂回给 LLM,它可以尝试替代方案
2. Checkpointing(检查点)
长任务定期保存状态,失败时能恢复:
// 每 N 步保存一次 AgentState
if (step % 10 === 0) saveCheckpoint(sessionId, step, state);
// 恢复
const state = loadLatestCheckpoint(sessionId);
Cursor 的 Checkpoint、Claude Code 的 /resume 都是这个机制。
Q10: 多 Agent 协作有哪些常见模式?
答案:
4 种模式:
1. 主从(Orchestrator-Worker)
主 Agent 派发子任务给 Subagent,收集结果。典型场景:大型搜索(主 Agent 派 Subagent 扫描代码库,只回传结论)。Claude Code 的 Task 工具就是这种。
2. 流水线(Pipeline)
需求分析 Agent → 代码生成 Agent → 审查 Agent
每个 Agent 专精一件事。适合有明确阶段划分的任务。
3. 辩论(Debate)
多个 Agent 独立给出方案,仲裁 Agent 对比择优。适合高风险决策(架构选型、安全审查)。成本高但质量好。
4. A2A 协议(跨组织 Agent)
Google A2A、Anthropic MCP 等协议让不同团队/公司的 Agent 互相调用。2026 年还在早期。
选型建议:
- 日常开发用主从(Subagent)足够
- 高风险决策用辩论
- 复杂多阶段用流水线
Q11: 怎么给 Agent 加可观测性?
答案:
5 大维度指标:
| 类别 | 指标 | 用途 |
|---|---|---|
| 成本 | Input/Output tokens、美元 | 成本优化 |
| 性能 | TTFT、工具耗时、总耗时 | 性能调优 |
| 质量 | 完成率、错误率、重试次数 | 质量评估 |
| 安全 | 权限拒绝次数、危险命令尝试 | 安全审计 |
| 行为 | 工具调用频次、Skill 命中率 | 行为分析 |
工具选型:
核心日志结构:
interface AgentLog {
sessionId: string;
step: number;
type: 'llm_call' | 'tool_call' | 'hook' | 'error';
model?: string;
inputTokens?: number;
outputTokens?: number;
latencyMs?: number;
toolName?: string;
toolArgs?: unknown;
error?: { message: string; stack?: string };
}
最小实践:每次 LLM 调用和工具调用打一条结构化日志(JSONL),用 Grafana/Loki 分析,基本够用。
Q12: 评估一个 Harness 好不好的关键指标?
答案:
设计/选型 Harness 看 12 个能力点:
- Agent Loop 循环终止清晰、支持多轮工具调用
- Tool Registry 结构化 schema、支持 MCP
- Permission 多模式(strict/auto/plan)、白+黑名单
- Sandbox 支持 worktree/docker/VM
- Hooks PreTool/PostTool/Stop 等事件完备
- Memory 跨会话持久化
- Context 管理 自动 Compaction + 动态注入
- Error Recovery 重试 + Checkpoint
- HITL 人工确认 + 异步通知
- Observability 结构化日志 + 成本追踪
- Multi-Agent 至少支持 Subagent
- Extensibility Skills / Custom Commands / 插件化
业界对比:
- Claude Code — 12 项全满,开源 SDK 可自建
- Cursor — IDE 体验最好,Sandbox/Hook 较弱
- Copilot — 企业生态好,自定义能力弱
- Aider / OpenHands — 开源、可魔改
相关链接
- Anthropic: Claude Code - Claude Code 完整文档
- Anthropic: Claude Agent SDK - 自建 Harness 的官方 SDK
- MCP Protocol Spec - Model Context Protocol
- Google A2A Protocol - Agent-to-Agent 协议
- Langfuse - LLM Agent Observability
- OpenHands - 开源 Harness 实现
- Aider - 终端内 AI 结对编程
- Context Engineering - 上下文工程
- AI 辅助开发 - Skills / Subagents / Slash Commands / Hooks
- AI 研发基建 - 企业级 AI 平台建设
- MCP 协议 - 工具标准化协议
- AI Agent 原理与架构 - Agent 架构