跳到主要内容

Omni 多模态与 Cross 跨会话记忆

30 秒导读: 前三章讲的是 SimpleMem 的文本核心(压缩 → 检索 → 自进化)。本章讲两支外延: Omni-SimpleMem 把同一套"压缩优先"哲学从纯文本扩到文本 / 图像 / 音频 / 视频四模态; Cross 则换一个维度——把"一次对话记得住"升级成"跨会话记得住",从 Claude 这类客户端 收集会话、双存储落库、合并去噪、下次开场自动注入。两支各占本章一半,偏结构导览,细节留给 你按章末代码地图下钻。

本章不重复其它章:文本三阶段压缩见 01-text-pipeline,意图规划 + 三视图召回见 02-hybrid-retrieval,AutoResearch 自进化见 03-evolvemem, 总览见 index


第一部分 · Omni-SimpleMem:多模态记忆

1. 这是什么(零基础也能懂)

  • 一句话定义: Omni-SimpleMem 是 SimpleMem 的多模态版——你可以往它里塞文字、图片、音频、视频, 它统一压缩成"记忆原子",查询时按 token 预算把最相关的取出来。
  • 解决什么问题: 纯文本记忆只能记"说过的话"。但 agent 越来越要处理截图、录音、录像。 直接把每一帧图、每一秒音频都存进去,既贵又全是冗余(连续 100 帧监控画面里 99 帧一模一样)。 Omni 要解决的是:在多模态洪流里,只留下真正有信息量的那部分,并且查得快、查得省。
  • 一句话直觉: 把它当一个会自动跳过重复画面的智能录像机——场景没变就不记,变了才记一笔。

统一抽象:MAU。 四种模态进来后不各存各的,而是都被包装成同一种单元 MAU (MultimodalAtomicUnit,多模态记忆原子)。文本、图、音、视在系统里"平权",只是 modality_type 字段不同(simplemem/multimodal/core/mau.py:17 class ModalityType:119 class MultimodalAtomicUnit)。

最小使用示例(来自 simplemem/router.py:20-21 的文档示例):

# 示意,非源码
from simplemem import create

mem = create(mode="omni", data_dir="./my_memory")
mem.add_text("User loves hiking.", tags=["session_id:D1"])
mem.add_image("photo.jpg") # 图像:熵触发决定要不要存
result = mem.query("What did I see?") # 按 token 预算取回摘要,必要时展开

create(mode="omni") 背后是 simplemem/router.py:233 _init_omni——它建一个默认 OmniMemoryConfig,再实例化 OmniMemoryOrchestrator(:240 register(mode="omni", ...))。

2. 顶层全景(它大概怎么转)

一句话: 一个中央编排器 OmniMemoryOrchestrator 把三件事串起来——摄入(各模态处理器 + 熵触发)、 存储(MAU 库 + 混合向量库 + 事件 + 知识图谱)、检索(金字塔预览 + 混合召回 + 图谱增强)。

OmniMemoryOrchestrator (orchestrator.py:74)
├── 编排 processors + routing + parametric

add_text/image/ │
audio/video ─────┤

┌──────── 摄入 ────────┐ ┌──────── 存储 ────────┐ ┌────── 检索 ──────┐
│ text/image/audio/ │ │ MAUStore │ │ PyramidRetriever │
│ video Processor │──▶│ HybridVectorStore │◀──│ (预览→展开) │
│ + 熵触发(triggers) │ │ (FAISS text+visual) │ │ + BM25Store │
│ 过滤冗余 │ │ BM25Store(关键词) │ │ + GraphRetriever │
└──────────────────────┘ │ KnowledgeGraph(图谱) │ │ + ParametricStore│
│ ParametricStore(蒸馏)│ └────────┬─────────┘
└──────────────────────┘ │

query() 返回摘要
(+ 展开 / 图谱 / 参数化答案)

部件一句话职责:

部件干什么在哪
OmniMemoryOrchestrator中央编排:摄入/存储/检索的总调度simplemem/multimodal/orchestrator.py:74
各模态 Processor把原始 text/image/audio/video 变成 MAUsimplemem/multimodal/processors/*
熵触发 triggers摄入前的轻量过滤,判"值不值得存"simplemem/multimodal/triggers/*
PyramidRetriever金字塔检索:先摘要预览,再按需展开simplemem/multimodal/retrieval/pyramid_retriever.py:73
BM25Store关键词召回,补 FAISS 的语义盲区simplemem/multimodal/retrieval/bm25_store.py
KnowledgeGraph + GraphRetriever跨模态实体图谱 + 多跳推理simplemem/multimodal/knowledge/*
MemoryRouter在 episodic / semantic / hybrid 路径间做策略路由simplemem/multimodal/routing/router.py:30
ParametricMemoryStore + MemoryDistiller把重要记忆蒸馏/固化进参数simplemem/multimodal/parametric/*

主线走一遍(高层):

  1. add_image(...)ImageProcessor.process → 内部熵触发判要不要存 → 命中则产出 MAU (orchestrator.py:284 add_image)。
  2. _store_mau 把这枚 MAU 写进 MAU 库、向量库、事件、并抽实体入图谱 (orchestrator.py:525 _store_mau)。
  3. query(...) → 金字塔预览拿摘要 → BM25 补关键词 → 图谱补实体 → 参数化补高置信答案 (orchestrator.py:700 query)。

重要:这套架构不是手工设计的。 README 明说:Omni 的架构是被一个自主研究流水线 (AutoResearch)"发现"出来的——它跑了约 50 组实验、自己诊断失败模式、提出架构改动、 甚至修数据管线 bug,全程内环无人(README.md:309)。所以下面这三条原则更像是"被搜出来的 结论",而非拍脑袋的设计。AutoResearch 机制本身见 03-evolvemem

3. 三条核心原则

Omni 把"压缩优先"落成三条原则(README.md:309)。下面逐条讲。

3.1 选择性摄入(按模态熵驱动过滤)

要解决的小问题: 多模态数据里冗余极高——静止画面、静音音频、连续相似帧。全存=浪费。

思路:昂贵处理之前放一个轻量"闸门",先算这份数据的信息熵/变化量,不够就直接扔。 每种模态用自己的闸门:

  • 视觉:用 CLIP/SigLIP 之类轻量编码器算与上一帧的余弦相似度,太像就丢 (triggers/visual_trigger.py:25 VisualEntropyTrigger,高阈值 0.9 判静止、低阈值 0.7 判显著变化)。
  • 音频:用 VAD(Voice Activity Detection,语音活动检测) + 能量阈值,判有没有说话/有没有显著声音 (triggers/audio_trigger.py:19 AudioEntropyTrigger)。

统一契约: 所有触发器都继承 BaseTrigger,吐一个三态决策 ACCEPT / REJECT / UNCERTAIN (triggers/base.py:11 TriggerDecision:19 TriggerResult)。should_process() 只在 ACCEPT 时为真:

# 真实源码 triggers/base.py:34-36
def should_process(self) -> bool:
"""Check if this data should be processed."""
return self.decision == TriggerDecision.ACCEPT

这行是整条"选择性摄入"的开关:闸门说 REJECT,后面的 VLM/ASR 就一步都不跑。

一句话直觉: 把连续的视觉/音频,压成稀疏的事件流—— "This converts continuous visual streams into sparse event streams"(triggers/visual_trigger.py:36)。

3.2 渐进式检索(金字塔 + 混合召回)

要解决的小问题: 多模态内容(尤其原始图/视频)展开起来极耗 token。查询时不该一上来就把原图塞进上下文。

思路——像文件管理器: 先给缩略图(摘要),你点了才加载原图(raw)。PyramidRetriever 把检索分成层级,由便宜到贵(retrieval/pyramid_retriever.py:30 RetrievalLevel):

层级内容成本
SUMMARY只文本摘要最便宜(默认预览)
METADATA摘要 + 元数据
DETAILS完整 MAU 明细(不含原始内容)
EVIDENCE含 raw 原始数据(原图/音频)最贵,仅按需

两阶段: retrieve_preview 先向量搜出一批 SUMMARY;真要细节才 expand 到更高层 (pyramid_retriever.py:114 retrieve_preview:223 expand)。当调用方给了 token 预算, retrieve_with_budget在预算内自动决定展开几条、展开到哪层——文本估 100 token/条、 非文本估 500 token/条,能塞下才展(pyramid_retriever.py:338 retrieve_with_budget、 逐条估价见 :366)。这就是任务里说的"金字塔 token 预算扩展"。

混合召回:FAISS + BM25。 语义向量(FAISS)擅长"意思相近",但会漏具体实体(书名、人名)。 所以 query() 在 FAISS 结果后追加 BM25 关键词命中,而且刻意不重排——保留 FAISS 的语义顺序, BM25 只作补充上下文(orchestrator.py:761-791,注释 Do NOT re-sort:764)。

# 真实源码 orchestrator.py:774-777(节选)
# Set-union: add BM25-only results after FAISS results (preserve FAISS order)
existing_ids = {item["id"] for item in result.items}
for mau_id, bm25_score in bm25_results:
if mau_id not in existing_ids:
...

这段是"混合"的落点:并集去重,FAISS 在前、BM25 补尾。

3.3 知识图谱增强(跨模态多跳)

要解决的小问题: 有些问题靠"相似度"答不了,得靠关系——"在咖啡店附近发生了什么?"要先找到 "咖啡店"这个实体,再顺着关系图往外跳。

思路: 每存一枚 MAU,就抽出其中的实体和关系,喂进一张知识图谱。这张图跨模态—— 文本里的"Alice"和图片里认出的"Alice"能链到同一个实体节点,provenance 记回源 MAU (knowledge/knowledge_graph.py:145 KnowledgeGraph,类注释明说"Supports multimodal entity linking (text + visual)""Enables cross-modal knowledge reasoning",:156-159)。

多跳遍历: 图谱支持"从某实体出发,N 跳内的邻居"(knowledge_graph.py:377 get_neighbors, max_hops 参数,BFS 逐跳扩展见 :392)。检索侧 GraphRetriever.retrieve 默认 max_hops=2, reason_multi_hop 可到 3 跳(knowledge/graph_retriever.py:78 retrieve:263 reason_multi_hop)。

接进主流程: 摄入时 _store_mauentity_extractor.extract(mau) 抽实体/关系再入图 (orchestrator.py:559-565);查询时 query()graph_retriever.retrieve(...) 把图谱实体挂到结果上 (orchestrator.py:797-802)。

4. 编排器怎么把它们串起来

一句话: OmniMemoryOrchestrator.__init__ 是一张"装配清单"——它一次性 new 出所有子系统, add_* 走摄入路径、query 走检索路径,中间共享同一套存储。

摄入端(add_*_store_mau): 四个 add_text/image/audio/video 长得几乎一样—— 都调对应 processor,result.success and result.mau 为真才 _store_mau (orchestrator.py:252/284/316/348)。_store_mau扇出写入的单点:MAU 库、向量库 (按模态分 text/visual 分支,orchestrator.py:544-552)、事件管理器、知识图谱、consolidator 一次全更新(orchestrator.py:525)。

检索端(query): 顺序叠加四类证据(orchestrator.py:700):

  1. 金字塔预览 / 预算检索(:744-759)
  2. BM25 关键词并集(:766-791)
  3. 参数化高置信答案:parametric_store.recall,置信度 > 0.8 才采纳(:793-795)
  4. 图谱实体:graph_retriever.retrieve(..., max_entities=5)(:797-802)

4.1 策略路由(routing)

要解决的小问题: 同一个查询,有时该走 episodic(逐条事件回忆)、有时该走 semantic (蒸馏后的语义记忆)、有时两者都要。谁来判?

思路: MemoryRouter 用一组分布特征做启发式判断——基尼系数 (gini_coefficient,衡量得分分布"尖不尖")、top1 分、top1 与 top2 的 gap (routing/features.py:8 gini_coefficient:51 compute_route_features)。规则大意 (routing/router.py:76 _heuristic_route):

  • episodic 的 top1 明显更高(超 episodic_margin)→ 走 BASELINE_EPISODIC(:83)。
  • semantic 分布"又尖又高又拉得开"(gini/top1/gap 三阈值全过)才认为"语义好答";否则退回 episodic(:92-104)。
  • 两者接近 → HYBRID 两路都用(:106-113);semantic 明显更好 → SEMANTIC_ONLY(:115)。

安全阀: BenchmarkSafeGuard 能强制回退到基线 episodic 路径,保证评测可复现 (routing/policy.py:15 BenchmarkSafeGuard)。当前 query() 里 router/guard 已初始化但尚未接管选路—— 代码注释明说完整路由集成"deferred to when SemanticStore has content",此刻查询仍走既有 episodic 路径以保证零行为变化 (orchestrator.py:730-735)。这是个诚实的"半成品接线"信号,读代码时别被 _memory_router 的存在误导。

4.2 记忆蒸馏 / 固化(parametric)

要解决的小问题: 反复被问到的高价值记忆,每次都去检索太浪费;能不能"背下来"?

思路——三件套:

  • MemoryConsolidator:给每枚记忆算重要性(含访问频率),追踪哪些值得固化 (parametric/consolidator.py:141 MemoryConsolidator:62 compute_importance)。
  • MemoryDistiller:把重要的 episodic 记忆蒸成 QA 对,攒够样本后用 LoRA 微调把知识写进模型参数 ——"Compresses episodic memories into parametric form",基座 Qwen2.5-7B、增量 SFT (parametric/memory_distiller.py:91 MemoryDistiller:213 should_distill:221 distill)。
  • ParametricMemoryStore:推理侧免检索地直接召回蒸馏后的答案,并估置信度 (parametric/parametric_store.py:43 ParametricMemoryStore:80 recall:148 _estimate_confidence)。

接线: 编排器构造时就 new 好 parametric_storeconsolidator(orchestrator.py:157-158); query()recall 只在置信度 > 0.8 时才把答案并入结果(orchestrator.py:793-795)—— 宁可不用,也不给低置信的"背诵答案"。


第二部分 · Cross:跨会话记忆

5. 这是什么(换个维度)

  • 一句话定义: Cross 是一个跨会话记忆扩展——让 agent 记住上一次对话里发生了什么, 下次开场自动带上相关上下文。
  • 和 Omni 的区别: Omni 解决"多模态",Cross 解决"跨时间"。Omni 是"记得住图片声音", Cross 是"记得住昨天那次对话的决定和踩过的坑"。
  • 给谁用: 接 Claude Desktop / Cursor 这类 MCP 客户端,或任何 agent 框架——它在每次会话的 start/message/tool/stop/end 生命周期上挂钩,悄悄采集、落库,下次开场注入 (cross/__init__.py:2-17 模块 docstring 与 quick start)。

最小使用示例(来自 cross/__init__.py:10-17):

# 示意,非源码
from cross.orchestrator import create_orchestrator

orchestrator = create_orchestrator(project="my-project")
result = await orchestrator.start_session(
content_session_id="session-1",
user_prompt="Continue working on the API",
)
print(result["context"]) # 上一次会话里注入进来的跨会话上下文

6. 顶层全景(数据怎么流)

一句话: 采集事件 → 双存储落库 → 会话结束时提炼 → 定期合并去噪 → 下次开场按预算注入。

Claude/Cursor 等客户端
│ 生命周期钩子 (hooks.py: SessionHooks)

start ─▶ message/tool ─▶ stop ─▶ end
│ │ │
│ │ │ finalize:提炼观察+摘要
▼ ▼ ▼
┌─────────────────────────────────────┐
│ CrossMemOrchestrator (orchestrator) │ ← 主入口,装配所有部件
│ ├── SessionManager (生命周期/提炼) │
│ ├── EventCollector (采集+脱敏) │
│ └── ContextInjector (开场注入) │
└───────────────┬─────────────────────┘

┌────────── 双存储 ──────────┐
│ SQLiteStorage (结构化: │
│ 会话/事件/观察/摘要/链接) │
│ CrossSessionVectorStore │
│ (LanceDB:语义向量) │
└─────────────┬───────────────┘

ConsolidationWorker (衰减/合并/剪枝)

对外:api_http (REST) / api_mcp (MCP tools)

部件一句话职责:

部件干什么在哪
CrossMemOrchestrator主入口:装配存储/会话/注入/钩子cross/orchestrator.py:51
SessionManager会话生命周期 + 结束时提炼(可跑 SimpleMem 3 阶段管线)cross/session_manager.py:166
EventCollector / ObservationExtractor采集消息/工具事件,提炼成观察cross/collectors.py:180 / :503
RedactionFilter脱敏(密钥、敏感文件等)cross/collectors.py:111
SessionHooks / DefaultHooks生命周期钩子接口 + 默认实现cross/hooks.py:41 / :148
ContextInjector开场按预算装配上下文 bundlecross/context_injector.py:118
ConsolidationWorker定期衰减/合并/剪枝cross/consolidation.py:110
SQLiteStorage结构化存储(6 张表)cross/storage_sqlite.py:29
CrossSessionVectorStoreLanceDB 语义向量存储cross/storage_lancedb.py:79
MCPToolRegistry / REST app对外暴露 MCP 工具 / HTTP APIcross/api_mcp.py:31 / cross/api_http.py

7. 主线走一遍:一次跨会话记忆的完整生命周期

生命周期是固定顺序(hooks.py:47):SessionStart → (UserMessage | ToolUse)* → SessionStop → SessionEnd

7.1 采集(collectors / hooks)

客户端在每个生命周期点回调钩子。SessionHooks 是抽象接口 (hooks.py:41,定义 on_session_start / on_user_message / on_tool_use / on_session_stop), DefaultHooks 是默认实现,把调用委托给 SessionManager + ContextInjector(hooks.py:148)。

EventCollector 负责把消息/工具调用/文件变更记成 SessionEvent (collectors.py:180,record_message :201record_tool_use :227record_file_change :279)。 入库前先过 RedactionFilter 脱敏——检测并抹掉密钥等敏感内容、判定敏感文件 (collectors.py:111 RedactionFilter:152 redact:172 should_redact_file)。 _is_valuable 会滤掉没价值的事件,不是什么都记(collectors.py:476)。

7.2 双存储(SQLite + LanceDB)

为什么两套? 结构化查询(按 session/tenant/时间捞事件)用 SQL 最顺;语义相似查询用向量最顺。 Cross 两者都要,所以并列两套后端:

  • SQLiteStorage:6 张表——sessions / session_events / observations / session_summaries / memory_links / consolidation_runs(storage_sqlite.py:72-138)。存"发生过什么、提炼出什么、 记忆之间怎么链、合并跑过几次"。
  • CrossSessionVectorStore:基于 LanceDB,把带 provenance 的记忆条目做成向量表,支持语义检索 (storage_lancedb.py:79,类注释"Uses a separate LanceDB table ... with provenance",:83)。 embedding 用 simplemem.core.utils.embedding.EmbeddingModel(storage_lancedb.py:21)—— 和文本核心复用同一套嵌入。

7.3 提炼(finalize:会话结束时)

会话 stop 时,SessionManager.finalize_session 是关键一步(session_manager.py:364,步骤见 docstring :376-380):

  1. 把采集的事件转成 Dialogue
  2. 若挂了 SimpleMem 实例,就跑文本核心的3 阶段管线(add_dialogues() + finalize(), 即 01-text-pipeline 那套语义无损压缩),产出富 MemoryEntry,带 provenance 存进向量库(session_manager.py:602 _run_simplemem_pipeline,调用点见 :496)。
  3. 生成模板化摘要,sqlite_storage.store_summary() 落库(session_manager.py:717 _generate_and_store_summary,调用点见 :510)。

这一步是 Omni/文本与 Cross 的接缝: Cross 不自己发明压缩,它复用 SimpleMem 的三阶段管线把 一次会话压成结构化记忆。

7.4 合并(consolidation:定期维护)

时间久了记忆会膨胀、重复。ConsolidationWorker.run 做一遍维护(consolidation.py:131,步骤见 :135-141):

  1. 取该 tenant 所有活跃条目。
  2. 衰减超龄条目的重要性(_decay_old_entries :203)。
  3. 合并余弦相似度超阈值的条目(_merge_similar_entries :248,余弦见 :75 _cosine_similarity)。
  4. 剪枝重要性掉到阈值以下的条目(_prune_low_importance :321)。
  5. 把这次 run 记进 SQLite(consolidation_runs 表)。

7.5 注入(context_injector:下次开场)

下次 start_session 时,ContextInjector.build_context优先级贪心装包,直到 token 预算耗尽 (context_injector.py:118,三档见类注释 :120-131):

  1. 会话摘要(最高优先):最近几次会话的紧凑概览。
  2. 观察:决策、bugfix 等细粒度事实。
  3. 语义搜索结果:拿本次 user_prompt 去向量库找相似的历史记忆(仅当传了 prompt)。

每档"贪心塞到预算用完为止",高优先级先占额度(context_injector.py:77 _budget_items)。 渲染成字符串塞进系统提示,就是示例里那个 result["context"] (orchestrator.py:174 _render_context_safeContextBundle.rendertypes.py:154)。

8. 对外接口:MCP 与 HTTP

Cross 通过两条通道暴露(cross/__init__.py:52-54):

  • MCP(api_mcp.py:31 MCPToolRegistry,create_mcp_tools:525):给 Claude Desktop / Cursor 这类客户端 8 个工具(api_mcp.py:75-288):
工具名生命周期点
cross_session_start开场:建会话 + 返回注入上下文
cross_session_message记一条消息
cross_session_tool_use记一次工具调用
cross_session_stop停止:触发提炼/摘要
cross_session_end结束会话
cross_session_search语义检索历史记忆
cross_session_context取某 prompt 的上下文
cross_session_stats统计信息
  • HTTP:api_http.pycreate_app / create_cross_router 提供 REST 版同等能力 (cross/__init__.py:53)。

9. 边界与局限(诚实)

  • Omni 路由未真正接管。 MemoryRouter/BenchmarkSafeGuard 已初始化,但 query() 目前恒走 既有 episodic 路径,完整选路"deferred"(orchestrator.py:730-735)。别以为查询已经在按 gini 分流。
  • 多模态尚未上 MCP。 README 路线图明说 memory_add_image/audio/video 还是"planned", 当前要用多模态得走 Python API(README.md:518-522)。Cross 的 8 个 MCP 工具都是文本会话语义。
  • 蒸馏依赖重型依赖。 MemoryDistiller 要 LoRA 微调 + Qwen2.5-7B 级基座 (parametric/memory_distiller.py:100-104),不是零成本"背下来"。
  • 架构是搜出来的,不是证明出来的。 Omni 架构由 AutoResearch 在 LoCoMo/Mem-Gallery 上"发现" (README.md:309);其泛化性以基准分为准,换域未必照搬。

10. 代码地图(导航索引)

用符号名 grep 比行号抗漂移。

Omni-SimpleMem

主题文件符号
中央编排 / add_* / querysimplemem/multimodal/orchestrator.pyOmniMemoryOrchestratoradd_textadd_imageadd_audioadd_videoquery_store_mau
omni 后端注册simplemem/router.py_init_omniregister(mode="omni")
记忆原子 / 模态simplemem/multimodal/core/mau.pyMultimodalAtomicUnitModalityType
熵触发基类simplemem/multimodal/triggers/base.pyBaseTriggerTriggerDecisionTriggerResultshould_process
视觉 / 音频触发simplemem/multimodal/triggers/{visual,audio}_trigger.pyVisualEntropyTriggerAudioEntropyTrigger
金字塔检索simplemem/multimodal/retrieval/pyramid_retriever.pyPyramidRetrieverRetrievalLevelretrieve_previewexpandretrieve_with_budget
BM25 关键词simplemem/multimodal/retrieval/bm25_store.pyBM25Store
知识图谱simplemem/multimodal/knowledge/knowledge_graph.pyKnowledgeGraphget_neighborsadd_extracted_entity
图谱检索 / 多跳simplemem/multimodal/knowledge/graph_retriever.pyGraphRetrieverretrievereason_multi_hop
策略路由simplemem/multimodal/routing/router.py.../policy.py.../features.pyMemoryRouter_heuristic_routeBenchmarkSafeGuardcompute_route_featuresgini_coefficient
记忆蒸馏 / 固化simplemem/multimodal/parametric/*MemoryConsolidatorMemoryDistillerdistillParametricMemoryStorerecall

Cross 跨会话

主题文件符号
主入口 / 生命周期 APIcross/orchestrator.pyCrossMemOrchestratorstart_sessionrecord_messagerecord_tool_usestop_sessionend_sessioncreate_orchestrator
会话管理 / 提炼cross/session_manager.pySessionManagerfinalize_session_run_simplemem_pipeline_generate_and_store_summary
采集 / 观察 / 脱敏cross/collectors.pyEventCollectorObservationExtractorRedactionFilterredact_is_valuable
生命周期钩子cross/hooks.pySessionHooksDefaultHooksNoOpHooksHookResult
合并去噪cross/consolidation.pyConsolidationWorkerrun_merge_similar_entries_prune_low_importancerun_consolidation
上下文注入cross/context_injector.pyContextInjectorbuild_context_budget_itemsContextRenderer
SQLite 存储cross/storage_sqlite.pySQLiteStoragestore_summarystore_observation
LanceDB 向量存储cross/storage_lancedb.pyCrossSessionVectorStore
MCP / HTTP 接口cross/api_mcp.pycross/api_http.pyMCPToolRegistrycreate_mcp_toolscreate_appcreate_cross_router
数据类型cross/types.pySessionRecordSessionEventCrossObservationSessionSummaryContextBundleMemoryLink