feat(memory-mcp): 专家记忆 MCP 增强——外部调用者追溯 + capture 自动提取流水线 - #445
Open
ArvinQi wants to merge 8 commits into
Open
Conversation
Add a memory MCP server (Streamable HTTP at /mcp/memory) so external agents (coding agents, bots) can read/write Octop expert memory directly, aligned with the in-process MemoryService capabilities. Tools (per expert, bound at connect time via X-Octop-Agent-Id header): - memory_recall(query, limit): full recall pipeline (tokenize + FTS + rerank), returns structured memories + rendered markdown - memory_save(content, source, topic?): persist a structured fact directly into the atom/tree (durable, no extraction) - memory_capture(content, source, session_id?): write an L0 raw event (extraction pipeline); visible immediately via memory_search_raw - memory_search_raw(query, limit): FTS-search L0 raw events (capture visible before extraction) - memory_update(atom_id, new_content, source): deprecate old atom + save new Auth: independent token via OCTOP_MEMORY_MCP_TOKEN (fail-closed if unset); authorization via Authorization: Bearer or X-Octop-Memory-Token. Implementation: - Lives in infra/agents/memory_mcp.py (no api-layer dependency; opens the agent Memory instance via open_memory_kwargs) - One FastMCP per agent, routed by X-Octop-Agent-Id header at /mcp/memory - DNS rebinding protection disabled (server runs behind a reverse proxy) - streamable_http task groups wired into the FastAPI lifespan Tests: tests/unit/agents/test_memory_mcp.py (tools, header routing, token middleware, unified mount).
保留两者: - main: enable_mobile 路由挂载 - PR: memory_mcp MCP server 挂载
- Add assert for server.services to satisfy mypy strict mode - Add return type annotation to _memory closure
基于社区 feat/memory-mcp-server (fea4c59) 的独立扩展分支,仅改 memory_mcp.py: - memory_capture 写 L0 后自动触发提取流水线(extract -> promote -> atom) - 缺省 session 派生 ext:{source}:{user},外部调用无需 octop 原生 session - X-Octop-User-Id header / user 参数,全部工具支持调用者追溯 - 工具描述区分日常(recall/capture)与显式(save/update) - stateless HTTP + mypy strict 修复
参考 DSH 记忆工具设计,把记忆生产流水线每环节暴露为 MCP 工具: - memory_raws: L0 原始事件结构化列表(session/host/user 过滤) - memory_candidates: L1 候选队列查询(pending/promoted/rejected) - memory_extract: 手动调度提取(L0→L1,promote 可直达 L2) - memory_promote: 审核晋升候选(L1→L2) - memory_reject: 拒绝候选(标记+审计) - memory_atoms: L2 原子记忆结构化查询 全部工具经 MemoryService / Memory 分层 API 实现,与 capture 自动流水线互补, 支持外部按需调度记忆生产。
12 tasks
- 删除 memory_atoms(memory_recall 已覆盖 L2 查询) - 删除 memory_search_raw(memory_raws 增加 query 参数走 FTS,覆盖 L0 搜索) - 保留 9 个:recall/save/capture/update(读写)+ raws/candidates/extract/promote/reject(分层查询+流水线调度)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary(摘要)
本次改动基于
feat/memory-mcp-server分支(基线fea4c59),对专家记忆 MCP 服务(/mcp/memory,Streamable HTTP)做三方面增强,全部改动集中在src/octop/infra/agents/memory_mcp.py一个文件:memory_capture写入 L0 原始事件后,自动触发专家记忆提取流水线(extract → candidate → promote → atom),外部 Agent / Bot 无需 octop 原生 session 也能通过 MCP 沉淀长期记忆。X-Octop-User-IdHTTP Header(或工具user参数),所有记忆工具(recall / search_raw / capture / save / update)都能感知"是谁在调用",每次写入落库可追溯,按人区分使用偏好。FastMCP启用 stateless HTTP,避免服务重启后Mcp-Session-Id失效导致的-32600 Session not found。Why(背景与动机)
Octop 专家的记忆(事实、对话、决策)目前只有 Octop 控制台 / 进程内 Agent 能访问。社区
feat/memory-mcp-server已把记忆通过 MCP 暴露给外部 Agent,但存在两个缺口:session_id分组,而外部接入方(coding agent、bot)没有 octop 原生 session,memory_capture记录后永远停留在 L0 原始层,无法蒸馏成可召回的 atom。本 PR 解决这两个问题,同时顺带提升 MCP 服务的稳定性(stateless HTTP)。
What(改动明细)
memory_capture写 L0 后调用_trigger_extract,复用专家进程内MemoryService异步执行extract(incremental=True, promote=True),raw 事件自动蒸馏 → 候选 → 晋升为 atom(best-effort,依赖运行时的 MemoryService,缺失时静默跳过不报错)session_id可选;缺省时派生为ext:{source}:{user}——同来源同用户的多次 capture 落入同一分组,提取器可聚合上下文蒸馏;不同用户自动隔离X-Octop-User-IdHeader 经 ASGI 中间件 → contextvar → 工具读取(或显式user参数覆盖);add_raw/store落库 user 字段;recall/search_raw返回caller字段memory_recall/memory_capture标记为日常使用(自动提取);memory_save/memory_update标记为显式记忆 / 更新(明确知道事实时才用)FastMCP(stateless_http=True):每次请求独立会话,杜绝服务重启后Mcp-Session-Id失效(代价是每次调用多一次 initialize)server.services/app_runtime非空断言、_memory() -> Any类型、ctx: Context泛型兼容(保持 mcp 工具注入可用)How it works(工作机制)
外部调用 → 原子记忆的完整链路:
调用者隔离:
Verification(验证结果)
memory_capture不带 session_id → 自动派生ext:{source}:{user},extract_scheduled: trueX-Octop-User-IdHeader 生效:L0 落库user字段(user-alice / user-bob 分属不同 session 组)memory_recall返回caller字段(识别调用者)Compatibility(兼容性说明)
stateless_http:依赖mcp>=1.28(上游 uv.lock 已锁 1.28.1)✅_trigger_extract:依赖 harness 运行时注入的agent._memory_runtime.service(octop 仓库内无此符号)。代码用getattr防御性访问——上游环境若无该属性会静默跳过提取(extract_scheduled: false),不会报错;提取触发为 best-effort_AgentRouter中间件写入 contextvar → 工具读取,不依赖 mcp SDK 内部request_context(stateless 模式下 SDK 不提供),跨 ASGI→工具稳定传递memory_mcp.py,不新增挂载、不改其他文件Target branch
develop(feature / fix — default)main(release/*orhotfix/*only)Type of change
Test plan
make allpasses locallyChecklist
CHANGELOG.md(if user-facing)