Memoh会话运行时原理:AI Agent会话如何在服务器重启后不丢失上下文
【免费下载链接】Memoh✨ The open-source multi-agent platform. Every agent gets its own computer, desktop, network, and long-term memory. You can bring your own key, or host your coding agent like Claude Code, Codex and so on.项目地址: https://gitcode.com/gh_mirrors/me/Memoh
Memoh 是一个开源多智能体(Multi-Agent)平台:每个 AI Agent 都拥有自己的"云端电脑"——独立的工作区、桌面、网络和长期记忆。这篇文章面向新手用户,用尽量少的代码,讲清楚 Memoh 的会话运行时(Session Runtime)是如何工作的:为什么服务器重启、甚至多实例滚动升级时,AI Agent 的会话上下文不会丢失,进行中的任务也能自动续跑。
你会遇到的问题:AI 会话怕"重启"
大多数聊天机器人都有一个通病:服务端一重启,正在进行的对话就"断片"了。轻则回答中断、需要你重发一遍,重则 Agent 忘记了自己正在执行什么任务,之前积累的判断全部归零。
对普通用户来说,这种体验很难受;对自托管(Self-Hosted)部署的管理员来说更糟——升级服务器、重启容器,都成了"高危操作"。Memoh 把这个问题当成架构级问题来解决:它不是"尽量别断",而是保证断得掉、续得上。
核心思想一:先写账本,再干活(持久化优先)
Memoh 的会话运行时建立在一个简单但关键的原则上:
任何输入在被接受(accepted)之前,必须先落到数据库;任何执行状态的变化,都以 PostgreSQL 中的"账本"(ledger)为准。
具体来说,你的每一次提问在系统里会生成一条session_runs账本记录,包含:
- 你的原始输入内容
- 全局唯一身份标识(
invocation_id、run_id、turn_id) - 当前执行状态(运行中 / 等待审批 / 已完成 / 已中断等)
- 已持久化的中间输出
这套账本的设计约束写在 会话运行时正确性要求 中,核心条款 SR-DUR-001 明确写道:
"Server 在返回 accepted 后退出,重启后的系统必须仍能查询到原始用户输入、run 身份和最后一个已持久化状态……不能只把已接受输入保存在 goroutine、WebSocket handler 或进程内 Manager 中。"
翻译成大白话:进程的内存只是"草稿纸",数据库才是"账本"。进程随时可以死,账本永远在。
核心思想二:给每次执行发"身份证",重复提交不会跑两遍
Memoh 用一组严格的身份标识把"一次对话"切成几个互不混淆的概念:
| 身份 | 作用 |
|---|---|
invocation_id | 标识"一次提交"。网络抖动导致你连点两次发送?系统保证只执行一次 |
run_id | 标识"一次被接受的执行"。整个执行过程围绕它展开 |
turn_id | 标识对话时间线上的一个回合,把你的输入、Agent 的回答、工具调用关联起来 |
decision_id | 标识一次审批或用户补充输入请求,保证你的回答不会被应用到错误的执行上 |
这带来两个对用户很友好的保证:
- 幂等重试:请求发出去没收到回音,你安全地重试,不会触发第二次模型调用、不会写入两条重复消息。
- 单会话串行:同一个会话同一时刻只有一个"执行者"(owner),不会出现两个服务器实例同时在跑同一个会话的情况。
这套算法和并发测试集中在 internal/agent/runtime/session/ 目录,其中的 admit.go 负责准入判断,fence.go 负责所有权"围栏"(fencing)校验。
核心思想三:优雅关机打标 + 自动恢复续跑
这是整个机制里最精彩的部分,分三步走:
第一步:关机前"留纸条"。服务器收到停机信号后,会先关闭新输入、给所有正在运行的会话打上session_runtime.interrupted(已中断)标记,然后才取消执行、关闭 HTTP 服务。整个优雅关机预算是 30 秒,Docker Compose 侧允许 45 秒。
第二步:启动后自动"翻账本"。服务器重启后,一个有界的工作协程会扫描账本中所有"被中断"的运行,读取其中保存的版本化恢复上下文(原始提问、模型选择、执行位置、剩余时间预算等),然后用一个稳定的身份(resume:<原run-id>)提交续跑任务。
第三步:续跑时"先看现场"。恢复出来的执行会重新加载已提交的历史,并指示模型先检查被中断的工具执行结果,再决定下一步动作——避免盲目重复执行一个已经跑了一半的命令。
这三步的完整描述在 docs/agent-runtime.md 的 "Graceful shutdown and session resume" 一节。需要说明的诚实边界:SIGKILL、OOM 这类"猝死"无法可靠地写标记,此时运行会收敛为lost(丢失)状态——但你的输入依然保存在账本里,可以手动重试或开启新任务。
核心思想四:多实例部署时的"租约 + 围栏"
如果你部署了多台服务器(比如做负载均衡或滚动升级),还会出现一个新问题:谁来执行这个会话?旧实例还没死透,新实例已经开始接手怎么办?
Memoh 用两个经典分布式手段解决:
- 租约(Lease):owner 身份必须持有跨实例可验证的租约(单实例部署用进程内内存后端,多实例部署用 Redis/Valkey,启动时会校验配置,缺少依赖会拒绝进入多实例模式)。
- 围栏令牌(Fencing Token):每次所有权转移都会让令牌单调递增,旧 owner 迟到的写入会带着"过期令牌",被数据库直接拒绝。
效果是:即使新旧两个实例短暂并存,也最多只有一个拥有写入权,会话不会出现"精神分裂"。
这套机制在多实例下的黑盒验收测试位于 internal/agent/runtime/session/acceptance/,其 README 中列出了关键场景:双服务器重连快照、跨实例 abort 路由、owner 进程被 SIGKILL 后状态仍可查询(收敛为lost)、终态重放不产生重复消息等。
真实压过一遍:滚动升级不丢会话
原理说得再漂亮,还是要实战检验。Agent 生命周期 QA 报告 记录了一次真实的"滚动替换"演练:
- 旧实例正在服务流量,新实例接入同一个 PostgreSQL 和 Valkey;
- 通过 UI 发起一个带真实长命令的会话,命令还在执行、模型还在流式输出;
- 切换代理上游到新实例,停掉旧实例(45 秒宽限);
- 不发送任何新的用户消息,观察新实例的账本、UI 和任务文件。
结果:会话在新实例上自动恢复(UI 出现ROLL_RESUMED标记),所有权从实例 C 转移到 D,围栏令牌从 26 递增到 27,代理健康探测 42 秒内 200/200 全部成功,任务文件的执行凭据在替换前后各一行、没有重复。长任务场景同样验证过:父子 Agent 各执行了 660 秒的真实子命令,跨越重启边界完整完成。
外部运行时(Claude Code / Codex)如何续命?
如果你托管的是 Claude Code、Codex 这类"自带会话记忆"的外部 Agent,Memoh 还有第二道保险:这些运行时会在 Bot 的工作区数据卷上保存自己的会话文件(Codex 的 rollout、Claude Code 的 transcript)。Memoh 记录的是"如何恢复它"的元数据(Codex thread id / rollout 路径、Claude 会话 ID),重启后按路径优先恢复原生会话;万一原生会话无法恢复,本轮会开启新会话并明确告知(native_history_lost通知),后续轮次从 Memoh 的组合上下文继续——而聊天历史始终完整保存在 PostgreSQL 里,不会丢。
写在最后
回到开头的问题:AI Agent 会话为什么能在服务器重启后不丢上下文?Memoh 的答案可以浓缩成四句话:
- 持久化优先:输入和状态先写进 PostgreSQL 账本,内存只是缓存;
- 身份严格:
invocation_id/run_id/turn_id保证幂等、防重、防错配; - 中断有标记、恢复有流程:优雅关机打
interrupted标记,重启后自动发现、自动续跑、先检查工具现场再行动; - 多实例有租约、有围栏:所有权转移全程可验证,旧实例的迟到写入一律作废。
对于自托管用户,这意味着升级、重启、扩容都成了低风险操作;对于普通用户,这意味着你的 AI Agent 真正"活在服务器上"——即使合上笔记本,它也记得自己正在做什么。想深入了解设计细节,可以对照阅读 docs/design/session-runtime-requirements.md 与 docs/design/context-memory-scheduling.md。
【免费下载链接】Memoh✨ The open-source multi-agent platform. Every agent gets its own computer, desktop, network, and long-term memory. You can bring your own key, or host your coding agent like Claude Code, Codex and so on.项目地址: https://gitcode.com/gh_mirrors/me/Memoh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考