claude-mem 默认使用系统钥匙串中的 OAuth Token 向 Anthropic API 鉴权。当你把 API 底座换成 DeepSeek 后,这个鉴权方式会直接失效,SDK 对每一次记忆生成请求都返回 Not logged in · Please run /login,然后被 parser 当作"空响应"静默丢弃——表面上一切正常,实际一条记忆都没落库。解决方案就是把鉴权从 OAuth token 切换为 API Key,并将 Base URL 指向 DeepSeek 的 Anthropic 兼容端点。


1. 背景

claude-mem 是 Claude Code 生态中最流行的记忆插件之一,它会在每次会话中自动捕捉你的操作上下文、生成结构化记忆(observation),并在后续会话中将这些记忆注入 system prompt,实现"跨会话记忆"。

我日常使用 DeepSeek v4 系列模型作为 Claude Code 的后端,API 底座是 https://api.deepseek.com/anthropic(DeepSeek 提供的 Anthropic Messages API 兼容端点)。在安装 claude-mem 之后,插件本身的安装和 MCP server 启动一切正常,/mem-search 技能也能成功加载——但无论经过多少次会话,dashboard 中始终看不到任何新产生的记忆,在设置面板中发现了No previous sessions found for this project yet.提示

这篇文章记录了我从发现问题到最终修复的完整过程。


2. 故障现象

检查项 状态 说明
claude-mem 插件安装 OK 13.2.0 版本,.claude/plugins/cache 中完整缓存
MCP server 启动 OK Search server 正常启动,无 crash
bun worker 进程 OK PID 正常,端口 37777 监听中
Worker dashboard OK http://127.0.0.1:37777 可访问,UI 完整渲染
Chroma MCP 向量搜索 FAIL MCP error -32000: Connection closed(次要问题,不阻塞记忆生成)
记忆生成 FAIL 所有 session 均无 observation 产生

最令人困惑的是:插件没有任何报错信息暴露给用户。你在 Claude Code 中正常使用,对话一切流畅,只是记忆永远不产生。


3. 排查过程

3.1 确认 worker 是否存活

第一步检查 worker 进程和其 dashboard:

# 检查 worker PID 文件
Get-Content ~/.claude-mem/worker.pid
# → {"pid":9908,"port":37777,"startedAt":"2026-05-21T07:23:48.616Z"}

# 检查进程是否存在
Get-Process -Id 9908

Dashboard 在 http://127.0.0.1:37777 正常渲染,说明 worker 本身没有问题。

3.2 检查 worker 日志——发现关键线索

日志位置:~/.claude-mem/logs/claude-mem-YYYY-MM-DD.log

# 每次 SDK 调用的返回都是完全相同的一句话(33 个字符):
[SDK] ← Response received (33 chars) Not logged in · Please run /login

# Parser 收到非 XML 响应后直接丢弃:
[PARSER] SDK returned non-XML/empty response — ignoring queued batch

这个错误重复了上百次——每一个 observation 的生成请求都以 Not logged in 被拒绝,而 parser 静默丢弃了这些非 XML 响应,导致用户层面完全无感知。

3.3 定位根因:鉴权方式不兼容

进一步查看 SDK 启动日志:

# 修复前(session-17/18):
[SDK] Starting SDK query {
  authMethod=Claude Code OAuth token (read from system keychain at spawn)
}
# → "Not logged in · Please run /login"

关键发现:claude-mem 默认使用 “Claude Code OAuth token” 鉴权方式——它从系统钥匙串中读取 Claude Code 的 OAuth token,然后向 Anthropic API 发起请求。这个 token 自然只在 Anthropic 的官方 API 上有效。当你把 ANTHROPIC_BASE_URL 指向 DeepSeek 后,DeepSeek 的 API 网关根本不认识这个 OAuth token,直接返回 Not logged in

# 修复后(session-19):
[SDK] Starting SDK query {
  authMethod=Gateway auth token (from ~/.claude-mem/.env)
}
# → observations 正常生成,Response 长度从 33 chars 跃升至 750~2687 chars

4. 解决方案

核心思路:将鉴权方式从 OAuth Token 切换为 API Key,并确保 .env 文件同时存在(claude-mem 的 SDK spawn 子进程需要从 .env 读取环境变量)。

4.1 修改 ~/.claude-mem/settings.json

{
  "CLAUDE_MEM_PROVIDER": "claude",
  "CLAUDE_MEM_MODEL": "deepseek-v4-flash",
  "CLAUDE_MEM_CLAUDE_AUTH_METHOD": "api-key",
  "ANTHROPIC_AUTH_TOKEN": "sk-你的DeepSeek-API-Key",
  "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
  "ANTHROPIC_API_KEY": "",
  "CLAUDE_MEM_OPENROUTER_API_KEY": "",
  "CLAUDE_MEM_OPENROUTER_MODEL": ""
}

每个字段的意义:

字段 为什么
CLAUDE_MEM_PROVIDER "claude" 使用 Anthropic SDK 兼容通道,而非 OpenRouter
CLAUDE_MEM_MODEL "deepseek-v4-flash" 模型名必须与 DeepSeek API 接受的名称一致。推荐 flash 版——记忆生成任务不需要强推理能力,速度快 3-5 倍且成本极低
CLAUDE_MEM_CLAUDE_AUTH_METHOD "api-key" 这是本次修复的关键——从 OAuth token 切换为 API Key 鉴权
ANTHROPIC_AUTH_TOKEN DeepSeek API Key 作为 bearer token 传递给 Anthropic SDK
ANTHROPIC_BASE_URL "https://api.deepseek.com/anthropic" DeepSeek 的 Anthropic Messages API 兼容端点

4.2 创建 ~/.claude-mem/.env

这是必须步骤——claude-mem 在 spawn 子进程调用 Claude SDK 时,会从 .env 文件读取环境变量。仅配置 settings.json 而缺少 .env 会导致子进程仍然回退到 OAuth token 鉴权。

# ~/.claude-mem/.env
ANTHROPIC_AUTH_TOKEN=sk-你的DeepSeek-API-Key
ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic

4.3 重启 worker

配置修改后,必须杀掉旧 worker 并重新启动:

# 1. 找到并杀掉旧 worker
Get-Content ~/.claude-mem/worker.pid  # 获取 PID
Stop-Process -Id <PID> -Force

# 2. 清理 PID 文件(可选,worker 会自动处理 stale PID)
Remove-Item ~/.claude-mem/worker.pid

# 3. 重新触发 Claude Code 会话——worker 会自动启动

新的 Claude Code 会话启动后,worker 会自动 spawn。你可以在日志中验证:

# 期望看到的日志行:
[SDK] Starting SDK query {
  authMethod=Gateway auth token (from ~/.claude-mem/.env)
}
# ↑ "Gateway auth token" 说明已切换到 .env 中的 API Key

4.4 验证修复

# 检查日志中的 SDK 返回长度——应该远超 33 chars
Select-String -Path ~/.claude-mem/logs/claude-mem-*.log -Pattern "Response received"

# 修复前:全部是 "(33 chars)"
# 修复后:出现 "(750 chars)"、"(1203 chars)"、"(2687 chars)" 等

# 打开 dashboard 确认 observation 已落库
Start-Process "http://127.0.0.1:37777"

5. 为什么这个 bug 如此隐蔽?

整个故障链路中有三个"静默失败"层叠在一起:

OAuth Token → DeepSeek API
  → SDK 返回 "Not logged in" (纯文本,非 XML)
    → Parser 判定为 "non-XML/empty response"
      → 静默丢弃,不报错,不告警
        → 用户界面无任何异常提示

claude-mem 的 parser 设计假设 SDK 返回的一定是合法 XML。当 API 返回纯文本错误消息时,parser 直接丢弃该批次,不会向上游报告错误。这是一个合理的"脏数据防御"策略,但在鉴权失败场景下会导致问题完全不可见。


6. 延伸:模型选择建议

对于记忆生成(observation generation)这个任务:

模型 适用性 理由
deepseek-v4-flash 推荐 记忆生成本质是"摘要+分类"任务,不需要深度推理。flash 模型速度快、成本极低,完全胜任
deepseek-v4-pro 过度 该任务不需要 1M context 和强推理能力,浪费成本和延迟
deepseek-chat (V3) 可用 成本低于 pro,但相比 flash 仍偏高

实际测试中,deepseek-v4-flash 每次 observation 生成约 500-2000 个 token(包含 XML 标记),延迟在 1-3 秒以内,效果完全满足需求。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐