ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MemOS core/session 模块深度解析:Agent 会话、回合(Episode)与意图分发的权威真相源

MemOS core/session 模块深度解析:Agent 会话、回合(Episode)与意图分发的权威真相源 人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin【免费下载链接】MemOSSelf-evolving memory OS for LLM AI Agents: ultra-persistent memory, hybrid-retrieval, and cross-task skill reuse, with 35.24% token savings and DeepSeek Harness support.项目地址https://gitcode.com/gh_mirrors/memos/MemOS点击查看免费下载导读本篇文章围绕 MemOS 开源仓库中 core/session 模块展开它负责管理 Agent 与记忆系统之间的会话Session与回合Episode生命周期是外部世界一个 adapter ↔ 一个 agent ↔ 多个用户与算法管线之间的分界线。读完本文你将掌握 session/episode/turn 三级模型的设计动机、SessionManager公共 API 的完整用法、混合意图分类器的决策流程与降级策略以及事件总线、存储写入模式、错误模型等在源码中的具体落地方式可直接用于接入或二次开发。1. 模块定位为什么需要一个专门的 Session 层在 core/session/README.md 的模块描述中session 层被定义为权威真相源authoritative source of truth——它回答一个贯穿整个记忆系统的问题我们现在处于哪一轮对话之中MemOS 采用一个 adapter 对接一个 agent、一个 agent 服务多个用户的拓扑。当来自 OpenClaw、Hermes 或 DeepSeek Harness 等外部适配器的请求进入时LLM 侧的每一次记忆操作都必须挂载到一个已知的 session并且一个已知的 episode 上。否则后续的捕获capture、反思reflection、奖励reward、技能结晶skill crystallization等算法管线将无法确定该把数据归类到哪一段对话上下文。从源码结构看该模块被拆成若干职责单一的文件统一由 core/session/index.ts 对外导出文件职责manager.tsSessionManageradapter 与编排器唯一可见的门面episode-manager.tsEpisodeManager回合级写入路径intent-classifier.ts意图分类器启发式 LLM 混合relation-classifier.ts回合关系分类器V7 §0.1 revision 路径heuristics.ts意图启发式规则集events.ts进程内同步事件总线persistence.ts存储接口与 SQLite 适配器types.ts全部类型契约2. 三个核心概念Session / Episode / Turn原文档给出了与 V7 §3.1 对齐的概念表概念定义Session与某个 agent 的长生命周期逻辑连接。每个运行中的 agent 进程一个落在sessions表中。Episode恰好一次用户查询 其完整的 agent 响应弧工具调用、子 agent。奖励与归纳induction的原子单元落在episodes表中。TurnEpisode 内部的单条消息user / assistant / tool / system。在 Phase 6 中作为 traces 持久化在此处只保留一份精简的内存副本。关键规则每次用户查询都会开启一个全新的 episode而子 agent 的跳转不会新开 episode它们只是向父 episode 追加更多 turn。这与 V7 每个 episode 一个奖励信号one reward signal per episode的决策保持一致同时trace_depth作为 per-trace 属性保留在追踪行上。也就是说奖励与归纳的粒度边界由 episode 决定而追踪的细粒度由 trace 决定二者互不耦合。在 types.ts 中这三个概念被建模为SessionSnapshot含openEpisodeCount、EpisodeSnapshot含status、turns、traceIds、rTask、intent与EpisodeTurn含role、content、meta。3. 公共 API 与最小接入示例原文档给出了模块的核心用法以下是完整的、可直接运行的接入代码import { createSessionManager, createIntentClassifier, adaptSessionsRepo, adaptEpisodesRepo } from memos/core; const intent createIntentClassifier({ llm, timeoutMs: 5000 }); const sm createSessionManager({ sessionsRepo: adaptSessionsRepo(sqliteSessions), episodesRepo: adaptEpisodesRepo(sqliteEpisodes), intentClassifier: intent, idleCutoffMs: 24 * 60 * 60 * 1000, }); const session sm.openSession({ agent: openclaw, meta: { hostPid: 1234 } }); const episode await sm.startEpisode({ sessionId: session.id, userMessage: fix the flaky test }); sm.addTurn(episode.id, { role: assistant, content: Sure. Reading the log… }); sm.addTurn(episode.id, { role: tool, content: log output, meta: { tool: read_file } }); sm.addTurn(episode.id, { role: assistant, content: Done. Patched mutex_lock. }); sm.finalizeEpisode(episode.id); // rTask scored later (Phase 7)结合 manager.ts 中的SessionManagerDeps与SessionManager接口可以对上述参数做更精确的说明sessionsRepo/episodesRepo必须是实现了 persistence.ts 中SessionRepo/EpisodesRepo接口的对象。生产环境用adaptSessionsRepo(sqliteSessions)/adaptEpisodesRepo(sqliteEpisodes)桥接core/storage/repos/中的真实 SQLite 仓储测试则注入内存 fake见 tests/unit/session/_in-memory-repos.ts。idleCutoffMs空闲判定阈值默认 24 小时被pruneIdle使用。intentClassifiercreateIntentClassifier({ llm, timeoutMs })创建timeoutMs默认 6000ms原文档示例中显式给了 5000ms。startEpisode是异步的因为回合开启时需要先跑一遍意图分类可能触发 LLM 调用。addTurn是同步的只做内存追加不触碰 SQLite详情见第 8 节。finalizeEpisode(episode.id)不带rTask时r_task保持null奖励打分由 Phase 7 异步完成。SessionManager接口还提供了closeSession、getSession、listSessions、pruneIdle、abandonEpisode、reopenEpisode、hydrateEpisode、attachTraceIds、patchEpisodeMeta、listOpenEpisodes与shutdown等完整生命周期方法。4. Session 生命周期打开、关闭、空闲剪枝与停机清理4.1 openSession幂等打开manager.ts 中openSession的实现要点通过ids.session()生成会话 id也可由调用方预置input.id底层调用sessionsRepo.upsertIfMissing(...)即INSERT 若缺失否则 no-op天然幂等当传入meta时额外触发touchLastSeen以刷新活跃时间写入后会立即回读并构造SessionSnapshot缓存在进程内liveMap 中同时广播session.started事件。SessionOpenInput只要求agent: AgentKind与可选的meta如hostPid、OS、版本号等自由元数据。4.2 closeSessionnormal lifecycle而不是 abandonmentcloseSession的语义在源码中有非常细致的处理manager.ts。它遍历该 session 下所有 open 的 episode若meta.lightweightMemory true轻量记忆模式或 episode 是已完成交换isCompletedExchange即已有 traceIds 或已含非空 assistant turn则finalize让捕获与奖励管线像用户完成任务一样运行若关闭原因是shutdown:*则打上topicState: pausedpauseReason供重启后的宿主延续同一话题否则同样暂停而非放弃。设计意图是/new、/quit、宿主正常退出都是正常生命周期不是episode 放弃因此要避免用户侧看到已跳过的误导性标记真正的崩溃孤儿由core/pipeline/memory-core.ts中的recoverOrphanedEpisodes在启动时单独恢复。4.3 pruneIdle 与 shutdownpruneIdle(nowTs)按idleCutoffMs计算截止时间只剪枝live中lastSeenAt早于截止点的 session并且绝不在 episode 进行中驱逐openEps.length 0时跳过随后广播session.idle_pruned。shutdown(reason)是进程级清理先兜底处理session 已被剪枝但 episode 仍 open的竞态场景此时按完成情况 finalize / discard / pause再对每个仍存活的 session 调用closeSession(id, shutdown:...。5. Episode 生命周期start → turns → finalize / abandon5.1 startEpisode预分配 id 意图分类 审计关联manager.ts 的startEpisode有一个值得一提的实现细节在意图分类器运行之前就预分配 episode id。这样分类器的 LLM 调用op 为session.intent.classify可以携带该 id使system_model_status审计行能与该 episode 的管线活动正确分组避免在 Logs viewer 里出现孤立的审计条目。id 铸造是纯字符串生成不写库因此没有双铸风险而IntentClassifier.classify捕获一切内部错误并返回 fallback 决策保证预分配 id 在 happy-path 上一定能到达插入路径。随后在withCtx({ sessionId, episodeId }, fn)包裹下调用epm.start(...)写入episodes行statusopen并sessions.touchLastSeen。5.2 addTurn热路径零 SQLiteaddTurn由 episode-manager.ts 实现要点用assertOpen校验 episode 存在且未关闭为 turn 铸入ids.span()作为短 id、打时间戳后追加到内存snap.turns若 role 为user同步更新topicState: active、pendingUserText、lastUserText并updateMeta写库若为assistant则类似处理lastAssistantText每次追加都会sessionsRepo.touchLastSeen刷新 session 活跃度广播episode.turn_added。Turn 级持久化属于 Phase 6traces的职责本层只保留内存快照这也是热路径性能的关键。5.3 finalize / abandon / discardEmptyfinalize置statusclosed、写endedAt、可选写入rTask与patchMetameta 强制打上topicState: ended、closeReason: finalized调用episodesRepo.close广播episode.finalizedclosedBy: finalized。abandon对未知 episode 抛错对已关闭的静默返回否则同样关闭但closeReason: abandoned并附带abandonReason广播episode.finalizedclosedBy: abandoned与episode.abandoned。discardEmpty仅当 episode 既无 traceIds 又无非空 assistant turn 时才允许删除否则抛conflict直接从内存与 SQLite 中移除——用于清理用户发问后立刻放弃产生的空壳。5.4 reopenEpisodeV7 revision 路径当回合关系分类器判定新消息是对上一答案的修正revision时episode-manager.ts 的reopen会把已关闭的 episode 翻回 open清空endedAt、置topicState: active、写入reopenedAt/reopenReason若此前已有rTask或 reward 则打上rewardDirty标记原因episode_reopened供奖励系统后续对既有 L1 trace 做反向传播。若 episode 已处于 open竞态则安全地 no-op。6. 事件总线进程内同步 pub/sub原文档给出了订阅示例sm.bus.on(episode.started, (e) console.log(e.episode.id)); sm.bus.onAny((e) console.log(e.kind));events.ts 实现了一个刻意保持精简的自研事件总线未使用 Node 内置EventEmitter原因有三需要返回退订函数的一次性订阅 API需要onAny通配通道以便统一转发给 viewerSSE、Phase 15 管线编排器与未来的遥测终端需要同步投递以保证编排器的顺序性。支持的事件全集见 types.tssession.started/session.closed/session.idle_prunedepisode.started/episode.reopened/episode.turn_addedepisode.finalized携带closedBy: finalized | abandoned/episode.abandonedepisode.relation_classified关系分类结果供 viewer 展示为何选择该关系异常隔离任何监听器抛出异常都会被捕获并记录到core.session通道绝不会破坏其他订阅者也绝不破坏投递顺序。listenerCount(kind?)可统计监听器数量供测试断言。原文档还明确了 Phase 15 管线对episode.finalized的订阅链路capture.extract → reflection → reward.R_human → backprop → l2.incremental ↓ skill.maybe_crystallize即一次 episode 的收尾会顺序触发捕获、反思、人类奖励、反向传播、L2 增量归纳并在适当时机触发技能结晶viewer 则订阅onAny实现实时 SSE 流式展示。7. 意图分类器启发式 LLM 混合决策意图分类器决定新 episode 的首条用户消息应当触发哪些检索层Tier 1/2/3。原文档给出了决策流user message ▼ heuristic rules (heuristics.ts) │ ├─ strong match (conf ≥ 0.85) ─→ DONE │ ├─ weak match ─→ LLM (if available) — fall back to weak match on failure │ └─ no match ─→ LLM (if available) — fall back to unknown ( full retrieval)五种规范类别canonical kinds及其检索行为Kind触发检索典型示例taskTier 1 2 3fix this bug、帮我改这个函数、write a blogmemory_probeTier 1 2what did we discuss last time、你还记得…chitchat无thanks、ok、你好meta无交给 adapter/memos status、/memory exportunknownTier 1 2 3有歧义时默认全量检索7.1 启发式规则源码级细节heuristics.ts 中的规则按顺序匹配、首个命中生效meta永远排在最前规则 id类别置信度触发条件meta.command_prefixmeta0.98以/memos、/memory、/memo开头chitchat.greetingchitchat0.90中英文问候/致谢/确认≤48 字符memory.past_referencememory_probe0.88what did we discuss、你还记得、我们聊过等过去时引用task.imperative_verbtask0.75祈使动词开头write/fix/帮/请/给我…task.long_freeformtask0.60约 40 词 / 60 个 CJK 字符以上的长文本其中task.imperative_verb0.75与task.long_freeform0.60低于 0.85 的强匹配阈值属于弱命中只在 LLM 不可用或失败时作为 fallback 生效。wordCount对 CJK 采用逐字符计数近似。retrievalFor(kind)同样导出自 heuristics.ts把 kind 映射为三个 tier 的布尔标志task与unknown全开memory_probe只开 Tier 12chitchat/meta全关——缺记忆比多花钱更糟因此歧义默认走全量检索。7.2 LLM tiebreaker当存在弱启发式命中或无命中、且配置了 LLM 时intent-classifier.ts 通过LlmClient.completeJson调用模型系统提示INTENT_SYSTEM强制模型从 5 个标签中选一并返回{kind, confidence, reason}结构通过schemaHint给出 JSON 形状提示validate回调校验kind必须在词表内、confidence必须为数字、reason必须为字符串违规即抛llm_output_malformed并有一次malformedRetries: 1重试机会temperature: 0保证判定的确定性用户文本截断到 2000 字符外层withTimeout以timeoutMs默认 6000ms限时超时抛llm_timeout。关键降级原则模型失败永远不阻止 session 推进。LLM 超时、输出畸形或不可用时分类器自动落到最强的弱启发式带llm_skipped信号或unknown全量检索。空/纯空白消息则零成本直接判为chitchat0.9。从 ALGORITHMS.md 可知其成本特征纯启发式路径约 0.1msLLM 路径约 700–2000ms取决于 provider受timeoutMs封顶。分类器只在 episode 开启时调用一次绝不在回合中途调用。8. 存储层与写入模式8.1 接口与适配器persistence.ts 定义了模块依赖的两个薄接口SessionRepoupsertIfMissing、touchLastSeen、getById、listRecent、deleteOlderThanEpisodesRepoinsert、updateTraceIds、updateMeta、deleteById、close、reopen、getById、getOpenForSession。adaptSessionsRepo/adaptEpisodesRepo将core/storage/repos/中的原始 SQLite 仓储makeSessionsRepo/makeEpisodesRepo桥接为上述核心友好的签名测试则注入实现同一接口的内存 fake无需启动真实数据库。8.2 写入模式一览操作落库行为openSessionINSERTsessions若缺失否则 no-opstartEpisodeINSERTepisodesstatusopensessions.touchaddTurn仅内存。Turn 级持久化由 Phase 6traces负责finalize/abandonUPDATEepisodesstatusclosed、ended_at、rTask、meta.closeReasonattachTraceIdsUPDATEepisodes.trace_ids_json由 Phase 6 写完 L1 行后调用所有写入都是同步 SQLite 调用热路径新 turn除sessions.touch外不触碰 SQLite。8.3 一个值得注意的陷阱close 必须用 UPDATE 而非 upsertadaptEpisodesRepo.close的实现注释persistence.ts记录了一个真实踩坑仓储层的upsert是INSERT OR REPLACESQLite 会把它执行为 DELETE INSERT而traces.session_id REFERENCES sessions ON DELETE CASCADE以及episode_id ON DELETE CASCADE会导致该 episode 下所有 trace 被静默级联删除。因此 close/reopen 只能做外科手术式的 UPDATEstatus/ended_at走sqlite.closemeta_json走updateMeta非 REPLACE。这是理解本模块持久化正确性的关键细节。9. 上下文传播withCtx 与关联 IDstartEpisode将主体包裹在withCtx({ sessionId, episodeId }, fn)中执行manager.ts使下游的log.info(...)调用自动携带这两个关联 id。编排器与 LLM 层复用同一模式因此不需要到处写log.child({ sessionId })样板代码。这一设计保证了日志、审计与追踪链路含system_model_status审计行能够按 session/episode 维度聚合。10. 错误模型原文档给出的错误码表与 agent-contract/errors.ts 中的MemosError/ERROR_CODES一一对应错误码触发时机session_not_found对未知 session 调用startEpisodeepisode_not_found对未知 episode 调用addTurn/finalize/abandonconflict对已关闭 episode 调用addTurninvalid_argumentstartEpisode传入空 user messageinternalupsert 后立即回读 DB 却无行llm_timeout意图分类器 LLM 超过timeoutMsllm_output_malformed意图分类器 LLM 返回不合规 JSON需要说明的是分类器内部会吞掉 LLM 相关错误并降级因此上述 LLM 错误码更多用于审计与可观测而非阻断用户请求。11. 日志通道core.session— session.opened / closed / pruned / shutdown 事件core.session.intent— 启发式命中、LLM 判定、失败降级core.episode— episode.begun / turn_addeddebug/ finalized / abandoned分类器还通过signal数组记录具体命中的规则 id如meta.command_prefix、llm、heuristic:task.imperative_verb(weak)配合reason≤120 字符在前端 viewer 与审计日志中提供可追溯的判定依据。12. 测试矩阵原文档列出的测试位于 tests/unit/session/heuristics.test.ts— 每个规范标签的规则匹配intent-classifier.test.ts— 强启发式、LLM tiebreak、LLM 失败回退、空消息、超时events.test.ts— on / onAny / listenerCount监听器抛错被隔离episode-manager.test.ts— start/addTurn/finalize/abandon 生命周期、已关闭 episode 防护、trace id 附加、事件发射顺序session-manager.test.ts— openSession 幂等、startEpisode、pruneIdle、shutdown 清理、open episode 计数relation-classifier.test.ts— V7 §0.1 规则 LLM tiebreaker 超时回退。所有测试通过 tests/unit/session/_in-memory-repos.ts 提供的内存仓储实现接口无需 SQLite 即可覆盖全生命周期。13. 已知约束与注意事项Caveats原文档明确列出的三条限制是接入时最容易踩的坑不支持 per-session 并发。模块假设同一时刻只有一个 agent 进程在写入。若需要并行 agent请为每个 agent 开启不同的 session idopenSession幂等 唯一 id 天然支持这一用法。Turn id 是临时性的。它们只在内存快照内稳定真正需要跨进程引用的 turn 会由 Phase 6 铸造成稳定的 trace idtr_…并提升到 L1 层。意图 LLM 从配置角度是 fire-and-forget 的。当配置llm.providerlocal_only时适配层会传递disableLlmtrue分类器因此永远不会发起注定失败的联网调用只走纯启发式路径。14. 延伸回合关系分类器V7 §0.1 revision 路径与意图分类器同处一个目录、同样由 ALGORITHMS.md 记录的还有决定下一轮与上一轮是什么关系的关系分类器 relation-classifier.ts。它判定新用户消息q_{k1}与上一 episode 的q_k ŷ_k之间的关系输出四种结果之一关系语义生命周期动作revision修正/细化上一答案同一任务同一 session、reopen 同一 episodeR_human反向传播到既有 L1 tracesfollow_up同领域、上一任务已完成的新子任务同一 session、新 episodenew_task无关任务新 session、新 episodeunknown无法判定按follow_up处理安全默认其决策流为无上一上下文 →new_task0.75bootstrap时间间隔大于 2h →new_task0.9强启发式≥0.85命中即用否则走 LLMcompleteJsonLLM 给出低置信度new_task时还有一次仲裁arbitration回合最终兜底为follow_up0.45–0.5最安全的中庸选项。与意图分类器一致它从不抛错——双失败时返回unknown/follow_up由编排器继续以全量检索 / 新 episode 方式运行。分类结果通过episode.relation_classified事件广播给 viewer。结语MemOS 的core/session模块是整个记忆算法管线的入口守门人它用 session/episode/turn 三级模型把无边界的对话流切割成可奖励、可归纳的原子单元用混合意图分类器决定每次查询的检索深度用同步事件总线把生命周期信号分发给捕获、奖励、技能结晶与 viewer并以热路径零 SQLite的写入策略保障性能。理解这一层是深入 MemOS 记忆管线capture → reflection → reward → backprop → skill的第一步。赞分享人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin【免费下载链接】MemOSSelf-evolving memory OS for LLM AI Agents: ultra-persistent memory, hybrid-retrieval, and cross-task skill reuse, with 35.24% token savings and DeepSeek Harness support.项目地址https://gitcode.com/gh_mirrors/memos/MemOS点击查看免费下载相关推荐Craft Agents v0.8.6 深度解析分块会话传输Chunked Session Transfers与自定义端点图像输入Craft Agents v0.8.6 深度解析分块会话传输Chunked Session Transfers与自定义端点图像输入 本文基于 Craft人工智能大模型AI AgentMCP Clients工具调用交互助手如何高效参与Veloren开源项目5个专业级代码贡献秘诀如何高效参与Veloren开源项目5个专业级代码贡献秘诀 Veloren是一款令人兴奋的开源体素RPG游戏深受《矮人要塞》和《Cube World》的启发。游戏开发深入解析vscode-leetcode的session模块用户认证与多会话管理机制深入解析vscode leetcode的session模块用户认证与多会话管理机制 vscode leetcode插件作为VSCode上最受欢迎的刷题工具其开发工具上一篇BabelDOC PDF 翻译教程保留公式与版式生成中英对照文档下一篇告别GAN训练困境数据预处理中的归一化与标准化实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表