Skip to main content
Glama
README.md
# cf-agent-memory

**Personal agent memory that lives on Cloudflare's free tier.**
一个跑在 Cloudflare 免费额度上的个人 Agent 长期记忆服务:MCP 协议接入,语义 + 关键词混合召回,零服务器、零数据库运维。

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/xxynly/cf-agent-memory)

```
Claude Code / Cursor / CodeBuddy / 任何 MCP 客户端
                 │  MCP over HTTP
                 ▼
        ┌────────────────────┐
        │  Worker (Hono)     │  鉴权 · 限流 · 路由
        └───┬────────┬───────┘
            │        │
     ┌──────▼──┐  ┌──▼─────────┐
     │   D1    │  │ Vectorize  │   ← 原文 / 全文索引 / 语义索引
     │  SQLite │  │  1024 维    │
     └────┬────┘  └──┬─────────┘
          └────┬─────┘
               ▼
        RRF 融合 + 时间加权  →  top-k 记忆
```

---

## 为什么做这个

市面上的 Agent 记忆方案大多要求你跑一个 Python 服务、维护一个向量库,或者接受 SaaS 的数据托管。个人用其实只需要三件事:

1. **记住**值得长期保留的事实、偏好、决策;
2. **召回**时不靠模型自觉,而是给一个稳定的检索接口;
3. **不花钱、不运维**。

这个项目把这三件事压进一个 Worker:D1 存原文与全文索引,Vectorize 存语义向量,Workers AI 提供 embedding,全部在 Cloudflare 免费额度内。

## 特性

- **MCP 原生**:12 个工具(记忆 6 + 技能 6),Claude Code、Cursor、CodeBuddy 等直接接入
- **代理注入**:把 `ANTHROPIC_BASE_URL` 指向本服务,自动把召回的记忆与技能列表插进当前轮次,不依赖模型自觉
- **混合召回**:语义(Vectorize)+ 关键词(FTS5 + 中文 bigram)+ RRF 融合 + 时间加权,任一路失败自动降级
- **同键覆盖**:`key='project.database'` 写入新值自动 supersede 旧值,不会积累互相矛盾的记忆
- **技能库**:SKILL.md 托管 + 版本快照 + R2 资源文件 + 按 agent 分配,同名再导入自动升版本
- **分层字段**:`l0/l1/l2/l3` + `fact/event/instruction/task`,为后续的自动提炼留好结构
- **REST API**:与 MCP 工具一一对应,方便脚本、Web UI、自定义客户端
- **自带面板**:单页 Web UI,记忆与技能两个标签页
- **限流**:每个用户一个 Durable Object 做固定窗口限流,跨边缘强一致
- **一键部署**:`npm run setup && npm run deploy`,自动创建 D1 / Vectorize / R2 / secret

## 快速开始

### 方式一:一键部署(推荐)

点击顶部 **Deploy to Cloudflare** 按钮。Cloudflare 会克隆仓库到你自己的账号,自动创建 D1、Vectorize 索引和 Durable Object,回填 `database_id`,并在部署时执行 `schema.sql` 建表。唯一需要你填的是 `ADMIN_TOKEN`:

```bash
openssl rand -hex 32     # 生成一个,粘到部署页面
```

也可以在本地部署:

```bash
git clone https://github.com/xxynly/cf-agent-memory.git
cd cf-agent-memory
npm install
npx wrangler login     # 只需一次
npm run setup          # 创建 D1 + Vectorize + 表结构 + ADMIN_TOKEN
npm run deploy         # 建表(幂等)后发布 Worker
```

`npm run setup` 会打印 `ADMIN_TOKEN`,并写入 `.dev.vars`(已 gitignore);它也可重复执行,已存在的资源会跳过。

### 方式二:本地调试

```bash
npm run setup
npm run dev     # http://localhost:8787
```

本地调试同样需要远程 D1/Vectorize(`--experimental-vectorize-bind-to-prod` 已在 `dev` 脚本外说明,见 docs/deploy.md)。

## 接入 MCP 客户端

把 `<WORKER_URL>` 和 `<TOKEN>` 换成部署输出与 `npm run setup` 打印的值。

**Claude Code**(`~/.claude.json` 或项目 `.mcp.json`):

```json
{
  "mcpServers": {
    "memory": {
      "type": "http",
      "url": "https://<WORKER_URL>/mcp",
      "headers": {
        "Authorization": "Bearer <TOKEN>",
        "X-User-Id": "personal"
      }
    }
  }
}
```

**Cursor / CodeBuddy / 其他 MCP 客户端**:同样填 HTTP 类型的 URL 与两个 header。

接好之后,让 Agent「记住这个」,它会调用 `memory_remember`;下次新会话问「我们之前定的数据库是什么」,会走 `memory_recall`。

## 代理注入(自动记忆,不依赖模型自觉)

如果你不想依赖模型主动调工具,把客户端指到本服务即可。请求经过时,Worker 会拿**最后一条用户消息**当 query 召回记忆、列出可用技能,插到那一轮之前,再转发给上游。

```bash
# Claude Code
export ANTHROPIC_BASE_URL="https://<WORKER_URL>"
export ANTHROPIC_AUTH_TOKEN="<PROXY_TOKEN>"

# 上游 API key 二选一:
#   1. 存进 Worker:npx wrangler secret put ANTHROPIC_API_KEY
#   2. 或客户端自己带 x-api-key(用谁的就扣谁的额度)
```

三个可观测响应头,调试时很好用:

| 头 | 值 |
|---|---|
| `x-cfam-injected` | `1` 本轮注入了,`0` 没有 |
| `x-cfam-reason` | `injected` / `no_content` / `body_too_large` / `not_injectable` / `injection_failed` |
| `x-cfam-upstream-ms` | 上游耗时 |

`body_too_large` 是**有意的保护**:免费层每请求只有 10ms CPU,一个带完整历史的 Claude Code 请求体解析+重新序列化就可能超预算。超过 `MAX_PROXY_BODY_BYTES`(默认 512KB)就原样转发,最坏情况就是这轮没有记忆——绝不会因为注入把请求搞失败。

代理路由用独立的 `PROXY_TOKEN`,与 `ADMIN_TOKEN` 分开:它花的是真实 LLM 额度。没配 `PROXY_TOKEN` 时该路由返回 503,其他接口不受影响。

详细设计与代价分析见 [docs/proxy-injection.md](docs/proxy-injection.md)。

## 工具一览

**记忆**

| 工具 | 作用 | 关键参数 |
|---|---|---|
| `memory_remember` | 写入一条记忆 | `content`、`key?`、`kind?`、`layer?`、`tags?`、`importance?` |
| `memory_recall` | 混合召回 | `query`、`limit?` |
| `memory_list` | 按时间倒序列出 | `limit?`、`offset?`、`layer?`、`kind?`、`tag?` |
| `memory_update` | 原地修正 | `id`、`content?`、`tags?`、`importance?` |
| `memory_forget` | 删除 | `id` |
| `memory_stats` | 按层/类型统计 | — |

**技能**

| 工具 | 作用 | 关键参数 |
|---|---|---|
| `skill_import` | 导入/更新 SKILL.md(同名升版本) | `name`、`content`、`description?`、`tags?` |
| `skill_list` | 列出技能库 | `limit?`、`offset?`、`tag?`、`agent_id?` |
| `skill_get` | 取全文,可钉住版本 | `name`、`version?` |
| `skill_search` | 关键词检索技能 | `query`、`limit?` |
| `skill_allocate` | 分配给某个 agent | `name`、`agent_id` |
| `skill_delete` | 删除技能及其文件 | `name` |

REST 接口与之一一对应,见 [docs/api.md](docs/api.md)。

## 召回是怎么工作的

```
query ──┬─► Workers AI embedding ──► Vectorize top-20 ─┐
        │                                              ├─► RRF(k=60) ─► 时间加权 ─► top-k
        └─► 分词(latin 词 + 中文 bigram)──► FTS5 top-20 ┘
```

- 写入和查询用**同一个分词函数**,避免两边 token 不一致导致召回静默失效
- RRF 不需要两路分数可比,天然适合语义 + 关键词融合
- 时间加权只在相关度接近时起作用(半衰期 30 天,权重 0.15)
- Vectorize 或 embedding 挂掉时自动降级为纯关键词,接口不报错

## 免费额度够用吗

| 资源 | 免费额度 | 个人用量 | 结论 |
|---|---|---|---|
| Workers | 10 万请求/天 | 每天几百次 | 富余 |
| D1 | 5GB / 10 万行写/天 | 每年几万条记忆 | 富余 |
| Vectorize | 500 万存储维度 ≈ 4900 条(1024 维) | 个人 1~3 年 | 够用,超了见下 |
| Workers AI | 1 万 Neurons/天 | embedding 消耗很低 | 富余 |
| R2 | 10GB / A 类 100 万次写/月 | 技能资源文件 | 富余 |
| Durable Objects | 10 万请求/天 | 只做限流 | 富余 |

**Vectorize 装不下之后**(约 4900 条):把 embedding 换成 384 维模型(免费额度约 1.3 万条),或只对 `l2/l3` 建向量、`l1` 只走关键词。取舍见 [docs/architecture.md](docs/architecture.md#向量容量)。

## 已知限制

- **Vectorize 写入有最终一致性**:新记忆通常几秒到 1 分钟内可被语义召回命中,关键词召回立即可用
- **MCP 是无状态的**:每次请求独立,不维护会话;记忆状态都在 D1 里
- **单用户模型**:用 `X-User-Id` 区分,但没有多租户 ACL。多人共用请自行加一层网关
- **中文分词用 bigram**:对短句足够,长文档的召回质量不如专业分词器
- **代理只支持 Anthropic 兼容协议**:Claude Code / CodeBuddy / WorkBuddy 可直接用;Codex、Cursor 等 OpenAI 协议客户端需要后续适配
- **代理不做响应侧提取**:对话不会自动写回记忆,仍需模型调 `memory_remember` 或调 `/v1/ingest`
- **代理能看到 LLM Key**:客户端自带 `x-api-key` 时会经过 Worker;介意的话用 Worker 上存的 `ANTHROPIC_API_KEY`

## 开发

```bash
npm run typecheck
npm test
npm run dev
npm run tail      # 线上日志
```

目录结构:

```
src/
├── index.ts          # Hono 路由 + MCP 挂载
├── mcp.ts            # 6 个 MCP 工具
├── auth.ts           # Bearer 鉴权 + DO 限流
├── memory/
│   ├── store.ts      # D1 读写 + FTS5
│   ├── vector.ts     # Workers AI embedding + Vectorize
│   ├── recall.ts     # 混合召回
│   └── ingest.ts     # 批量写入
├── do/user-memory.ts # 每用户限流
└── lib/              # 分词 / RRF / id
```

## 路线图

- [ ] 对话批量摄入 + 可选 LLM 提炼(L0 → L1)
- [ ] 自动去重与冲突检测(除了 key 覆盖之外的语义重复)
- [ ] 记忆导出 / 导入(JSON)
- [ ] 定时归档:冷数据转 R2,只保留摘要向量
- [ ] 多用户 ACL 与共享空间

## 与同类项目的区别

| | 本项目 | mcp-memory | TencentDB Agent Memory |
|---|---|---|---|
| 部署 | 一键,CF 免费层 | 一键,CF 免费层 | Docker 三件套 |
| 记忆结构 | 分层字段 + 同键覆盖 | 平铺文本 | L0–L3 完整提炼 |
| 召回 | 语义 + 关键词 + RRF | 纯语义 | BM25 + 向量 + RRF |
| Skill / Wiki / CodeGraph | 无 | 无 | 有 |
| 面板 | 简单列表页 | 简单列表页 | 完整 Hub |

本项目的定位是「个人用、够用、可维护」:不做团队资产化,但把记忆的写入、覆盖、召回做扎实。

## License

[MIT](LICENSE)

Maintenance

ActivityMaintained
ResponsivenessNo issues