W
AI-Wiki
CONCEPT

Worker Service Architecture

定义

Worker Service Architectureclaude-mem 中用于承接 Hook 后台处理的一种解耦架构。它把“捕获事件”与“执行 AI 压缩和存储”拆成两段:前者由 Hook 快速完成,后者由独立 worker 异步完成。

核心目标:Hook 必须快速返回;慢操作不能阻塞 Hook。

在本文档中的语境

本文档讨论的是 Claude Code Hook 体系中的后台处理问题:Hook 触发频繁,而且对响应时间敏感,但 observation 的 AI compression 每条可能耗时 5–30 秒。如果在 Hook 内直接同步调用 AI 处理,会导致 Hook 阻塞,破坏整体交互体验。

因此该架构的基本策略是:Hook 只负责读取 stdin、写入队列、立即返回成功;worker 再轮询队列并调用 Claude SDK 做真正的慢处理。

为什么需要后台 Worker

性能矛盾

这个架构要解决的是一个明确的时间尺度冲突:

  • Hook 侧目标是快速返回,整体应少于 1 秒。
  • 实际上 AI compression 对每条 observation 可能耗时 5–30 秒。

结论:不能让 Hook 等待压缩完成,必须把处理异步化。

直接同步处理为什么不可取

如果 Hook 在收到输入后立刻执行完整处理链路,例如解析 observation、调用 Claude SDK、等待压缩结果、再写回数据库,那么单次 Hook 可能阻塞数秒到数十秒。这既违背了 Hook 的快速返回目标,也会让上层系统把记忆功能的延迟感知为主流程故障。

核心机制

1. Hook 侧:快速捕获并入队

Hook 的职责被压缩到最小:

  1. 从 stdin 读取输入。
  2. 解析出要保存的 observation。
  3. 将 observation 插入队列。
  4. 立即返回成功。

文档给出的目标时间分布非常明确:

  • 读取 stdin:小于 1 ms
  • 插入队列:小于 10 ms
  • 总体返回:小于 20 ms

即使实现细节会有环境差异,设计目标仍然是让 Hook 明显快于 1 秒,并尽量接近“几毫秒到几十毫秒”的量级。

2. Worker 侧:慢速异步处理

worker 是独立于 Hook 的后台进程,负责消费队列中的 observation。其处理流程包括:

  1. 以固定频率轮询队列。
  2. 取出待处理 observation。
  3. 通过 Claude SDK 执行 AI 处理。
  4. 解析结果并存储。
  5. 在完成后将该 observation 标记为已处理。

文档中给出的轮询节奏与耗时特征是:

  • Worker 每 1 秒轮询一次队列。
  • 每条 observation 的 AI 处理大约需要 5–30 秒。

这意味着 worker 的职责不是“实时同步返回”,而是“尽快、稳定地完成后台任务”。

处理链路

端到端流程

整个架构可以概括为: Hook(捕获) → 队列(缓冲) → Worker(处理)

Hook 先读取 stdin、插入队列并立即返回成功。随后 worker 轮询队列、调用 Claude SDK 处理 observation,并在完成后标记已处理。

PostToolUse 的意义

PostToolUse 阶段,像 save-hook.js 这样的脚本并不负责完成整条 AI 压缩链路,而是把工具使用后产生的信息排队送入异步流程。这与 PostToolUse 页面中“排队进入 AI 压缩流程”的定义完全一致。

设计模式

Fire-and-Forget Hooks

该架构首先体现的是“发后即忘”模式:Hook 只发起任务,不等待任务完成。

原则:Hook 应立即返回,而不是等待处理结束。

这意味着正确的 Hook 设计不是直接 await processObservation(...),而是只执行快速的 enqueueObservation(...) 后返回成功。

Queue-Based Processing

第二个关键模式是基于队列的处理。队列把“事件捕获”和“实际处理”隔离开,成为二者之间的缓冲层。

原则:通过队列把 capture 与 processing 解耦。

这种设计带来的好处包括:

  • 并行 Hook 执行更安全,不必互相等待。
  • worker 失败不会直接影响 Hook 的返回。
  • 重试逻辑可以集中在 worker 一侧。
  • 更容易处理背压。

Graceful Degradation

第三个模式是优雅降级。记忆系统失败时,不应该拖垮 Claude Code 的主流程。

原则:memory 失败不应导致 Claude Code 失败。

具体表现是:当捕获 observation、写入队列或后续处理出错时,系统倾向于记录错误并继续,而不是把异常向上抛出阻塞主流程。

文档列出的失败模式包括:

  • 数据库被锁定:跳过该 observation,并记录错误。
  • worker 崩溃:由 Bun 自动重启。
  • 网络问题:使用指数退避重试。
  • 磁盘满:告警用户,并禁用 memory。

Progressive Enhancement

第四个模式是 Progressive Enhancement。其含义不是“memory 一定存在”,而是“核心功能在没有 memory 时也能正常工作,memory 只是增强层”。

没有 memory:Claude Code 正常工作。 "> 有 memory:Claude Code 在正常工作基础上获得跨会话上下文。 "> memory 出故障:退回到正常可用状态。

因此 Worker Service Architecture 的后台化、异步化和降级机制,本质上是在为 Progressive Enhancement 提供工程实现。

Worker 进程管理

技术选型:Bun

worker 使用 Bun 作为 JavaScript runtime 和进程管理器。文档给出的选择理由包括:

  • 失败后可自动重启
  • 启动快、内存占用低
  • 内建 TypeScript 支持
  • 跨平台,可在 macOS、Linux、Windows 上运行
  • 不需要额外的独立进程管理器

生命周期管理

worker 可以由 Hook 自动启动:如果 worker 未运行,Hook 会触发启动流程。

文档中的说明是“Started by hooks automatically (if not running)”。

同时还提供了常见的运维命令:

  • npm run worker:start:启动 worker
  • npm run worker:status:检查状态
  • npm run worker:logs:查看日志
  • npm run worker:restart:重启
  • npm run worker:stop:停止

这里的重点不是命令本身,而是该架构假设 worker 是一个长期存在、可被自动拉起、可被独立观测和管理的后台进程。

Worker HTTP API

接口形态

worker 还暴露了一个基于 Express.js 的 REST API。它运行在每用户一个端口的模式上,默认端口公式为:

  • 37700 + (uid % 100)

也可以通过 CLAUDE_MEM_WORKER_PORT 覆盖默认端口。

主要端点

文档列出的端点包括:

  • GET /health:健康检查
  • POST /sessions:创建 session
  • GET /sessions/:id:获取 session 状态
  • PATCH /sessions/:id:更新 session
  • POST /observations:将 observation 入队
  • GET /observations/:id:获取 observation

为什么使用 HTTP API

采用 HTTP API 的原因有四点:

  • 与语言无关,Hook 可以用任意语言实现
  • 调试方便,可以直接用 curl
  • 错误处理标准化
  • 更适合表达异步交互

这说明 Worker Service Architecture 并不强依赖“Hook 和 worker 必须在同一语言、同一进程、同一调用栈内”,而是主动用 HTTP 作为解耦边界。

细节与边界

Hook 的成功不等于处理完成

在这种架构里,Hook 返回成功,只表示“输入已读取且已成功进入队列”或至少已完成快速捕获阶段;它不表示 observation 已被压缩、解析、存储。

这是理解异步架构最容易混淆的边界之一。

轮询不是实时完成

worker 每 1 秒轮询一次队列,因此即便 Hook 侧几乎瞬时返回,真正的处理也至少会受到轮询周期和队列长度影响。再叠加 5–30 秒的 AI compression 时间,最终完成时间通常显著晚于 Hook 返回时间。

队列是隔离层,不是最终结果