2026 年的「工具使用」正在发生一次静悄悄却影响深远的状态归属迁移:一边是 MCP 2026-07-28 版本候选规格(5 月 21 日锁定)把协议级会话彻底删除、走向无状态核心;另一边,工具本身却越来越「有状态」——Cloudflare 用 Durable Objects 给每个 MCP 服务挂上持久 SQLite,OpenAI Responses API 通过 previous_response_id 把会话态外包给平台,学术界则用 DialogTool 专门评测多轮有状态工具交互。结论很反直觉:状态正在同时离开上下文窗口、离开协议会话,沉淀进工具自己的耐久存储。这重构了 Agent 循环的 token 经济学、安全边界与评测口径,本期实测六栈。
| 形态 | 状态存放位置 | 跨轮持久 | token 成本 | 典型代表 |
|---|---|---|---|---|
| 无状态函数(Stateless Function) | 无,每次调用独立 | ❌ | 最低,但需上下文回灌参数 | 传统 Function Calling |
| 上下文窗口态 | 每轮把 cursor/cart/token 塞回 prompt | 限窗口 | 随轮次线性膨胀 | 早期 ReAct 手搓 Agent |
| 协议会话态 | initialize 握手拿 session_id |
✅ 会话内 | 中,但有锁定/兼容债 | MCP 2025-06-18 及更早 |
| 工具服务器耐久态 | Durable Object / 平台存储 / SQLite | ✅ 跨会话 | 工具侧自付,上下文只持 opaque 句柄 | Cloudflare Agents、Responses API |
| 代码解释器沙箱态 | 进程内变量 / 文件系统 | 会话内 | 零序列化,崩溃即丢 | OpenAI Code Interpreter |
| 远程有状态 MCP + OAuth | 服务端 session + token 缓存 | ✅ 跨会话 | token 留服务端,但要管刷新 | Cursor/Responses 接远程 MCP |
工具调用的「状态」无非三类东西:游标(分页 cursor)、累计态(购物车、草稿、计数器)、凭证态(OAuth token、临时签名)。问题是它们到底该放哪。放上下文窗口,意味着每一轮都要把整份状态重新喂给模型——一个 10 件的购物车约 300 token,跑 20 轮就是 6000 token 纯用来「回声」状态,模型还容易在复制中抄错。放协议会话,状态在 initialize 握手后留在 server 端,上下文只持一个 session_id,干净但有锁定税。最干净的是第三种:状态焊进工具自己的耐久存储,上下文只持一个 opaque 句柄(cursor、resource URI、cart_id)。这也是 2026 年的主旋律——状态「下沉」到离数据最近的地方。
2026-07-28 RC 是 MCP 自发布以来最大一次修订,最抓眼球的是「删除协议级 session」。很多人据此推断「MCP 全面无状态化、工具不能记东西」,这是致命误读。规格删除的是 协议层 的会话语义(initialize 握手、session_id 维护),目的是让远程 MCP 变成纯无状态的 streamable HTTP,便于水平扩展和边缘部署。但工具本身完全可以有状态——只不过这份状态不再由协议替你托管,而要由工具服务器用自己的存储(Durable Object、Redis、数据库)来管。换句话说:协议卸下了「状态托管」的包袱,把它还给了应用层。旧式依赖 session_id 的服务端必须迁移:要么把状态外化到存储里用 opaque key 寻址,要么接受无状态重启。
Cloudflare 的打法最彻底:每个 Agent/MCP 服务就是一个 Durable Object,自带一份持久 SQLite。状态不再需要外部数据库,也不再需要协议会话——它就在工具进程里。其官方示例就是一个由 Durable Object 支撑的「有状态 MCP 服务」,状态跨请求持久:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class CartMCP extends McpAgent {
server = new McpServer({ name: "cart", version: "1.0.0" });
async init() {
this.server.tool("add_to_cart",
{ sku: z.string(), qty: z.number() },
async ({ sku, qty }) => {
const cart = (await this.ctx.storage.get<Record<string,number>>("cart")) ?? {};
cart[sku] = (cart[sku] ?? 0) + qty;
await this.ctx.storage.put("cart", cart); // 耐久落盘,跨请求/跨重启存活
return { content: [{ type: "text", text: JSON.stringify(cart) }] };
});
}
}
注意 this.ctx.storage——这就是「工具吞下状态」的物理实现。模型调用 add_to_cart 时,上下文里不需要带着整辆购物车,工具自己记得。代价是:状态和工具生命周期强绑定,迁移成本高(见第七节)。
previous_response_id 把状态外包给平台OpenAI 走的是另一条路:状态外包给平台。Responses API 用 previous_response_id 把整段会话(含已发生的工具调用、工具返回、中间推理)留在 OpenAI 服务端,下一轮只传一个 ID。叠加内置的 mcp 工具类型,远程有状态工具的 OAuth 与 server 端会话也可由平台协调:
resp = client.responses.create(
model="gpt-5",
input="把 SKU-7 再加两件",
tools=[{"type": "mcp", "server_label": "cart",
"server_url": "https://cart.example.com/mcp",
"require_approval": "never"}],
store=True,
previous_response_id=prev_id, # 服务端持有会话态 + 工具调用历史
)
prev_id = resp.id
这是最省心智的形态——开发者几乎不用管状态。但它是最强的供应商锁定:状态黑箱化在平台内,跨厂商迁移意味着整段会话历史不可携带,审计、回放、合规都得依赖平台 API。store=True 还意味着敏感数据进了厂商存储,合规团队要单独评估。
学界已经意识到这个评测缺口。ACL 2025 Findings 的《Rethinking Stateful Tool Use in Multi-Turn Dialogues》(arXiv:2505.13328)直指:绝大多数工具使用基准评测的是无状态、单轮调用,模型调一次工具、拿到结果、回答,结束。但真实世界是多轮的:工具状态会在轮次间变化,用户会回头修改,模型必须基于「当前真实状态」而非「历史快照」决策。该工作提出 DialogTool,把整段对话历史与工具状态联合建模。这解释了为什么 BFCL 上成绩漂亮的模型,到了 τ-bench 多轮零售/航空场景会「塌方」——单轮能调对工具 ≠ 能在状态持续演化的多轮里调对。选型时务必看多轮有状态基准,而不是单轮准确率。
状态一旦下沉到工具服务器,那份耐久状态本身就成了攻击面与信任边界,这恰恰是被严重低估的一环。三类典型风险:其一,状态投毒——攻击者通过一次 add_to_cart 注入恶意结构(超长字段、嵌套对象),若工具未做 schema 校验就持久化,后续每一轮都带着毒数据跑。其二,跨会话污染——若 cart_id 或 resource URI 可枚举/可预测,A 用户的状态会被 B 用户读到,等于把鉴权从「调用时」推迟到「状态寻址时」。其三,OAuth token 滞留——远程有状态 MCP 把刷新令牌缓存在服务端以省去每轮授权,一旦该服务被攻破或下线,令牌不会自动失效。把状态放进服务端不是免责声明,而是新增了一份「耐久态的存储安全 + 访问控制 + 生命周期治理」债务。
把六种形态放在三条轴上权衡,结论相当清晰。token 轴:上下文窗口态最贵(线性膨胀),服务端态最省(上下文只持句柄),代价是工具自付存储成本。延迟轴:上下文态首轮快但随轮次劣化(窗口越长推理越慢);服务端态每轮接近恒定,但多一次存储往返。恢复轴最反直觉:上下文态崩溃即全丢;协议会话态断连需重新握手;服务端耐久态反而最健壮——Durable Object 重启后 SQLite 自动恢复,跨进程、跨重启存活。也就是说,「有状态工具」在工程鲁棒性上完胜手搓上下文回灌,真正要付的代价是迁移与锁定(见第四节)和安全债务(见第六节)。
从商业视角,有状态工具是一把双刃剑。B2B 交付场景下它是护城河:客户的状态(购物车、合同草稿、工单上下文)沉淀在你的服务端耐久存储里,迁移成本极高,天然形成黏性;这也是 Cloudflare Agents 把 Durable Objects 当卖点的原因——锁定的是运行时与数据。但通用 Agent 框架/训练营场景下它是负债:一旦教学生「状态外包给平台」,就等于把他们绑死在某个 Responses API 或某家 DO 厂商上,迁移时整段会话历史不可携带。训练营正确教法应是:先讲透三种归属的权衡与迁移成本,再让学生用「opaque 句柄 + 自有存储」实现一次可移植的有状态工具——这才是真正值钱、不绑定单一厂商的能力。
flowchart LR
subgraph GEN1[第一代:状态在上下文窗口]
A1[Agent Loop] -->|每轮把 cursor/cart/token 塞回| A2[Context Window 线性膨胀]
end
subgraph GEN2[第二代:状态在协议会话]
B1[Agent] -->|initialize 握手拿 session_id| B2[MCP 会话态]
B2 -. 2026-07-28 RC 删除 .-> BX[❌ 协议会话移除]
end
subgraph GEN3[第三代:状态在工具服务器耐久态]
C1[Agent Loop 瘦身] -->|opaque cursor / resource URI / cart_id| C2[Durable Object / 平台存储]
C2 --> C3[(SQLite / KV 持久化)]
end
GEN1 ==> GEN2 ==> GEN3
BX ==> C2
previous_response_id + 内置 MCP,心智成本最低,但要清醒接受最强锁定,会话历史不可携带。2026-07-28 RC 把会话外化到存储、用 opaque key 寻址,这是被迫也是最好的一次重构窗口。previous_response_id 服务端会话态、内置 mcp 工具类型与 OAuth 远程工具支持。[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
