Appearance
第1章 Agent 不等于大模型:Harness 的价值
1.1 Demo 五分钟,生产五个月
如果你在 2025 年参加过任何 AI Hackathon,一定见过这样的场景:一个三人小队用一个周末搭出一个令人惊叹的 AI Agent Demo——它能读代码、调 API、写测试、甚至自动修 bug。评委赞不绝口,观众掌声雷动。
然后这个 Demo 就死了。
不是因为它不够酷,而是因为它无法在真实环境中存活。用户输入一段中文夹英文的需求,它的 prompt 解析崩了;调用一个返回 500 的 API,它陷入了无限重试;用户连续追问三轮,它的上下文窗口爆了;更要命的是,有一次它"自主决策"删掉了用户的生产数据库——因为没有任何权限控制。
这就是 Agent 工程的核心悖论:大模型的能力已经足够强大,但把这个能力安全、可靠、高效地交付给用户,是一个完全不同的工程问题。
让我用一个简单的对比来说明:
python
# 这是一个 "Demo 级" Agent —— 30 行代码
import anthropic
client = anthropic.Anthropic()
def simple_agent(user_input: str) -> str:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[{"role": "user", "content": user_input}]
)
return response.content[0].text
# 它能工作吗?能。它能上生产吗?不能。这 30 行代码缺少什么?缺少的东西可以列一个长长的清单:
- 工具系统:模型需要调用外部工具(读文件、执行命令、查数据库),但谁来定义工具、校验参数、处理超时?
- 权限控制:模型说"我要删除这个文件",谁来决定它是否有权这样做?
- 上下文管理:对话进行到第 20 轮,token 数量逼近窗口上限,谁来决定裁剪哪些消息?
- 错误恢复:工具调用失败了,是重试、跳过、还是降级处理?
- 流式输出:用户不想等 30 秒才看到第一个字,怎么实现逐字输出?
- 可观测性:生产环境中出了问题,怎么知道模型在第几步、因为什么原因做出了错误决策?
- 成本控制:模型陷入循环疯狂调用工具,一个请求烧掉 50 美元,谁来刹车?
- 多模态输入:用户传了一张截图,怎么处理?
- 会话持久化:用户关掉浏览器再回来,之前的对话还在吗?
这些"缺少的东西",就是 Harness。
1.2 什么是 Harness Engineering
1.2.1 一个类比:马与马具
"Harness"这个词在英文中的本意是马具——缰绳、鞍座、马镫、笼头的总称。一匹马的奔跑能力再强,没有马具,骑手就无法驾驭它。马具不是马的一部分,但没有马具,马对骑手的价值就无法兑现。
这个类比精确地映射到 AI Agent 的世界:
| 马的世界 | Agent 的世界 |
|---|---|
| 马(原始动力) | 大模型(LLM) |
| 缰绳(方向控制) | Prompt Engineering + 编排逻辑 |
| 鞍座(稳定接口) | 工具系统 + API 抽象 |
| 马镫(安全保障) | 权限模型 + 安全边界 |
| 笼头(约束范围) | 上下文管理 + 输出校验 |
| 马车(承载用途) | 用户界面 + 交互协议 |
| 骑手(使用者) | 终端用户 |
Harness Engineering,就是设计和构建这套"马具"的工程学科。
下面这张图展示了 Harness 在整个 Agent 系统中的位置:
更正式地定义:
Harness Engineering 是指在大模型(LLM)和终端用户之间,设计、实现、优化工程层的方法论。这个工程层负责工具编排、提示词管理、安全控制、上下文管理、状态持久化、可观测性等一系列关键功能,使得大模型的能力能够安全、可靠、高效地交付给用户。
1.2.2 Harness 的六大支柱
一个成熟的 Harness 系统通常包含以下六个核心子系统:
┌──────────────────────────────────────────────┐
│ 用户界面层 │
│ (CLI / Web / IDE / API) │
├──────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 提示词 │ │ 工具系统 │ │ 编排引擎 │ │
│ │ 管理 │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 安全与 │ │ 记忆与 │ │ 可观测 │ │
│ │ 权限 │ │ 上下文 │ │ 性 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Harness Engineering │
├──────────────────────────────────────────────┤
│ 大模型 (LLM) API │
└──────────────────────────────────────────────┘第一支柱:提示词管理(Prompt Management)
提示词不只是一句"你是一个有帮助的助手"。在生产级 Agent 中,System Prompt 可能长达数万字,包含角色定义、行为规范、工具使用说明、输出格式约束、安全规则等多个维度。提示词管理需要解决版本控制、A/B 测试、动态注入、上下文长度优化等问题。
typescript
// Claude Code 的 System Prompt 构建(简化示意)
function buildSystemPrompt(context: AgentContext): string {
return [
getBasePersona(), // 基础人格
getToolDescriptions(context.tools), // 工具描述(根据可用工具动态生成)
getEnvironmentInfo(context.env), // 环境信息(OS、Shell、CWD)
getMemoryContext(context.memory), // 记忆上下文(.claude/CLAUDE.md)
getSafetyRules(context.permissions), // 安全规则
getUserPreferences(context.config), // 用户偏好
].join("\n\n");
}第二支柱:工具系统(Tool System)
工具系统是 Agent 与外部世界交互的桥梁。一个健壮的工具系统需要处理:工具注册与发现、参数校验(schema validation)、执行超时、错误处理、结果格式化、并发控制。
typescript
// 一个生产级工具定义的骨架
interface Tool {
name: string;
description: string;
inputSchema: ZodSchema; // 运行时类型校验
permissions: PermissionRule[];// 权限声明
timeout: number; // 超时限制
execute(input: unknown, context: ToolContext): Promise<ToolResult>;
}
// 工具执行不是简单的函数调用
async function executeTool(tool: Tool, input: unknown, context: ToolContext) {
// 1. 参数校验
const parsed = tool.inputSchema.safeParse(input);
if (!parsed.success) return { error: `Invalid input: ${parsed.error}` };
// 2. 权限检查
const allowed = await checkPermission(tool, parsed.data, context);
if (!allowed) return { error: "Permission denied" };
// 3. 带超时的执行
const result = await Promise.race([
tool.execute(parsed.data, context),
timeout(tool.timeout).then(() => ({ error: "Timeout" }))
]);
// 4. 结果记录(用于可观测性)
context.telemetry.recordToolCall(tool.name, parsed.data, result);
return result;
}第三支柱:编排引擎(Orchestration Engine)
编排引擎负责 Agent 的核心循环——接收用户输入、调用模型、解析工具调用、执行工具、将结果反馈给模型、判断是否终止。这听起来简单,但实际上涉及大量的工程决策:循环终止条件、并行工具调用、子 Agent 协调、流式输出管理。
python
# Agent 循环的伪代码
async def agent_loop(user_input: str, context: AgentContext):
messages = context.history + [{"role": "user", "content": user_input}]
for step in range(MAX_STEPS):
# 调用模型
response = await call_model(messages, tools=context.available_tools)
# 检查是否有工具调用
tool_calls = extract_tool_calls(response)
if not tool_calls:
# 模型直接给出了最终回答
yield FinalAnswer(response.text)
return
# 执行所有工具调用(可能并行)
results = await execute_tools_parallel(tool_calls, context)
# 将工具结果追加到消息历史
messages.append(response.to_message())
messages.extend(tool_result_messages(results))
# 上下文窗口管理:如果消息太长,进行裁剪
messages = truncate_if_needed(messages, context.max_tokens)
# 达到最大步数,强制终止
yield Error("Max steps exceeded")这个循环的核心流程可以用下图表示:
第四支柱:安全与权限(Safety & Permissions)
这是 Harness 中最容易被低估、也最容易出灾难性事故的部分。安全系统需要回答:哪些工具在什么条件下可以被使用?文件系统的哪些路径可以被读写?哪些操作需要用户确认?如何防止 Prompt Injection?
第五支柱:记忆与上下文(Memory & Context)
短期记忆(当前对话历史)和长期记忆(跨会话持久化的知识)的管理。上下文窗口的裁剪策略、向量数据库的检索、项目级知识的注入(如 Claude Code 的 CLAUDE.md 机制),都属于这个支柱。
第六支柱:可观测性(Observability)
在生产环境中,你需要知道:Agent 执行了多少步?每步花了多长时间?调用了哪些工具?消耗了多少 token?为什么在第 7 步做出了错误决策?这些信息对于调试、优化和合规审计都至关重要。
1.3 真实世界的 Harness:三个案例
理论总是苍白的,让我们看看真实世界中 Harness Engineering 的具体体现。
1.3.1 Claude Code:51 万行 TypeScript 的 Harness
Claude Code 是 Anthropic 官方的 AI 编程助手 CLI。它的底层模型是 Claude——和 API 里调用的同一个模型。但 Claude Code 包含了大约 51 万行 TypeScript 代码,这些代码全部是 Harness。
模型本身的代码量?零。因为模型是通过 API 调用的云服务。
这 51 万行代码在做什么?
| 子系统 | 代码量(估算) | 职责 |
|---|---|---|
| 工具系统 | ~8 万行 | 40+ 内置工具的定义、校验、执行 |
| 权限模型 | ~3 万行 | 三级权限、规则匹配、动态分类器 |
| 流式引擎 | ~5 万行 | Async Generator 管道、SSE 解析、UI 渲染 |
| MCP 集成 | ~6 万行 | MCP 客户端、服务发现、OAuth 认证 |
| IDE Bridge | ~4 万行 | VS Code 扩展、WebSocket 通信、JWT 认证 |
| 终端 UI | ~5 万行 | React/Ink 组件、主题、快捷键 |
| 上下文管理 | ~3 万行 | 对话裁剪、记忆注入、文件索引 |
| 多 Agent | ~4 万行 | 子 Agent 生成、Task 分发、结果聚合 |
| 基础设施 | ~13 万行 | 配置管理、日志、遥测、测试、构建 |
关键洞察:Claude Code 的用户体验——流畅的交互、安全的文件操作、智能的上下文理解——不是来自模型本身,而是来自这 51 万行 Harness 代码。换一个同等能力的模型,只要 Harness 不变,用户体验几乎不会有本质差异。反过来,同样的模型,配上一个粗糙的 Harness,体验会天差地别。
1.3.2 LangChain / LangGraph:通用 Harness 框架
如果说 Claude Code 是一个"垂直集成"的 Harness(为特定产品定制),那么 LangChain 和 LangGraph 就是"通用"的 Harness 框架——它们提供构建 Harness 的积木。
LangChain 的核心贡献是建立了一套标准化的抽象:
python
# LangChain 的 Harness 抽象层
from langchain_core.language_models import BaseChatModel # 模型抽象
from langchain_core.tools import BaseTool # 工具抽象
from langchain_core.prompts import ChatPromptTemplate # 提示词抽象
from langchain_core.output_parsers import BaseOutputParser # 输出解析抽象
from langchain_core.runnables import RunnableSequence # 编排抽象LangGraph 在此基础上加入了有状态的图执行引擎:
python
from langgraph.graph import StateGraph, START, END
# 定义状态
class AgentState(TypedDict):
messages: list[BaseMessage]
tool_results: list[dict]
step_count: int
# 构建图
graph = StateGraph(AgentState)
graph.add_node("think", call_model)
graph.add_node("act", execute_tools)
graph.add_edge(START, "think")
graph.add_conditional_edges("think", should_continue, {
"continue": "act",
"end": END,
})
graph.add_edge("act", "think")
# 编译并运行
agent = graph.compile(checkpointer=MemorySaver())这段代码的每一行都是 Harness,没有一行是模型。模型只是 call_model 节点内部的一个 API 调用。图的定义、状态管理、条件路由、检查点持久化——这些全部是 Harness Engineering 的范畴。
1.3.3 OpenAI Agents SDK:最小化 Harness
2025 年 OpenAI 发布的 Agents SDK 代表了另一种 Harness 设计哲学——极简主义:
python
from agents import Agent, Runner
agent = Agent(
name="assistant",
instructions="你是一个有帮助的助手。",
tools=[web_search, file_reader],
)
result = Runner.run_sync(agent, "帮我搜索最新的 AI 论文")看起来只有几行代码,但 Runner.run_sync 内部隐藏了一个完整的 Harness:Agent 循环、工具调度、Handoff(Agent 间切换)、Guardrail(安全护栏)、Tracing(追踪)。它的设计选择是"约定优于配置"——大量默认行为被封装在框架内部,开发者只需要声明式地定义 Agent 的行为。
三个案例的对比揭示了一个重要事实:不管外在形态如何不同,Harness 的核心组件是相同的——工具、编排、安全、记忆、可观测性。 差异只在于哪些部分暴露给开发者,哪些部分被封装起来。
1.4 为什么是现在
Harness Engineering 并不是一个全新的概念。从某种意义上说,任何在 LLM 和用户之间写代码的人,都在做 Harness Engineering。但为什么在 2025-2026 年,它突然成为了一个值得系统化研究的学科?
1.4.1 模型能力已经跨过了"够用"线
在 GPT-3 时代,模型的能力是主要瓶颈。你可以写出最精巧的 Harness,模型依然会在基本的推理任务上犯错。那个时候,提升模型能力的 ROI 远远高于优化 Harness。
但到了 2025 年,以 Claude Sonnet、GPT-4o、Gemini 2.0 为代表的一代模型,在代码生成、逻辑推理、工具调用等 Agent 核心能力上已经"够用"了。这里的"够用"不是说完美,而是说模型的能力上限已经远远超过了大多数 Harness 的兑现能力。
换句话说:瓶颈从模型侧转移到了 Harness 侧。
一个形象的比喻:在 2023 年,你有一匹小马驹,即使给它配上最好的马具,它也跑不了多远。所以你的核心投资应该是"养马"。但到了 2025 年,你的马已经是一匹千里马了,限制它速度的不再是它的腿,而是你的马具是否合脚、缰绳是否顺手、道路是否平坦。
1.4.2 工具调用成为一等公民
早期的 LLM API 需要开发者自己解析模型输出来提取"函数调用"意图。这种方式不可靠、不规范,导致 Harness 的工具系统需要做大量的容错处理。
2024-2025 年,所有主流 LLM API 都内置了结构化的 Tool Use / Function Calling 能力。模型可以可靠地输出 JSON 格式的工具调用请求,Harness 只需要做参数校验和执行即可。这大大降低了 Harness 的工程复杂度,但同时也提高了标准——用户的期望从"能调工具就行"变成了"调工具要快、要准、要安全"。
1.4.3 Agent 从实验室走向生产
2024 年是 AI Agent 概念爆发的一年,但大多数 Agent 还停留在 Demo 阶段。2025-2026 年,Agent 开始真正进入生产环境:
- Devin、Claude Code 等 AI 编程 Agent 被开发者日常使用
- Customer Service Agent 开始处理真实的客户工单
- Data Analysis Agent 在企业内部分析报表、生成洞察
- Workflow Automation Agent 替代传统的 RPA 脚本
生产环境对可靠性、安全性、可维护性的要求,比 Demo 高出几个数量级。这些要求全部落在 Harness 的肩上。
1.4.4 行业开始标准化
MCP(Model Context Protocol)的出现是一个标志性事件。它试图标准化 Agent 与外部工具/数据源的交互协议——这本质上就是对 Harness 工具系统的标准化尝试。
类似的标准化趋势还包括:
- OpenAI 的 Function Calling 规范 → 工具定义的标准化
- LangChain 的 Runnable 接口 → 编排单元的标准化
- OpenTelemetry for LLM → 可观测性的标准化
当一个领域开始出现标准化努力时,说明它已经从"手工作坊"阶段进入了"工程学科"阶段。Harness Engineering 正处在这个转折点上。
1.5 Harness Engineering 的核心挑战
既然 Harness 如此重要,它的核心工程挑战是什么?我们来逐一分析。
1.5.1 不确定性管理
传统软件的输入输出是确定的:给定相同的输入,函数总是返回相同的输出。但 Agent 的核心组件——LLM——是概率性的。同一个输入,模型可能返回不同的工具调用序列,甚至可能返回完全不同的推理路径。
这意味着 Harness 不能假设模型会"按照预期"行动。它必须:
python
# 不确定性管理的典型模式
async def robust_tool_call(model_response):
# 1. 校验模型输出的格式
if not is_valid_tool_call(model_response):
# 不是简单报错,而是尝试修复
model_response = await retry_with_clarification(model_response)
# 2. 校验参数的语义合理性
tool_name = model_response.tool_name
if tool_name not in ALLOWED_TOOLS:
return fallback_response("Unknown tool requested")
# 3. 执行时的防御性编程
try:
result = await execute_with_timeout(tool_name, model_response.args)
except ToolExecutionError as e:
# 将错误信息反馈给模型,让它自行修正
return ToolResult(error=str(e), suggestion="Please try a different approach")
# 4. 校验结果的合理性
if is_suspicious_result(result):
return await human_in_the_loop_review(result)
return result1.5.2 安全边界的划定
Agent 能力越强,安全风险越大。一个能够执行 Shell 命令的 Agent,本质上拥有了操作系统级别的能力。Harness 需要在"有用"和"安全"之间找到平衡:
- 太严格 → Agent 什么都做不了,用户放弃使用
- 太宽松 → Agent 一个错误决策,可能造成不可逆的损失
Claude Code 的权限模型提供了一个优秀的参考设计:将操作分为三级(允许、需确认、禁止),并支持用户通过配置文件自定义规则。这种"分级授权 + 用户可配置"的模式,是目前业界最成熟的安全 Harness 设计。
1.5.3 上下文窗口的经济学
大模型的上下文窗口虽然在持续扩大(从 4K 到 128K 到 1M),但窗口越大,成本越高、延迟越大。Harness 需要做"上下文经济学"的决策:
- 哪些信息值得放进上下文?(相关的代码文件 vs 无关的日志)
- 什么时候该裁剪历史消息?(保留最近 N 轮 vs 摘要式压缩)
- 如何在工具结果太长时进行截断?(保留前 N 行 vs 智能摘要)
这些决策直接影响 Agent 的质量、速度和成本,是 Harness 工程中最需要精心调优的部分之一。
1.5.4 状态管理的复杂性
一个生产级 Agent 需要管理多层状态:
对话状态 ← 当前会话的消息历史
工具状态 ← 工具执行的中间结果、文件句柄、数据库连接
Agent 状态 ← 当前在执行计划的第几步、已完成的子任务
用户状态 ← 用户的偏好设置、历史行为、权限级别
环境状态 ← 当前工作目录、Git 分支、运行时环境这些状态之间有复杂的依赖关系。例如,工具执行的结果会改变环境状态(写了一个文件),环境状态的变化会影响后续工具调用的行为(文件已存在,不需要再创建)。LangGraph 把这个问题抽象为"有状态图",是目前处理 Agent 状态管理最系统化的方法之一。
1.6 本书会教什么,不会教什么
1.6.1 本书的定位
这本书是关于 Harness Engineering 方法论 的书。它关注的是"如何构建从模型到用户之间的工程层"这个普适性问题,而不是某个特定框架的使用教程。
本书会教:
- Harness 各子系统(工具、编排、安全、记忆、可观测性)的设计原则和权衡取舍
- 来自真实项目(Claude Code、LangGraph、OpenClaw 等)的架构模式和实现细节
- 可以直接迁移到你自己项目中的工程模式——不管你用什么语言、什么框架
- 如何评估和选择 Harness 架构方案
- 生产环境中的常见陷阱和最佳实践
本书不会教:
- 大模型的训练和微调(那是模型工程的范畴)
- 某个特定框架的入门教程(官方文档做这件事比我好)
- Prompt Engineering 的技巧大全(这个领域变化太快,任何书籍都会迅速过时)
- 特定业务场景的完整解决方案(但会提供可组合的构建模块)
1.6.2 本书的读者
这本书适合以下读者:
- 正在构建 AI Agent 产品的工程师:你已经会调模型 API,现在需要把 Demo 变成生产级系统
- 技术负责人和架构师:你需要评估和选择 Agent 架构方案,或者需要设计内部的 Agent 平台
- 对 AI Agent 内部机制好奇的开发者:你用过 Claude Code、LangGraph、Dify 等工具,想理解它们内部是怎么工作的
1.6.3 本书的结构
本书将按照 Harness 的六大支柱组织内容,每个支柱对应若干章节:
- Agent 基础(第 1-3 章):什么是 Harness、Agent 循环的核心模型、提示词工程的系统化方法
- 工具与编排(第 4-7 章):工具系统设计、编排引擎、流式处理、多 Agent 协调
- 安全与控制(第 8-10 章):权限模型、沙箱机制、Guardrail 设计
- 记忆与上下文(第 11-13 章):上下文管理、长期记忆、RAG 集成
- 可观测性与运维(第 14-15 章):Tracing、评估、成本控制
- 实战与展望(第 16-18 章):端到端案例、设计模式总结、未来趋势
每一章的组织遵循 "为什么 → 是什么 → 怎么做 → 可迁移模式" 的递进结构。
1.7 小结
回到本章开头的问题:为什么拥有一个强大的大模型远远不够?
因为大模型只是一个概率推理引擎。它不知道你的文件系统长什么样,不知道你的 API 需要什么认证,不知道哪些操作是危险的,不知道你的对话历史有多长,不知道用户等了多久。把这个"不知道"变成"知道",把"不安全"变成"安全",把"不可靠"变成"可靠"——这就是 Harness Engineering 的使命。
在接下来的章节中,我们将逐一深入 Harness 的每个子系统,从原理到实现,从理论到生产实践。
让我们从 Agent 的心脏开始——编排循环。