ARTICLE DETAIL

资讯详情

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

claude-mem 架构解析:Hook 生命周期、Worker 守护进程与双数据库存储设计

claude-mem 架构解析:Hook 生命周期、Worker 守护进程与双数据库存储设计 claude-mem 架构解析Hook 生命周期、Worker 守护进程与双数据库存储设计【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem本文基于 claude-mem 仓库内的 架构总览文档完整还原该系统的四层结构宿主 Hook 层、CLI 层、Worker 守护进程、存储层并逐一对照源码验证 Hook 生命周期表、数据流 API、Pending 队列、生成器重启循环、优雅降级、观察去重与双会话 ID 等关键设计。读完你可以掌握claude-mem 如何在 Claude Code 会话中无侵入地捕获工具调用、用 AI 压缩成观察记录写入 SQLite ChromaDB以及这套架构绝不打断用户会话的错误处理哲学是如何落地的。系统分层总览claude-mem 的官方架构文档将系统划分为四层。以下分层图完整继承自 docs/architecture-overview.md----------------------------------------------------------- | Claude Code (host) | | -- Hook System (Setup 5 lifecycle events) | | -- MCP Client (search tools) | ----------------------------------------------------------- | CLI Layer (Bun) | | -- bun-runner.js (Node-Bun bridge) | | -- hook-command.ts (orchestrator) | | -- handlers/ (context, session-init, observation, | | summarize, session-complete) | ----------------------------------------------------------- | Worker Daemon (Express, per-user port 37700(uid%100)) | | -- SessionManager (session lifecycle) | | -- SDKAgent (Claude Agent SDK) | | -- SearchManager (search orchestration) | | -- ProcessRegistry (subprocess management) | | -- ChromaSync (embedding synchronization) | ----------------------------------------------------------- | Storage Layer | | -- SQLite (claude-mem.db) -- structured data | | -- ChromaDB (chroma.sqlite3) -- vector embeddings | | -- MCP Server (interface for Claude Code) | -----------------------------------------------------------各层的职责边界如下Claude Code宿主层通过 Hook System 在生命周期事件点调用插件脚本并通过 MCP Client 消费 claude-mem 暴露的搜索工具。CLI 层Bun负责协议适配。入口是 plugin/scripts/bun-runner.jsNode 到 Bun 的桥接器它被 Claude Code 的 Node 环境调起后再转发给 Bun 执行真正的 TypeScript 逻辑真正的编排者是 src/cli/hook-command.ts具体事件分发到 src/cli/handlers/ 下的 context、session-init、observation、summarize、session-complete 等处理器。Worker 守护进程基于 Express 的长驻服务每个用户独占一个端口公式为37700 (uid % 100)。这一默认值可以在 src/shared/SettingsDefaultsManager.ts 中得到印证CLAUDE_MEM_WORKER_PORT: String(37700 ((process.getuid?.() ?? 77) % 100))且该端口还可经CLAUDE_MEM_WORKER_PORT环境变量覆盖。存储层SQLite 存结构化数据ChromaDB 存向量嵌入MCP Server 作为对 Claude Code 的对外接口。关于 CLI 层有一个值得注意的实现细节plugin/scripts/bun-runner.js 在启动前会读取~/.claude/settings.json若检测到enabledPlugins[claude-memthedotmack] false直接process.exit(0)静默退出——用户禁用插件时Hook 完全不产生副作用。而findBun()则按which bun/where bun、~/.bun/bin、Homebrew 路径等多级策略定位 Bun 可执行文件见 bun-runner.js。Hook 生命周期与事件注册架构文档给出的 Hook 生命周期表完整继承如下EventHandler作用超时Setupversion-check.js小于 100ms 的版本标记检查不匹配时提示运行npx claude-mem repair60sSessionStartworker start context启动 worker 服务并注入上下文60sUserPromptSubmitsession-init注册会话 启动 SDK agent 语义注入60sPostToolUseobservation捕获工具使用 - 入队到 worker120sSummarysummarize向 SDK agent 请求会话摘要120sSessionEndsession-complete结束会话 排空待处理消息30s这些声明在当前的 Hook 注册配置 plugin/hooks/hooks.json 中可以逐条对上且能看到更多落地细节SessionStartmatcher 为startup|clear|compact实际注册了两个 Hook第一个是worker-service.cjs start启动守护进程第二个是hook claude-code context注入上下文timeout 均为 60 秒UserPromptSubmit注册hook claude-code session-inittimeout 60 秒PostToolUsematcher 为*注册hook claude-code observationtimeout 120 秒且标记async: true——观察捕获异步执行不阻塞工具调用返回Stop注册hook claude-code summarizetimeout 120 秒、async: true。从源码结构看文档表中的 Summary / SessionEnd 在当前注册配置中分别对应 Stop 事件驱动的 summarize 与内部 session-complete 处理器当前配置中还额外注册了一个文档表格未列出的PreToolUsematcher 为Read事件调用file-context处理器timeout 60 秒、async: true文档表中 Setup 标注超时 60s而当前 hooks.json 中 Setup 条目实际配置为timeout: 300。每条 Hook 命令都遵循同一个模式先用一段 bash 在~/.claude/plugins/cache/thedotmack/claude-mem/下按语义化版本排序选出最新插件缓存再用node bun-runner.js script启动。这种版本号排序选择器保证了多版本缓存共存时始终运行最新版并显式跳过带.orphaned_at标记的孤儿缓存目录。文档还说明了首次安装的行为npx claude-mem install会全局安装 Bun 和 uv、在插件缓存目录执行bun install、并写入.install-version版本标记文件全程在可见的 clack spinner 下完成。此后每次启动 Claude CodeSetup Hook 都会运行version-check.js做版本标记比对如果插件被外部升级例如claude plugin update它向 stderr 写入提示要求用户运行npx claude-mem repair。该 Hook 始终 exit 0绝不阻塞宿主。数据流从用户提示到向量库文档描述的核心数据流完整继承如下User prompt - session-init - /api/sessions/init /api/context/semantic | Tool use - observation - /api/sessions/observations | | | PendingMessageStore.enqueue() | | | SDKAgent.startSession() | | | Claude Agent SDK - ResponseProcessor | | | -- storeObservations() - SQLite | -- chromaSync.sync() - ChromaDB | -- broadcastObservation() - SSE/UI | Stop - summarize - /api/sessions/summarize - session-complete - /api/sessions/complete drain对照 Worker 的路由实现 src/services/worker/http/routes/SessionRoutes.tssetupRoutes恰好注册了文档所述的三个端点且每个端点都套了 zod 请求体校验POST /api/sessions/initschema 定义字段为contentSessionId必填、project、prompt、platformSource、customTitle。处理逻辑包括超过 256KB 的 prompt 会在边界处按 UTF-8 字节安全截断stripMemoryTags会剥离隐私标记若 prompt 剥离后为空则直接返回skipped: private重复 prompt 会经findRecentDuplicateUserPrompt在去重窗口内命中并返回skipped: duplicate最后store.saveUserPrompt落库调用sessionManager.initializeSession初始化会话并触发ensureGeneratorRunning(sessionDbId, init)启动 SDK agent。POST /api/sessions/observationsschema 定义字段为contentSessionId、tool_name、tool_input、tool_response、cwd、agentId、agentType等委托给ingestObservation完成入队成功时返回{ status: queued }。POST /api/sessions/summarizeschema 定义接收last_assistant_message、observedModel、observedBilling等字段若带agentId子代理上下文则跳过先经PrivacyCheckValidator隐私检查再通过sessionManager.queueSummarize入队并broadcastSummarizeQueued。数据流中入队 - SDK 生成 - 三路分发SQLite / ChromaDB / SSE的环节分别对应PendingMessageStore队列、ClaudeProvider等生成器实现以及ChromaSync与SSEBroadcaster均位于 src/services/worker/ 目录。优雅降级Worker 不可用绝不阻塞宿主数据流的第一原则是Worker 挂了也不影响用户会话。文档的表述是Transport errors (ECONNREFUSED, timeout, 5xx) - exit 0 (never block Claude Code) Client bugs (4xx, TypeError, ReferenceError) - exit 2 (blocking, needs fix)这一策略在 src/cli/hook-command.ts 的isWorkerUnavailableError中有完整实现它匹配econnrefused、econnreset、epipe、etimedout、fetch failed、socket hang up等传输层错误模式、超时关键字、5xx/429 状态码一律判定为Worker 不可用而 4xx、TypeError、ReferenceError、SyntaxError则被明确排除——这些是代码自身 bug需要暴露出来。对应的处理路径在 hookCommand 的 catch 块Worker 不可用记一条 warn 日志recordWorkerUnreachable累加失败计数连续失败达到阈值后触发 fail-loud 提示与阈值门控的hook_failed遥测然后exit 0静默放行客户端 bug上报hook_failed遥测后调用emitBlockingError把错误冲刷到 stderr 并exit 2让 Claude Code 将错误呈现给模型和用户适配器拒绝输入 / transcript 缺失返回 no-op 结果并 exit 0。退出码常量定义在 src/shared/hook-constants.tsSUCCESS: 0、BLOCKING_ERROR: 2。同一文件还给出了 Hook 内部的超时预算如API_REQUEST: 30000、READINESS_WAIT: 30000并提供getTimeout函数在 Windows 上统一乘以 1.5 的系数——Windows 冷启动更慢超时放宽是这套跨平台系统的常规手段。另外值得一提的细节hook 执行期间 stderr 是被缓冲的installHookStderrBuffer见 hook-command.ts只有决定呈现的路径阻塞错误、fail-loud 计数才冲刷缓冲区成功退出则丢弃缓冲——保证第三方库的意外 stderr 写入不会污染注入到模型上下文的输出。关键模式Pending 队列PendingMessageStore文档描述的队列语义enqueue() - INSERT row with pending status clearPendingForSession() - DELETE all pending rows for session (called whenever the parser returns a parseable response, regardless of whether observations were extracted)解析器是二值的{ valid: true, observations, summary }或{ valid: false }。不可解析的响应不触碰队列会话迭代器继续运行。这个设计的精妙之处在于以解析成功而非是否抽取出观察作为清队条件即使某次响应里没有可提取的观察也说明 Agent 输出通道是健康的积压消息可以安全丢弃避免旧消息污染后续上下文。clearPendingForSession的实现可在 src/services/worker/SessionManager.ts 中找到。生成器重启循环SessionRoutesGenerator crash - retry 1 (1s) - retry 2 (2s) - retry 3 (4s) - consecutiveRestarts 3 - stop and let the iterator end指数退避1s - 2s - 4s连续重启超过 3 次即放弃让迭代器自然结束生成器自然完成工作后计数器归零。待处理消息跨重启保留在队列中等下一次解析器产出合法响应时再被清除。consecutiveRestarts字段在 src/services/worker-types.ts 的类型定义中可以确认初始化为 0 的位置在 SessionManager.ts。值得补充的是当前的生成器启动路径ensureGeneratorRunningSessionRoutes.ts在重启循环之外还叠加了两个熔断器溢出熔断overflow breakersession.overflowPausedUntilMs冷却期内直接跳过启动防止两次回收recycle仍装不下上下文时陷入每次工具调用都 spawn 一次、随即因预算检查中止的循环配额熔断quota breaker通过tryAdmitQuotaProbe声明式抢占探针资格配额耗尽期间扣留请求、冷却结束后只放一个重探针通过避免每个观察都买回同一个配额拒绝。这两层保护与文档描述的重试循环共同构成生成器的完整自愈体系。观察去重DeduplicationSHA256(memory_session_id title narrative)[:16] - content_hash (16 hex chars) If hash exists within 30s window - return existing ID (no insert)16 位十六进制内容哈希的截断在 src/services/sqlite/observations/store.ts 的.slice(0, 16)中得到印证。同一观察在 30 秒窗口内重复到达时例如 Hook 重试、同一工具调用被多次捕获直接返回已有 ID不产生重复行。两种会话 IDcontentSessionId—— 来自 Claude Code会话期间保持不变memorySessionId—— 来自 SDK Agent每次 Worker 重启都会变化。两者的映射关系由 SessionStore 维护是外键约束正确性的关键。SessionRoutes.ts 中的日志可以直观看到这个映射过程store.createSDKSession(contentSessionId, ...)创建或复用数据库会话行新会话尚无memory_session_id日志标注will be captured on first SDK response等 SDK 首次响应后才回填。所有对外 APIinit / observations / summarize都以contentSessionId为入口参数内部再换算为数据库行 ID——这正是两种 ID设计对 API 表面透明化的体现。存储层SQLiteclaude-mem.db文档给出的表结构一览表关键字段用途sdk_sessionscontent_session_id, memory_session_id, status会话生命周期observationsmemory_session_id, type, title, narrative, content_hash工具使用观察session_summariesmemory_session_id, request, learned, completed会话摘要user_promptscontent_session_id, prompt_text用户提示历史pending_messagessession_db_id, message_type每会话的待处理队列observation_feedbackobservation_id, signal_type使用信号追踪这几张表的写入路径与上文数据流一一对应user_prompts由 session-init 写入saveUserPromptpending_messages由 observation 入队写入observations与session_summaries由生成器响应解析后写入。测试侧可以在 tests/sqlite/session-store-sessions.test.ts、tests/sqlite/session-store-observations.test.ts 等用例中查看各表的读写契约。ChromaDBchroma.sqlite3ChromaDB 承载语义搜索所需的向量嵌入。每条观察会生成多个文档obs_{id}_narrative - 主文本 obs_{id}_fact_0 - 第一条事实 obs_{id}_fact_1 - 第二条事实 ...将一条观察拆成主叙事 逐条事实多个嵌入文档可以让语义检索在细粒度事实上命中而不是只在整段叙事上模糊匹配。访问链路是经 chroma-mcp独立 MCP 进程stdio 通信完成对应实现位于 src/services/sync/ChromaMcpManager.ts用户 prompt 的向量化同步则由 SessionRoutes.ts 中的dbManager.getChromaSync()?.syncUserPrompt(...)在 session-init 阶段 fire-and-forget 触发同步失败只记错误日志、continuing without vector search不阻塞主流程。搜索侧的编排由 src/services/worker/search/SearchManager.ts 与搜索策略ChromaSearchStrategy、SQLiteSearchStrategy、HybridSearchStrategy完成最终通过 src/servers/mcp-server.ts 暴露给 Claude Code 的 MCP Client。进程管理文档对 Worker 进程管理给出三条要点ProcessRegistry追踪所有 Claude SDK 子进程管理其 PID 生命周期。对应 src/supervisor/process-registry.tsOrphan Reaper5 分钟周期性清理没有活跃会话的孤儿进程GracefulShutdown7 步关停序列——PID 文件、子进程、HTTP 服务器、会话、MCP、数据库、最终强制 kill。关停逻辑分布在 src/services/worker-shutdown.ts 与 src/supervisor/shutdown.ts行为契约由 tests/services/worker-shutdown-sequence.test.ts 等测试验证。这套进程管理与 Hook 层的永远 exit 0策略形成互补Hook 侧保证单次调用故障不外溢Worker 侧的注册表 孤儿收割 分步关停保证长驻进程自身不泄漏、可干净重启。小结回到 docs/architecture-overview.md 的核心命题claude-mem 的架构可以用三个词概括分层解耦——宿主 Hook、CLI 编排、Worker 守护、双库存储各管一段Hook 通过 HTTP API 与 Worker 通信任何一层失效都不会跨层传导无阻塞优先——从 Setup Hook 的永远 exit 0到传输错误 exit 0 / 客户端 bug exit 2 的分类退出码再到 Chroma 同步失败仅降级为日志整个系统把绝不打断用户会话做成了贯穿各层的硬约束自愈与幂等——Pending 队列 解析器清队、生成器指数退避重启 溢出/配额双熔断、30 秒窗口内的观察内容去重、跨重启保留的 pending 消息共同让异步的捕获 - 压缩 - 存储流水线在崩溃、重试、重启场景下保持不丢不重。如果你想进一步深入建议从 src/cli/handlers/ 的事件处理器、src/services/worker/http/routes/ 的完整路由集以及 tests/ 下按模块组织的测试用例入手它们与本文各章节一一对应。【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表