你不知道的 Agent:原理、架构与工程实践

原文: 你不知道的 Agent:原理、架构与工程实践
作者: Tw93 (@HiTw93)
发布时间: 2026-03-21
整理: AI Assistant
标签: #Agent #AI #架构设计 #工程实践


📌 太长不读

在研究完 Claude Code 的架构之后,发现自己对 Agent 底层的理解还不够深入,加上团队在 Agent 方向已经有不少业务落地,但一直缺少一份系统梳理,所以又补了一轮资料、开源实现和轻量实验,把控制流、上下文工程、工具设计、记忆、评测和安全这些问题重新串了一遍。

这篇文章主要讲 Agent 架构里几块最影响工程效果的内容,最后用 OpenClaw 的实现把这些设计原则串起来看一遍。

核心结论

  • 更贵的模型带来的提升,很多时候没有想象中那么大
  • Harness 和验证测试质量对成功率的影响更大
  • 调试 Agent 行为时,应优先检查工具定义(多数工具选择错误都出在描述不准确)
  • 评测系统本身的问题,很多时候比 Agent 出问题更难发现

1. Agent Loop 的基本运转方式

Agent Loop 的核心实现逻辑抽象后其实不到 20 行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const messages: MessageParam[] = [{ role: "user", content: userInput }];

while (true) {
const response = await client.messages.create({
model: "claude-opus-4-6",
max_tokens: 8096,
tools: toolDefinitions,
messages,
});

if (response.stop_reason === "tool_use") {
const toolResults = await Promise.all(
response.content
.filter((b) => b.type === "tool_use")
.map(async (b) => ({
type: "tool_result" as const,
tool_use_id: b.id,
content: await executeTool(b.name, b.input),
}))
);
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
} else {
return response.content.find((b) => b.type === "text")?.text ?? "";
}
}

控制流:感知 → 决策 → 行动 → 反馈四个阶段不断循环,直到模型返回纯文本为止。

核心设计原则

看过不少 Agent 实现和官方 SDK,结构都差不多,循环本身相当稳定,从最小实现一路扩展到支持子 Agent、上下文压缩和 Skills 加载,主循环基本没有变化

新增能力通常都是叠加在循环外部,而不是改动循环内部:

  • 扩展工具集和 handler
  • 调整系统提示结构
  • 把状态外化到文件或数据库

不应该让循环体本身变成一个巨大的状态机,模型负责推理,外部系统负责状态和边界,一旦这个分工确定下来,核心循环逻辑就很少需要频繁调整了。


2. Workflow 和 Agent 有什么区别

Anthropic 对这两类系统有一个直接区分:

Workflow:执行路径由代码预先写死
Agent:由 LLM 动态决定下一步

核心区别在于控制权掌握在谁手里。现实中很多标着 Agent 的产品,深入看其实更接近 Workflow,不过两者本身并无高下之分,真正重要的是给任务找到更适合的解决方案。

维度 Workflow Agent
控制权 代码预定义,同输入必走同一路径 LLM 动态决策,可能需要评测验证
执行方式 工具顺序固定,错误走预设分支 工具按需选择,模型可尝试自我修复
状态与记忆 显式状态机,节点跳转清晰 隐式上下文,状态在对话历史中累积
维护成本 改流程需修改代码并重新部署 调整系统提示即可,无需重新部署
可观测性 日志定位节点,延迟可预估 需完整执行记录理解决策链,轮数不固定
人机协作 人在预设节点介入 人在任意轮次介入或接管
适用场景 流程固定、输入边界清晰 需要中间推理与灵活判断

3. 五种常见控制模式

大多数 AI 系统拆开看,其实都是这五种模式的组合,很多场景并不需要完整的 Agent 自主权,把其中几种模式搭起来就够了,关键还是看任务本身适合哪一种设计。

3.1 提示链 (Prompt Chaining)

任务拆成顺序步骤,每步 LLM 处理上一步的输出,中间可加代码检查点。

适用场景:生成后翻译、先写大纲再写正文这类线性流程。

1
用户输入 → Prompt 1 → 输出 1 → Prompt 2 → 输出 2 → ... → 最终输出

3.2 路由 (Routing)

对输入分类,定向到对应的专用处理流程。

适用场景:简单问题走轻量模型,复杂问题走强模型;技术咨询和账单查询走不同逻辑。

1
输入 → 分类器 → 路由到专用流程 A/B/C

3.3 并行 (Parallelization)

两种变体:

  • 分段法:把任务拆成独立子任务并发跑
  • 投票法:把同一任务跑多次取共识

适用场景:高风险决策或需要多视角的场景。


3.4 编排器 - 工作者 (Orchestrator-Workers)

中央 LLM 动态分解任务,委派给工作者 LLM,综合结果。

典型案例:nanobot 的 spawn 工具和 learn-claude-code 的子 Agent 模式都是这个原型。

1
中央 LLM → 分解任务 → 工作者 LLM 1/2/3 → 综合结果

3.5 评估器 - 优化器 (Evaluator-Optimizer)

生成器产出,评估器给反馈,循环直到达标。

适用场景:翻译、创意写作这类质量标准难以用代码精确定义的任务。

1
生成器 → 评估器 → 反馈 → 优化 → 循环直到达标

4. 为什么 Harness 比模型更关键 🔥

Harness 是指围绕 Agent 构建的测试、验证与约束基础设施,至少包括四个部分:

  • 验收基线
  • 执行边界
  • 反馈信号
  • 回退手段

模型虽然重要,但决定系统能不能稳定运行的,往往是这些外围工程条件。这个判断在代码编写这类高可验证任务上最成立,但在开放式研究、多轮协商这类弱验证任务里,模型上限本身仍然更关键。

4.1 OpenAI 的 Agent 优先开发实践

数据:3 个工程师 5 个月写了百万行代码,将近 1500 个 PR,是传统开发速度的 10 倍。

这个速度背后不是模型有多强,而是几个工程决策做对了:

✅ Agent 看不到的内容等于不存在

知识必须存在于代码库本身,外部文档对运行中的 Agent 不可见。AGENTS.md 只保留约 100 行作为索引,细节拆到各 docs 目录按需引用。

✅ 约束编码化而非文档化

写在文档里的规范很容易被忽略,编码进 Linter、类型系统或 CI 规则里的约束才具备可执行性。架构分层靠自定义 Linter 机械强制,不靠人工 Review。

✅ Agent 端到端自主完成任务

从验证当前状态、复现 Bug、实现修复、驱动应用验证,到开 PR、处理 Review 反馈、自主合并,全链路不需要人介入。查日志、查指标、查追踪都由 Agent 主动完成。

✅ 最小化合并阻力

测试偶发失败用重跑处理而不是阻塞进度,在高吞吐环境下等待人工审查的成本往往高于修复小错误的成本。写代码的纪律没有消失,只是从人工 Review 变成了机器执行的约束,一次写进去,到处生效。


4.2 Harness 的关键结论

任务清晰度验证自动化程度把任务分成四种状态:

1
2
3
4
5
6
7
8
9
             验证自动化程度
低 高
┌───────────┬───────────┐
高 │ 人在盯 │ 🎯 目标区 │
任 │ (左上角) │ (右上角) │
务 ├───────────┼───────────┤
清 │ 无效区 │ 危险区 │
晰 │ (左下角) │ (右下角) │
度 └───────────┴───────────┘

右上角(目标明确 + 可自动验证)是最适合 Agent 发挥的区域。

Harness 要做的:就是把任务推进右上角,让对错有机器可以执行的判断标准,而不是靠人盯。


5. 上下文工程为什么决定稳定性

Transformer 的注意力复杂度是 $O(n^2)$,上下文越长,关键信号越容易被噪声稀释。实践里最常见的失效模式是:无关内容一旦占到上下文的大头,Agent 的决策质量就会明显下滑。

这类现象通常被叫作 Context Rot(上下文腐烂),很多看起来像模型能力不足的问题,往往可以追溯到上下文组织不当。

5.1 上下文为什么要分层

问题通常不是窗口不够长,而是信息密度不对

  • 偶尔用的东西每次都加载进来
  • 稳定的规则和动态的状态混在一起
  • 模型能看到的内容越来越多,但真正有用的部分越来越难被注意到

解决方式:按信息的使用频率和稳定性分层管理,每层只放自己该放的东西。

层级 内容 加载方式
常驻层 身份定义、项目约定、绝对禁止项 每次都加载
按需加载 Skills 和领域知识 触发时注入
运行时注入 当前时间、渠道 ID、用户偏好 每轮按需拼入
记忆层 跨会话经验 (MEMORY.md) 需要时读取
系统层 Hooks 或代码规则 不进上下文

关键原则:别把确定性逻辑放进上下文,凡是可以通过 Hooks、代码规则或工具约束表达的内容,都应交给外部系统处理,而不是让模型反复读取。


5.2 三种常见压缩策略

策略 成本 丢失内容 适用场景
滑动窗口 极低 早期上下文 简短对话
LLM 摘要 细节,保留决策 长任务、含关键决策
工具结果替换 极低 工具原始输出 工具调用密集型

滑动窗口实现最简单,但会丢掉早期决策背景。

LLM 摘要的进阶做法是 branch summarization,摘要时明确保留架构决策、未完成任务和关键约束。

工具结果替换里,micro_compact 每轮替换旧工具输出,auto_compact 在上下文超阈值时自动触发。


5.3 Prompt Caching 减少重复开销

原理:Transformer 推理时,如果当前请求的输入前缀和之前某次请求完全一致,这部分 KV 就不需要重新计算,直接从缓存读取。

命中的前提:精确前缀匹配,不是内容相似就能触发,任何一个 token 不同都会破坏匹配。

设计要点

  • 常驻层越稳定,前缀命中率越高
  • 动态信息(当前时间、用户输入、工具调用结果)放在后面
  • 反直觉:稳定的大系统提示,比频繁变动的小提示实际成本更低(因为写入成本只付一次,后续每次调用读取的折扣可以达到 90%)

5.4 为什么 Skills 要按需加载

Skills 是上下文工程里非常有效的一种模式,核心思路是:系统提示只保留索引,完整知识按需加载

1
2
3
4
5
6
7
8
9
10
const systemPrompt = `
可用 Skills:
- deploy: 部署到生产环境的完整流程
- code-review: 代码审查检查清单
- git-workflow: 分支策略和 PR 规范
`;

async function executeLoadSkill(name: string): Promise<string> {
return fs.readFile(`./skills/${name}.md`, "utf-8");
}

Skill 描述最佳实践

  • ✅ 高效(约 9 tokens):Use when deploying to production or rolling back.
  • ❌ 低效(约 45 tokens):冗长的功能介绍

关键数据

条件 准确率 响应时间
没有反例 53% ↓ 基准
加上反例 85% ↑ -18.1% ↓

反例不是可选项,是 Skill 描述能不能起作用的关键

Skill 描述符的两个陷阱

  1. 字数:每个启用的 Skill 描述符都常驻上下文,Skill 一多,长描述的累积成本很可观
  2. 精度:描述太短(help with backend)等于任何后端工作都能触发,路由会乱

真正有效的描述符是路由条件,不是功能介绍,”何时该用我”比”我能做什么”重要得多。


5.5 压缩最容易丢掉什么

压缩阶段最常见的问题,不是摘要不够短,而是保留顺序设错了

LLM 通常会优先删除那些看起来还可以重新获取的信息,早期的 tool output 通常最先被移除,但与之相关的架构决策、约束理由和失败路径也很容易一并丢失。

保留优先级(建议在 CLAUDE.md 或等价文档里明确写出):

1
2
3
4
5
6
7
8
9
10
11
12
### Compact Instructions

保留优先级:
1. 架构决策,不得摘要
2. 已修改文件和关键变更
3. 验证状态,pass/fail
4. 未解决的 TODO 和回滚笔记
5. 工具输出,可删,只保留 pass/fail 结论

⚠️ 不要改动标识符:
- UUID、hash、IP、端口、URL、文件名必须原样保留
- 一旦把 PR 编号或 commit hash 改错一位,后续工具调用就会直接失效

5.6 文件系统为什么适合做上下文接口

Cursor 把这种方式叫 Dynamic Context Discovery,默认少给,只在需要时读取。

文件系统天然适合做这个接口:

  • 工具调用经常返回大量 JSON,几次搜索就能堆出成千上万 token
  • 不如直接写入文件,让 Agent 通过 greprg 或脚本按需读取
  • 工具写文件,Agent 读文件,开发者也可以直接查看

6. 与 OpenClaw 的对照

这篇文章提到的很多设计原则,在 OpenClaw 中都有对应实现:

设计原则 OpenClaw 实现
本地记忆系统 ~/.openclaw/workspace/memory/ 目录
Skills 按需加载 skills/ 目录 + SKILL.md 描述
上下文分层 常驻层 (SOUL.md) + 按需加载 (技能) + 记忆层
文件系统接口 工具写入文件,Agent 读取
Harness 工程 工具审批 + 执行验证 + 错误处理
Prompt Caching 稳定的系统提示 + 动态信息后置

7. 总结

核心结论

  1. Agent 循环本身很简单,稳定性来自外部工程而非模型
  2. Harness 比模型能力更关键,尤其在可验证任务上
  3. 上下文工程决定稳定性,分层管理是核心
  4. Skills 按需加载大幅降低成本,提高路由准确率
  5. 文件系统是优秀的上下文接口,工具写文件,Agent 读文件

实践建议

  • ✅ 调试 Agent 行为时,优先检查工具定义(多数错误出在描述不准确)
  • ✅ 把任务推进”右上角”(目标明确 + 可自动验证)
  • ✅ 常驻层短而稳定,保护 Prompt Caching 命中
  • ✅ Skill 描述写”何时该用我”,不是”我能做什么”
  • ✅ 压缩时保留架构决策和标识符,不改动 UUID/hash

📚 参考资料


原文作者: Tw93 (@HiTw93)
原文链接: https://tw93.fun/2026-03-21/agent.html
整理时间: 2026-03-23
许可: CC BY-NC-ND 4.0


本文经授权整理,版权归原作者所有。