你不知道的 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 | const messages: MessageParam[] = [{ role: "user", content: userInput }]; |
控制流:感知 → 决策 → 行动 → 反馈四个阶段不断循环,直到模型返回纯文本为止。
核心设计原则
看过不少 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 | 验证自动化程度 |
右上角(目标明确 + 可自动验证)是最适合 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 | const systemPrompt = ` |
Skill 描述最佳实践:
- ✅ 高效(约 9 tokens):
Use when deploying to production or rolling back. - ❌ 低效(约 45 tokens):冗长的功能介绍
关键数据:
| 条件 | 准确率 | 响应时间 |
|---|---|---|
| 没有反例 | 53% ↓ | 基准 |
| 加上反例 | 85% ↑ | -18.1% ↓ |
反例不是可选项,是 Skill 描述能不能起作用的关键。
Skill 描述符的两个陷阱:
- 字数:每个启用的 Skill 描述符都常驻上下文,Skill 一多,长描述的累积成本很可观
- 精度:描述太短(
help with backend)等于任何后端工作都能触发,路由会乱
真正有效的描述符是路由条件,不是功能介绍,”何时该用我”比”我能做什么”重要得多。
5.5 压缩最容易丢掉什么
压缩阶段最常见的问题,不是摘要不够短,而是保留顺序设错了。
LLM 通常会优先删除那些看起来还可以重新获取的信息,早期的 tool output 通常最先被移除,但与之相关的架构决策、约束理由和失败路径也很容易一并丢失。
保留优先级(建议在 CLAUDE.md 或等价文档里明确写出):
1 | ### Compact Instructions |
5.6 文件系统为什么适合做上下文接口
Cursor 把这种方式叫 Dynamic Context Discovery,默认少给,只在需要时读取。
文件系统天然适合做这个接口:
- 工具调用经常返回大量 JSON,几次搜索就能堆出成千上万 token
- 不如直接写入文件,让 Agent 通过
grep、rg或脚本按需读取 - 工具写文件,Agent 读文件,开发者也可以直接查看
6. 与 OpenClaw 的对照
这篇文章提到的很多设计原则,在 OpenClaw 中都有对应实现:
| 设计原则 | OpenClaw 实现 |
|---|---|
| 本地记忆系统 | ~/.openclaw/workspace/memory/ 目录 |
| Skills 按需加载 | skills/ 目录 + SKILL.md 描述 |
| 上下文分层 | 常驻层 (SOUL.md) + 按需加载 (技能) + 记忆层 |
| 文件系统接口 | 工具写入文件,Agent 读取 |
| Harness 工程 | 工具审批 + 执行验证 + 错误处理 |
| Prompt Caching | 稳定的系统提示 + 动态信息后置 |
7. 总结
核心结论
- Agent 循环本身很简单,稳定性来自外部工程而非模型
- Harness 比模型能力更关键,尤其在可验证任务上
- 上下文工程决定稳定性,分层管理是核心
- Skills 按需加载大幅降低成本,提高路由准确率
- 文件系统是优秀的上下文接口,工具写文件,Agent 读文件
实践建议
- ✅ 调试 Agent 行为时,优先检查工具定义(多数错误出在描述不准确)
- ✅ 把任务推进”右上角”(目标明确 + 可自动验证)
- ✅ 常驻层短而稳定,保护 Prompt Caching 命中
- ✅ Skill 描述写”何时该用我”,不是”我能做什么”
- ✅ 压缩时保留架构决策和标识符,不改动 UUID/hash
📚 参考资料
- Anthropic: Building Effective AI Agents
- LangChain: Workflows and agents
- OpenClaw GitHub
- Cursor: Dynamic Context Discovery
原文作者: Tw93 (@HiTw93)
原文链接: https://tw93.fun/2026-03-21/agent.html
整理时间: 2026-03-23
许可: CC BY-NC-ND 4.0
本文经授权整理,版权归原作者所有。