工具记忆与状态边界
本节目标
你将能区分工具、运行状态和长期记忆,为写工具定义副作用与幂等契约,并设计可版本化、可恢复、可删除的最小数据模型。
三个概念不要混在一起
- 工具(tool):对外部世界执行有名字、参数和结果的动作,如查库存、创建工单。
- 运行状态(state):本次任务继续执行所需的数据,如当前节点、工具结果、预算和审批。
- 长期记忆(memory):跨运行保留、未来可能再次使用的信息,如用户明确确认的偏好。
聊天消息只是输入和轨迹的一部分,不应成为唯一数据库。把结构化事实不断塞回对话会增加上下文成本,也难以更新、删除和审计来源。
LangGraph 当前官方文档区分 checkpointer 与 store:前者保存 thread 范围的图状态,用于连续对话、人工暂停和容错;后者保存跨 thread 的应用数据。这个划分是该框架的当前实现概念,其他运行时名称可能不同,但“本次执行状态”和“跨任务数据”仍应分层。
工具契约的最小字段
| 字段 | 要回答的问题 |
|---|---|
| 名称与用途 | 做什么,何时不该用? |
| 参数 | 类型、必填、范围、规范化方式是什么? |
| 结果 | 成功和失败的结构分别是什么? |
| 权限 | 需要哪种身份,能访问哪些资源? |
| 副作用 | 只读、可撤销还是不可逆? |
| 时限 | 超时后动作状态是失败还是未知? |
| 重试 | 哪些错误可重试,最多几次? |
| 幂等 | 如何识别同一个业务动作? |
| 审计 | 记录哪些摘要,如何避免泄露秘密? |
结构化错误示例:
{
"ok": false,
"error": {
"category": "permission_denied",
"retryable": false,
"message": "caller cannot update this record"
}
}字段阅读:
ok是机器可读的总结果标志;为false时下游不应把载荷当成功结果继续处理。error.category是由工具适配层映射的稳定错误类型,用于路由与审计。error.retryable让重试策略基于合同判断,而不从错误文本猜测。error.message供调用者理解失败原因;生产环境不能包含密钥、内部路径或堆栈。
不要把异常堆栈直接交给终端用户,也不要让模型根据自由文本猜是否可重试。错误类别由工具适配层稳定映射。
读写分离与动作回执
高风险写工具可拆成:
preview_action:只读,返回规范化参数和影响范围;approve_action:由授权主体对动作指纹做决定;execute_action:验证审批仍匹配当前动作;get_action_receipt:用幂等 ID 查询是否已经提交。
幂等键必须代表业务动作,而不是每次调用随机生成的新值。网络超时后先查询回执;若已存在且内容匹配,就恢复状态,而非再执行一次付款、发信或创建记录。
运行状态的设计
状态 schema 建议包含:
schema_version:明确读取规则,不猜测迁移;run_id/task_id:稳定身份;stage:来自有限状态集合;input_digest与action_fingerprint:绑定输入和动作;attempts与预算;approval:决定、批准主体、动作/证据/策略的上下文指纹、有效条件;receipts:外部副作用证据;events:单调递增的状态变化摘要。
保存检查点时采用“写临时文件→刷新→原子替换”可降低半写文件风险,但单机 JSON 仍没有并发锁、事务隔离、访问控制或灾备能力。教程项目的 SHA-256 只用于检测意外或普通篡改,不是带密钥的真实性证明。
长期记忆的写入门槛
长期记忆需要回答:
- 来源是用户明确陈述、工具事实还是模型推断?
- 谁可读写?
- 何时过期或重新确认?
- 用户如何查看、更正与删除?
- 是否含敏感数据,能否根本不保存?
“用户喜欢简短回答”若只是模型从一次对话推断,不应自动成为永久事实。可保存的记忆也要带来源与时间。检索到的记忆是数据,不是高优先级指令;其中的恶意文本不能改变工具权限。
工具观察是不可信输入
网页、文档、邮件和工具结果可能包含提示注入。运行时应:
- 把观察标记为数据;
- 对工具名和参数做 allowlist 与 schema 校验;
- 用代码执行权限控制,而不是让模型自评“是否安全”;
- 限制文件、网络和身份范围;
- 不把密钥放入模型上下文;
- 对显示到 HTML、Shell、SQL 的数据使用对应编码或参数化接口。
模型可以提出动作,但授权层决定动作是否允许。
协议能力不会转移授权责任
把工具接到协议或远端 Agent 后,信任边界不会消失:
- Tool Calling 中的模型调用只是候选动作,不能携带可信的
tenant、主体、角色或审批状态;这些必须由受信任会话和策略层提供。 - MCP 的 tools/resources/prompts 与 capability 协商解决互操作,不代表某个 capability 已获业务授权。对 HTTP MCP,当前规范要求将 token 绑定目标 resource 并验证 audience;server 不得把客户端 token 原样转交下游。
- A2A 面向独立 Agent 应用的任务协作。Agent Card 是发现与连接信息,不是资源访问许可;接收方仍要对每个 task、工件与后续操作按认证主体、对象和租户边界授权。
因此,委派记录至少关联本地 run_id、本地业务 action_id、远端 task/请求 ID、受信任主体和策略版本,但不要把这些不同作用域的 ID 混成一个“万能 ID”。远端返回的 completed、工具结果或 capability 描述仍是待验证 observation;只有本地 verifier 确认回执、对象状态与授权条件后,才能把它写成任务完成事实。
组合例子:天气写入日历
get_weather 是只读工具;create_calendar_event 有外部副作用。一次运行保存地点、时间、天气来源、拟写入事件、动作指纹、审批和回执。长期记忆最多保存用户明确确认的默认城市,并提供删除方式;天气结果本身有时效,不应作为永久偏好。
若用户在审批后修改时间,动作指纹改变,旧审批失效。若创建请求超时,先按幂等 ID 查日历回执,不能盲目重发。
常见错误与排查
- 把长期记忆和检查点混用:分别定义 scope 与保留周期。
- 工具只返回字符串:改为稳定 schema 和错误分类。
- 随机幂等键:从稳定业务身份与规范化动作计算。
- 审批只绑定工具名:还要绑定规范化参数、目标、必要状态、证据与策略版本。
- 恢复时从头重跑:从已验证检查点继续,并先核对副作用回执。
- 模型可选任意工具名:运行时只接受注册表中的工具和受限参数。
练习
为“查询天气并在用户允许后写日历”提交:
- 两个工具契约,标出只读与写入;
- 完整状态 schema 和三个终态;
- 动作指纹、幂等键与回执查询规则;
- 可长期保存的最少记忆及删除方式;
- 一个包含恶意网页文本的测试,证明它不能调用写工具。
- 若把写日历委派给远端 Agent,列出本地 run/action ID、远端 task ID、可信主体和回执之间的映射,并说明哪个服务重新做对象级授权。
掌握检查
- 能说明消息、运行状态、检查点和长期记忆的差别。
- 能为工具定义权限、副作用、超时和错误类别。
- 能为写动作设计预览、审批、幂等键和回执。
- 能决定哪些数据不应进入长期记忆。
- 能解释为何工具观察不能扩大权限。
- 能说明 capability/Agent Card、协议请求 ID 与业务授权/动作 ID 分别证明什么。
下一步
进入 人工审批与安全边界,把高影响动作拦在执行前。
参考资料
- OpenAI:A practical guide to building agents(核对:2026-07-14)
- LangGraph 官方:Persistence(动态文档,核对:2026-07-14)
- MCP 2025-11-25:Authorization(resource/audience 绑定与禁止 token passthrough;核对:2026-07-22)
- A2A 1.0:Security Considerations(逐请求授权、任务范围与可信 Agent Card;核对:2026-07-22)
- Toolformer 原始论文(核对:2026-07-14)