← 返回博客简历 ↗

没有 Session Store,Context Engineering 只完成了一半

Context Engineering 决定模型这一轮看到什么,而 Session Store 保存会话跨轮运行、异常恢复和状态追踪所依赖的连续性。

这两年,Context Engineering 越来越火。

大家开始讨论上下文应该放什么、按什么顺序组装、什么时候压缩、哪些信息需要保留,以及怎样在有限的窗口里让 Agent 看到当前最重要的内容。

这些问题当然重要。但我一直觉得,很多讨论停在了上下文本身,却很少继续追问一句:这些上下文究竟从哪里来?

一个运行了几十轮的 Agent,为什么能记得任务目标、刚刚修改过的文件、工具执行结果和当前进度?如果进程意外退出,重新启动以后,它又凭什么知道上一次停在哪里?

总不能每一轮都从头翻阅完整对话,再临时猜测一次系统状态。

所以我想讨论一个经常躲在 Context Engineering 背后的东西:Session Store。

上下文可以承载状态,但不能成为唯一的状态源

我在上一篇文章里说过,上下文不能承担状态的职责。

更准确地说,上下文不能完全承担这个职责。

上下文当然是状态延续的一部分。Agent 这一轮知道什么,会直接影响它下一步做什么。任务摘要、近期文件、工具结果和历史消息进入 Prompt 后,都会成为模型当前判断的依据。

但上下文只是系统在某个时刻投射给模型的一张切片,并不等于系统本身。

Prompt 里写着“任务正在执行”,不代表任务此刻一定还在执行;历史消息里有人说“已经完成”,也不代表产物存在,更不代表验证已经通过。

上下文描述的是 Agent 当前看见的世界,外部状态记录的才是系统承认的事实。

如果把二者混在一起,Agent 就只能依靠自己的记忆判断进度。上下文一旦被压缩、裁剪、覆盖或丢失,任务的连续性也会一起消失。

LLM 是大脑,Harness 才是身体

LLM 本身没有我们通常理解的持续记忆。

每一次调用,它都只是根据这一轮收到的输入继续生成。它可以表现得像是记得之前发生过什么,但这种连续性并不天然存在,而是外部系统重新把相关信息交给了它。

我脑海里有一个很直观的画面:电影《银河护卫队 2》里的伊戈。

最开始的伊戈只是一个浩瀚、强大,却没有稳定形态的大脑。它拥有巨大的能量,但仍然需要为自己塑造身体,才能持续感知世界、执行动作,并把意图真正作用到外部环境。

LLM 也有点像这样。

它拥有让人高山仰止的语言、推理和知识能力,但如果没有上下文、工具、状态、权限和运行循环,它再强也只是一次性的生成能力,是一团没有落点的智能。

Harness Engineering 做的事情,就是为这颗大脑搭建身体。

Context Engineering 决定它这一刻能够感知什么,工具系统决定它能够触碰什么,状态机约束它可以怎样行动,而 Session Store 则保存这具身体在时间中留下的连续性。

没有这层连续性,每次模型调用都像重新醒来一次。

Context Engineering 负责选择,Session Store 负责保存

Context Engineering 和 Session Store 很容易被混在一起,因为它们最终都会影响 Prompt。

但两者解决的不是同一个问题。

Context Engineering 关心的是:面对当前任务,这一轮究竟应该让模型看到什么。

Session Store 关心的是:过去发生过的事情被保存在哪里,哪些信息仍然有效,以及系统下次需要时能否重新找到它们。

一个负责选择,一个负责保存。

Context Engineering 会把稳定规则、任务摘要、相关记忆、近期历史和当前请求重新组装起来。它可能压缩旧对话,裁剪冗长工具输出,只提取与当前步骤有关的少量信息。

但无论采用什么策略,都需要先有一个可以读取的来源。

任务目标、历史消息、工具调用、文件摘要和执行进度不可能凭空出现在 Prompt 中。它们必须先被记录、更新和失效管理,然后才能在下一轮被重新选择。

因此,Session Store 并不是 Prompt Cache 的另一种叫法。

Prompt Cache 主要复用已经计算过的稳定输入,解决重复计算与调用成本。Session Store 保存的则是不断变化的会话事实,解决跨轮连续、异常恢复和状态追踪。

一个在复用输入,一个在保存过程。

Session Store 确实可能让上下文组装更快,因为 Agent 不必每次重新扫描整个世界。但“快”只是结果。它更重要的价值,是让上下文的来源稳定、可验证,并且能够恢复。

Session Store 到底保存什么

Session Store 不一定是一张名为 sessions 的数据库表,也不一定是某个固定文件。

我更愿意把它理解成:为了让一次 Agent 会话能够继续运行,系统需要持久保存的那组信息。

第一类是会话历史。

它包括用户请求、模型响应和 Agent 之间已经发生的消息。历史不一定每轮全量进入上下文,但系统需要保留一份可追溯的来源,才能在压缩错误或任务回退时重新检查。

第二类是工作记忆。

例如当前任务摘要、最近操作的文件、文件短摘要,以及后续步骤仍可能使用的少量结论。它不是完整历史的复制,而是从过程里提炼出的高价值状态。

第三类是工具调用记录。

Agent 调用了什么工具、传入什么参数、得到什么结果,以及调用是否成功。这些记录既能帮助下一轮避免重复劳动,也能在出现问题时解释系统为什么走到了当前位置。

第四类是显式任务状态。

当前处于哪个阶段,哪些子任务已经完成,哪些任务正在执行,阻塞原因是什么,剩余预算还有多少。这些信息不能只写在模型的自然语言回答里。

第五类是 Checkpoint 和运行工件。

计划、补丁、报告、验证结果和中间产物,都可能成为恢复任务所需的依据。Agent 意外退出以后,新的执行者需要接管这些事实,而不是重新猜测原来的工作过程。

至于 Trace,我更倾向把它看成与 Session Store 相邻、但目的不同的一层。

Session Store 主要服务“任务如何继续”,Trace 主要服务“这次运行究竟发生了什么”。两者可能共享数据,也可能落在同一套存储里,但设计时最好不要把恢复状态与观察日志完全混成一团。

为什么 Coding Agent 通常选择本地存储

对于 Coding Agent 来说,Session Store 经常保存在本地。

它可能是一组 Markdown 或 JSON 文件,也可能是 SQLite,甚至只是工作区里一套结构清楚的目录。具体形式没有那么重要,重要的是它与当前代码仓库离得足够近。

首先是隐私。

代码、命令输出、文件路径和环境信息都可能包含敏感内容。把会话状态留在本地,可以减少不必要的数据传输,也更符合开发者对本地工具的预期。

其次是延迟和读写成本。

Coding Agent 的运行伴随着高频读写。它需要不断更新任务摘要、文件状态、工具结果和运行工件。相比每次访问远程服务,本地文件或嵌入式数据库通常更直接,也更容易控制成本。

第三是它与工作区之间天然存在归属关系。

一个会话服务于哪个仓库、哪个分支、哪次任务,本地存储更容易建立明确对应。恢复时,系统也能先确认当前工作区是否仍与保存状态匹配,避免拿旧状态操作已经变化的代码。

这里需要区分“与版本绑定”和“把会话数据提交到 Git”。

大多数 Session 数据并不适合进入代码版本库,但它应该知道自己基于哪个提交、哪个分支或哪份工作区快照生成。这样才能判断过去的文件摘要和任务结论是否已经过期。

对于单机、单用户、以一个代码仓库为工作边界的 Agent,几乎可以说本地保存是唯一的巴塞勒斯。

本地文件不是答案,只是当前场景的答案

但 Agent 不会永远停留在单机 Coding 场景。

一旦系统需要支持多设备、多实例、多个并发 Agent,或者由服务端统一调度任务,本地文件的边界就会很快暴露出来。

两个 Agent 同时更新一份会话状态时,谁的修改生效?一个实例退出以后,另一个实例怎样立刻接管?消息如何确认已经消费?状态更新与产物提交如何保持一致?

这些问题已经不只是文件读写,而是并发、一致性、锁、事务和故障恢复问题。

这时,数据库、缓存系统、对象存储或消息队列都可能进入设计。选择哪一种,并不存在脱离场景的标准答案。

真正应该先问的是:状态由多少实例共享,读写频率多高,是否允许短暂不一致,恢复时间要求是什么,以及哪些数据必须持久保存。

对于单机 Agent,SQLite 可能已经足够;对于需要低延迟协作的多个执行者,可能需要共享的状态服务;对于大体积工件,则可能更适合单独存储,只在 Session 中保留引用。

Session Store 不是某种具体技术,而是一项系统职责。存储介质可以变化,但“谁负责保存会话连续性”这个问题不能消失。

上下文是投影,Session Store 才是连续性的来源

现在再回头看 Context Engineering,会发现它并不是孤立地设计一份 Prompt。

每一轮上下文,都是系统根据当前目标,从协议、状态、历史、记忆和运行工件中投射出来的一张临时视图。

这张视图需要足够相关、足够精简,也必须尽量站在真实状态上。Context Engineering 决定这张视图怎样形成,Session Store 则保存形成视图所需要的事实。

没有好的 Context Engineering,Session Store 只会变成一间堆满历史数据的仓库;没有可靠的 Session Store,Context Engineering 则只能依赖零散的聊天记录,反复重建一个并不确定的过去。

两者真正的关系不是替代,而是上下游。

Context Engineering 管的是 Agent 这一轮看什么,Session Store 管的是下一轮还有什么可以看;前者组织当前认知,后者保存时间连续性。

Prompt 是 Agent 此刻看到的世界。

Session Store 保存的,是这个世界下一次还能继续存在的依据。