内容
# Aelios
> 给 AI 装一颗跨窗口的长期记忆大脑。换窗口、换客户端、换模型,记忆跟着你走。
这份 README 分两段。**上半段给人看**:草履虫也能懂,照着做就能用。**下半段给 AI 看**:端点、MCP、管线细节,给 Codex / Claude Code / Cursor 维护调试用。
- 我是人类,想部署使用 → 看 [人类版](#人类版)
- 我是 AI 助手,想维护调试 → 看 [AI 版](#ai版维护交接)
---
# 人类版
## 一句话
Aelios 是一个跑在 Cloudflare 上的记忆服务。你的 AI 客户端(Chatbox、Cherry Studio、网页、脚本)连上它之后,AI 就能**永远记住**你的偏好、规则、项目背景和重要的话——不是这一次记得,是下一次、下下次都记得。
## 它替你解决了什么
- 每次开新窗口 AI 就失忆 → 它把聊天存下来,自动整理成长期记忆,下次自动召回。
- 记忆太多把 AI 搞蠢 → 它先粗筛、再精排、再压缩,最后只塞几百字进上下文。
- 换个客户端记忆就没了 → 记忆存在你自己的 Cloudflare 里,换客户端只改一个地址。
- 想给 Claude Code / Codex 加记忆 → 它能当成 MCP 工具挂上去,跨设备随身。
## 三步搞定
### 1. 部署
1. Fork [wusaki0723/Aelios](https://github.com/wusaki0723/Aelios) 到自己 GitHub。
2. Cloudflare Dashboard → Workers & Pages → Create application → 连你的 GitHub → 选你的 fork。
3. 填配置:
- Project name: `companion-memory-proxy`
- Production branch: `main`
- Root directory: `/`
- **Build command:** `npm ci`
- **Deploy command:** `npm run deploy:cloudflare`
> ⚠️ 必须是 `npm run deploy:cloudflare`,**不是** `npm run deploy`,**不是** `wrangler deploy`。这条命令会自动建好 D1 数据库 + Vectorize 向量库 + Queue 队列。用错命令数据库不会建。
### 2. 设一把钥匙
部署完,去 Worker 的 Settings → Variables and Secrets 加三个:
| 变量名 | 类型 | 填什么 |
|---|---|---|
| `CLOUDFLARE_ACCOUNT_ID` | Variable | 你的 Cloudflare Account ID |
| `CLOUDFLARE_API_TOKEN` | Secret | 你的 Cloudflare API Token |
| `CHATBOX_API_KEY` | Secret | 自己编一个密码,比如 `sk-my-aelios` |
> 名字里带 `KEY` / `TOKEN` 的必须选 **Secret**(加密、不进 git),不要选 Variable。详见 [SECRETS.md](./SECRETS.md)。
保存后重新部署。你会拿到一个地址:`https://companion-memory-proxy.<你的子域>.workers.dev`
### 3. 接客户端
以 Chatbox 为例:
- **Base URL:** `https://<你的 Worker 地址>/v1`
- **API Key:** 你设的 `CHATBOX_API_KEY`
- **Model:** `companion`
试着说:"请记住:我的测试暗号是苹果星星-0428。" 过一会儿问:"我的测试暗号是什么?" 答出来就通了。
## 管理面板(推荐用这个)
有面板了,**日常管记忆不用敲命令**。浏览器打开:
```
https://<你的 Worker 地址>/admin
```
填入 Worker URL 和 API Key,进去就是可视化界面,底部 5 个标签:
| 标签 | 干什么 |
|---|---|
| **今日** | 今天聊了什么、L1 摘要、昨日日志、今日消息、记忆类型统计,一眼看完 |
| **审核队列** | AI 每 4 小时自动抽出来的低置信度记忆会到这里,你点**通过 / 丢弃 / 合并 / 取代**,不让垃圾记忆污染记忆库 |
| **重要记忆** | 所有长期记忆,按类型分页浏览、搜索、编辑、删除 |
| **更多** | 珍贵记忆(只增不删的原文)、黑话表(术语别名)、世界知识、维护工具 |
| **设置** | 主题、地址、密钥 |
**想让 AI 记住什么、忘掉什么、改什么,都在面板点。** 不用调 API。
## 想要完整聊天网关(可选)
只想要记忆库可以跳过这步。想让 Aelios 当聊天转发网关:
1. Cloudflare → AI → AI Gateway → 建一个 gateway,复制地址。
2. 在 AI Gateway 的 Provider Keys 里加你的模型 API key。
3. 回 Worker → Variables and Secrets 加:
| 变量名 | 填什么 |
|---|---|
| `AI_GATEWAY_BASE_URL` | 刚复制的 Gateway Endpoint |
| `CF_AIG_TOKEN` | AI Gateway 调用 token |
保存重新部署。
> ⚠️ **用 OpenRouter 调 Claude,必须走「自定义 provider」加 key,不能用官方 provider 路径。**
> 官方 provider 路径会把请求按 Anthropic 原生格式发,和 OpenRouter 的 OpenAI 兼容格式打架,导致缓存失效、格式错乱。在 AI Gateway 里选 custom-providers 加 OpenRouter key,参考:`https://dash.cloudflare.com/?to=/:account/ai/ai-gateway/custom-providers`。
## 给 Claude Code / Codex 加记忆(可选)
环境变量加 `MEMORY_MCP_API_KEY`,然后在客户端的 MCP 配置里填:
```
URL: https://<你的 Worker 地址>/mcp?token=<MEMORY_MCP_API_KEY>
```
你的官方客户端就有跨设备随身记忆了。
## 看图模式(可选)
纯文本模型看不了图?加 `GUIDE_DOG_API_KEY`,客户端改成:
- **Base URL:** `https://<你的 Worker 地址>/v1/guide-dog`
- **API Key:** `GUIDE_DOG_API_KEY`
- **Model:** `companion`
导盲犬只转述图片,不写记忆、不存聊天。
## ⚠️ 注意 Workers AI 免费额度会被记忆压缩烧光
记忆召回链路里有个 **LLM 压缩模型**(默认 `llama-3.3-70b`,跑在 Cloudflare Workers AI 上)。每轮聊天只要召回到记忆,它就会把候选记忆压缩一遍——这是整条链路里调得最勤、最吃 token 的地方。
Cloudflare Workers AI 免费额度有限,**压缩模型是消耗大头**。额度一旦被它烧光,虽然 reranker 和 embedding 用量极小、本来根本花不完,但它们也跑在 Workers AI 上,会被一起连坐断供,整个记忆召回就哑火:记不进、也召不出。
用量小(每天几十轮聊天)没事,默认配置够用。**用量大只需换掉压缩模型一个**,把它指到你的 AI Gateway 走付费 key,不占 Workers AI 免费额度:
| 变量 | 换成什么 |
|---|---|
| `MEMORY_FILTER_MODEL` | 走 AI Gateway 的便宜 LLM(如 `deepseek/deepseek-v4-flash`) |
reranker 和 embedding 用量很小,留在 Workers AI 上就行,不用换。真要换 `EMBEDDING_MODEL` 注意维度会变(旧向量不兼容,需重建 Vectorize 索引,面板「更多 → 维护」里有工具)。
## 最容易踩的坑
- 部署命令必须是 `npm run deploy:cloudflare`,别的会覆盖变量、不建库。
- 重新部署变量不会丢(命令带 `--keep-vars`)。
- Vectorize 索引 `memo-kb`(768 维 cosine)别手动删。
- 看图会切到 `VISION_MODEL`,留意它的价格。
到这儿就够了,剩下的交给 AI。
---
# AI 版(维护交接)
> 给 Codex / Claude Code / Cursor / Gemini CLI。本节是端点、MCP、记忆管线的精确描述,用于维护和调试。
## 项目定位
Cloudflare Workers 上的 OpenAI-compatible Memory Proxy。帮用户部署时:**只关联用户自己的 fork**,Secrets / Variables 都在用户自己的 Cloudflare 账号,不要关联 wusaki0723/Aelios。
## 资源约定
| 资源 | 值 |
|---|---|
| Worker | `companion-memory-proxy` |
| D1 | `companion_memory_proxy` |
| Vectorize | `memo-kb`(768 维 cosine) |
| Queue | `companion-memory` |
| Embedding | `workers-ai/@cf/google/embeddinggemma-300m` |
| Dimensions | 768(覆盖 `EMBEDDING_MODEL` 时输出维度需匹配) |
记忆库默认走 **v2**(`MEMORY_LIFECYCLE_ENABLED` 隐式开启):D1 是本体,Vectorize 是镜像。兼容/回退开关默认隐藏。
## 三种模式边界
| 模式 | 入口 | 做什么 | 不做什么 |
|---|---|---|---|
| 完整版 | `POST /v1/chat/completions` | 认证、模型路由、记忆召回/注入、消息存 D1、Queue 维护、D1 清理、Claude cache | — |
| 纯记忆 MCP | `/mcp` | 暴露记忆工具 | 不代理聊天 |
| 导盲犬 | `POST /v1/guide-dog/chat/completions` | 转发 + 看图 | 不写/不读记忆、不存聊天 |
## 模型路由
```
model=companion → CHAT_MODEL
请求含 image → VISION_MODEL
anthropic/claude* → Anthropic native (/anthropic/v1/messages)
├─ 显式 cache_control 锚定稳定 system 前缀(persona_pinned / boot_stable / client_system 稳定段)
├─ 多断点策略:system 锚 + tail 锚 + 长 history 的 bridge 锚,≤4 个标记
├─ dynamic_memory_patch 后移到当前 user 块、不打 cache_control,绝不破坏缓存前缀
└─ rolling user cache 默认开,automatic cache 默认关
custom-provider/claude-* → Provider native (/custom-provider/messages)
其他 → OpenAI compat (/compat/chat/completions)
workers-ai/@cf/... → env.AI.run(不走 AI Gateway)
```
**缓存安全要点**:召回补丁(每轮都变)被从 system blocks 里剥离,作为无 `cache_control` 的文本块追加到当前 user turn 末尾,位于所有断点之后。历史轮的召回补丁已固化成稳定 history,落在 tail 断点之前,可正常命中缓存。`verify-cache-strategy.mjs` T14 校验断点数 ≤4。
**OpenRouter + Claude 路由约束**:OpenRouter 调 Claude 必须在 AI Gateway 里以 **custom-provider** 方式加 key,不能走官方 provider 路径。官方路径按 Anthropic 原生格式发请求,与 OpenRouter 的 OpenAI 兼容格式冲突,会破坏缓存和格式。模型名走 `custom-provider/claude-*` → Provider native 分支。
**Workers AI 额度风险**:召回链路里的压缩模型(`MEMORY_FILTER_MODEL`,默认 `llama-3.3-70b`)、reranker(`MEMORY_RERANKER_MODEL`)、embedding(`EMBEDDING_MODEL`)默认都跑在 Workers AI 上,共享同一份免费额度。**压缩模型每轮召回都调、是消耗大头**;reranker 和 embedding 用量极小,本来花不完,但额度被压缩模型烧光后会被一起连坐断供,导致记不进、召不出。用量大时只需把 `MEMORY_FILTER_MODEL` 指到 AI Gateway 走付费 key;reranker / embedding 留 Workers AI 即可。换 `EMBEDDING_MODEL` 会改维度,需重建 Vectorize 索引。
## REST 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/health` | 健康检查 |
| GET | `/admin` `/memory-admin` | 管理面板(HTML) |
| GET | `/v1/models` | 模型列表 |
| POST | `/v1/chat/completions` | 聊天网关(完整版) |
| POST | `/v1/guide-dog/chat/completions` | 导盲犬(无记忆) |
| GET / POST | `/mcp` `/memory-mcp` | MCP 端点 |
| GET / POST | `/v1/memories` `/v1/memory` | 记忆列表 / 新建(v2 必须带 `fact_key`,走 upsert) |
| GET / PATCH / DELETE | `/v1/memories/:id` `/v1/memory/:id` | 单条记忆操作 |
| POST | `/v1/search/memories` | 记忆搜索(召回用,可被压缩加工;`filter:false` 跳 reranker,`include_prompt:true` 拿可注入文本) |
| POST | `/v1/ingest/messages` `/v1/messages/ingest` | 写入原始聊天(v2 只落 raw,不触发旧抽取) |
| GET / PATCH | `/v1/memory_boot` | 冷启动包:digest + 昨日日志 + precious + glossary + longtail + 今日消息 + 统计;PATCH 写 L1 摘要 |
| GET / POST / DELETE | `/v1/precious` `/v1/precious/:id` | 珍贵记忆(只增不删的原文) |
| GET / POST / PATCH / DELETE | `/v1/glossary` `/v1/glossary/:id` | 黑话表(term + aliases + definition) |
| GET | `/v1/candidates` | 候选审核队列列表(`status` 默认 pending) |
| POST | `/v1/candidates/:id/approve` | 通过候选 → 落库 |
| POST | `/v1/candidates/:id/discard` | 丢弃候选 |
| POST | `/v1/candidates/:id/merge` | 合并到既有记忆(`target_id`) |
| POST | `/v1/candidates/:id/supersede` | 取代既有记忆(`target_id`) |
| GET / PUT / DELETE | `/v1/cache/:namespace/:key` | 缓存 CRUD |
| GET | `/api/memories/export` | 记忆导出 |
| GET | `/v1/debug/cache_health` | 缓存健康 |
| GET | `/v1/debug/vector_health` | 向量库健康 |
| POST | `/v1/debug/vector_reindex` | 向量重建 |
所有非 `/health` `/admin` `/v1/models` 端点都要 `Authorization: Bearer <CHATBOX_API_KEY>`,按 scope(`memory:read` / `memory:write`)鉴权。
## MCP 工具(`/mcp`)
v2 暴露 15 个工具。`memory_create` 已废弃,调用会报错要求改用 `memory_upsert`。
| 工具 | 作用 | 关键参数 / 备注 |
|---|---|---|
| `memory_search` | 向量搜索长期记忆 | `query`;`min_score`(0–1,默认 0.15) |
| `memory_list` | 列记忆 | `type` / `status` / `limit` / `cursor` |
| `memory_export` | 导出记忆 | 返回全量 |
| `memory_get` | 取单条 | `id` |
| `memory_delete` | 软删 | `id` |
| `memory_ingest` | 写入消息 + 触发维护 | v2 只落 raw |
| `memory_boot` | 拉冷启动包 | digest + 日志 + precious + glossary + longtail |
| `memory_recall` | 召回并返回可注入文本 | 用于 MCP 客户端自己拼上下文 |
| `memory_pin` | 写珍贵记忆 | 只增不删 |
| `glossary_set` | 写黑话术语 | term / aliases / definition |
| `memory_upsert` | v2 主写入(需 `fact_key`) | 撞键 → supersede / mark-seen |
| `memory_supersede` | 显式取代 | `old_id` + 新内容 |
| `memory_archive` | 归档 | `id` |
| `digest_get` | 读 L1 摘要 | — |
| `digest_set` | 写 L1 摘要 | `content`(截 500 字) |
## 记忆管线
**注入(聊天前):**
```
取最后一条 user 消息 → embedding → Vectorize 搜索 top K
→ 分数地板过滤噪音 → 去重 → reranker 重排 → 压缩模型精简
→ 作为 dynamic_memory_patch 追加到当前 user turn(不打 cache_control)
```
**抽取(每 4 小时 cron `0 */4 * * *`):**
```
按 4h 窗口读 D1 messages(首次无游标只处理当前窗口,不从 1970 回抽)
→ EXTRACT_MODEL 抽稳定事实,带 fact_key
→ confidence < EXTRACT_REVIEW_CONFIDENCE(0.76) 进候选审核队列
→ 有 fact_key:撞键且 embedding cosine ≥ DEDUP_COSINE(0.9) → mark-seen;否则 supersede
→ 无 fact_key:按同 type 的 Vectorize cosine 判重,命中 → mark-seen;否则新建
→ 游标推进;窗口抽完写 done 标记;model error 不推进,下个 cron 重试
```
**整理(每天 04:10 本地时区 cron `10 20 * * *`):**
```
scheduled dream:
├─ 不再首次抽取(已交给 4h extractor),memories_to_add 默认空
├─ 合并重复、替换过时、更新冲突(memories_to_update / memories_to_delete)
├─ 保留重要原文摘录
└─ 重写 L1 摘要(截 500 字)+ 昨日日志
```
**清理(后台 Queue,24h 节流):**
```
messages: 14 天删
usage_logs: 30 天删
memory_events: 30 天删
idempotency_keys: 7 天删
memories: 非 pinned/identity/persona 180 天标 expired → 同步删 Vectorize
hard delete: deleted/superseded/expired 超 30 天 → 先删 Vectorize 再删 D1
(Vectorize 失败不删 D1,避免失配)
```
## 环境变量速查
### 最小必填
| 变量 | 说明 |
|---|---|
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare Account ID |
| `CLOUDFLARE_API_TOKEN` | Cloudflare API Token(Secret) |
| `CHATBOX_API_KEY` | 客户端 + 面板访问密钥(Secret) |
### 完整网关加填
| 变量 | 说明 |
|---|---|
| `AI_GATEWAY_BASE_URL` | AI Gateway Endpoint |
| `CF_AIG_TOKEN` | AI Gateway 调用 token |
### 模型(都有默认)
| 变量 | 默认 | 说明 |
|---|---|---|
| `CHAT_MODEL` | `deepseek/deepseek-v4-pro` | 主聊天 |
| `EXTRACT_MODEL` | `deepseek/deepseek-v4-flash` | 4h 小批抽取 |
| `DREAM_MODEL` | `deepseek/deepseek-v4-pro` | 夜间整理 |
| `VISION_MODEL` | `google-ai-studio/gemini-3-flash-preview` | 看图 |
| `EMBEDDING_MODEL` | `workers-ai/@cf/google/embeddinggemma-300m` | 嵌入 |
| `EMBEDDING_DIMENSIONS` | `768` | 非 Workers AI embedding 目标维度 |
| `MEMORY_RERANKER_MODEL` | `workers-ai/@cf/baai/bge-reranker-base` | reranker |
| `ENABLE_MEMORY_RERANKER` | `true` | `false` 跳过 |
| `MEMORY_FILTER_MODEL` | `workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast` | 压缩 |
### 记忆抽取 / 整理
| 变量 | 默认 | 说明 |
|---|---|---|
| `EXTRACT_MAX_MESSAGES` | `40` | 每窗口最多处理消息数 |
| `EXTRACT_MAX_RUNS` | `4` | 每次 cron 最多连续批数 |
| `EXTRACT_MAX_TOKENS` | `1200` | 抽取模型输出上限 |
| `EXTRACT_REVIEW_CONFIDENCE` | `0.76` | 低于此值进候选队列 |
| `DEDUP_COSINE` | `0.9` | embedding 判重阈值 |
| `MEMORY_FILTER_MAX_CANDIDATES` | `12` | 进 reranker 候选上限 |
| `MEMORY_FILTER_MAX_OUTPUT` | `5` | 注入条数 |
| `MEMORY_FILTER_OUTPUT_CHARS` | `300` | 每条压缩后字数 |
| `MEMORY_FILTER_MAX_TOKENS` | `1400` | 压缩模型 JSON 上限 |
| `MEMORY_FILTER_MIN_SCORE` | `0.1` | 进 reranker 前地板(故意低) |
| `MEMORY_MIN_IMPORTANCE` | `0.55` | 写入最低重要性 |
| `DREAM_TIME_ZONE` | `Asia/Singapore` | 按此时区切自然日 |
| `DREAM_MAX_MESSAGES` | `40` | 每次 dream 最多消息数 |
| `DREAM_MAX_RUNS` | `10` | 每次 cron 最多 dream 批数 |
| `DREAM_MAX_TOKENS` | `8000` | dream 输出上限 |
| `DREAM_MEMORY_CONTEXT_LIMIT` | `40` | dream 参考旧记忆数 |
| `DREAM_EXCERPT_LIMIT` | `8` | 每天最多原文段落数 |
### Claude 缓存
| 变量 | 默认 | 说明 |
|---|---|---|
| `ANTHROPIC_CACHE_ENABLED` | `true` | prompt cache 开关 |
| `ANTHROPIC_CACHE_TTL` | `5m` | `5m` / `1h` |
| `ANTHROPIC_AUTO_CACHE_ENABLED` | `true` | 顶层 automatic cache |
| `ANTHROPIC_ROLLING_CACHE_ENABLED` | `true` | 滚动打点 |
| `ANTHROPIC_ROLLING_CACHE_WINDOW_SIZE` | `20` | 历史窗口 |
| `ANTHROPIC_CACHE_USER_ID` | 空 | 多客户端 cache 隔离用 `metadata.user_id` |
| `ANTHROPIC_THINKING_ENABLED` | `false` | 深度思考 |
| `ANTHROPIC_THINKING_BUDGET` | `1024` | 思考 token(1024–32000) |
| `FORCE_ANTHROPIC_NATIVE` | 空 | `true` 强制 Anthropic native |
| `CUSTOM_ANTHROPIC_MESSAGES_PATH` | `messages` | 原生 messages 路径 |
### 高级
| 变量 | 默认 | 说明 |
|---|---|---|
| `MEMORY_TOP_K` | `50` | 向量粗召回条数 |
| `MEMORY_MIN_SCORE` | `0.1` | 召回地板(故意低,精排交给 reranker) |
| `MEMORY_FILTER_MAX_CONTENT_CHARS` | `700` | 候选每条保留字数 |
| `VECTORIZE_INDEX_NAME` | `memo-kb` | Vectorize 索引名 |
| `ENABLE_AUTO_MEMORY` | 空(开启) | `false` 关自动记忆 |
| `EMPTY_MEMORY_MIN_CHARS` | `4` | 清短空记忆阈值 |
| `PUBLIC_MODEL_NAME` | `companion` | 客户端看到的模型名 |
| `IM_API_KEY` | 空 | 第二把钥匙(IM bot) |
| `DEBUG_API_KEY` | 空 | 调试接口钥匙 |
| `MEMORY_MCP_API_KEY` | 空 | 纯记忆 MCP 单独钥匙 |
| `GUIDE_DOG_API_KEY` | 空 | 导盲犬单独钥匙 |
## 本地开发与验证
```bash
npm install
npm run deploy:cloudflare # 建库 + 升级 + 部署
npm run worker:test # 主测试套件(177 项)
node scripts/verify-extract-pipeline.mjs # 4h 抽取管线行为测试
node scripts/verify-cache-strategy.mjs # Claude 缓存断点策略(15 项)
npx tsc --noEmit # 类型检查
```
改记忆 / 缓存 / 抽取相关代码后,至少跑后三个脚本。
## 记忆库清洗
旧 Vectorize 里长块、多主题块、重复总结多时,可让 LLM 先生成清洗计划:
```bash
AELIOS_BASE_URL="https://<worker>" \
AELIOS_API_KEY="<CHATBOX_API_KEY>" \
AI_GATEWAY_BASE_URL="<AI Gateway Endpoint>" \
CF_AIG_TOKEN="<AI Gateway Token>" \
CLEANUP_MODEL="deepseek/deepseek-v4-flash" \
npm run vectorize:clean:llm
```
## License
MIT
## 交流与反馈
有问题、想交流,欢迎来 QQ 群:**1091783659**
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器