Agent = Model + Harness。 LLM 不是万能的——它是概率采样器而非计算器,是文本生成器而非事实数据库。设计 Agent 系统的本质,就是在 LLM 擅长的推理能力和它的固有缺陷之间,用 Harness 工程 搭建一座桥。
本文综合 Anthropic、OpenAI、LangChain 等团队的一线工程实践,系统梳理 LLM 的核心局限与 Agent 系统的设计方法。一个核心发现:LangChain 的 coding agent 在 Terminal Bench 2.0 上从 52.8% 跳到 66.5%(Top 30 → Top 5),改的不是模型,只是 harness。
一、精确计算盲区#
LLM 通过 token 预测生成文本,本质上是概率采样器而非计算器。它不擅长数数、排序、精确算术——这些任务对人类来说”简单”,对 LLM 来说恰恰最不可靠。
典型案例:代码编辑中的行号问题#
一个经典的反面设计是让 Agent 输出行号——“替换第 142-156 行”。Agent 经常数错行号,导致修改错位甚至破坏代码。
正确的设计是把定位的工作交给确定性工具,让 LLM 只负责生成内容:
| 方案 | 原理 | 适用场景 |
|---|---|---|
| SEARCH_AND_REPLACE | Agent 提供要替换的完整原文片段 + 新片段,底层脚本做精确匹配 | 通用场景,Claude Code 和 Codex 都采用 |
| AST 级替换 | Agent 指定 函数名.参数名 等语义锚点,底层用 AST parser 定位 | 结构化语言(Python/Go/Rego) |
| Patch 文件 | Agent 生成标准 unified diff,底层用 patch 命令应用 | Git 工作流 |
Anthropic 的 Writing Tools for Agents ↗ 明确指出:选择 LLM 能自然书写的输出格式。写 diff 需要在生成新代码之前就算好 chunk header 的行数——LLM 做不到这种”先算后写”;写 JSON 需要转义换行和引号——增加了不必要的出错机会。SEARCH_AND_REPLACE 之所以胜出,因为 Agent 只需要”复制原文 + 写新代码”,这是训练数据中最常见的模式。
代码实现:SEARCH_AND_REPLACE 工具#
import re
def search_and_replace(file_path: str, old_text: str, new_text: str) -> dict:
"""Agent-friendly 的代码编辑工具。
Agent 只需要提供:
1. old_text: 要被替换的原文(必须在文件中唯一匹配)
2. new_text: 替换后的新文本
定位工作由工具完成,不需要 Agent 提供行号。
"""
with open(file_path, 'r') as f:
content = f.read()
# 精确匹配——Agent 不需要数行号
occurrences = content.count(old_text)
if occurrences == 0:
# Agent-friendly error: 告诉 Agent 怎么修,而不只是说"失败了"
# 尝试模糊匹配,给出修复建议
lines = content.split('\n')
candidates = []
for i, line in enumerate(lines):
if any(word in line for word in old_text.split()[:3]):
candidates.append(f" Line {i+1}: {line.strip()}")
suggestion = "\n".join(candidates[:3]) if candidates else "No similar lines found."
return {
"success": False,
"error": f"old_text not found in {file_path}. "
f"Did you mean one of these?\n{suggestion}\n"
f"Re-read the file and provide the exact text to replace."
}
if occurrences > 1:
return {
"success": False,
"error": f"old_text matches {occurrences} locations in {file_path}. "
f"Provide more surrounding context to make the match unique."
}
# 唯一匹配——执行替换
new_content = content.replace(old_text, new_text, 1)
with open(file_path, 'w') as f:
f.write(new_content)
return {"success": True, "message": f"Replaced 1 occurrence in {file_path}"}python通用原则:将精确计算卸载到外部工具#
| LLM 不擅长的 | 应该交给 | 示例 |
|---|---|---|
| 算术 | Python 解释器 / calculator tool | 2^32 - 1 = ? |
| 排序 | sort 命令 / 数据库 ORDER BY | 按日期排序文件 |
| 字符串计数 | len() / wc | ”这段代码有多少行” |
| 正则匹配 | grep / ripgrep | 查找所有 TODO 注释 |
| 行号定位 | SEARCH_AND_REPLACE / AST | 修改第 142 行 |
| Token 计算 | tiktoken / tokenizer | ”这段文本有多少 token” |
设计原则:LLM 负责决策(用什么工具、传什么参数),工具负责执行(精确计算)。这就是 ReAct 模式的核心——Reasoning 归 LLM,Acting 归 Tool。
二、幻觉(Hallucination)#
LLM 的优化目标是”下一个 token 的似然概率最高”,而非”事实正确”。这导致它会自信地编造不存在的 API、不存在的参数、不存在的事实。
分层防御体系#
每一层的实现细节:
Layer 1: Schema 约束 — Function calling 使用严格 JSON Schema,只允许调用预定义的工具列表。Agent 无法”发明”新工具。
# 工具定义的 JSON Schema —— Agent 只能在这个范围内操作
tools = [
{
"type": "function",
"function": {
"name": "search_files",
"description": "Search for files matching a glob pattern",
"parameters": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "Glob pattern, e.g. '**/*.py'"
},
"path": {
"type": "string",
"description": "Directory to search in",
"default": "."
}
},
"required": ["pattern"],
# strict: true 阻止 Agent 添加未定义的参数
"additionalProperties": False
},
"strict": True # 严格模式:Agent 不能发明新参数
}
}
]pythonLayer 2: 参数校验 + Agent-friendly Error — Anthropic 在 Writing Tools for Agents ↗ 中强调:错误信息应该告诉 Agent 怎么修复,而不只是说”失败了”。
def validate_and_execute(tool_name: str, params: dict) -> dict:
"""校验工具参数,返回 agent-friendly 错误信息。"""
if tool_name == "schedule_event":
# 校验日期格式
if "start_date" in params:
if not re.match(r'\d{4}-\d{2}-\d{2}', params["start_date"]):
return {
"error": "start_date must be YYYY-MM-DD format. "
"Example: 2025-09-01. "
"You provided: " + repr(params["start_date"]),
"retry": True # 明确告诉 Agent 可以重试
}
# 校验必填字段
if "project_id" not in params:
return {
"error": "project_id is required. "
"Use list_projects tool first to find available project IDs.",
"retry": True
}
# 校验通过,执行工具
return execute_tool(tool_name, params)pythonLayer 3: 运行时沙箱 — 代码执行在沙箱中(Docker/gVisor/E2B),即使幻觉也无法造成破坏。
Layer 4: 输出验证 — 不是”Agent 说成功了”就算成功,要验证环境状态。
def verify_outcome(agent_claim: str, actual_state: dict) -> bool:
"""Outcome > Narration: 验证环境状态,不信 Agent 的自述。"""
if agent_claim == "Tests pass":
# 不信 Agent 的话,自己跑测试
result = subprocess.run(["pytest", "--tb=short"], capture_output=True)
return result.returncode == 0
if agent_claim == "File created":
return os.path.exists(actual_state["expected_path"])
if agent_claim == "Bug fixed":
# 跑完整测试套件 + lint
test_ok = subprocess.run(["pytest"], capture_output=True).returncode == 0
lint_ok = subprocess.run(["ruff", "check", "."], capture_output=True).returncode == 0
return test_ok and lint_okpython量化与监控#
在生产环境中,幻觉需要被量化和持续监控:
- Intrinsic 指标:SelfCheckGPT(对比同一问题的多次采样一致性)、token-level confidence scoring
- Extrinsic 指标:与 ground truth 比对(ROUGE、BERTScore、exact match)
- 生产方案:vLLM 团队的 HaluGate ↗——token 级幻觉检测管道,125ms 延迟,可同步嵌入请求流
- 人工校准:定期抽样,human-in-the-loop 校准自动评分器的准确性
三、上下文窗口与 Context Engineering#
Anthropic 在 Effective Context Engineering ↗ 中提出了一个核心原则:找到最小的高信号 token 集合,最大化期望输出的似然概率。 Context Engineering 已经取代 Prompt Engineering 成为 Agent 系统设计的核心学科。
Context Rot:为什么更大的窗口 ≠ 更好的性能#
关键数据:OpenAI 发现性能在上下文使用率超过 ~40% 后开始显著衰减。这不是软件 bug,而是 Transformer 架构的固有特性——每个 token 都要 attend to 所有其他 token( 关系),上下文越长,噪声越多。
三大应对策略#
策略 1:主动压缩(Compaction)#
对话历史接近窗口上限时,自动摘要压缩。OpenAI Codex 现在使用专门的 /responses/compact 端点,返回一个 type=compaction 项,用加密内容保留模型的潜在理解。
class ContextManager:
"""上下文管理器:自动压缩 + 分层加载。"""
def __init__(self, max_tokens: int = 200_000, compact_threshold: float = 0.4):
self.max_tokens = max_tokens
self.compact_threshold = compact_threshold # OpenAI 发现 40% 是拐点
self.messages = []
def should_compact(self) -> bool:
"""超过阈值就触发压缩。"""
current = self.estimate_tokens()
return current / self.max_tokens > self.compact_threshold
def compact(self):
"""压缩策略:优先清理旧的工具输出。"""
# Step 1: 清理旧的工具输出(最大的 token 消耗源)
for msg in self.messages[:-10]: # 保留最近 10 条
if msg.get("role") == "tool":
msg["content"] = self._summarize(msg["content"])
# Step 2: 如果还是太大,摘要早期对话
if self.should_compact():
early = self.messages[:len(self.messages)//2]
summary = self._summarize_conversation(early)
self.messages = [
{"role": "system", "content": f"Earlier conversation summary:\n{summary}"}
] + self.messages[len(self.messages)//2:]
def _summarize(self, content: str) -> str:
"""截断长工具输出,保留关键信息。"""
if len(content) > 2000:
return content[:1000] + "\n...[truncated]...\n" + content[-500:]
return contentpython缓存与压缩的矛盾:Prompt caching 要求前缀完全一致才能命中缓存;压缩会改变历史消息,破坏缓存。构建 Agent 需要在两者之间找到平衡。OpenAI 的做法是把静态内容(指令、工具定义)放在最前面,变化的消息追加在末尾,尽量保留前缀不变。
策略 2:外部记忆(Artifact-based Memory)#
关键信息应该即时持久化到文件,而不是依赖对话历史:
# Claude 打 Pokemon 的实践:通过 NOTES.md 跨 context reset 保持精确状态
class ExternalMemory:
def __init__(self, path="NOTES.md"):
self.path = path
def save_progress(self, task_id: str, status: str, findings: list[str]):
"""每完成一步就写文件——context reset 后仍可恢复。"""
with open(self.path, 'a') as f:
f.write(f"\n## Task {task_id}: {status}\n")
for finding in findings:
f.write(f"- {finding}\n")
def load_progress(self) -> str:
"""新会话开始时,先读取之前的进度。"""
if os.path.exists(self.path):
with open(self.path) as f:
return f.read()
return ""python策略 3:JIT 上下文检索(按需加载)#
最好的 Agent 不会预加载所有信息。它们保持轻量引用,运行时动态获取:
Progressive Disclosure 策略
├── L0: 全局地图(文件树 + 模块摘要) < 2K tokens ← 始终在 context 里
├── L1: 当前任务相关文件列表 < 5K tokens ← 任务开始时加载
└── L2: 具体代码片段(只读相关函数) < 30K tokens ← 按需检索plaintextClaude Code 就是这样做的:启动时加载 CLAUDE.md,然后用 glob 和 grep 按需探索文件。
“Lost in the Middle” 问题#
研究表明 ↗ LLM 对 context 中间部分的信息关注度最低。系统设计对策:
| 位置 | 应该放什么 | 原因 |
|---|---|---|
| 开头 | System prompt、AGENTS.md、核心规则 | 关注度最高 |
| 中间 | 参考材料、工具输出、历史对话 | 允许部分丢失 |
| 结尾 | 当前任务指令、关键约束 | 关注度次高 |
关键规则应该双重强化:同时写在 system prompt 和工具输出中。
四、Harness Engineering#
“当 Agent 失败时,不要调 prompt——调 harness。” — OpenAI Harness 团队
这是近半年 Agent 工程领域最重要的认知转变。OpenAI 的 Harness 团队在 5 个月内让 Agent 生成了约 100 万行代码、~1500 个 PR,零手写代码。瓶颈从来不是 Agent 写代码的能力,而是缺少结构、工具和反馈机制。
核心原则#
原则 1:机械化执行 > 文档约束#
告诉 Agent “不要做 X”是建议;让 X 触发构建失败是规则。
OpenAI 最精妙的创新:linter 错误信息兼做修复指令。当 Agent 违反架构约束时,错误消息直接告诉它怎么修复——工具在工作的同时”教”Agent:
# 自定义 linter 规则:强制分层架构
# OpenAI 的做法:每个 domain 内代码只能 "forward" 依赖
# Types -> Config -> Repo -> Service -> Runtime -> UI
LAYER_ORDER = ["types", "config", "repo", "service", "runtime", "ui"]
def check_import(importing_file: str, imported_module: str) -> str | None:
"""检查导入是否违反分层架构。"""
importer_layer = get_layer(importing_file)
imported_layer = get_layer(imported_module)
if LAYER_ORDER.index(imported_layer) < LAYER_ORDER.index(importer_layer):
# 反向依赖——违规!
return (
f"ERROR: {importing_file} (layer: {importer_layer}) "
f"cannot import from {imported_module} (layer: {imported_layer}).\n"
f"FIX: Move shared types to the 'types' layer, "
f"or use the Provider interface for cross-cutting concerns.\n"
f"SEE: docs/architecture/layering.md"
) # ← 这条错误信息就是给 Agent 看的修复指令
return Nonepython原则 2:深度优先解决问题#
当 Agent 卡住时,不要替它写代码。诊断缺少什么能力(工具、护栏、抽象、文档),然后让 Agent 自己把这个能力编码进仓库。每个编码的能力都变成未来所有 Agent 任务的基础设施,复利效应随时间累积。
原则 3:仓库即文档#
所有团队知识必须以版本化、共存的制品形式存在于仓库中。Slack 讨论、Google Docs、口头默契对 Agent 来说不存在。如果在仓库里搜不到,Agent 就不知道。
OpenAI 最初尝试把所有信息塞进一个巨大的 AGENTS.md 文件——失败了。更好的方案是 ARCHITECTURE.md 模式 ↗(matklad, 2021):
repo/
├── ARCHITECTURE.md # 高层架构概览(< 500 行)
├── docs/
│ ├── architecture/
│ │ ├── layering.md # 分层规则
│ │ └── providers.md # 跨切面接口
│ └── decisions/
│ ├── 001-use-react.md # ADR: 为什么用 React
│ └── 002-auth-flow.md # ADR: 认证流程
└── CLAUDE.md # Agent 专用指令plaintext原则 4:Codebase Readiness#
让仓库对 Agent 友好的具体措施:
- Architecture Decision Records (ADRs) — 每个架构决策的背景和理由
- Golden Path Templates — 标准化的服务/模块/管道模板
- 统一入口 —
make test、make lint、make ci - 机器可读的 ownership 映射 — 目录级别的 CODEOWNERS
LangChain 的中间件架构#
LangChain 把 harness 设计成可组合的中间件管道:
class AgentHarness:
"""LangChain 风格的中间件管道。"""
def __init__(self):
self.middlewares = [
LocalContextMiddleware(), # 映射工作环境
LoopDetectionMiddleware(), # 防止重复失败
ReasoningSandwichMiddleware(), # 推理三明治
PreCompletionChecklistMiddleware(), # 完成前检查
]
async def run(self, task: str) -> str:
context = {"task": task, "messages": []}
for mw in self.middlewares:
context = await mw.process(context)
return context["result"]
class LocalContextMiddleware:
"""映射工作环境,避免 Agent 浪费时间探索。"""
async def process(self, ctx: dict) -> dict:
# 自动注入:目录结构、可用工具、Python 版本等
env_info = {
"cwd": os.getcwd(),
"python_version": sys.version,
"available_tools": list_tools(),
"directory_structure": get_tree(".", max_depth=2),
}
ctx["messages"].insert(0, {
"role": "system",
"content": f"Environment:\n{json.dumps(env_info, indent=2)}"
})
return ctx
class LoopDetectionMiddleware:
"""检测并阻断重复失败的循环。"""
def __init__(self, max_retries: int = 3):
self.action_history = []
self.max_retries = max_retries
async def process(self, ctx: dict) -> dict:
last_action = ctx.get("last_action")
if last_action:
same_count = sum(1 for a in self.action_history[-5:] if a == last_action)
if same_count >= self.max_retries:
ctx["messages"].append({
"role": "system",
"content": f"LOOP DETECTED: You've tried '{last_action}' "
f"{same_count} times and it keeps failing. "
f"Try a completely different approach."
})
self.action_history.append(last_action)
return ctx
class ReasoningSandwichMiddleware:
"""推理三明治:关键决策用高推理,执行用低推理。
LangChain 发现:xhigh 推理全程用反而得分低(53.9%,超时太多),
high 推理得 63.6%。最佳策略是 xhigh-high-xhigh(三明治):
- 规划阶段:xhigh reasoning(仔细想)
- 执行阶段:high reasoning(快速做)
- 验证阶段:xhigh reasoning(仔细查)
"""
async def process(self, ctx: dict) -> dict:
phase = ctx.get("phase", "plan")
if phase in ("plan", "verify"):
ctx["reasoning_effort"] = "high" # 关键决策用高推理
else:
ctx["reasoning_effort"] = "medium" # 执行阶段用快速推理
return ctxpython五、Agent Loop 架构#
OpenAI 在 Unrolling the Codex Agent Loop ↗ 中公开了 Agent 循环的完整技术蓝图。
核心循环#
async def agent_loop(user_input: str, tools: list, max_iterations: int = 50):
"""Agent 核心循环——用户输入到最终回复。
来源:OpenAI Codex Agent Loop 架构。
关键:每次 API 请求都带完整对话历史(stateless),
支持 Zero Data Retention 合规。
"""
messages = build_initial_messages(user_input) # system + tools + user
for i in range(max_iterations):
# 1. 调用模型(streaming)
response = await client.responses.create(
model="claude-sonnet-4-6",
input=messages,
tools=tools,
stream=True
)
# 2. 检查终止条件
if response.stop_reason == "end_turn":
# Agent 认为任务完成,返回最终消息
return response.content
if response.stop_reason == "tool_use":
# 3. 提取工具调用
tool_calls = extract_tool_calls(response)
for call in tool_calls:
# 4. 执行工具(在沙箱中)
result = await execute_in_sandbox(call.name, call.arguments)
# 5. 追加结果到消息历史
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "tool",
"tool_use_id": call.id,
"content": truncate(result, max_tokens=25_000) # 截断长输出
})
# 6. 回到步骤 1(循环继续)
continue
# 达到最大迭代次数
return "Max iterations reached. Progress saved to NOTES.md."pythonPrompt 结构与缓存优化#
Codex API 会重排 prompt 组件的顺序以最大化缓存命中率:
请求结构(按优先级递减):
┌─────────────────────────────────────────┐
│ Server System Message (最高优先级) │ ← 静态,缓存友好
│ Tool Definitions │ ← 静态,缓存友好
│ Client Instructions (CLAUDE.md 等) │ ← 较静态,缓存友好
├─────────────────────────────────────────┤
│ Message History │ ← 变化的部分
│ ├── User Message 1 │ 只追加不修改
│ ├── Assistant Response 1 │ 以保留前缀
│ ├── Tool Result 1 │
│ ├── ... │
│ └── User Message N (最新) │
└─────────────────────────────────────────┘
缓存命中条件:前缀完全一致。
所以:静态内容在前,变化内容追加在后。plaintext实现选择:Codex CLI 用 Rust 编写,目标是大规模运行时的毫秒级性能。超过 90% 的 Codex 应用代码由 Codex 自身生成。
六、规划能力不稳定#
Agent 执行多步任务时,经常”跑偏”或忘记原始目标。这不是 prompt 写得不好,而是 LLM 在长程推理中的固有弱点。
外部计划 + 逐步验证#
OpenAI Codex 团队在生产中总结的四个关键机制:
- 外部计划文件 — Agent 不靠”记住计划”,而是每步都重读
exec-plan - 逐步验证 — 每完成一个 work slice,运行测试/lint/boundary check。不通过 = 立即修复,不继续下一步
- Done-when routine — Agent 不自行判断”做完了”,由外部 grader/checklist 判断
- 强制 shutdown — 到了 context budget 阈值就必须停,更新 progress → 提交 → 新会话接手
Plan-Act-Reflect 循环#
Harness 层面的保障:用 structured output 强制 Agent 在每步输出三元组:
# 强制 Agent 输出结构化的思考-行动-观察三元组
step_schema = {
"type": "object",
"properties": {
"thought": {
"type": "string",
"description": "你的推理过程:当前状态分析 + 下一步计划"
},
"action": {
"type": "string",
"enum": ["search_code", "read_file", "edit_file", "run_test",
"run_lint", "commit", "ask_human", "done"],
"description": "要执行的动作"
},
"action_input": {
"type": "object",
"description": "动作的参数"
},
"confidence": {
"type": "number",
"minimum": 0, "maximum": 1,
"description": "对当前方向的信心分数"
}
},
"required": ["thought", "action", "action_input", "confidence"]
}
# 当信心分数低于阈值时,强制 Agent 回到 Plan 阶段
if step["confidence"] < 0.3:
messages.append({
"role": "system",
"content": "LOW CONFIDENCE DETECTED. Re-read the exec-plan and "
"reconsider your approach before continuing."
})pythonLangChain 的 Pre-Completion Checklist#
Agent 最常见的失败模式:写完代码 → 重读自己的代码 → 觉得没问题 → 结束。 实际上代码有 bug。
class PreCompletionChecklistMiddleware:
"""在 Agent 宣布"完成"之前,强制执行检查清单。"""
async def process(self, ctx: dict) -> dict:
if ctx.get("agent_wants_to_finish"):
checklist = [
("tests_pass", "Run ALL tests, not just the ones you wrote"),
("lint_clean", "Run linter on changed files"),
("spec_match", "Re-read the original task spec and verify each requirement"),
("no_debug_code", "Remove any debug prints or temporary code"),
("edge_cases", "Test at least 2 edge cases"),
]
failed = []
for check_id, instruction in checklist:
result = await self.run_check(check_id, ctx)
if not result["pass"]:
failed.append(f"FAILED: {instruction}\n Reason: {result['reason']}")
if failed:
ctx["agent_wants_to_finish"] = False
ctx["messages"].append({
"role": "system",
"content": "COMPLETION BLOCKED. Fix these issues:\n" +
"\n".join(failed)
})
return ctxpython七、自我偏见(Self-Bias)#
确认偏误#
在同一个 context window 内,Agent 对自己生成的代码有确认偏误——它”记得”自己为什么这样写,所以倾向于认为是对的。就像作者校对自己刚写的稿子,总是看不到错误。
Writer/Reviewer 分离#
解决方案:用全新 context window 的独立 Agent 做 review,打破确认偏误。
async def agent_code_review(pr_diff: str, spec: str) -> dict:
"""独立 Reviewer Agent:全新 context,没有 Writer 的记忆。"""
review_prompt = f"""Review this code change against the spec.
SPEC: {spec}
DIFF:
{pr_diff}
Check for:
1. Does the change actually satisfy the spec? (not just "looks reasonable")
2. Are there edge cases not handled?
3. Are there security issues (injection, XSS, etc.)?
4. Are there performance issues?
Be specific. If you find issues, show the exact line and explain why."""
# 关键:这是一个全新的 context window
# Reviewer 不知道 Writer 的推理过程,只看代码本身
response = await client.messages.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": review_prompt}],
max_tokens=4096
)
return parse_review(response.content)pythonOpenAI 的实践:他们把几乎所有 code review 都推给了 agent-to-agent 循环,人类只在 2 轮 review 后仍有分歧时才介入。
八、工具调用设计(ACI)#
Anthropic 在 Writing Tools for Agents ↗ 中提出了一个核心类比:在 ACI(Agent-Computer Interface)上投入的精力应该和 HCI(Human-Computer Interface)一样多。 为人类开发者设计的工具,直接给 Agent 用往往效果很差。
设计原则#
完整的设计原则清单(综合 Anthropic + OpenAI):
- 命名清晰:工具名 = 动作 + 对象(
search_files、create_user) - 参数最小化:必填参数越少越好,可选参数有合理默认值
- 输出结构化:返回 JSON,不返回自由文本
- 错误即指令:报错信息告诉 Agent 怎么修,不只是说”失败了”
- 幂等安全:同一个调用执行多次结果一样(Agent 可能重试)
- 不暴露内部状态:工具是黑盒,Agent 不需要理解实现
- Token 效率:默认截断长响应,返回续取指令(Claude Code 限制工具响应 25,000 tokens)
- 提供输入示例:复杂工具在定义中附带合法输入的具体例子
- 合并任务单元:
schedule_event一步完成候选搜索 + 可用性检查 + 创建,不要拆成 3 个工具 - 避免让 LLM 做 LLM 不擅长的事
完整的工具定义示例#
# 一个设计良好的搜索工具——遵循所有 10 条原则
search_tool = {
"type": "function",
"function": {
"name": "search_codebase", # 原则 1: 动作 + 对象
"description": (
"Search for code patterns in the repository. "
"Returns matching file paths and line numbers. "
"Use this instead of reading entire files — it's faster and "
"uses less context." # 告诉 Agent 什么时候用这个工具
),
"parameters": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": (
"Regex pattern to search for. "
"Examples: 'def process_payment', 'class.*Handler', "
"'TODO|FIXME|HACK'" # 原则 8: 提供输入示例
)
},
"file_type": {
"type": "string",
"description": "Filter by file extension",
"enum": ["py", "ts", "js", "go", "rs", "java", "all"],
"default": "all" # 原则 2: 可选参数有默认值
},
"max_results": {
"type": "integer",
"description": "Max results to return (default 20, max 100)",
"default": 20,
"maximum": 100 # 原则 7: 控制输出大小
}
},
"required": ["pattern"], # 原则 2: 最少必填参数
"additionalProperties": False
},
"strict": True
}
}pythonMCP vs 原生 CLI#
| 场景 | 推荐 | 原因 |
|---|---|---|
git、docker、kubectl | CLI 优先 | 可组合(pipe)、训练数据丰富、不需要额外服务 |
| 私有 API(无 CLI) | MCP | 没有现成的 CLI 工具 |
| 认证的内部服务 | MCP | 需要 token 管理和 session 保持 |
| 只包装了 CLI 的 MCP | 直接用 CLI | 多此一举 ↗ |
九、架构模式选择#
Anthropic 在 Building Effective Agents ↗ 中识别了五种基本模式。指导哲学:只在简单方案明显不足时才增加复杂度。
五种模式对比#
| 模式 | 适用场景 | 不适用场景 |
|---|---|---|
| Prompt Chaining | 明确的顺序阶段(内容生成、多步转换) | 步骤无法预先确定 |
| Parallelization | 独立子任务(文件分析、测试生成)或投票提高置信度 | 子任务之间有依赖 |
| Routing | 不同输入类型需要不同处理路径 | 简单的单路径工作流 |
| Orchestrator-Worker | 复杂、步数不固定的任务 | 线性工作流;会乘以成本 |
| Evaluator-Optimizer | 需要迭代改进的任务(代码审查、内容优化) | 一次性任务 |
Plan-then-Execute 跨切面模式#
将规划(用强推理模型)和执行(用快速便宜模型)解耦,大幅减少对主 LLM 的调用次数:
async def plan_then_execute(task: str):
"""规划用 Opus,执行用 Sonnet。"""
# Phase 1: 规划(高推理模型)
plan = await client.messages.create(
model="claude-opus-4-6", # 强推理
messages=[{"role": "user", "content": f"Plan steps to: {task}"}],
max_tokens=2048
)
steps = parse_plan(plan.content)
# Phase 2: 逐步执行(快速模型)
results = []
for step in steps:
result = await client.messages.create(
model="claude-sonnet-4-6", # 快速执行
messages=[{
"role": "user",
"content": f"Execute this step:\n{step}\n\nContext:\n{results[-3:]}"
}],
tools=tools
)
results.append(result)
return resultspython多 Agent 扩展:Google 的发现#
Google 2026 年 3 月的研究 ↗发现三个关键效应:
- 工具协调权衡 — 需要多工具协作的任务,多 Agent 的开销反而降低性能
- 能力饱和 — 增加 Agent 数量的收益在某个阈值后急剧递减
- 通信开销非线性增长 — Agent 间通信成本随数量指数级上升
子 Agent 的正确用法不是”角色扮演”(前端工程师 + 后端工程师),而是上下文隔离:父 Agent 只看到子 Agent 的 prompt 和最终结果,中间的工具调用、工具输出都不会污染父 Agent 的 context window。
十、评估与可观测性#
Agent 系统的评估不能只看最终输出对不对。一个”碰巧正确”的结果和一个”稳定正确”的过程,价值完全不同。
五层评估体系#
| 评估层次 | 方法 | 示例指标 |
|---|---|---|
| 结果正确性 | End-state check | 测试通过率、环境状态匹配 |
| 过程质量 | Agent trace analysis | 步骤合理性、无效循环次数 |
| 效率 | 资源消耗 | Token 消耗、工具调用次数、完成时间 |
| 鲁棒性 | 多次 trial | 同一任务多次运行的一致性 |
| 安全性 | 边界检测 | 权限越界、危险操作拦截率 |
核心观点:Agent 的 narration(“我完成了”)不能作为验证依据。 必须检查环境状态——Anthropic 称之为 “outcome beats narration”。
Trace-based Debugging#
传统日志对 Agent 的非确定性、多步流程无效——同一输入可能产生完全不同的执行路径。LangChain 使用 trace 级别的可视化:
import langsmith
@langsmith.traceable(name="bug_fix_agent")
async def fix_bug(issue: str):
"""每个工具调用、LLM 调用、状态转换都被 trace。"""
# 这些调用会自动记录到 LangSmith trace
files = await search_code(issue) # trace: search_code
context = await read_files(files[:3]) # trace: read_files
fix = await generate_fix(context) # trace: generate_fix (LLM call)
result = await apply_and_test(fix) # trace: apply_and_test
return result
# 事后分析:
# - 哪些步骤耗时最长?
# - 哪些工具调用失败了?
# - Agent 是否进入了无效循环?
# - Token 消耗分布如何?python迭代改进工具#
Anthropic 的实践:用 Agent 自己来改进工具。
- 搭建快速原型工具
- 本地测试
- 跑全面评估
- 把评估日志批量喂给 Claude Code,让它改进工具定义
用 Claude 协作改进 Slack 和 Asana MCP 工具的准确率,比人工手写的工具定义更高。
关键基准数据#
| 指标 | 数据 | 来源 |
|---|---|---|
| Harness-only 提升 | 52.8% → 66.5% (Terminal Bench 2.0) | LangChain |
| 失败率降低 | 62% | LangChain |
| 推理三明治 vs 全程高推理 | 63.6% vs 53.9%(后者超时太多) | LangChain |
| 上下文性能拐点 | ~40% 使用率 | OpenAI |
| Agent 生成代码量 | ~100 万行、~1500 PR、0 手写 | OpenAI Harness |
| Context Engineering 提升 | 最高 54% benchmark 改进 | Anthropic |
十一、安全与信任#
Zero Trust for Agents#
每个 Agent 都应被视为非人类身份(NHI),拥有独立的权限体系:
- 每个任务使用临时的、窄范围的凭证,而非长期静态 API key
- 工具不应全局可用——按 Agent 或按任务限定工具访问范围
- Agent 生成的代码应像新员工的代码一样对待——同样严格的 code review、lint、测试
TDD with Agents#
Anthropic 推荐的 Claude Code 最佳实践 ↗中特别强调 TDD 工作流:
# Step 1: 让 Agent 写测试(明确禁止写实现)
claude "Write failing tests for the payment retry logic.
DO NOT write any implementation code."
# Step 2: 提交测试
git add tests/ && git commit -m "Add payment retry tests"
# Step 3: 让 Agent 写实现(明确禁止改测试)
claude "Write code to make all tests pass.
DO NOT modify any test files."bashTDD 在 Agent 场景下比人类场景更强大,因为它提供了机械化的验证循环——Agent 不需要主观判断”写完了没有”,测试结果会客观地告诉它。
十二、综合实战:自主修复 Bug 的 Agent 系统#
把前面所有原则综合起来,设计一个端到端的 bug 修复 Agent:
async def bug_fix_pipeline(issue_url: str):
"""端到端 Bug 修复 Agent——综合运用所有设计原则。"""
# ===== 阶段 1: 理解 =====
issue = await fetch_issue(issue_url)
# 用 code search 工具,不让 Agent 猜路径 (§1 精确计算)
related_files = await search_codebase(
pattern=extract_keywords(issue.body),
file_type=detect_language(issue)
)
# 复现 bug——验证确实失败 (§2 幻觉防御)
repro_result = await run_tests(filter=issue.labels)
assert not repro_result.all_pass, "Bug not reproduced, investigate further"
# ===== 阶段 2: 定位 =====
# 只加载相关代码,最小化 context 占用 (§3 上下文)
context = await read_files(
related_files[:5], # 最多 5 个文件
sections="relevant_functions_only" # 只读相关函数
)
# 生成假设,写入外部计划 (§6 规划)
plan = await generate_plan(issue, context)
await save_to_file("exec-plan.md", plan)
# ===== 阶段 3: 修复 =====
# 用 SEARCH_AND_REPLACE,不用行号 (§1)
fix = await generate_fix(plan, context)
await apply_search_and_replace(fix.file, fix.old_text, fix.new_text)
# 逐步验证 (§4 Harness)
test_result = await run_tests()
if not test_result.all_pass:
# 不继续下一步,立即修复
await retry_fix(test_result.failures, plan)
lint_result = await run_lint()
assert lint_result.clean, f"Lint violations: {lint_result.issues}"
# ===== 阶段 4: 验证 =====
# Outcome > Narration: 验证环境状态 (§10 评估)
final_tests = await run_tests(full_suite=True)
assert final_tests.all_pass, "Full test suite must pass"
# ===== 阶段 5: 提交 =====
pr = await create_pr(issue, fix)
# 独立 Reviewer Agent (§7 自我偏见)
review = await agent_code_review(pr.diff, issue.body)
if review.approved:
return pr.url
elif review.round < 2:
# 根据 review 反馈修改,再次提交
await address_review_comments(review.comments)
return await resubmit_pr(pr)
else:
# 2 轮不过 → 升级给人类 (§11 安全)
await escalate_to_human(pr, review)python关键设计决策回溯#
| 代码中的决策 | 对应原则 |
|---|---|
search_codebase() 而非猜路径 | §1 精确计算 |
SEARCH_AND_REPLACE 而非行号 | §1 精确计算 |
run_tests() 验证而非信 Agent | §2 幻觉防御 + §10 评估 |
| 只加载 5 个相关文件 | §3 上下文管理 |
| linter 错误兼做修复指令 | §4 Harness Engineering |
写入 exec-plan.md | §6 规划稳定 |
| 独立 Reviewer Agent | §7 自我偏见 |
| 2 轮后升级给人类 | §11 安全 |
总结:核心原则速查表#
| 原则 | 在系统中的体现 | 来源 |
|---|---|---|
| LLM 负责推理,工具负责执行 | 不让 LLM 数数/算术/精确匹配 | ReAct 模式 |
| Outcome > Narration | 用环境状态验证,不信 Agent 自述 | Anthropic |
| 机械化执行 > 文档约束 | 规则写在 CI/linter 里,不只是 docs | OpenAI Harness |
| 外部记忆 > 对话历史 | progress 文件、exec-plan、git history | Anthropic Context Engineering |
| 分离 > 集中 | 写和审分开、规划和执行分开 | OpenAI Agent-to-Agent |
| Agent 失败 = 环境 bug | 调 harness,不只调 prompt | LangChain |
| 40% 上下文拐点 | 超过 40% 使用率后性能衰减 | OpenAI |
| 推理三明治 | 关键决策高推理,执行快推理 | LangChain |
| 子 Agent 是上下文隔离 | 不是角色扮演,是 context firewall | HumanLayer |
| TDD > 主观判断 | 测试是 Agent 的机械化验证循环 | Anthropic |
References#
- Anthropic: Writing Tools for Agents ↗
- Anthropic: Effective Context Engineering for AI Agents ↗
- Anthropic: Building Effective Agents ↗
- Anthropic: Claude Code Best Practices ↗
- OpenAI: Harness Engineering ↗
- OpenAI: Unrolling the Codex Agent Loop ↗
- HumanLayer: Skill Issue — Harness Engineering for Coding Agents ↗
- LangChain: Improving Deep Agents with Harness Engineering ↗
- LangChain: The Anatomy of an Agent Harness ↗
- Google: Scaling Principles for Agentic Architectures ↗
- Building AI Coding Agents for the Terminal (arXiv) ↗
- Lost in the Middle: How Language Models Use Long Contexts ↗
- vLLM: HaluGate — Token-level Hallucination Detection ↗
- The Emerging Harness Engineering Playbook ↗
- Fighting Context Rot (Inkeep) ↗