- 服务有多个实例,请求可能在实例之间切换
- 容器或 Serverless 环境的本地磁盘不可靠
- 需要自行管理会话数据的权限、加密、备份或保留周期
工作方式
- 写入是尽力而为的。 SDK 在后台镜像 entry。写入失败会被上报,但绝不会中断正在进行的对话;参见下文的运维。
- entry 对存储不透明。 你的存储原样保存并返回 SDK 定义的 transcript entry。应用不需要理解 qodercli 的本地文件格式。
快速开始
InMemorySessionStore 只把数据保存在当前进程中。在接入共享后端之前,用它来验证接线是否正确——即 query 确实会写入 store 并能从中恢复。它无法演示跨机器恢复,因为进程退出后数据就没了;跨机器场景需要实现真正的存储,见实现存储。
cwd,SDK 才能把请求识别为同一个项目。
在应用中使用
每次需要镜像或恢复会话的 query,都在options.sessionStore 中传入 store,然后选择如何指定会话:
- 已知 session ID —— 传入
resume: sessionId。 - 该项目最近的会话 —— 传入
continue: true。这要求 store 实现listSessions。
管理外置会话
会话管理函数接受同一个sessionStore 选项,并在 store 而不是本地文件上操作:
listSessions 要求 store 实现 listSessions;listSubagents 要求 listSubkeys;deleteSession 要求 delete。如果实现了 listSubkeys,getSubagentMessages 也会使用它。
要把已有的本地会话复制进 store——例如迁移一台此前未启用外置存储的机器——使用 importSessionToStore:
实现存储
SDK 不提供可直接用于生产的存储实现;应用需要针对自己选择的共享存储实现SessionStore 接口。Redis 和 PostgreSQL 的可运行参考实现展示了具体用法,见 TypeScript 示例或 Python 示例。它们是起点,而非可直接用于生产的实现。
SessionKey 标识一份 transcript。主会话没有 subpath;每份子代理 transcript 复用相同的 projectKey 和 sessionId,但带有不同的 subpath。把 key 和 entry 当作不透明数据——原样保存并返回,不要解析其中的消息内容。
两个必需方法提供保存与恢复。每个可选方法解锁一项能力:
实现检查清单
- 同一个 key 保持追加顺序,
load返回完整历史。回放依赖顺序。 - 隔离各 key。 绝不要把一个 key 的 entry 返回到另一个 key 下。
- 让
append幂等。 SDK 可能用相同的 entry 重试失败的写入,因此重试不能重复写入历史。 listSessions().mtime返回 Unix 毫秒时间戳,并且只返回主会话,即不带subpath的会话。- 级联删除。 删除主会话时,必须同时删除它的子代理 transcript。
listSubkeys只返回相对标识——不要返回绝对路径,也不要返回包含.或..的路径。这些会成为存储键,路径穿越片段会让 transcript 逃出它所在的命名空间。- 串行化并发写入。 如果同一会话可能被多个进程写入,在存储层串行化这些写入。
SessionStore conformance 测试检查这些通用行为,并为具体后端补充并发和重试测试。连接管理、权限、加密、备份、迁移和数据保留仍由应用负责。
运维
失败处理。 外置写入失败不会中断当前对话。最后一次重试失败后,SDK 会发出system/mirror_error 消息。对镜像完整性有要求时应监控它——即使某次写入没成功,对话仍可能成功完成。
调优。
- 外置读取默认最多等待 60 秒。用
loadTimeoutMs调整。 sessionStoreFlush默认为'batched'。设为'eager'会更早镜像 entry,代价是更多的存储请求。
- 显式指定的会话在 store 中不存在时,SDK 仍可回退到本机上的同 ID 会话。
sessionStore不能与persistSession: false、文件 checkpoint、自定义 transport provider 或实验性的 Cloud Agent runtime 同时使用。- store 在内置的 Process 和 Worker transport 上受支持。
- store 只保存会话历史,不保存鉴权状态、应用配置、文件 checkpoint 或数据保留策略。