原文:The Session You Cannot Take With You — Earendil Engineering
推理 API 最初的承诺非常简单:发送一些输入,接收一些输出。只要把两者都保存下来,你就拥有了这段对话。你可以检查、归档、重放它,也可以把它交给另一个模型。
这个抽象从来都不完全真实。例如,提示缓存存在于别人的 GPU 上,不同模型的分词方式不同,采样也无法复现——而且这是有意为之。但至少,会话以对话记录形式存在的语义记录仍然可以属于用户。一份对话记录应当包含指令、消息、工具调用和工具结果。另一个能力足够强的模型也许不会以完全相同的方式继续,但它可以理解之前发生了什么,并接手后续工作。
令人沮丧的是,推理 API 正在一定程度上远离这一属性。它们返回的内容越来越像是文本与提供商绑定状态的混合物,而且这种状态被刻意设计成不可移植。
- 用户为推理 token 付费,但拿到的只有不透明的加密数据块,最多附带一份用处不大的摘要
- 模型在网页搜索中看到了客户端永远看不到的源材料
- 压缩后的上下文只有原提供商能够解密
- 子代理的指令与消息以加密载荷的形式,对运行这些代理的应用隐藏
- 文件、向量存储、容器和缓存引用离开原提供商后便无法解析
- 响应与会话状态完全依赖保存在提供商服务器上的 ID
对于每一种功能,提供商都很容易给出基本理由,也能提出它为什么对用户有益的合理论据。但这些功能叠加在一起,改变了 AI 会话的所有权现实:你机器上的对话记录不再等于你的会话,它只是某个会话的局部视图;真正的运行状态属于推理提供商,而不是你。
我们不认同这个方向。下面想谈谈它对作为用户的你意味着什么,也谈谈它对我们这些在这一领域开发工具的人意味着什么。
检验会话所有权的实用方法
所谓可移植会话,并不是说从一个模型切换到另一个模型后,必须生成完全相同的下一个 token。模型拥有不同的能力、训练出的性格、上下文窗口和工具使用方式,本来就不可能如此;更何况整个过程都带有很强的非确定性。可移植性的要求其实更朴素:
const transcript = session.export();
revokeCredentials(oldProvider);
session = newProvider.continueFrom(transcript);
导出的归档应当包含足够多、可被理解的信息,让另一个模型能够继续工作。它不应要求原提供商解析某个 ID、解密某个数据块、记住某次搜索结果,或重新构建某份摘要。
由此可以得到五项实用测试:
- 检查: 用户能否看到模型看到了什么、调用了哪些工具,以及代理之间说了什么?
- 导出: 除了同样可以下载的普通产物之外,会话本身是否自包含?
- 重放: 另一套实现能否重建语义等价的上下文?
- 审计: 事后能否由人解释系统为什么采取了某个行动?
- 删除: 用户能否识别并删除会话所依赖的每一份服务端副本?
响应 ID 不是对话记录,因为数据存放在服务器上;密文不是用户可控的状态,因为用户无法解密;引用列表也不是搜索结果曾放进模型上下文的证据,因为你通常无法取回模型当时看到的同一份数据。
加密是为谁而做?
这些功能的命名和营销方式可能会误导用户。encrypted_content 听上去像是一项由用户掌控的隐私功能,但它通常只是一个客户端无法读取、只有提供商能够打开的胶囊。由提供商选择密钥、为自家模型解密内容,并决定这些数据可以在哪里重放。
更准确的叫法应该是:提供商密封状态(provider-sealed state)。
提供商密封确实可能带来隐私收益。例如,OpenAI 可以在使用 store: false 时向客户端返回加密推理,并在下一次请求中于内存里解密,而不持久化中间状态。这比强制在服务器上保存对话更好,尤其适合零数据保留(Zero Data Retention)客户。但别忘了:这里原本并没有什么非加密不可的东西。
这种加密并没有向推理提供商隐藏数据,它隐藏的是你自己的数据。
存储会话把对话记录变成了一个指针
OpenAI 的 Responses API 默认存储响应。其文档称,响应对象默认至少保留 30 天。API 提供了 store: false,而且用户应该使用它,因为这样会更接近 completions 的工作方式:数据不会保存在 OpenAI 的服务器上。
新的 Gemini Interactions API 做出了类似选择,默认值也是 store: true。付费层的 interaction 会保留 55 天,免费层则保留一天。
当然,把状态保存在服务器上确实很有吸引力:
const first = responses.create({
model: "frontier-model",
input: "Investigate this production failure",
store: true,
});
const second = responses.create({
model: "frontier-model",
previousResponseId: first.id,
input: "Now implement the fix",
store: true,
});
应用需要发送的数据更少,提供商可以保留隐藏推理和工具状态,缓存路由也更容易实现。但如果本地应用只记录用户消息和最终文本,first.id 就成了一个指向外部数据库的外键,而这个数据库不受应用控制。
推理不给你看
所有主要实验室都声称,不公开原始思维链有其正当理由。因此,在非开放权重模型上,我们通常看不到这些 token。
API 不会展示原始推理。使用已存储的响应时,可以通过 previous_response_id 恢复先前推理;使用 store: false 时,API 会返回 encrypted_content,客户端必须保存并在后续请求中原样传回。即使 reasoning.context: "all_turns" 允许后续采样利用历史推理,持久化的推理仍然是不透明的。
Anthropic 会把加密后的完整思考放在 signature 字段中。即使启用了可读的 thinking 文本,它也只是另一个模型生成的摘要,并非原始思维链。在工具使用轮次中,thinking 数据块必须原样传回。Anthropic 的文档还指出,这些 thinking 数据块与生成它们的模型绑定,切换模型时应将其移除。换句话说,即使在 Anthropic 内部,这些推理痕迹也没有追求可移植性。
所有闭源权重模型上都在重复同样的故事。
这些加密机制能够维持一个生态系统内部的连续性,却无法生成一份可以交给其他提供商模型的可移植对话记录。会话归档可以保存这些数据块,但另一个模型无法利用其中的含义:
{"type": "reasoning", "encrypted_content": "gAAAAAB..."}
{"type": "thinking", "thinking": "", "signature": "EqQBCg..."}
{"type": "thought", "summary": [], "signature": "EpoGCp..."}
隐藏的搜索过程
以网页搜索为例。服务端网页搜索最清楚地展示了对话记录如何出现用户不可见的缺口。客户端搜索工具与其他普通工具一样工作:
const result = search(query);
record({
query,
retrievedAt: now(),
results: result.map((item) => ({
url: item.url,
title: item.title,
passages: item.passages,
})),
});
model.send({ toolResult: result });
用户可以检查排序和段落,重新抓取网页,缓存一份副本,或把同一批证据交给另一个模型。
使用托管搜索时,提供商会在内部执行一套私有的工具循环。OpenAI、Google 和 Anthropic 会公开搜索动作、引用,有时还会给出源 URL 列表,却不会公开生成答案时使用的完整文本上下文。URL 不是稳定的重放记录:网页内容可能会改变,也可能在被模型看到之前已经被压缩成很短的片段。
最终答案完全可能很好。问题会出现在下一轮:
把第三个来源与第一个来源做比较,重新核对有争议的数字,然后换一个模型继续这项研究。
新模型拿到的是一个答案和几个 URL。它拿不到搜索结果排序、提取的段落、被过滤掉的材料,也拿不到第一个模型使用的精确证据。即使下一次请求发给了别家,旧提供商仍然是这段会话的一部分。即使你拥有全部引用并重新抓取网页,也无法复现当时的精确数据。
托管搜索应当提供完整保真的导出模式,包含查询、结果元数据、检索段落、时间戳和留存内容。简洁引用可以继续作为用户界面,但不能成为唯一记录。
不透明的上下文压缩
长时间运行的代理会话最终都需要压缩。由客户端控制、用户可见的摘要虽然有损,但至少可以检查和转移。用户可以审阅、编辑它,也可以让另一个模型重新生成一份摘要。
OpenAI 的服务端压缩则会生成一个加密的 compaction 项。文档称它“是不透明的,不适合人类解读”。独立的 /responses/compact 端点会返回一个“规范的下一上下文窗口”,并要求客户端原样传递。
从概念上看,这个转变如下:
// 压缩前:昂贵,但可以移植
let history = [
userMessage,
assistantMessage,
toolCall,
fullToolResult,
// ... 另外 200,000 个可理解的历史 token
];
// 压缩后:只有原提供商可以低成本地继续
history = [
{
type: "compaction",
encryptedContent: "enc_provider_only_state...",
},
...recentItems,
];
OpenAI 可以从压缩后的含义继续,而另一家提供商看到的只有一段不可读的字符串和最近的一小段上下文——严格来说,它本可以看到这些,但 Pi 从不把这类信息传给另一家提供商。
这并非技术上的必然。Anthropic 的服务端压缩会返回一个带有可读 content 字段的 compaction 数据块。客户端可以自定义摘要指令,生成的摘要能够被检查,也能传给另一个模型。任何提供商同样都可以采用客户端压缩。
OpenAI 的密封产物也许比普通摘要保留了更多模型专属状态,在原模型上也可能表现得更好。把它作为一项可选优化是合理的,但它应当同时提供可读的交接摘要,而不是取而代之。当然,这种做法同时还会带来一个“好处”:让用户更深地被锁在同一生态系统中。
子代理带着隐藏指令而来
多代理系统会让问题更加严重,因为此时不再只有一份对话记录,而是一棵会话树和代理之间的消息流。这些消息通常就像人写的提示,只不过现在是由一台机器写给另一台机器。
OpenAI 托管的 Responses Multi-agent 测试版会返回三种新项目:multi_agent_call、multi_agent_call_output 和 agent_message。spawn_agent 示例中的 message 参数是加密的,代理间消息也只有 encrypted_content。启用 Multi-agent 后,每个代理都会被隐式开启自动服务端压缩,即使客户端没有提出要求。API 不支持推理摘要,还会注入开发者无法编辑或移除的根代理与子代理指令。
这是一整套不可转移的状态:密封的委托、密封的代理消息、分别自动压缩的上下文、隐藏推理,以及由提供商托管的编排。
2026 年 6 月,开源 Codex 客户端也合入了一项相关改动。该提交名为 “Encrypt multi-agent v2 message payloads”,其中直接说明了数据流:
// 父模型发出的工具调用,Codex 会这样持久化
{
"name": "spawn_agent",
"arguments": {
"task_name": "worker",
"message": "<ciphertext>"
}
}
// 子模型收到的输入
{
"type": "agent_message",
"author": "/root",
"recipient": "/root/worker",
"content": [{
"type": "encrypted_content",
"encrypted_content": "<ciphertext>"
}]
}
Responses API 会加密父代理输出的工具参数,Codex 将其转发,API 再在内部为子代理解密。Codex 自己的 InterAgentCommunication.content 是空的。可读的 rollout 和历史记录中,不存在那项任务的准确内容。
这大概不只是切换模型时才会遇到的抽象问题。假设子代理改错了文件、泄露了秘密、重复了另一个代理的工作,或遵循了错误假设,用户将无法回答一个非常简单的问题:那个代理当时究竟接到了什么任务?
一个尚未关闭的 Codex issue要求在加密传递之外保留一份独立、可读的审计副本。这是最低限度可以接受的设计。更理想的做法是,让明文代理间消息继续成为默认选项。
“大多数人不会在会话中途切换模型”
也许确实如此。大多数人也不会每周更换操作系统或手机运营商。但即使你不使用这种自由,它仍然重要,因为它会改变你与提供商之间的关系,也会改变提供商对待你的方式。
用户也可能因为模型退役、服务中断、价格变化、策略阻止下一次请求(你好,Fable)、机密阶段必须在本地运行,或审计人员需要重建事件经过,而不得不迁移会话。代理也让会话变得越来越长:一次编码或研究会话可能积累数天的决策与证据,个人助手甚至可能积累跨越数年的会话记录——至少未来可能如此,因为我们还没有使用它们那么久。
能够离开,也会带来约束。如果提供商知道用户可以去别处继续,它就必须在模型质量、价格、可靠性和信任上竞争。如果用户积累的上下文只能由一家提供商解释,就会形成非常糟糕的激励。
可移植的推理 API 应当承诺什么
我们希望推理提供商和代理开发者采用以下几条规则:
- 本地事件日志是规范记录。 服务端存储可以镜像或加速它,但客户端无需解析服务端 ID 就能重建会话。
- 存储必须是显式选择。
store: false应当易用、有清晰文档,而且最好成为默认值。需要保留数据的功能,应当在使用时直接说明。 - 任何不透明项目都不能成为意义的唯一载体。 为提升同一提供商内的效果,可以包含加密推理、压缩数据和工具签名,但每一项都必须有可读、与提供商无关的交接表示。
- 托管工具必须提供完整保真日志。 记录精确的输入、输出、证据、过滤过程、来源、时间戳和内容哈希,而不是只记录润色后的答案和引用。
- 子代理通信必须可审计。 为每个代理持久化准确、可读的任务、消息、结果、继承关系、模型和工具权限。
- 上下文压缩必须可检查。 返回可读摘要、创建摘要时使用的指令,以及足以理解哪些内容被丢弃的沿袭信息。
- 产物必须可导出。 文件、容器输出、搜索快照和生成媒体都能够下载到按内容寻址的本地归档中。
实际上,蒸馏是一件好事
模型层也存在一种相关的锁定形式。
美国一些规模最大的闭源权重实验室对外部蒸馏越来越敌视。Anthropic 在2026 年 2 月的文章中,把 DeepSeek、Moonshot 和 MiniMax 涉嫌开展的相关活动称为“蒸馏攻击”。它的商业条款称客户拥有输出,却禁止用这些服务训练与其竞争的 AI 模型。与此同时,Anthropic 自己的文章也承认,当头部实验室把蒸馏用于自家模型时,“蒸馏是一种被广泛使用的正当训练方法”。
Anthropic 使用爬虫从公共网络收集模型开发数据,也曾以拆书扫描的方式闻名。OpenAI 同样表示会使用可自由访问的公共互联网内容训练模型,并主张利用公开网络材料进行训练属于合理使用。两家公司都认为,当蒸馏发生在自家内部时,它是生产更小模型的正常手段。OpenAI 还提供过明确的第一方 API 蒸馏流程,让用户用更强 OpenAI 模型的输出微调较小的 OpenAI 模型。
其中的道德不对称显而易见。实验室要求社会接受机器从人类放在互联网上的海量作品中学习——往往事先没有取得每个人的许可——同时又坚持其他机器不得学习这些实验室生成的输出。这个原则最宽泛的版本,恰好允许学习成果流入闭源模型,却不允许它们重新流出。
我们认为,对蒸馏的默认态度应当从敌视转向支持。蒸馏可以把昂贵的前沿能力转化为更小、更便宜、更快的模型,让它们能够在本地、离线、受限硬件上,或完全受用户控制的环境中运行。它可以促进竞争,在某个 API 消失后保留能力,并减少常见任务所需的算力与能源。
最低限度的自由
用户应当能够关闭账户、保留会话,并把它交给另一个模型。新模型可能不同意之前的结论,可能提出问题,也可能表现更差。但它不该只能盯着一段密文,而旧模型看到的明明是用户的历史、证据、计划和已委派的工作。
我们并不反对提供商构建更好的有状态 API。我们反对的是,把更好的性能与更少的用户控制捆绑起来。有状态存储应当是可选项,托管工具应当可观察,压缩应当可读,代理通信应当可审计;理想情况下,不透明推理不应当继续不透明,至少也要提供可移植的交接信息。蒸馏应当成为让能力更加普及的路径,而不是一个被用来为越来越高的围墙辩护的禁忌。