内容
# \<OmniMem\>
开发工作在Codeberg(https://codeberg.org/ric_harvey/omnimem)进行 — 请在那里提交问题和拉取请求。
---
[](https://codeberg.org/ric_harvey/omnimem/actions)
[](https://codeberg.org/ric_harvey/omnimem/actions)
[](https://codeberg.org/ric_harvey/omnimem/actions)
> [!WARNING]
> **SSE 传输已弃用。** OmniMem 3.10 默认使用 SSE 但现在支持通过 `MCP_TRANSPORT=http` 的 Streamable HTTP。SSE 将在未来的版本中删除。要迁移:请在您的 `.env` 中设置 `MCP_TRANSPORT=http` 并更新您的客户端配置以使用 `"type": "http"` 和 URL `.../mcp` 而不是 `.../sse`。请参阅 [连接指南](guides/) 以获取更新的示例。
>
> Streamable HTTP 支持是社区贡献,来自 [@timstoop](https://codeberg.org/timstoop) — 谢谢!
**停止重复经历相同的会话。**
每个 Claude Code 会话都从零开始。没有项目记忆。没有记忆上周失败的内容。没有记忆您上周二花了三个小时发现为什么 `onnxruntime` 在 Alpine 上崩溃的原因,然后才找到有效的解决方法。
所以您再次解释该项目。Claude 建议使用相同的已损坏库。再一次相同的警报。相同的歌曲。您是 Bill Murray,而 Claude 是 Punxsutawney。
OmniMem 解决了这个问题。它是一个自我托管的 MCP 服务器,为 Claude Code 提供跨会话、项目和机器的持久记忆。它运行在您自己的硬件上,并且永久免费。
---
## 它能做什么
OmniMem 为 Claude Code 提供了四样东西,目前它还缺乏这些:
**情景记忆** 是您做出的决定,您修复的错误,您发现的模式。那些需要真正努力学习并且不应该每天早上重新学习的事情。
**项目上下文** 是您的技术栈,目标和当前状态。Claude 一开始就已经熟悉,而不是从头开始。
**被动知识** 来自您配置的 RSS 源。它们按计划获取,由 Claude Haiku 总结,嵌入并存储。当您正在处理 Rust 问题时,一周前摄入的相关文章会作为一个值得阅读的起点浮现。
**偏好** 是关于您如何工作的规定性规则 - “每次功能上线后总是更新 README 和 CHANGELOG”,“更喜欢没有尾部摘要的简洁响应”。这些规则通过新的事实提取摄入模式自动从您的对话中提取,并在适用时显示。
所有四个命名空间在回忆时一起被搜索。顶部的结果可能是一个来自不同项目、昨天或星期二晚上摄入到您的知识库的文章的决定。它不重要它来自哪里,只要它有用。
---
## 其他记忆系统没有的功能
### 记忆不是二进制的
大多数系统会记住或忘记。OmniMem 有一个生命周期:
```
ACTIVE -> DEPRIORITISED -> ARCHIVED -> DELETED
1.0x 0.2x 0.0x 已删除
```
当您说“忘记 X”时,您通常的意思不是销毁它。您的意思是停止将其浮现。OmniMem 降低优先级而不是删除,在回忆时应用表面分数乘数。如果某些东西再次变得相关,它可以重新获得其地位。
您还可以抑制整个主题。调用 `suppress_topic("pisource.org")` 意味着任何涉及该主题的内容都不会在任何回忆中浮现,跨任意会话,直到您解除抑制。
### 墓地
OmniMem 跟踪不仅跟踪有效的方法,还跟踪无效的方法以及原因。
每个被放弃的方法都记录其名称,类型和失败原因。在 Claude 建议库或架构模式之前,首先会检查墓地。如果您尝试过某事并放弃了,它会在结果顶部显示警告。
```
警告:以前的放弃方法与此查询匹配
onnxruntime 库 Alpine musl libc 上的 SIGILL 崩溃 努力程度:4/5
FLAT index 方法 向量超过 10k 时太慢 努力程度:3/5
openai embeddings service API 成本和延迟过高 努力程度:2/5
```
死胡同不会有第二次机会浪费您的下午。
### 经验评分
并非所有成功的记忆都是一样的。有些事情第一次就成功了很有用。有些事情需要四次尝试,放弃两个库和一个奇怪的 Alpine 特定解决方法才成功,是宝贵的经验,并且应该更 readily 地浮现。
OmniMem 为每个记忆根据努力和结果分配一个经验权重:
| 努力程度 | 含义 | 回忆权重 |
|---|---|---|
| 1 | 第一次就成功 | 1.0x |
| 2 | 轻微摩擦 | 1.1x |
| 3 | 多次迭代 | 1.25x |
| 4 | 显著挣扎 | 1.5x |
| 5 | 久经沙场 | 1.8x |
回忆分数公式:
```
分数 = 相似度 x 表面分数 x 新鲜度 x 经验权重
```
一个评分为 5 的成功在回忆排名中几乎是 trivial 成功的重要性的两倍。知识赢得其排名。
### 语义重复数据删除
随着时间的推移,记忆系统会积累近乎相同的条目。OmniMem 在两个点上捕获这一点。
在写入时,`remember()` 嵌入新内容并检查是否存在具有超过余弦相似度阈值(默认为 0.92,可通过 `DEDUP_SIMILARITY_THRESHOLD` 配置)的现有记忆。如果已经存在几乎相同的记忆,它返回重复的记忆而不是存储冗余副本。传递 `force=True` 时,您可以真正想要两个版本。
对于批量清理,`find_duplicates()` 扫描整个命名空间,批量嵌入所有内容,计算成对相似度,并返回通过 union-find 分组的重复项簇。将它指向您的情景命名空间,每月一次,并归档额外的项。
### 矛盾检测
墓地提醒您注意失败的事情。矛盾检测提醒您注意相互矛盾的事情。
当 `remember()` 存储新记忆时,它会运行快速启发式检查 - 查找语义相似的记忆并扫描否定模式不匹配(例如,一个说“使用 X”,而另一个说“避免 X”)。如果检测到潜在矛盾,它会存储记忆但返回警告,以便您进行调查。
对于更深入的分析,`check_contradictions()` 可以可选地调用 Claude Haiku(第 2 级)来评估候选对。确认的矛盾会在两个记忆上相互链接,并在其中一个浮现时标记。
```
contradiction_warning:
existing_key: mem:episodic:01ARZ3NDEK...
existing_content: "Always use connection pooling for Valkey..."
explanation: "These memories discuss the same topic but contain opposing language"
```
### 会话简介
在会话开始时,不需要进行三次单独的调用,单个 `briefing(project="myproject")` 返回 Claude 需要的所有信息,以便快速进入状态:
- **项目上下文** - 当前状态,技术栈,最后更新
- **经验总结** - 努力统计,墓地,突破
- **过期记忆** - 活跃的记忆,30+ 天未更新(可通过 `STALE_MEMORY_DAYS` 配置)
- **新知识** - 最近 7 天摄入的 RSS 文章
- **矛盾警告** - 具有未解决矛盾的记忆
- **重新审视候选** - 优先级降低的记忆,其重新审视提示与当前工作匹配
- **已抑制主题** - 当前过滤掉的内容
一次调用,一个响应,完整的上下文。
### 自动维护
记忆系统会随着时间的推移积累重复和矛盾。OmniMem 自动处理这些。
每 N 次 `briefing()` 调用(默认为 10,可通过 `AUTO_MAINTENANCE_INTERVAL` 配置),服务器会运行一次维护:
1. **重复数据删除扫描** - 查找近乎相同的情景记忆的簇,并归档每个簇中最老的记录,同时保留最新的记录
2. **矛盾扫描** - 检查语义相似的活跃项目记忆的否定模式不匹配(需要余弦相似度 >= 0.5,然后限制在 10 个结果)
3. **知识过期** - 存档 RSS 摄入的知识文章,这些文章已经过了其 `expires_at` 时间戳(默认为摄入后 30 天,可通过 `MAX_KNOWLEDGE_AGE_DAYS` 配置)。手动存储的知识项永远不会受到影响
结果在简介响应中出现在 `auto_maintenance` 下,这样您就知道清理了什么。设置 `AUTO_MAINTENANCE_INTERVAL=0` 以禁用。手动 `find_duplicates()` 和 `check_contradictions()` 调用仍然像以前一样工作。
---
## 自主托管,开源,您的
没有 SaaS。没有供应商锁定。没有上下文发送到别人的服务器。
- **Valkey** 是 Redis 的开源分叉。您的数据都存储在您自己的机器上的命名 Docker 卷中。
- **多架构 Docker 镜像** 支持 amd64 和 arm64。它在 Raspberry Pi、AWS Graviton 或 Apple Silicon 上运行得和 x86 一样好。
- **sentence-transformers** 在本地运行嵌入,无需 API 调用。
- **MIT 许可** 意味着您可以随意分叉、扩展、运行它。
- **一个备份命令** 调用 `dump_to_file()` 并将所有内容导出到您拥有的 JSON 文件中。
通过 Traefik 公开 MCP 端口,您工作的每个机器都共享相同的记忆。一次部署,无处不在。
---
## 快速开始
```bash
git clone https://codeberg.org/ric_harvey/omnimem.git && cd omnimem
cp .env.example .env
# 在 .env 中设置 VALKEY_PASSWORD 和 ANTHROPIC_API_KEY
docker compose up -d
```
编辑 `.env` 文件,至少设置 `VALKEY_PASSWORD` 为一个安全值。您也可以设置 `ANTHROPIC_API_KEY`,如果您想要 AI 驱动的 RSS 文章摘要和更丰富的矛盾检测。如果您不设置 `ANTHROPIC_API_KEY`(或留空),OmniMem 仍然有效 - RSS worker 将退回到简单的截断摘要,矛盾检查将仅使用嵌入相似度。
四个容器启动:带有向量搜索的 Valkey,OmniMem MCP 服务器,RSS worker 和 Web UI。MCP 服务器默认监听端口 `8765`,Web UI 监听端口 `8080`。
在浏览器中打开 `http://localhost:8080` 以访问管理仪表板 - 浏览记忆,运行语义搜索,管理项目,跟踪经验,并处理备份,无需使用 MCP 工具调用。
将您的编码代理连接到 OmniMem。下面是 Claude Code 的示例 - 请参阅其他工具的完整指南:
| 代理 | 指南 | 传输 |
|-------|-------|-----------|
| claude.ai | [guides/claude-ai.md](guides/claude-ai.md) | Streamable HTTP + OAuth 2.1 |
| Open Design | [guides/open-design.md](guides/open-design.md) | Streamable HTTP + OAuth 2.1 (public/PKCE) |
| Claude Code | [guides/claude-code.md](guides/claude-code.md) | SSE(默认)/ Streamable HTTP |
| Claude Desktop | [guides/claude-desktop.md](guides/claude-desktop.md) | SSE / Streamable HTTP(via mcp-remote) |
| GitHub Copilot | [guides/github-copilot.md](guides/github-copilot.md) | SSE(默认)/ Streamable HTTP |
| GitLab Duo | [guides/gitlab-duo.md](guides/gitlab-duo.md) | SSE(默认)/ Streamable HTTP |
| Cursor | [guides/cursor.md](guides/cursor.md) | SSE(默认)/ Streamable HTTP |
| AWS Kiro | [guides/kiro.md](guides/kiro.md) | SSE(默认)/ Streamable HTTP |
| OpenCode | [guides/opencode.md](guides/opencode.md) | SSE(默认)/ Streamable HTTP |
| OpenAI Codex CLI | [guides/codex.md](guides/codex.md) | SSE(默认)/ Streamable HTTP |
**Claude Code** (`~/.claude.json`):
```json
{
"mcpServers": {
"omnimem": {
"type": "sse",
"url": "http://localhost:8765/sse"
}
}
}
```
如果您在 `.env` 中设置了 `MCP_AUTH_TOKEN`,请将令牌添加到配置中:
```json
{
"mcpServers": {
"omnimem": {
"type": "sse",
"url": "http://localhost:8765/sse",
"headers": {
"Authorization": "Bearer your-token-here"
}
}
}
}
```
为了防止 Claude Code 每次调用 OmniMem 工具时都要求许可,请在您的全局设置(`~/.claude/settings.json`)中添加通配符允许规则:
```json
{
"permissions": {
"allow": [
"mcp__omnimem__*"
]
}
}
```
这允许所有 OmniMem MCP 工具(`remember`、`recall`、`briefing` 等)在所有项目中无需提示运行。如果您已经有其他条目在 `allow` 数组中,请将 `"mcp__omnimem__*"` 添加到它。
就是这样。服务器会自动通过 MCP 协议的 `instructions` 字段向任何连接的代理交付其使用指南。Claude Code 将在会话开始时加载项目上下文,在建议方法之前检查墓地,并存储您边走边学的知识 - 不需要手动配置文件。
如果您想自定义说明或在不支持 MCP 说明的设置中使用 OmniMem,请复制指南至 `claude_config/CLAUDE.md` 进行手动使用。
---
## MCP 工具
### 核心记忆
| 工具 | 它做什么 |
|---|---|
| `remember(content, project?, tags?, force?, mode?)` | 存储记忆。在 `full` 模式(默认)下,通过 Claude Haiku 提取原子事实,并将偏好路由到偏好命名空间;在 `raw` 模式下,原样存储。自动检查重复和矛盾 |
| `remember_document(content, chunk_strategy, project?, tags?, namespace?, chunk_size?, mode?)` | 通过将长文档分成块(`turn_pairs`、`sentences`、`paragraphs` 或 `fixed_tokens`)并存储每个记忆链接到共享的 `doc_id` 来索引长文档 |
| `recall(query, top_k?, project_filter?, expand_queries?)` | 跨所有命名空间的语义搜索。启用 `expand_queries=true` 时,通过 Claude Haiku 生成替代措辞,并合并结果以提高召回率,当查询词汇与存储内容不匹配时 |
| `deprioritise(key_or_query, reason, reinstate_hints?)` | 软抑制而非删除 |
| `archive(key_or_query)` | 从回忆中删除但保留历史记录 |
| `reinstate(key_or_query)` | 重新审视优先级降低的记忆 |
| `forget(key_or_query, confirm=True)` | 硬删除,需要显式确认 |
| `suppress_topic(topic)` | 从所有未来的回忆中过滤主题 |
| `unsuppress_topic(topic)` | 从抑制列表中删除主题 |
| `list_suppressions()` | 显示当前抑制的所有主题 |
| `find_duplicates(namespace?, threshold?, project_filter?)` | 扫描近乎相同的记忆的集群 |
| `check_contradictions(query?, namespace?, use_api?)` | 检测相互矛盾的记忆 |
| `briefing(project?, include_knowledge?)` | 单次调用会话启动,提供完整上下文 |
### 项目上下文
| 工具 | 它做什么 |
|---|---|
| `set_project_context(name, description, stack, goals, current_state)` | 创建或更新项目记忆 |
| `get_project_context(name)` | 检索它,在每次会话开始时调用 |
| `update_project_state(name, current_state, notes?)` | 更新状态无需重新嵌入 |
| `compile_project_context(name, auto_save?)` | 自动生成或刷新项目上下文,从其情景记忆、标签、经验数据和废弃方法中 |
| `list_projects()` | 查看所有存储的项目 |
| `delete_project(name, confirm?, include_context?)` | 通过直接键扫描批量删除属于项目的每个记忆(无语义搜索,因此不会错过任何内容)。默认预览;`confirm=True` 以流水线批量删除;`include_context=True` 也会删除项目上下文条目 |
### 经验评分
| 工具 | 它做什么 |
|---|---|
| `record_experience(key, effort_score, outcome, abandoned_approaches?, breakthrough?, gotchas?)` | 记录难度和失败的内容 |
| `log_abandoned(key, name, type, reason)` | 增量添加死胡同 |
| `warn_if_abandoned(query)` | 调用前检查墓地 |
| `experience_summary(project?)` | 墓地、突破和努力统计 |
| `get_experience(key)` | 获取单个记忆的完整经验数据 |
### 知识
| 工具 | 它做什么 |
|---|---|
| `recent_knowledge(days?, feed_name?, topics?, limit?)` | 查询最近的 RSS 文章,可选过滤器,按最新顺序排序 |
| `promote_knowledge(key)` | 通过清除过期清除标记为永久有用 |
### 审计和备份
| 工具 | 它做什么 |
|---|---|
| `memory_audit(project?, namespace?, limit?, offset?)` | 所有记忆按状态;完整状态计数摘要和分页 `entries` 列表(默认 100,最多 500) |
| `explain_memory(key)` | 单个记忆的完整历史记录 |
| `why_did_you_mention(query)` | 调试为什么某些内容被提及 |
| `dump_to_file(filename?)` | 导出所有内容到时间戳 JSON 文件 |
| `restore_from_file(filename, dry_run?)` | 从备份恢复,合并而不是覆盖,重新嵌入以立即回忆 |
| `list_backups()` | 查看可用备份文件 |
| `health()` | 服务器、Valkey、索引和模型状态 |
| `queue_status()` | 丰富深度分析 |
| `reindex(namespace?)` | 丢弃并重新创建 Valkey 搜索索引以清除孤立的向量条目。数据安全 |
| `version()` | 返回当前的 OmniMem 版本 |
---
## 配置
| 变量 | 默认值 | 描述 |
|---|---|---|
| `VALKEY_PASSWORD` | `changeme` | 请更改此密码 |
| `VALKEY_HOST` | `valkey` | Valkey 服务器的主机名(Compose 服务名称;仅在指向外部 Valkey 时更改) |
| `VALKEY_PORT` | `6379` | Valkey 服务器的端口 |
| `ANTHROPIC_API_KEY` | 需要 | 用于通过 Claude Haiku 进行 RSS 摘要 |
| `MCP_AUTH_TOKEN` | *(未设置)* | 设置以启用 MCP 端点的 bearer token 身份验证(恒定时间比较)。当未设置时,不需要身份验证 — 但服务器拒绝在非环回 `MCP_HOST` 上启动未经身份验证 |
| `WEB_UI_AUTH_TOKEN` | *(未设置)* | 设置以启用 Web 仪表盘上的 bearer token 身份验证(恒定时间比较)。`/metrics` 和静态资产豁免 |
| `OAUTH_ENABLED` | *(未设置)* | 设置为 `true` 以启用 claude.ai 和其他 OAuth MCP 客户端的 OAuth 2.1 授权服务器 |
| `OAUTH_BASE_URL` | *(未设置)* | OmniMem 实例的可外部访问的 URL(例如 `https://mcp.example.com`)。在启用 OAuth 时需要 |
| `OAUTH_ADMIN_USER` | *(未设置)* | OAuth 管理帐户的用户名。在启用 OAuth 时需要 |
| `OAUTH_ADMIN_PASSWORD` | *(未设置)* | OAuth 管理帐户的密码。在启用 OAuth 时需要 |
| `OAUTH_REFRESH_MAX_DAYS` | `30` | OAuth 刷新令牌链的绝对生命周期。每个轮换都会静默地重新颁发令牌,而不会提示用户,直到达到此上限。硬上限为 `90` |
| `OAUTH_REFRESH_GRACE_SECONDS` | `120` | 刷新令牌轮换后的宽限期。旧令牌在此期间仍然有效,重复返回相同的令牌对,因此 claude.ai 不会在多个连接刷新时注销。限制为 `3600`;设置为 `0` 以进行严格的单次轮换 |
| `MCP_ALLOWED_HOSTS` | *(未设置)* | 除 localhost 和 `OAUTH_BASE_URL` 主机外,`Host` 头中允许的额外主机名。在反向代理或隧道下提供服务时需要,否则 FastMCP 3.x 返回 `421 Misdirected Request`。参见 [故障排除:421 / 403 behind a proxy](#getting-421-misdirected-request-or-403-forbidden-origin-behind-a-proxy) |
| `MCP_ALLOWED_ORIGINS` | *(未设置)* | 除 `OAUTH_BASE_URL` 源外,登录页面信任的额外浏览器源。在代理终止 TLS 并通过 http 转发时需要,否则浏览器登录 POST 获取 `403 Forbidden Origin`。参见 [故障排除](#getting-421-misdirected-request-or-403-forbidden-origin-behind-a-proxy) |
| `OAUTH_LOGIN_MAX_ATTEMPTS` | `10` | 每个客户端 IP 地址的失败管理员登录次数,在临时阻止登录表单之前。设置为 `0` 以禁用 |
| `OAUTH_LOGIN_WINDOW_SECONDS` | `900` | 滑动窗口,用于限制失败登录次数 |
| `RSS_MAX_PAGE_BYTES` | `10485760` | RSS 工作线程在获取完整文章页面时读取的最大字节数(10 MB),以防止恶意或无尽的响应 |
| `ABANDONED_CACHE_TTL_SECONDS` | `60` | 回忆管道缓存解析的废弃方法列表的时间长度,在此期间会进行周期性扫描。在同一过程中,自动缓存失效;设置为 `0` 以在每次回忆时进行扫描 |
| `DASHBOARD_STATS_TTL` | `60` | Web 仪表盘缓存仪表盘统计信息(命名空间计数 + 最近列表)的时间长度,以便页面不会在每次加载时扫描键空间。页面显示统计信息计算的时间,并提供刷新链接;设置为 `0` 以在每次加载时重新计算 |
| `MCP_PORT` | `8765` | MCP 服务器监听的端口 |
| `MCP_HOST` | `127.0.0.1` | MCP 服务器的绑定地址(在 Docker 内设置为 `0.0.0.0`) |
| `MCP_TRANSPORT` | `sse` | MCP 传输:`http`(Streamable HTTP,推荐)或 `sse`(已弃用,默认,将在未来版本中删除) |
| `VALKEY_MAX_CONNECTIONS` | `20` | MCP 服务器和 Web 仪表盘的 Valkey 连接池大小(RSS 工作线程默认为 `50`) |
| `VALKEY_RAW_MAX_CONNECTIONS` | `4` | 第二个二进制安全的 Valkey 客户端的池大小,用于读取存储的向量(重复,维护,矛盾检查) |
| `OAUTH_VALKEY_MAX_CONNECTIONS` | `5` | OAuth 令牌存储的专用 Valkey 客户端的池大小 |
| `EMBEDDING_MODEL` | `all-MiniLM-L6-v2` | 本地句子转换器模型 |
| `RSS_SCHEDULE_HOURS` | `6` | 馈送被摄取的频率 |
| `RSS_MAX_ARTICLES_PER_FEED` | `20` | 每个馈送每个周期的文章数量 |
| `RSS_MAX_DIGEST_ENTRIES` | `2` | 以 `mode: digest` 设置的馈送每个周期的摄取条目数 |
| `FEEDS_CONFIG_PATH` | `/app/feeds.yml` | RSS 工作线程和 Web 仪表盘容器内 feeds.yml 的路径 |
| `FEEDS_WATCH_INTERVAL` | `10` | feeds.yml 的 mtime 轮询间隔,用于检测更改(inotify 在 Docker 绑定挂载上不起作用) |
| `MEMORY_RECALL_TOP_K` | `5` | 默认的回忆结果数量 |
| `DEPRIORITISED_WEIGHT` | `0.2` | 降级记忆的表面评分 |
| `RECENCY_DECAY_DAYS` | `90` | 年龄惩罚开始生效的天数 |
| `INGEST_MODE` | `full` | `full` 以原始内容存储,然后在后台通过 Claude Haiku 提取原子事实 — 事实作为补充信息存储在知识命名空间中(首选项存储在首选项命名空间中),并继承其时间戳,以便时间回忆正常工作;`raw` 只存储原始内容。当未设置 API 密钥时,自动回退到原始模式 |
| `FACT_EXTRACTION_MODEL` | `claude-haiku-4-5-20251001` | 用于 `full` 摄取模式下的后台事实提取的 Claude 模型 |
| `RECALL_EXPAND_QUERIES` | `false` | 在 `recall()` 上全局启用查询扩展。通过 Claude Haiku 生成替代短语,并联合结果 |
| `RECALL_EXPAND_COUNT` | `3` | 启用扩展时生成的变体查询数量 |
| `QUERY_EXPANSION_MODEL` | `claude-haiku-4-5-20251001` | 用于生成查询扩展变体的 Claude 模型 |
| `ENRICHMENT_BATCH_MODE` | `false` | 当 `true` 时,`remember_document()` 发送所有块作为单个增强作业,以便后台工作线程进行一次 Haiku API 调用,而不是 N 次调用。对于大型文档和基准测试运行,速度更快 |
| `DEDUP_SIMILARITY_THRESHOLD` | `0.92` | `remember()` 时用于重复检测的余弦相似性阈值 |
| `CONTRADICTION_SIMILARITY_THRESHOLD` | `0.7` | 矛盾候选搜索的相似性阈值 |
| `STALE_MEMORY_DAYS` | `30` | 没有更新时,记忆被标记为过时的天数 |
| `AUTO_MAINTENANCE_INTERVAL` | `10` | 每个项目在自动维护运行之前调用 `briefing()` 的次数(禁用时为 0) |
| `MAX_KNOWLEDGE_AGE_DAYS` | `30` | RSS 摄取的知识文章过期并在维护期间自动存档的天数 |
| `METRICS_CACHE_TTL` | `60` | `/metrics` 端点结果在 Prometheus 抓取之间的缓存秒数 |
| `TELEMETRY_COLD_DAYS` | `60` | 没有回忆时,记忆被标记为“冷”的天数 |
| `OMNIMEM_INSTRUCTIONS_CHARS` | `5600` | 仪表盘页面 token-overhead 校准的字符数:MCP 指令文本 |
| `OMNIMEM_TOOL_SCHEMAS_CHARS` | `5835` | 仪表盘页面 token-overhead 校准的总字符数:工具模式 |
| `WEB_PORT` | `8080` | Web 仪表盘监听的端口 |
| `BACKUP_DIR` | `/app/backups` | 备份文件写入的位置(MCP 服务器和 Web 仪表盘共享) |
---
## RSS 配置
编辑 `rss_worker/feeds.yml` 以选择要摄取的馈送:
```yaml
feeds:
- url: https://blog.rust-lang.org/feed.xml
name: Rust 官方博客
topics: [rust, systems, language]
- url: https://this-week-in-rust.org/rss.xml
name: This Week in Rust
topics: [rust, community, crates]
- url: https://blog.n8n.io/rss/
name: n8n 博客
topics: [automation, workflow, n8n]
```
每个文章都会被抓取,剥离 HTML,摘要为几句话,然后通过 Claude Haiku 嵌入,并存储在具有 `expires_at` 时间戳的 `knowledge` 命名空间中(默认为 30 天,可通过 `MAX_KNOWLEDGE_AGE_DAYS` 配置)。过期文章在维护期间自动存档。如果一篇文章被证明是有用的,请调用 `promote_knowledge(key)` 以清除其过期并永久保留。通过 URL 跳过重复项。工作线程在启动时运行一次,然后按照您在 `RSS_SCHEDULE_HOURS` 中设置的计划运行。
---
## 记忆生命周期
```
ACTIVE (1.0x) -> DEPRIORITISED (0.2x) -> ARCHIVED (0.0x)
| | |
+--------------------+------------------------+
|
DELETED
```
使用 `deprioritise` 当某些内容应该停止浮现但可能再次需要时。添加 `reinstate_hints` 以描述应该将其带回的内容。如果未来的查询与提示强烈匹配,则记忆会重新浮现,并附有说明为什么最初将其降级。
使用 `archive` 用于确定过时但具有历史价值的内容。
使用 `forget` 仅当您希望某些内容永久消失时才使用。它需要 `confirm=True`,因此不会意外消失。
有一件事值得注意:如果您使用 `effort_score >= 4` 降级记忆,则系统将在允许您继续之前标记它。 它不是阻止您,只是确保您打算软抑制一些确实很难理解的东西。
---
## 从多台机器使用
通过反向代理公开 `MCP_PORT`。 Traefik 示例:
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.omnimem.rule=Host(`omnimem.yourdomain.com`)"
- "traefik.http.routers.omnimem.tls.certresolver=letsencrypt"
- "traefik.http.services.omnimem.loadbalancer.server.port=8765"
```
将 MCP 配置 URL 更新为 `https://omnimem.yourdomain.com/sse`(或 `.../mcp` 如果使用 Streamable HTTP),并且您工作的每台机器都共享相同的记忆、相同的墓地和相同的项目上下文。有关如何配置每个编码代理的信息,请参阅[连接指南](guides/)。
您也可以通过相同的方式公开 Web 仪表盘 — 为 `WEB_PORT` 添加一个具有基本身份验证中间件的路由。有关 Traefik 和 Caddy 示例,请参阅 `docs/reverse-proxy.md`。
安全检查清单:强 `VALKEY_PASSWORD`(Compose 现在拒绝启动如果它为空),在您的 `.env` 中设置 `MCP_AUTH_TOKEN` 和 `WEB_UI_AUTH_TOKEN`,公开时使用 TLS,如果公开,请将 Valkey 端口关闭在公共互联网上。承载令牌在恒定时间内进行比较,MCP 服务器不会在非环回地址上启动未经身份验证,备份文件名会验证路径遍历,并且上传/恢复的备份和抓取的 RSS 页面都会限制大小。
### 用于 claude.ai 的 OAuth 2.1
如果您想从 **claude.ai** (或使用 OAuth 的任何 MCP 客户端)连接,OmniMem 有可选的内置 OAuth 2.1 支持。在您的 `.env` 中启用它:
```bash
OAUTH_ENABLED=true
OAUTH_BASE_URL=https://mcp.yourdomain.com # 可外部访问的 URL
OAUTH_ADMIN_USER=admin
OAUTH_ADMIN_PASSWORD=a-strong-password-here
```
启用后,OmniMem 充当具有以下功能的完整 OAuth 2.1 授权服务器:
- **发现** (`/.well-known/oauth-authorization-server`) — 客户端自动发现
- **动态客户端注册** (`/register`) — 客户端自动注册(RFC 7591)
- **授权码流程 + PKCE** (`/authorize` + `/oauth/login`) — 基于浏览器的登录
- **令牌交换和刷新** (`/token`) — 1 小时访问令牌,30 天刷新令牌并旋转
将 claude.ai 指向您的 OmniMem URL,它处理其余部分 — 发现、注册、浏览器登录和令牌管理都自动发生。
**保持登录状态。** 刷新令牌在每次使用时都会轮换,但旧令牌不会立即丢弃 — 它在一段宽限期内(`OAUTH_REFRESH_GRACE_SECONDS`,默认为 120 秒)仍然有效,并且在此期间重复返回相同的令牌对。 claude.ai 保持多个连接打开,可以从多个连接刷新相同的令牌;如果没有宽限期,所有但第一个刷新都会失败 `invalid_grant`,并且 claude.ai 将断开连接并提示您再次登录。令牌状态也会持久化到 Valkey 并启用 AOF,因此 `docker compose restart` 不会丢失会话。如果您仍然早于 `OAUTH_REFRESH_MAX_DAYS` 注销,那是查找的地方。 登录表单按 IP 地址限制速率(`OAUTH_LOGIN_MAX_ATTEMPTS` / `OAUTH_LOGIN_WINDOW_SECONDS`)以阻止对管理员密码的暴力攻击。
OAuth 与承载令牌身份验证配合使用。如果您同时设置 `OAUTH_ENABLED` 和 `MCP_AUTH_TOKEN`,则两种身份验证方法都通过 `MultiAuth` 接受。本地 Claude Code 实例可以继续使用承载令牌,而 claude.ai 使用 OAuth。
#### 在反向代理后面获取 `421 Misdirected Request` 或 `403 Forbidden Origin`
如果通过反向代理或隧道连接突然停止工作,这是 FastMCP 3.x 的 Host/Origin 保护程序,不是 OAuth 问题 — 看起来像 OAuth 问题。 有两个症状:
- **`421 Misdirected Request`** 在所有内容上(例如 `curl https://your-host/mcp` 返回 `421`,以前返回 `401`)。 FastMCP 拒绝任何 `Host` 头部不在其允许列表中的请求,默认情况下仅限于 localhost 加上绑定地址。您的公共主机名不在其中,因此每个请求都会返回 421,包括 claude.ai 首先探测的 `/.well-known/*` 发现端点。本地 `curl` 到 `localhost` 仍然有效,这使得很容易误读为 OAuth 故障。
- **`403 Forbidden Origin`** 提交登录表单时。大多数代理终止 TLS 并转发到纯 http,因此服务器看到请求为 `http://`,而浏览器发送 `Origin: https://your-host`。 FastMCP 将此方案不匹配视为不受信任的源并阻止登录 POST。
服务器自动允许 `OAUTH_BASE_URL`(和 `MCP_PUBLIC_URL`)主机名 **和** 其 https 源,因此如果正确配置 OAuth,通常只需工作。如果您在其他主机名或源下提供服务,请添加它们:
```bash
MCP_ALLOWED_HOSTS=mcp.example.com,alt.example.com
MCP_ALLOWED_ORIGINS=https://mcp.example.com```
您也可以直接设置 FastMCP 的原始旋钮,但它们是 JSON 数组 — 纯字符串不会解析:
```bash
FASTMCP_HTTP_ALLOWED_HOSTS=["mcp.example.com"]
FASTMCP_HTTP_ALLOWED_ORIGINS=["https://mcp.example.com"]```
快速检查: `curl -s -o /dev/null -w '%{http_code}\n' https://your-host/.well-known/oauth-authorization-server` 应该返回 `200`。 `421` 表示主机名不在允许列表中;`403` 表示登录时源不正确。
---
## Web 仪表盘
OmniMem 包括一个基于浏览器的管理界面,位于 `http://localhost:8080` 处。它直接连接到 Valkey,不依赖于 MCP 服务器运行。
| 页面 | 它做什么 |
|---|---|
| **仪表盘** | 命名空间计数、状态分解、健康指示器、最近活动 |
| **记忆** | 浏览所有记忆,具有命名空间、状态和项目过滤器。分页,htmx 支持 |
| **搜索** | 使用完整的回忆管道进行语义搜索。废弃警告突出显示 |
| **详情** | 完整的记忆内容、元数据、标签、体验数据、矛盾。生命周期操作按钮 |
| **创建** | 存储新的记忆,具有内联显示的重复检测 |
| **项目** | 列表、查看、编辑和创建项目上下文 |
| **体验** | 总结仪表盘,包含努力统计、突破和废弃方法墓地 |
| **重复** | 扫描命名空间中的几乎相同的记忆集群。直接存档额外内容 |
| **矛盾** | 记忆矛盾的并排比较,具有解决操作 |
| **抑制** | 内联添加和删除抑制主题 |
| **遥测** | 回忆计数器、最多回忆、冷冻、从未回忆。按项目过滤 |
| **工具超额** | 测量的工具调用指标自启动以来:调用次数、平均持续时间和平均令牌数、每个工具的错误次数。静态上下文成本分解 |
| **备份** | 创建备份、预览恢复内容并确认恢复 |
### 普罗米修斯指标
Web 仪表盘在普罗米修斯文本格式中公开了一个 `/metrics` 端点。 将您的 Grafana 或 Prometheus 刮刀指向 `http://localhost:8080/metrics`,每 15-60 秒刮取一次。
可用的仪表:
| 指标 | 标签 | 描述 |
|---|---|---|
| `omnimem_memories_total` | `namespace`, `state` | 按命名空间和生命周期状态划分的总记忆数 |
| `omnimem_memories_never_recalled` | `namespace` | 零次回忆的有效记忆 |
| `omnimem_recalls_total` | — | 所有记忆的回忆次数总和 |
| `omnimem_memories_gone_cold` | — | 以前被回忆但未达到冷阈值的记忆 |
| `omnimem_tool_calls_total` | `tool` | 按工具名称划分的 MCP 工具调用总数 |
| `omnimem_tool_errors_total` | `tool` | 按工具名称划分的 MCP 工具调用错误总数 |
指标每 60 秒缓存一次(可通过 `METRICS_CACHE_TTL` 配置),以避免每次抓取时扫描所有记忆。
Web 仪表盘支持通过 `WEB_UI_AUTH_TOKEN` 环境变量的可选承载令牌身份验证。`/metrics` 端点豁免认证,因此 Prometheus 可以在没有凭证的情况下抓取。对于其他安全选项(TLS,IP 允许列表,SSO),请参阅 `docs/reverse-proxy.md`。
## 架构
```
Claude 代码(任意机器) 浏览器
| |
| SSE / MCP | HTTP :8080
v v
+-------------------------+ +-------------------------+
| OmniMem MCP 服务器 | | OmniMem 网页界面 |
| Python fastmcp | | Starlette htmx |
| | | Jinja2 模板 |
| 记忆 回忆 | | |
| 降级 存档 | | 仪表盘 搜索 |
| 记录体验 | | 浏览 创建 |
| 废弃警告 | | 项目 体验 |
| 简报 健康 | | 重复 备份 |
+-----------+-------------+ +-----------+-------------+
| |
+-------------+---------------+
|
+-------------+-------------+
| |
v v
+---------------+ +------------------+
| Valkey | | RSS 工作线程 |
| + 搜索 | <--- | |
| | | feedparser |
| 索引:情景 | | APScheduler |
| 索引:项目 | | Claude Haiku |
| 索引:知识 | +------------------+
+---------------+
MCP 服务器和网页界面都直接连接到 Valkey
并共享 mcp_server/memory/ 包。
回忆流程:
查询
-> 废弃快速路径(关键词扫描,无需嵌入)
-> 嵌入查询
-> 向量搜索,每个命名空间前 20 个候选项
-> 过滤存档和已删除
-> 过滤抑制话题
-> 应用表面评分(生命周期状态乘数)
-> 应用时效衰减(90 天后年龄惩罚)
-> 应用经验权重(努力 x 结果乘数)
-> 检查恢复资格
-> 显示矛盾警告
-> 合并,重新排序,返回 top_k
-> 记录回忆事件 + 增加每个记忆的回忆计数器
```
---
## 贡献
问题和拉取请求都是欢迎的。 OmniMem 被设计为可扩展的,评分流程的结构允许添加新的乘数而不影响核心。新的 MCP 工具、额外的命名空间类型和替代嵌入后端都是合理的发展方向。
---
## 许可
MIT。免费使用、fork 和修改。无企业版,无托管版,无附加条件。
---
*由 Ric Harvey @ [SquareCows Ltd](https://squarecows.com) 构建,SquareCows Ltd 是一家为希望拥有自己的工具的人提供 AI 和自动化咨询的公司。*
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器