一、架构全景图 - 理解整体设计
1.1 核心组件关系图
claude-mem 的架构由四大核心组件构成:
┌─────────────────────────────────────────────────────┐
│ Claude Code CLI (主进程) │
│ ├─ 用户对话 │
│ ├─ 工具调用 (Read, Write, Edit, Bash...) │
│ └─ 系统提示 │
└─────────────────────────────────────────────────────┘
↓ (触发 Hook)
┌─────────────────────────────────────────────────────┐
│ Plugin Hooks (6个生命周期拦截器) │
│ ├─ Setup Hook: 版本检查、Worker启动 │
│ ├─ SessionStart Hook: 会话初始化 │
│ ├─ UserPromptSubmit Hook: 用户输入预处理 │
│ ├─ PreToolUse Hook: 工具调用前拦截 │
│ ├─ PostToolUse Hook: 工具调用后拦截 │
│ └─ Stop Hook: 会话结束总结 │
└─────────────────────────────────────────────────────┘
↓ (HTTP调用)
┌─────────────────────────────────────────────────────┐
│ Worker Service (独立进程) │
│ ├─ Express HTTP API (port 37700+) │
│ ├─ 异步队列 (后台处理AI生成) │
│ ├─ Claude Agent SDK (生成observation) │
│ ├─ SQLite Service (持久化存储) │
│ ├─ Chroma Service (向量索引) │
│ └─ Session Manager (会话管理) │
└─────────────────────────────────────────────────────┘
↓ (存储数据)
┌─────────────────────────────────────────────────────┐
│ 数据持久层 │
│ ├─ SQLite Database (~/.claude-mem/claude-mem.db) │
│ │ ├─ sessions 表 (会话元数据) │
│ │ └─ observations 表 (观察记录) │
│ ├─ Chroma Vector DB (~/.claude-mem/chroma/) │
│ │ ├─ 向量嵌入 (文本→向量) │
│ │ └─ 语义索引 (相似度搜索) │
└─────────────────────────────────────────────────────┘
↓ (检索注入)
┌─────────────────────────────────────────────────────┐
│ 未来会话 (PreToolUse Hook) │
│ ├─ 查询Chroma (向量搜索相似文件) │
│ ├─ 查询SQLite (历史observation) │
│ └─ 注入到Claude系统提示 │
└─────────────────────────────────────────────────────┘1.2 进程模型详解
claude-mem 采用多进程架构,这是关键设计:
Claude Code 主进程
• 职责: 运行用户对话,执行工具调用 • 特点: 主进程,负责交互 • Hook影响: Hook在主进程中同步执行,必须快速响应
Worker 子进程
• 职责: 异步处理耗时操作(AI生成、数据存储) • 特点: 独立进程,不阻塞主进程 • 端口: 37700 + (uid % 100) • 管理: Bun runtime管理进程生命周期
为什么用 Worker 而不是直接处理?
设计哲学: Hook是同步拦截点,必须快速响应;Worker是异步处理器,可以耗时操作。
二、6个生命周期Hook详解 - 触发时序和职责
2.1 Hook 触发时序图
完整的Hook触发顺序(从插件安装到会话结束):
[插件安装]
↓
Setup Hook (执行一次)
├─ 版本检查 (version-check.js)
├─ Bun/uv依赖安装
└─ Worker进程启动
↓
[用户启动Claude Code]
↓
SessionStart Hook (每个会话开始)
├─ 生成session_id (UUID)
├─ 记录开始时间戳
└─ Worker创建session记录
↓
[用户输入问题]
↓
UserPromptSubmit Hook (可选,未使用)
├─ 输入预处理
└─ 添加时间戳/标签
↓
[Claude处理用户输入,决定调用工具]
↓
[工具调用: Read]
↓
PreToolUse Hook (拦截Read工具)
├─ 查询Chroma: 找相似文件
├─ 查询SQLite: 找历史observation
└─ 注入到Claude系统提示:
"上次你读取app.ts时,还修改了config.ts..."
↓
[Claude执行Read工具]
↓
PostToolUse Hook (捕获Read输出)
├─ 拦截工具输出(file content)
├─ 发送到Worker队列
├─ Worker后台处理:
│ ├─ Claude SDK生成observation
│ ├─ SQLite存储observation
│ └─ Chroma索引observation
└─ 快速响应(job_id返回)
↓
[会话继续,多次工具调用...]
↓
[用户退出Claude Code]
↓
Stop Hook (会话结束)
├─ 查询session的所有observation
├─ Claude SDK生成session summary
├─ SQLite更新session结束时间
└─ Chroma索引summary2.2 Setup Hook - 插件初始化
触发时机
• 何时: 插件安装时执行一次 • 场景: 用户首次安装插件或版本更新
核心职责
1. 版本检查: 确保插件版本与已安装版本一致 2. 依赖安装: 检查Bun、uv是否安装,缺失则安装 3. Worker启动: 启动Worker进程,确保后续Hook可用
实际代码分析
从 hooks.json 提取的Setup Hook配置:
{
"Setup":[
{
"matcher":"*",
"hooks":[
{
"type":"command",
"shell":"bash",
"command":"... version-check.js",
"timeout":300
}
]
}
]
}关键点:
• timeout: 300秒(5分钟),因为可能需要安装Bun/uv • 独立脚本: Setup Hook不走bun-runner,直接执行version-check.js
version-check.js 核心逻辑
// plugin/scripts/version-check.js
const currentVersion = '13.3.0';
// 1. 检查已安装版本
const installedVersion = getInstalledVersion();
if (currentVersion !== installedVersion) {
console.error('Version mismatch, please reinstall');
exit(1); // 非阻塞错误,显示给用户
}
// 2. 检查Bun是否安装
if (!commandExists('bun')) {
console.log('Installing Bun...');
// 自动安装Bun
}
// 3. 检查uv是否安装(用于Chroma)
if (!commandExists('uv')) {
console.log('Installing uv...');
// 自动安装uv
}
// 4. 启动Worker进程
startWorkerProcess();
exit(0); // 成功设计理念: Setup Hook确保插件环境就绪,后续Hook可以正常工作。
2.3 SessionStart Hook - 会话初始化
触发时机
• 何时: 每个Claude Code会话开始时 • matcher: startup|clear|compact(匹配特定命令)
核心职责
1. 生成session_id: UUID唯一标识会话 2. 记录时间戳: 会话开始时间 3. Worker初始化: 创建session记录,启动Worker
hooks.json 配置
{
"SessionStart":[
{
"matcher":"startup|clear|compact",
"hooks":[
{
"command":"... bun-runner.js ... start",
"timeout":60
},
{
"command":"... bun-runner.js ... context",
"timeout":60
}
]
}
]
}两个Hook子命令:
1. start: 启动Worker进程(如果未运行) 2. context: 注入初始上下文到Claude
Worker处理流程
[SessionStart Hook]
↓ (调用Worker)
POST /api/session-init
↓ (Worker处理)
1. 生成session_id: UUID
2. SQLite INSERT:
sessions (session_id, created_at, ...)
3. 返回session_id给Hook
↓ (Hook输出)
stdout: {"session_id": "abc-123"}
↓ (Claude读取)
Claude系统提示收到session_id实际数据示例:
-- SQLite sessions表记录
INSERT INTO sessions VALUES (
'abc-123-def-456', -- session_id
'2026-05-29T10:00:00Z', -- created_at
NULL, -- ended_at (会话结束时更新)
NULL-- summary (会话结束时生成)
);2.4 UserPromptSubmit Hook - 用户输入预处理(预留)
触发时机
• 何时: 用户输入提交前 • matcher: 无(所有用户输入)
设计意图(预留接口)
虽然hooks.json中配置了UserPromptSubmit Hook,但当前未实际使用,预留用于:
• 输入预处理: 添加时间戳、标签 • 隐私处理: 检测 <private>标签,标记敏感内容• 输入验证: 检查输入格式、长度
hooks.json 配置
{
"UserPromptSubmit":[
{
"hooks":[
{
"command":"... bun-runner.js ... session-init",
"timeout":60
}
]
}
]
}注意: 配置中使用了session-init子命令,但实际功能未激活。
潜在应用场景
场景1: 隐私标签处理
// 未来实现: 检测<private>标签
if (userMessage.includes('<private>')) {
// 标记为隐私,不存储
markAsPrivate(sessionId);
}场景2: 输入增强
// 未来实现: 添加时间戳
const enhancedMessage = `[${timestamp}] ${userMessage}`;
// 注入到Claude2.5 PreToolUse Hook - 最关键的注入点
触发时机
• 何时: 工具调用前 • matcher: Read(只拦截Read工具)
核心职责(关键功能)
这是claude-mem最核心的Hook,负责注入历史上下文:
1. 拦截Read工具: Claude要读取文件前触发 2. 查询Chroma: 向量搜索相似文件 3. 查询SQLite: 查找历史observation 4. 注入系统提示: 将历史信息注入到Claude
hooks.json 配置
{
"PreToolUse":[
{
"matcher":"Read",
"hooks":[
{
"command":"... bun-runner.js ... file-context",
"timeout":60
}
]
}
]
}关键参数:
• matcher: "Read"只拦截Read工具,不拦截Write/Edit• timeout: 60秒,查询Chroma可能耗时
完整注入流程详解
步骤1: Hook接收Read工具参数
// stdin输入(来自Claude Code)
{
"tool_name":"Read",
"tool_params":{
"file_path":"/src/app.ts"
},
"session_id":"abc-123"
}步骤2: Worker查询Chroma(向量搜索)
// Worker处理
const filePath = '/src/app.ts';
// Chroma向量搜索
const similarFiles = await chromaCollection.query({
queryEmbeddings: [embedText(filePath)],
nResults: 5
});
// 返回结果示例:
[
{ file_path: '/src/utils.ts', similarity: 0.85 },
{ file_path: '/src/config.ts', similarity: 0.78 },
{ file_path: '/src/router.ts', similarity: 0.72 }
]向量搜索原理:
• 文件路径 /src/app.ts→ 文本 → Embedding向量• Chroma计算向量相似度(余弦距离) • 返回语义相似的文件(即使路径不同)
步骤3: Worker查询SQLite(历史observation)
// SQLite查询
const observations = await sqlite.query(`
SELECT observation_text, timestamp, tool_name
FROM observations
WHERE tool_params LIKE '%/src/app.ts%'
ORDER BY timestamp DESC
LIMIT 10
`);
// 返回结果示例:
[
{
observation_text: '读取了app.ts,包含Router配置',
timestamp: '2026-05-28T15:00:00Z',
tool_name: 'Read'
},
{
observation_text: '修改了app.ts,增加了新路由',
timestamp: '2026-05-27T10:00:00Z',
tool_name: 'Edit'
}
]步骤4: 构建注入文本
// 构建系统提示注入
const injectionText = `
历史上下文:
- 相似文件: utils.ts, config.ts (上次你读取app.ts时还查看了这些文件)
- 历史操作:
* 2026-05-28: 读取了app.ts,包含Router配置
* 2026-05-27: 修改了app.ts,增加了新路由
`;
// 输出到stdout
console.log(JSON.stringify({
type: 'context_injection',
text: injectionText
}));步骤5: Claude系统提示接收
Claude的系统提示被更新:
原始系统提示:
"You are Claude, a helpful AI assistant..."
注入后系统提示:
"You are Claude, a helpful AI assistant...
历史上下文:
- 相似文件: utils.ts, config.ts (上次你读取app.ts时还查看了这些文件)
- 历史操作:
* 2026-05-28: 读取了app.ts,包含Router配置
* 2026-05-27: 修改了app.ts,增加了新路由"步骤6: Claude使用历史信息
现在Claude读取/src/app.ts时,会:
• 理解关联: 知道app.ts与utils.ts、config.ts相关 • 参考历史: 知道之前做过Router配置 • 提供精准建议: 基于历史提供更准确的修改建议
实际体验示例:
用户: "帮我优化app.ts的路由性能"
Claude (看到历史上下文):
"根据历史记录,你在app.ts中配置了Router,上次还修改了config.ts。
我建议优化路由性能可以从以下方面入手:
1. config.ts中的路由缓存设置...
2. utils.ts中的路由工具函数..."2.6 PostToolUse Hook - 最关键的数据收集点
触发时机
• 何时: 工具调用后 • matcher: *(拦截所有工具)
核心职责(关键功能)
这是另一个核心Hook,负责捕获工具输出并生成observation:
1. 拦截所有工具: Read、Write、Edit、Bash等 2. 捕获工具输出: 工具执行结果 3. 发送到Worker: 添加到后台队列 4. Worker后台处理: AI生成observation + 存储 + 索引
hooks.json 配置
{
"PostToolUse":[
{
"matcher":"*",
"hooks":[
{
"command":"... bun-runner.js ... observation",
"timeout":120
}
]
}
]
}关键参数:
• matcher: "*"拦截所有工具,不限制• timeout: 120秒(2分钟),因为需要等待Worker快速响应
完整数据收集流程详解
步骤1: Hook接收工具输出
// stdin输入(来自Claude Code)
{
"tool_name":"Read",
"tool_params":{
"file_path":"/src/app.ts"
},
"tool_output":"import express from 'express';\nconst app = express();\n...",
"session_id":"abc-123",
"timestamp":"2026-05-29T10:00:00Z"
}注意: tool_output 可能很大(文件内容),Hook不处理内容,直接转发Worker。
步骤2: Hook调用Worker API
// PostToolUse Hook脚本
const response = awaitfetch('http://127.0.0.1:37700/api/observation', {
method: 'POST',
body: JSON.stringify({
tool_name: 'Read',
tool_params: { file_path: '/src/app.ts' },
tool_output: 'file content...', // 可能很大
session_id: 'abc-123',
timestamp: '2026-05-29T10:00:00Z'
})
});
// Worker快速响应(<1秒)
const result = await response.json();
// { status: 'queued', job_id: 'xyz-789' }快速响应设计:
• Worker收到请求后立即返回 job_id• 不等待AI生成完成(耗时5-10秒) • Hook收到响应后立即退出
步骤3: Hook快速退出
// Hook输出job_id(可选)
console.log(`Observation queued: ${result.job_id}`);
// 快速退出,不阻塞Claude
process.exit(0);步骤4: Worker后台队列处理
[Worker收到请求]
↓
快速响应: {status: 'queued', job_id: 'xyz-789'}
↓
添加到后台队列:
Queue.add({
job_id: 'xyz-789',
tool_name: 'Read',
tool_output: 'file content...',
session_id: 'abc-123'
})
↓
[Claude继续工作,不受影响]
↓
[Worker后台处理,5-10秒]步骤5: Claude SDK生成observation
// Worker后台队列处理
asyncfunctionprocessObservation(job) {
// 1. 调用Claude Agent SDK生成一句话总结
const prompt = `
Analyze the following tool usage and generate a one-line observation:
Tool: ${job.tool_name}
File: ${job.tool_params.file_path}
Output: ${job.tool_output.substring(0, 1000)}
Generate a concise summary of what was done.
`;
const observation = await claudeSDK.generate(prompt);
// observation示例: "读取了app.ts,包含Express Router配置"
// 2. 存储到SQLite
await sqlite.insert('observations', {
observation_id: job.job_id,
session_id: job.session_id,
tool_name: job.tool_name,
observation_text: observation,
timestamp: job.timestamp
});
// 3. 索引到Chroma
await chroma.addEmbedding({
id: job.job_id,
text: observation,
metadata: {
session_id: job.session_id,
tool_name: job.tool_name,
file_path: job.tool_params.file_path
}
});
}步骤6: 数据持久化完成
-- SQLite observations表记录
INSERT INTO observations VALUES (
'xyz-789', -- observation_id
'abc-123', -- session_id
'Read', -- tool_name
'{"file_path":"/src/app.ts"}', -- tool_params
'读取了app.ts,包含Express Router配置', -- observation_text
'2026-05-29T10:00:00Z', -- timestamp
NULL-- metadata
);Chroma向量索引:
Observation文本 → Embedding向量 → Chroma存储
"读取了app.ts,包含Express Router配置"
↓ (text-embedding-ada-002)
[0.123, -0.456, 0.789, ...] (1536维向量)
↓ (Chroma.add)
向量索引,支持语义搜索2.7 Stop Hook - 会话总结
触发时机
• 何时: 会话结束(用户退出Claude Code) • matcher: 无(所有会话结束)
核心职责
1. 查询session observations: 获取会话的所有observation 2. 生成session summary: Claude SDK生成会话总结 3. 更新session结束时间: SQLite更新ended_at字段 4. 索引summary: Chroma索引会话总结
hooks.json 配置
{
"Stop":[
{
"hooks":[
{
"command":"... bun-runner.js ... summarize",
"timeout":120
}
]
}
]
}timeout: 120秒,因为需要等待AI生成summary。
会话总结流程
[Stop Hook触发]
↓
查询SQLite:
SELECT observation_text FROM observations
WHERE session_id = 'abc-123'
ORDER BY timestamp
↓
返回observation列表:
[
'读取了app.ts,包含Router配置',
'修改了config.ts,增加了环境变量',
'调试了API调用,修复了bug'
]
↓
调用Claude SDK生成summary:
"本次会话修改了配置文件,调试API,修复bug"
↓
更新SQLite:
UPDATE sessions SET
ended_at = '2026-05-29T11:00:00Z',
summary = '本次会话修改了配置文件...'
WHERE session_id = 'abc-123'
↓
索引到Chroma:
会话summary也加入向量索引三、Worker Service架构 - HTTP API核心
3.1 Express HTTP API设计
Worker Service是独立进程,提供HTTP API供Hook调用。
端口计算公式
// src/shared/SettingsDefaultsManager.ts
constWORKER_PORT_BASE = 37700;
const uid = process.getuid(); // Unix用户ID
const port = WORKER_PORT_BASE + (uid % 100);示例计算:
• uid=1000 → port=37700 + (1000%100)=37700 • uid=1001 → port=37700 + (1001%100)=37701 • uid=2050 → port=37700 + (2050%100)=37750
设计目的: 避免多用户或多账号场景下的端口冲突。
核心API路由
Worker提供以下HTTP API:
/api/session-init | |||
/api/observation | |||
/api/file-context | |||
/api/summarize | |||
/health |
3.2 异步处理机制 - 核心设计
Worker的核心设计是异步队列,这是让Hook不阻塞Claude的关键。
快速响应模式
// Worker收到Hook请求
app.post('/api/observation', async (req, res) => {
const job_id = uuidv4();
// 1. 快速响应(<1秒)
res.json({
status: 'queued',
job_id: job_id,
message: 'Observation generation started'
});
// 2. 后台异步处理(不阻塞响应)
observationQueue.add({
job_id,
tool_name: req.body.tool_name,
tool_output: req.body.tool_output,
session_id: req.body.session_id
});
});时间对比:
• Hook → Worker HTTP调用: ~50ms • Worker快速响应: ~100ms • Hook阻塞时间: ~150ms (总) • Worker后台AI生成: ~5-10秒 (异步)
后台队列实现
// Worker后台队列
classObservationQueue {
privatequeue: Job[] = [];
privateisProcessing: boolean = false;
add(job: Job): void {
this.queue.push(job);
if (!this.isProcessing) {
this.processQueue(); // 启动后台处理
}
}
privateasyncprocessQueue(): void {
this.isProcessing = true;
while (this.queue.length > 0) {
const job = this.queue.shift();
try {
// 1. Claude SDK生成observation (耗时5-10秒)
const observation = await claudeSDK.generate(job);
// 2. 存储到SQLite
await sqlite.insert('observations', observation);
// 3. 索引到Chroma
await chroma.addEmbedding(observation);
} catch (error) {
console.error('Job failed:', error);
// 错误处理,继续下一个任务
}
}
this.isProcessing = false;
}
}3.3 Claude Agent SDK集成
Worker使用Claude Agent SDK生成observation和summary。
SDK调用示例
importAnthropicfrom'@anthropic-ai/sdk';
const client = newAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY
});
// 生成observation
asyncfunctiongenerateObservation(toolOutput: string): string {
const prompt = `
Analyze the following tool output and generate a one-line observation:
${toolOutput.substring(0, 1000)}
`;
const message = await client.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 200,
messages: [{ role: 'user', content: prompt }]
});
return message.content[0].text;
}模型选择: claude-sonnet-4-5 (平衡速度和质量)
四、数据流详解 - 完整路径追踪
4.1 一个Read工具调用的完整数据流
让我们追踪Claude执行Read /src/app.ts的完整数据流:
[Claude决定调用Read工具]
↓ (1)
[PreToolUse Hook触发]
↓ (2) stdin接收
{
"tool_name": "Read",
"tool_params": {"file_path": "/src/app.ts"},
"session_id": "abc-123"
}
↓ (3) Hook调用Worker
POST http://127.0.0.1:37700/api/file-context
↓ (4) Worker处理
Chroma向量搜索: "app.ts"
返回相似文件: [utils.ts, config.ts]
↓ (5) Worker查询SQLite
SELECT * FROM observations
WHERE tool_params LIKE '%app.ts%'
返回历史: ["读取了app.ts...", "修改了app.ts..."]
↓ (6) Worker返回Hook
{
"similarFiles": ["/src/utils.ts", "/src/config.ts"],
"observations": [
{"text": "读取了app.ts,包含Router", "time": "2026-05-28"},
{"text": "修改了app.ts,增加路由", "time": "2026-05-27"}
]
}
↓ (7) Hook输出到Claude
stdout: {
"type": "context_injection",
"text": "上次读取app.ts时,还查看了utils.ts..."
}
↓ (8) Claude系统提示更新
[Claude收到历史上下文]
↓ (9) Claude执行Read
Read /src/app.ts
返回文件内容: "import express..."
↓ (10) [PostToolUse Hook触发]
↓ (11) stdin接收
{
"tool_name": "Read",
"tool_output": "import express...",
"session_id": "abc-123"
}
↓ (12) Hook调用Worker
POST http://127.0.0.1:37700/api/observation
↓ (13) Worker快速响应
{status: "queued", job_id: "xyz-789"}
↓ (14) Hook退出
process.exit(0)
↓ (15) Claude继续工作
[不受阻塞]
↓ (16) Worker后台处理
[Claude SDK生成observation]
"读取了app.ts,包含Express Router配置"
↓ (17) SQLite存储
INSERT INTO observations ...
↓ (18) Chroma索引
向量嵌入 → Chroma.add
↓ (19) 完成
日志: Observation processed: xyz-789时间统计:
• 步骤1-8(PreToolUse): ~1-2秒 • 步骤9(Read执行): ~0.5秒 • 步骤10-14(PostToolUse响应): ~0.15秒 • 步骤15(Claude继续): 0秒(无阻塞) • 步骤16-19(Worker后台): ~5-10秒(异步)
4.2 一个会话的完整生命周期数据流
从会话开始到结束的完整数据:
[会话开始: SessionStart Hook]
↓
session_id: "abc-123"
created_at: "2026-05-29T10:00:00Z"
SQLite: INSERT INTO sessions ...
[会话期间: 多次PostToolUse Hook]
↓
observation_1: "读取了package.json,版本13.3.0"
observation_2: "修改了README.md,添加新章节"
observation_3: "调试了API调用,修复bug"
observation_4: "读取了app.ts,包含Router配置"
SQLite: INSERT INTO observations ...
Chroma: 向量索引每个observation
[会话结束: Stop Hook]
↓
查询observations:
SELECT observation_text FROM observations
WHERE session_id = 'abc-123'
返回: [observation_1, observation_2, ...]
↓
Claude SDK生成summary:
"本次会话读取了配置文件,修改文档,调试API,修复bug"
↓
SQLite更新:
UPDATE sessions SET
ended_at = '2026-05-29T11:00:00Z',
summary = '本次会话...'
WHERE session_id = 'abc-123'
↓
Chroma索引summary:
会话总结也加入向量索引五、实践任务 - 验证架构理解
任务1: 观察 Hook触发顺序
目标: 实际观察Hook的触发时序。
步骤:
# 1. 启动Claude Code
claude-code
# 2. 打开Worker日志
tail -f ~/.claude-mem/logs/worker.log
# 3. 在Claude Code中执行操作
# 输入: "请读取package.json文件"
# 4. 观察日志输出(应该看到):
[SessionStart] Session initialized: abc-123
[PreToolUse] Intercepting Read: package.json
[Worker] Chroma search: query="package.json"
[Worker] SQLite query: file_path LIKE '%package.json%'
[Hook] Context injection: "上次读取package.json..."
[PostToolUse] Observation queued: job_id=xyz-789
[Queue] Processing job...
[Claude SDK] Generating observation...
[SQLite] Observation stored
[Chroma] Observation indexed分析要点:
• SessionStart最先触发 • PreToolUse在Read前触发 • PostToolUse在Read后触发 • Worker后台异步处理observation
任务2: 分析 SQLite数据
目标: 查看Hook生成的实际数据。
步骤:
# 1. 打开SQLite
sqlite3 ~/.claude-mem/claude-mem.db
# 2. 查询session
SELECT session_id, created_at, ended_at, summary FROM sessions;
# 应该看到:
abc-123 | 2026-05-29T10:00:00Z | NULL | NULL
# 3. 查询observation
SELECT observation_id, tool_name, observation_text FROM observations LIMIT 5;
# 应该看到:
xyz-789 | Read | 读取了package.json,版本13.3.0
...
# 4. 分析关系
SELECT s.session_id, o.observation_text
FROM sessions s
JOIN observations o ON s.session_id = o.session_id;
# 理解session和observation的关系任务3: 追踪 PreToolUse注入
目标: 验证PreToolUse Hook的上下文注入。
步骤:
# 1. 在Claude Code中读取一个文件
# 输入: "请读取src/services/worker-service.ts"
# 2. 观察Worker日志
grep "file-context" ~/.claude-mem/logs/worker.log
# 应该看到:
[Worker] Chroma search: query="worker-service.ts"
[Worker] Similar files: [worker-cli.ts, server-beta-service.ts]
[Worker] SQLite query: file_path LIKE '%worker-service%'
[Worker] Observations: ["读取了worker-service...", "修改了worker-service..."]
# 3. Claude的响应应该包含历史信息
# Claude可能说: "根据历史记录,上次读取worker-service.ts时..."任务4: 测试 Worker健康检查
目标: 验证Worker进程管理。
步骤:
# 1. 查看Worker端口
grep "Worker running on port" ~/.claude-mem/logs/worker.log
# 输出: Worker running on port 37700
# 2. 测试健康检查API
curl http://127.0.0.1:37700/health
# 应该返回:
{
"status": "healthy",
"checks": {
"sqlite": true,
"chroma": true,
"queue": true
},
"uptime": 3600
}
# 3. 如果Worker未运行,bun-runner会自动启动
# 手动停止Worker:
pkill -f worker-service
# 重新启动Claude Code,观察bun-runner启动Worker六、关键学习点总结
6.1 Hook vs Worker的职责分离
| 执行方式 | ||
| 生命周期 | ||
| 进程位置 | ||
| 职责 | ||
| 阻塞影响 |
设计哲学: Hook是快速拦截器,Worker是重型处理器。
6.2 数据流是单向的
完整的数据流路径:
Hook → Worker → SQLite → Chroma → 注入特点:
• 单向流动: 数据从Hook流向存储,再流向注入 • 没有反向: Worker不会调用Hook • 清晰职责: 每个阶段职责明确,易于理解
6.3 PreToolUse是最关键的注入点
为什么PreToolUse Hook最关键?
• 时机关键: 在Claude执行工具前,可以影响Claude的决策 • 信息注入: 查询历史,注入上下文,让Claude"记住"过去 • 精准增强: 只拦截Read工具,精准注入文件相关历史
实际价值: Claude看到历史信息后,能提供更精准的建议。
6.4 PostToolUse是最关键的数据收集点
为什么PostToolUse Hook最关键?
• 时机关键: 在工具执行后,捕获所有输出 • 数据收集: 拦截所有工具(Read、Write、Edit等) • 持久化基础: 生成的observation是未来注入的数据源
实际价值: 没有PostToolUse收集数据,PreToolUse就没有历史可注入。
6.5 Worker的异步设计是核心创新
为什么Worker必须异步?
• AI生成耗时: Claude SDK生成observation需要5-10秒 • 用户体验: 如果Hook等待,Claude会阻塞10秒 • 并发处理: Worker队列可以并行处理多个observation
对比: 如果没有异步设计,用户体验会是灾难性的。
下一步: 第三章将深入Hook系统的实现细节,学习hooks.json配置、bun-runner进程管理、Hook脚本编写技巧。
夜雨聆风