1. 为什么代理需要单独的内存
这一部分承接模块 12 的前置课程(关于代理):我们已经讨论了基础架构、run 循环与工具; 这里重点讲内存与状态。
如果类比常见的 Web 应用,这里的 LLM 就像一颗非常聪明的 CPU,能执行复杂的“文本程序”。 而代理的状态就像 RAM 与 SSD 的组合:短期的会话数据与长期存储。
在经典的 ChatGPT 聊天(没有你的代码)里,“内存”只是当前请求中模型可见的消息列表 system/user/assistant/tool。 对于一个代理而言,这远远不够,因为:
- 它需要记住复杂流程的进度:workflow 已完成到哪一步、哪些礼物候选已被过滤、用户确认了什么;
- 它需要了解用户的长期事实:偏好、收货地址、历史订单;
- 它需要具备容错性:如果服务器在挑选礼物中途宕机,用户不该被迫全部重来。
如果只把这些都塞进 prompt 上下文,你会很快碰到上下文窗口的上限,并为重复事实不断支付 token 成本。 同时还会有安全风险:太多无关数据会被频繁发送给模型。 因此,在代理系统中总会有显式状态——由你管理的、存在于消息历史之外的对象。
2. 代理状态的分层:上下文、session、persistent
先按层次拆分。一个代理通常至少有三层“内存”:
- 消息历史(dialogue context)。
- 会话状态(session state)。
- 长期状态(persistent state)。
不要把这些概念混为一谈。
消息历史:“脏内存”
消息历史是 LLM 在每一步能看到的东西: system 指令、用户请求、代理回答、工具结果。
好处是你无需手动维护——Agents SDK 和平台本身会通过 Session/Conversation 来管理。
缺点是它是“脏”的:包含大量冗词、重复、用户的偶发信息。 这些数据在 token 上很昂贵且结构化程度差。 你不会希望一个 200 项的“已过滤礼物”清单每次都以纯文本被读给模型。
Session state:工作的短期内存
Session 状态是一个结构化对象,作用域限定在一次代理会话/对话中。 前端开发者可以将其类比为 useState 或 Redux store,只在浏览器标签打开期间存在。
它通常包含:
- 当前流程步骤(例如 "collecting_profile" 或 "filtering_candidates");
- 工具结果的临时缓存;
- 会话参数:本地化、选定渠道,以及诸如“用户已同意条款”的标志位。
这种状态可以放在靠近代理的一侧——比如 Redis、内存 KV 存储,或使用特定 SDK 自带的 SessionService。 关键点是:不要试图把它们都塞进 system prompt。
Persistent state:长期数据
Persistent 状态是长寿命的:跨会话、跨结账、跨设备。包括用户档案、订单、已保存的心愿单、设置等。
核心思想:代理不会“魔法地记住” persistent 数据,而是通过工具去“读取”——例如 get_user_profile、get_past_orders。 不要在代理内部藏全局变量;一切都应是显式调用。
对比表
| 层 | 驻留位置 | 生命周期 | 数据示例 |
|---|---|---|---|
| Messages | Session / SDK / OpenAI | 单次 run / 对话 | system/user/tool 消息 |
| Session state | KV / SessionService / Redis | 会话存活期间 | 工作流步骤、临时缓存 |
| Persistent | 数据库(Postgres/NoSQL/ACP 后端) | 跨会话与对话 | 个人资料、订单、已保存清单 |
3. Session state:是什么以及如何存放
想象代理 GiftGenius 在执行一个多步骤流程:
- 收集收礼人的个人档案。
- 生成候选清单。
- 按预算、配送、地区进行过滤。
- 准备最终推荐。
过程中它会不断与用户对话并调用工具。 凡是“本次礼物挑选会话的进度”,都适合放在 session state 中。
GiftGenius 的会话状态结构示例
用 TypeScript 描述会话状态类型:
// 单次“选礼物”会话中的状态 export type GiftSessionState = { step: | "collecting_profile" | "generating_candidates" | "filtering" | "finalizing"; // 收礼人档案草稿 profileDraft?: { recipientType?: string; ageRange?: string; interests?: string[]; dislikes?: string[]; }; // 后端返回的候选商品 id candidateIds?: string[]; // 用户已选礼物 selectedGiftI