LLM Agent 增强架构
业界目前没有公认的"LLM Agent增强架构"定义,各框架(LangChain、OpenAI Assistants、Anthropic MCP等)各有侧重、术语不统一。本文尝试用计算机系统作为统一类比,梳理常见组件的职责边界和协作关系。
类比是为了建立直觉,不是严格同构。具体实现因框架而异。
背景:从堆训练量到外部增强
LLM能力与训练规模是对数关系,边际收益递减。继续堆算力和数据不是不行,是性价比崩了。
于是业界转向:
- 推理时计算:让模型"多想一会儿"
- 外挂组件:把知识、记忆、能力外置
- 工具调用:让模型能"动手"
本质上,LLM从"全能选手"变成"推理引擎",其他能力由外部系统提供。
整体架构
可以用计算机系统来理解这套架构------LLM是概率CPU,周围是为它构建的操作系统。
组件对照
| 组件 | 作用 | 类比 |
|---|---|---|
| LLM本体 | 推理引擎,自带固化知识,执行不确定 | 概率CPU + ROM |
| 原始上下文 | 当前直接参与计算的内容,最完整,容量最小 | 执行窗口/寄存器 |
| 压缩上下文 | 有损压缩换容量,仍在"片上",易失 | 内存 |
| 情景记忆 | 时序追加,持久,按时间索引,可回溯 | WAL日志 |
| 状态记忆 | 结构化,持久,按实体索引,覆盖更新 | 数据库表 |
| Skill | 完整的能力定义,按需加载 | 磁盘上的程序+文档 |
| Tool | 函数签名,常驻可见,告诉LLM"能调什么" | 内存中的符号表 |
| Tool Calling | 调用协议,触发实际执行 | syscall |
| MCP | 标准化接口+通信协议,屏蔽外部服务差异 | 设备驱动 + 总线协议 |
| System Prompt | 启动配置,定义基础行为 | BIOS + init |
| Agent循环 | 决定下一步做什么,但没有强制力 | 软调度器 |
架构图
┌─────────────────────────────────────────────────────┐
│ LLM (概率CPU + ROM) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 原始上下文 │ → │ 压缩上下文 │ │
│ │ (执行窗口) │ │ (内存) │ │
│ └─────────────┘ └─────────────┘ │
│ ↑ │
│ System Prompt (BIOS/init) │
│ ↑ │
│ Tool (符号表,常驻) │
├─────────────────────────────────────────────────────┤
│ 持久存储 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 情景记忆 │ │ 状态记忆 │ │ Skill │ │
│ │ (WAL日志) │ │ (数据库) │ │ (程序+文档) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────┤
│ 外部资源 │
│ ┌─────────────────────────────────────────────┐ │
│ │ MCP Server 集群 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ RAG │ │ 搜索 │ │ 工具 │ │ 服务 │ │ │
│ │ └───────┘ └───────┘ └───────┘ └───────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ ↑ │
│ MCP协议(驱动层) │
├─────────────────────────────────────────────────────┤
│ Tool Calling (syscall) ←→ Agent循环 (软调度) │
└─────────────────────────────────────────────────────┘
执行流程
一次典型请求:
1. 用户输入进入原始上下文
2. System Prompt已在上下文,定义基础行为
3. Tool符号表告诉LLM有哪些能力可用
4. LLM判断需要外部能力 → 发起Tool Calling
5. 需要详细操作指南 → 从持久存储加载Skill
6. 需要外部数据 → 通过MCP访问对应Server
7. Agent循环决定是否继续、重试、或返回
8. 相关信息写入情景记忆和状态记忆
核心组件详解
情景记忆 vs 状态记忆
两种持久记忆解决不同问题:
情景记忆 - 回答"发生过什么"
[1月10日] 用户问了 java 并发问题
[1月11日] 用户解决了依赖冲突
→ append-only,时序索引
状态记忆 - 回答"当前是什么"
用户.技术栈 = {Java, MySQL}
用户.偏好 = {简洁, 中文}
→ 覆盖更新,实体索引
理论上可以互推:状态是事件的折叠,事件是状态变更的展开。实际系统两者都存,因为查询模式不同。
Skill vs Tool vs Tool Calling
Skill = 完整的能力定义
"创建docx时用python-docx库,注意字体兼容性..."
→ 几百行,存磁盘,按需加载
Tool = 能力的接口声明
{"name": "create_file", "params": {"path": "string"}}
→ 几行,常驻上下文
Tool Calling = 实际调用动作
<invoke name="create_file"><param>...</param></invoke>
→ LLM输出,触发执行
Skill是完整程序,Tool是函数声明,Tool Calling是syscall。
MCP的双重角色
对内(LLM视角):
屏蔽后端差异,提供统一接口
LLM不需要知道RAG用的是Pinecone还是Milvus
对外(服务视角):
定义服务如何暴露能力、如何被调用
类似USB------既是接口标准,也是通信协议
MCP Server的常见类型:
- 知识类:RAG、搜索引擎、文档库
- 工具类:代码执行、文件操作
- 服务类:邮件、日历、数据库
- 设备类:浏览器、终端
边界与局限
这套架构有几个特性需要注意:
- 非确定性执行:同样的输入可能不同输出,不能假设幂等
- 上下文不可寻址:不能随机访问,只能整体处理
- 软调度:Agent循环是建议性的,LLM可以不听
- Skill加载不保证一致:同一份Skill,不同上下文可能产生不同执行路径
这些影响:调试方式、可靠性假设、是否能做强一致系统。
延伸:SKILL.md作为编程语言
一个有趣的视角:SKILL.md定义了一种"面向Agent的编程语言"。
| 传统编程 | SKILL.md |
|---|---|
| 源代码 | Markdown + 自然语言 |
| 语法 | 约定的结构(标题、代码块、示例) |
| 编译器 | LLM |
| 运行时 | Agent循环 + 工具调用环境 |
| 标准库 | 可用的Tools和其他Skills |
关键差异:没有中间表示,没有确定性,"编译"和"执行"边界模糊。
这意味着:调试困难,版本控制语义不清,没有类型系统,但灵活性极高。
本质是从命令式编程到声明式意图编程的范式转换。
延伸:多Agent架构
本文讨论的是单Agent视角。多Agent相当于从单核到多核,引入新的协调问题。
| 多核问题 | 多Agent对应 |
|---|---|
| 共享内存 vs 消息传递 | 共享Memory/RAG vs Agent间消息 |
| 缓存一致性 | 状态同步:一个Agent改了,其他怎么知道 |
| 任务调度 | 子任务分配给哪个Agent |
| 同步原语 | 等待、汇总、投票 |
常见模式:
- 主从:一个主Agent分发任务、收集结果
- 流水线:A输出 → B输入 → C输入
- 对等协商:多Agent讨论达成共识
- 层级:高层规划,低层执行
一句话总结
LLM系统 = 概率执行的推理引擎 + 分层上下文 + 持久存储 + 标准化外部访问 + 自然语言系统调用。