<!-- title: 有状态工具：协议删会话，工具藏状态 -->

## 有状态工具：协议删会话，工具藏状态

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 年的主旋律——状态「下沉」到离数据最近的地方。

### 二、MCP 删会话 ≠ 工具无状态：被误读的 2026-07-28 RC

`2026-07-28` RC 是 MCP 自发布以来最大一次修订，最抓眼球的是「删除协议级 session」。很多人据此推断「MCP 全面无状态化、工具不能记东西」，这是**致命误读**。规格删除的是 *协议层* 的会话语义（`initialize` 握手、session_id 维护），目的是让远程 MCP 变成纯无状态的 streamable HTTP，便于水平扩展和边缘部署。但**工具本身完全可以有状态**——只不过这份状态不再由协议替你托管，而要由工具服务器用自己的存储（Durable Object、Redis、数据库）来管。换句话说：协议卸下了「状态托管」的包袱，把它还给了应用层。旧式依赖 `session_id` 的服务端必须迁移：要么把状态外化到存储里用 opaque key 寻址，要么接受无状态重启。

### 三、Cloudflare Durable Objects：把状态焊进工具运行时

Cloudflare 的打法最彻底：**每个 Agent/MCP 服务就是一个 Durable Object，自带一份持久 SQLite**。状态不再需要外部数据库，也不再需要协议会话——它就在工具进程里。其官方示例就是一个由 Durable Object 支撑的「有状态 MCP 服务」，状态跨请求持久：

```ts
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` 时，上下文里不需要带着整辆购物车，工具自己记得。代价是：状态和工具生命周期强绑定，迁移成本高（见第七节）。

### 四、Responses API：`previous_response_id` 把状态外包给平台

OpenAI 走的是另一条路：**状态外包给平台**。Responses API 用 `previous_response_id` 把整段会话（含已发生的工具调用、工具返回、中间推理）留在 OpenAI 服务端，下一轮只传一个 ID。叠加内置的 `mcp` 工具类型，远程有状态工具的 OAuth 与 server 端会话也可由平台协调：

```python
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` 还意味着敏感数据进了厂商存储，合规团队要单独评估。

### 五、DialogTool：现有基准对有状态工具的集体盲区

学界已经意识到这个评测缺口。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 / 延迟 / 恢复）

把六种形态放在三条轴上权衡，结论相当清晰。**token 轴**：上下文窗口态最贵（线性膨胀），服务端态最省（上下文只持句柄），代价是工具自付存储成本。**延迟轴**：上下文态首轮快但随轮次劣化（窗口越长推理越慢）；服务端态每轮接近恒定，但多一次存储往返。**恢复轴**最反直觉：上下文态崩溃即全丢；协议会话态断连需重新握手；服务端耐久态反而最健壮——Durable Object 重启后 SQLite 自动恢复，跨进程、跨重启存活。也就是说，**「有状态工具」在工程鲁棒性上完胜手搓上下文回灌**，真正要付的代价是迁移与锁定（见第四节）和安全债务（见第六节）。

### 八、变现与训练营评估：护城河还是负债

从商业视角，有状态工具是一把双刃剑。**B2B 交付场景下它是护城河**：客户的状态（购物车、合同草稿、工单上下文）沉淀在你的服务端耐久存储里，迁移成本极高，天然形成黏性；这也是 Cloudflare Agents 把 Durable Objects 当卖点的原因——锁定的是运行时与数据。但**通用 Agent 框架/训练营场景下它是负债**：一旦教学生「状态外包给平台」，就等于把他们绑死在某个 Responses API 或某家 DO 厂商上，迁移时整段会话历史不可携带。训练营正确教法应是：先讲透三种归属的权衡与迁移成本，再让学生用「opaque 句柄 + 自有存储」实现一次可移植的有状态工具——这才是真正值钱、不绑定单一厂商的能力。

## 状态归属迁移架构图

```mermaid
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
```

## 推荐方案

- **高频长会话 + 数据敏感（电商、CRM）**：首选服务端耐久态（Durable Object / 自建存储），上下文只持 opaque 句柄。token 最省、崩溃可恢复，但必须自担安全债务。
- **快速原型 / 单一厂商生态**：Responses API + `previous_response_id` + 内置 MCP，心智成本最低，但要清醒接受最强锁定，会话历史不可携带。
- **多厂商、要可移植**：坚持「工具自有存储 + opaque 句柄」，拒绝把状态外包给任一平台；用 DialogTool/τ-bench 这类多轮有状态基准选模型。
- **迁移已上线的旧 MCP 服务**：趁 `2026-07-28` RC 把会话外化到存储、用 opaque key 寻址，这是被迫也是最好的一次重构窗口。
- **安全红线**：任何落盘状态都要过 schema 校验、状态键不可枚举、OAuth 令牌要设短 TTL + 可吊销，把「耐久态治理」纳入运维，而不是写进免责声明。

## 参考来源

1. [Cloudflare Agents · Build and deploy AI Agents](https://github.com/cloudflare/agents) — 官方仓库，明确「persistent, stateful execution environments」并由 Durable Object + 持久 SQLite 支撑，含 stateful MCP 示例。
2. [The 2026-07-28 MCP Specification Release Candidate](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) — MCP 官方博客，确认这是发布以来最大修订，删除协议级会话、走向无状态核心。
3. [Rethinking Stateful Tool Use in Multi-Turn Dialogues (arXiv:2505.13328)](https://arxiv.org/abs/2505.13328) — ACL 2025 Findings 论文，指出多数基准只评无状态单轮调用，提出 DialogTool 评测多轮有状态交互。
4. [OpenAI Responses API · Migrate to the Responses API](https://platform.openai.com/docs/api-reference/responses) — `previous_response_id` 服务端会话态、内置 `mcp` 工具类型与 OAuth 远程工具支持。
5. [BFCL V3 · Multi-Turn & Multi-Step Function Calling](https://gorilla.cs.berkeley.edu/blogs/13_bfcl_v3_multi_turn.html) — Berkeley 函数调用榜单 V3 引入多轮/多步评测，佐证单轮高分 ≠ 多轮有状态可靠。

> [!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 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
