内容
# 回忆
一个多用户个人知识库。向它添加笔记、片段和网址;通过混合语义 + 全文搜索(pgvector + Postgres FTS,结合互惠排名融合)按含义检索,而不仅仅是关键词。检索通过 **模型上下文协议(MCP)** 公开,以便具有MCP能力的LLM客户端(Claude Desktop、Claude Code、Cursor)可以在对话过程中查询知识库,借助一个托管服务器——无需每个客户端的集成代码,也无需本地数据库凭证。
**在线:**
| 界面 | 网址 |
|---|---|
| 首页 | [tryrecall.net](https://tryrecall.net) |
| 网页应用(使用GitHub/Google登录) | [app.tryrecall.net](https://app.tryrecall.net) |
| API + MCP (`/mcp`) | `https://api.tryrecall.net` (通过ALB上的ACM使用TLS) |
## 项目状态
**原始的8周路线图已经完成并上线,同时还有路线图后的阶段:TLS、多用户OAuth、营销首页和完整的应用程序重新设计。** 实现了端到端的功能:
- 笔记的CRUD(包括`PATCH`,可重新分段和重新嵌入),带有光标分页
- 基于块的Voyage嵌入 + 混合语义 + 关键词搜索,与RRF融合
- 网址摄入(`POST /ingest/url`:获取 + 主内容提取)和批量Markdown导入(`POST /ingest/markdown`,带有前言, 原子批次)
- **多用户身份验证**:通过Auth.js在Vercel BFF上进行OAuth登录(GitHub/Google),为每个请求生成一个短期的 **Ed25519(EdDSA)JWT** ;API验证它(算法固定,iss/aud检查),获取或创建`users`行,每个数据查询都以所有者为范围。服务令牌作为第二道外部门户。一个格式错误的签名密钥在启动时在BFF上失败,并带有命名原因(`web/instrumentation.ts`)。
- MCP服务器(`search_notes` / `add_note` / `get_related`)通过Streamable HTTP在`/mcp`上,**每个用户**:每个用户在网页应用中生成可撤销的**个人访问令牌**(存储为SHA-256哈希,一次显示明文),每个请求在运行任何工具之前解析其令牌为所有者用户。
- **CLI** (`recall`) 使用相同的PAT:`recall login`存储令牌(掩码提示,`~/.config/recall/config.toml`,`RECALL_API_TOKEN`/`RECALL_API_URL`环境变量覆盖),然后`add`、`add --url`、`search`、`list`、`import`、`delete`针对实时API——PAT是唯一的凭证,因此没有共享的秘密需要分发。
- 生产优化:所有者范围的Redis嵌入 + 查询缓存(失败打开;~200倍更快的暖搜索),Voyage上的tenacity重试,带有请求相关ID的结构化JSON日志,Redis支持的每IP速率限制。
- **部署在AWS**(us-east-2):一个ECS Fargate ARM64任务,背后带有ALB和 **ACM TLS**,位于`api.tryrecall.net`,RDS Postgres 16 + pgvector,Redis作为任务内的边车,Secrets Manager中的秘密,CloudWatch日志。GitHub Actions通过OIDC(无长期AWS密钥)在`main`推送时构建和滚动服务。
- **网页应用** (`web/`):Next.js(App Router)客户端,背后带有 **前端代理** —— 浏览器看不到服务令牌或签名密钥。重新设计了温暖的浅色/深色主题、自定义登录页面和环境“突触”背景,对笔记活动做出反应。**连接**页面通过引导式教程(自动打开一次,可通过“?”重新打开;MCP和CLI跟踪一个令牌;平台感知命令;键盘导航步骤)为非浏览器界面进行用户引导。部署到Vercel。
- **首页** (`landing/`):一个单独的静态Next.js应用,位于顶级域名,具有交互式产品演示;它自己的Vercel项目,因此营销部署永远不会破坏应用。
- **测试:** 99个单元测试 + 55个数据库支持的集成测试(testcontainers pgvector;包括跨用户隔离在REST上证明——JWT和PAT路径——以及MCP接口、身份获取或创建和缓存范围测试套件)。两者都阻止部署。
**已知问题 / 下一步** (详情见 [SPEC.md §10](SPEC.md#10-whats-next)):
1. **`/mcp`上的OAuth** —— Claude Desktop / claude.ai自定义连接器需要OAuth而不是静态的Bearer;令牌解析缝隙结构化,以便OAuth验证器是一个附加分支。
2. API已经实现的UI:笔记编辑(`PATCH`)、批量Markdown导入和显示`get_related`。
3. 每用户(非每IP)速率限制;重新排名(Voyage `rerank-2.5`);检索质量评估集;Terraform。
权威文档是 [SPEC.md](SPEC.md) (架构、数据模型、API、部署、后续步骤)和 [RESEARCH.md](RESEARCH.md) (每个依赖项、选择原因、击败原因)。本README是一个入口;深度内容在那里。
## 架构
```mermaid
graph TD
subgraph Clients
LLM["LLM客户端<br/>(Claude Desktop, Claude Code, Cursor)"]
Browser["浏览器"]
end
subgraph "Vercel"
Landing["首页<br/>(tryrecall.net)"]
BFF["网页应用 + BFF代理<br/>(app.tryrecall.net)<br/>OAuth via Auth.js, 生成EdDSA JWT"]
end
subgraph "回忆服务(一个Fargate任务,api.tryrecall.net)"
MCP["MCP服务器<br/>(Streamable HTTP, /mcp)"]
REST["REST API<br/>(FastAPI, /notes /search)"]
Core["核心服务层<br/>块 · 嵌入 · 混合搜索 · 持久化<br/>(每个查询所有者范围)"]
MCP --> Core
REST --> Core
end
subgraph "托管状态"
PG[("Postgres 16<br/>+ pgvector + tsvector<br/>(RDS)")]
Redis[("Redis<br/>所有者范围缓存,<br/>速率限制<br/>(任务内侧车)")]
end
Voyage["Voyage AI<br/>嵌入式API"]
Browser --> Landing
Browser -->|同源,会话cookie| BFF
Landing -.链接到.-> BFF
BFF -->|"用户JWT + 服务令牌(TLS)"| REST
LLM -->|"Streamable HTTP + 每用户PAT(TLS)"| MCP
Core --> PG
Core --> Redis
Core -->|缓存未命中| Voyage
```
查看 [SPEC.md §2](SPEC.md#2-system-architecture) 获取组件边界契约。
## 快速开始(本地)
```bash
cp .env.example .env
# 然后在.env中设置:
# VOYAGE_API_KEY=... (写入/搜索嵌入所需)
# AUTH_TOKEN=... (必需;REST数据端点在没有它的情况下关闭)
# RECALL_JWT_PUBLIC_KEY=... (PEM SPKI Ed25519 — 用户JWT验证密钥)
docker compose up --build
```
`http://localhost:8000`上的API。交互式文档(打开)位于`/docs`;`/health`是开放的。REST数据端点通过两种方式之一进行身份验证:BFF路径需要 **两个** 头部 —— `X-Recall-Service-Token: $AUTH_TOKEN` 加上 `Authorization: Bearer <用户JWT>` (由BFF生成 —— 参见 `web/README.md` 以在本地运行前端)—— 而 `Authorization: Bearer` 中的个人访问令牌是完整的身份验证(CLI发送的内容;指向这里并使用 `recall login --api-url http://localhost:8000`)。
## 将Claude连接到您的知识库(MCP)
托管的MCP端点是 `https://api.tryrecall.net/mcp` (Streamable HTTP),通过 **个人访问令牌** 进行身份验证:在 [app.tryrecall.net](https://app.tryrecall.net) 上登录,打开 **连接**,并生成一个(`recall_pat_…` — 显示一次,可随时撤销)。工具作为令牌的所有者操作,因此每个用户都使用自己的令牌连接。
**Claude Code:**
```bash
claude mcp add --transport http recall https://api.tryrecall.net/mcp \
--header "Authorization: Bearer <您的令牌>"
```
**Claude Desktop** (其连接器UI仅支持OAuth,因此使用`mcp-remote`桥接Bearer令牌;需要Node.js)—— 在`claude_desktop_config.json`(macOS:`~/Library/Application Support/Claude/`,Windows:`%APPDATA%\Claude\`),然后完全退出并重新打开Desktop:
```json
{
"mcpServers": {
"recall": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.tryrecall.net/mcp",
"--header", "Authorization:${AUTH_HEADER}"],
"env": { "AUTH_HEADER": "Bearer <您的令牌>" }
}
}
}
```
(`${AUTH_HEADER}`间接引用是`mcp-remote`的文档化变通方法,用于解决Cursor/Claude Desktop on Windows的错误,该错误会破坏参数中的空格 — 一个代码段,适用于所有平台。网页应用的**连接**页面一步一步地介绍了这一流程。)
然后询问Claude类似的问题,如“关于速率限制我保存了什么?”,“记住X”,或“与那条笔记相关的其他内容是什么?”。
## 从终端(CLI)使用
相同的令牌驱动CLI —— 没有单独的凭证,没有服务秘密:
```bash
uv tool install git+https://github.com/adityapall1/Recall # 安装`recall`
recall login # 在掩码提示符处粘贴您的令牌(以0600存储在~/.config/recall/config.toml中)
recall add "RRF通过1/(k+rank)融合排名列表;k=60是标准"
recall search "互惠排名融合"
recall add --url https://example.com/post # 获取 + 提取 + 嵌入页面
recall import ./notes/ # 批量Markdown(带有前言)
```
`RECALL_API_TOKEN` / `RECALL_API_URL`覆盖配置文件(在CI或针对本地API时很有用);`recall logout`会忘记存储的令牌。撤销的令牌会失败,并带有一行指向`recall login`的指针。
## 关键技术决策一览
- **FastAPI + async SQLAlchemy 2.0 + asyncpg** —— 异步原生端到端;FastAPI免费提供Pydantic验证和OpenAPI。
- **Postgres 16 + pgvector** —— 一个ACID数据库,用于用户、笔记、块、向量(HNSW)和全文(`tsvector` + GIN)。无需第二个矢量DB进行同步。
- **具有互惠排名融合的混合搜索** —— 密集(语义)和稀疏(词典)臂,通过RRF(`k=60`)融合;无需分数归一化,对任一臂在查询时失败具有鲁棒性。
- **互联网API上的双重身份验证** —— 对于浏览器路径,共享服务令牌(`X-Recall-Service-Token`)控制流量,身份验证是单独的缝隙:BFF生成每个请求的5分钟 **EdDSA JWT** ,API使用固定的算法(无`alg`混淆)和公共密钥验证(仅持有公共密钥)。受损的API无法生成令牌。对于MCP和CLI,每个用户的 **PAT** 是唯一的凭证(可撤销,每个用户 — 分发给每个用户的共享秘密将自取其辱),使用SHA-256哈希 — 故意不是bcrypt:256位随机秘密没有蛮力破解面,而快速哈希保持每个请求的查找免费。令牌管理本身仅限于Web会话,因此泄露的PAT无法生成在其撤销后仍然有效的替换令牌。
- **Voyage AI `voyage-3.5-lite` 嵌入** —— 1024维,查询/文档非对称,成本非常低;Anthropic推荐的嵌入提供商。
- **MCP通过Streamable HTTP** —— FastMCP作为ASGI子应用安装在FastAPI进程中的`/mcp`;一个托管端点,多个客户端(每个客户端都有自己的可撤销令牌),无需每个机器的MCP设置。
- **AWS Fargate + RDS + ALB/ACM,Redis作为任务内的边车** —— 一个Docker镜像,一个始终在线的ARM64任务(us-east-2);GitHub Actions通过OIDC部署。ElastiCache是文档化的扩展路径;边车节省了其每月固定成本,并且很好,因为每个缓存都会打开失败。
[RESEARCH.md](RESEARCH.md) 中包含每个选择的完整理由 —— 包括考虑和拒绝的内容。
## 文档
- **[SPEC.md](SPEC.md)** —— 愿景、架构、数据模型、API表面、搜索设计、非功能需求、部署拓扑、后续步骤。
- **[RESEARCH.md](RESEARCH.md)** —— 每个依赖项,它存在的原因,它击败了替代方案。文档面试问题由此而来。
- **[docs/multiuser-auth-plan.md](docs/multiuser-auth-plan.md)** —— 多用户身份验证设计(已实施;保留为理由记录)。
- **[docs/mcp-pat-plan.md](docs/mcp-pat-plan.md)** —— 每用户MCP访问令牌设计(已实施;保留为理由记录)。
- **[web/README.md](web/README.md)** —— 网页应用 + BFF代理。 **[landing/README.md](landing/README.md)** —— 首页。