# agentmemory 后端

OpenHuman 的默认 `记忆` 特性后端是 `sqlite` —— 该统一存储在 [Memory Trees](/openhuman/zh/gong-neng/obsidian-wiki/memory-tree.md)中有文档说明。对于已经自行托管 [agentmemory](https://github.com/rohitg00/agentmemory) —— 通常是因为他们希望在 Claude Code、Cursor、Codex、OpenCode 和 OpenHuman 之间共享一个持久化的单一记忆——OpenHuman 提供了一个可选后端，通过 agentmemory 的 REST 接口代理每一次特性调用。

选择 `backend = "agentmemory"` 会完全跳过 OpenHuman 的 SQLite + 嵌入器路径。agentmemory 负责存储、嵌入和检索层。OpenHuman 变成一个轻量 REST 客户端。

## 何时使用这个

如果满足以下条件，请使用 agentmemory 后端：

* 你已经在运行 `npx -y @agentmemory/agentmemory` 用于一个或多个编码代理，并希望 OpenHuman 共享同一个持久化存储。
* 你希望使用混合 BM25 + 向量 + 图检索，而无需在 OpenHuman 侧额外配置独立的嵌入器。
* 你更偏好 agentmemory 的生命周期（整合、保留评分、自动遗忘、图提取），而不是 OpenHuman 的统一存储。

保留默认的 `sqlite` 后端，如果：

* 你希望进行自包含的单进程运行，不依赖外部守护进程。
* 你依赖 OpenHuman 特有的 Memory Tree 功能（分块、封存、摘要树），这些功能运行在 SQLite 存储之上。Memory Tree 管线不受特性后端影响——它运行在主机的文档存储之上，彼此正交——但当你已经在其他代理中统一使用 agentmemory 时，agentmemory 后端最有价值。

## 快速开始

1. **安装并启动 agentmemory** （一个终端）：

   ```bash
   npx -y @agentmemory/agentmemory
   ```

   默认值为 `http://localhost:3111` （REST） + `ws://localhost:49134` （引擎）。首次启动会在 `~/.agentmemory/.hmac` 生成一个 HMAC 密钥并只打印一次。
2. **将 OpenHuman 指向它** 在你的 `config.toml`:

   ```toml
   [memory]
   backend = "agentmemory"
   # 下面是默认值——仅在覆盖时设置。
   # agentmemory_url        = "http://localhost:3111"
   # agentmemory_secret     = ""           # HMAC bearer 令牌，可选
   # agentmemory_timeout_ms = 5000
   ```
3. **重启 OpenHuman**。工厂会直接跳过 SQLite 路径并记录 `[memory::factory] using agentmemory backend at <url>`.

就这样。现有的 OpenHuman 调用点（`存储`, `回忆`, `get`, `list`, `遗忘`, `namespace_summaries`, `count`, `health_check`）都无需更改即可工作。

## 配置键

| 字段                       | 默认值                     | 用途                                                      |
| ------------------------ | ----------------------- | ------------------------------------------------------- |
| `agentmemory_url`        | `http://localhost:3111` | agentmemory REST 服务器的基础 URL                             |
| `agentmemory_secret`     | *无*                     | 可选的 HMAC bearer 令牌。发送为 `Authorization: Bearer <secret>` |
| `agentmemory_timeout_ms` | `5000`                  | 每次请求的 reqwest 超时                                        |

当 `backend == "agentmemory"`时，以下现有的 `MemoryConfig` 字段会被 **忽略** —— agentmemory 通过 `~/.agentmemory/.env`:

* `embedding_provider`
* `embedding_model`
* `embedding_dimensions`
* `sqlite_open_timeout_secs`

在这条路径上设置它们不会产生任何作用。本地 AI 的 Ollama 健康检查门也不会在这条路径上运行——agentmemory 的守护进程会管理自己的嵌入器生命周期。

## 字段映射

OpenHuman 的 `MemoryEntry` ↔ agentmemory 传输行：

| OpenHuman 字段             | agentmemory 字段                   | 说明                                          |
| ------------------------ | -------------------------------- | ------------------------------------------- |
| `namespace`              | `project`                        | 默认值为 `"default"` 当为空时                       |
| `key`                    | `title`                          |                                             |
| `content`                | `content`                        |                                             |
| `id`                     | `id`                             | 由 agentmemory 生成（`mem_<rand>`)              |
| `category: Core`         | `type: "fact"`                   |                                             |
| `category: Daily`        | `type: "conversation"`           |                                             |
| `category: Conversation` | `type: "conversation"`           |                                             |
| `category: Custom(s)`    | `type: "fact"` + `concepts: [s]` | 自定义标签会折叠进 concepts 数组，因此仍可被查询               |
| `session_id`             | `sessionIds: [...]`              | OpenHuman 只暴露一个 id；agentmemory 持久化的是一个数组    |
| `timestamp`              | `updatedAt` （RFC3339）            | 回退为 `createdAt` 如果 `updatedAt` 不存在          |
| `score` （仅回忆命中）          | smart-search `score`             | 填充于 `回忆` responses， `None` 于 `get` / `list` |

agentmemory 还包含额外字段—— `concepts` （自动提取）、 `files` （路径标签）、 `strength` （保留评分）、 `version`, `supersedes` （生命周期链）—— 这个后端保持这些字段的默认值。它们是 agentmemory 生命周期层内部使用的，不需要通过 OpenHuman 的特性来回传。

## 特性方法 → 端点

| `记忆` 方法               | agentmemory REST                                     | 说明                                                      |
| --------------------- | ---------------------------------------------------- | ------------------------------------------------------- |
| `存储`                  | `POST /agentmemory/remember`                         | `{project, title, content, type, concepts, sessionIds}` |
| `回忆`                  | `POST /agentmemory/smart-search`                     | 混合 BM25 + 向量 + 图                                        |
| `get`                 | `POST /agentmemory/smart-search`                     | + 客户端精确标题过滤                                             |
| `list`                | `GET /agentmemory/memories?latest=true&project=<ns>` |                                                         |
| `遗忘`                  | `get(ns, key)` → `POST /agentmemory/forget`          | 两步：先解析 id 再遗忘                                           |
| `namespace_summaries` | `GET /agentmemory/projects`                          | 返回 `[{name, count, lastUpdated}]`                       |
| `count`               | `GET /agentmemory/health`                            | 直接读取 `memories` 字段                                      |
| `health_check`        | `GET /agentmemory/livez`                             |                                                         |

`RecallOpts.category`, `RecallOpts.session_id`，以及 `RecallOpts.min_score` 会被应用为 **客户端侧过滤器** 作用于 smart-search 响应。agentmemory 的 REST 接口目前并不支持将它们作为服务端过滤器。对于非常大的回忆窗口（limit > 100），建议发出更严格的查询字符串，以减少服务端工作量，而不是依赖客户端后置过滤。

## 安全性

当 `agentmemory_secret` 已设置时，客户端会遵守 agentmemory v0.9.12 明文 bearer 守卫契约：

* **回环主机** (`localhost`, `127.0.0.1`, `::1`）通过 `http://` —— 允许。本地开发路径。
* **`https://`** 到任意主机——允许。
* **对非回环主机使用明文 HTTP** —— 在构造时会发出一次性的 stderr 警告。bearer 会在传输过程中被观察到。
* **`AGENTMEMORY_REQUIRE_HTTPS=1`** （进程环境，ASCII 大小写不敏感匹配 `1` 或 `true`）—— 会在客户端构造时把警告升级为硬性拒绝。后端会启动失败，而不是泄露 bearer 一次。

生产环境部署应设置 `AGENTMEMORY_REQUIRE_HTTPS=1` ，这样错误配置的 TLS 终止器会明确失败，而不是悄悄泄露。

明文 bearer 守卫与 agentmemory 中的集成插件守卫一致， [PR #315](https://github.com/rohitg00/agentmemory/pull/315) 因此，见过 Hermes / OpenClaw / pi 上该警告的操作员，会在 OpenHuman 上认出相同的信息。

## 失败模式

| 失败                                              | 后端行为                                                                                   |
| ----------------------------------------------- | -------------------------------------------------------------------------------------- |
| 启动时守护进程不可达                                      | `from_config` 成功（URL 解析通过），但 `health_check()` 在第一次调用时返回 false。特性方法会向上抛出 `reqwest` 传输错误 |
| 网络超时                                            | `anyhow::Error` 根据特性契约；向调用方暴露                                                          |
| 4xx / 5xx 响应                                    | `anyhow::Error` 附带状态码 + 正文片段                                                           |
| 通过明文非回环的 bearer（无环境变量）                          | 一次性 stderr 警告，请求继续                                                                     |
| 通过明文非回环的 bearer + `AGENTMEMORY_REQUIRE_HTTPS=1` | 构造时直接拒绝                                                                                |
| 空的 `agentmemory_url`                            | 构造时硬拒绝，并提示默认值应保持未设置                                                                    |
| 无效的 URL 语法                                      | 构造时硬拒绝，并附带解析器错误                                                                        |

**不会自动回退到 SQLite。** 如果守护进程在启动时关闭，后端会明显暴露传输错误。运维人员可切回 `backend = "sqlite"` 在 `config.toml` 以恢复。理由：静默回退到 SQLite 会掩盖守护进程配置错误——“私有、简单、可预测”胜过“神奇地宽容”。

## 性能说明

这个后端是一个轻量 REST 代理——它会为每次特性调用增加一次 HTTP 往返。实际影响：

* `存储` 和 `遗忘` 是单次 RTT。
* `回忆`, `get`, `list` 是单次 RTT。
* `遗忘` 针对未知键的查询是双 RTT（隐式的 `get` lookup
  * 是一次空操作确认）。调用方可以通过检查先前一次 `list`.
* agentmemory 的 REST 默认是 `127.0.0.1` 的——同主机延迟低于 1 毫秒。在带有 HTTPS 终止的托管部署中，每次 RTT 预计约 10–30ms。
* 默认的每次请求超时是 5 秒。如果你在 iii 引擎冷启动时看到间歇性超时，请提高 `agentmemory_timeout_ms` ；agentmemory 在长时间空闲后的首次请求延迟会根据持久化状态而上升到约 3–5 秒。

## 迁移：从 SQLite 到 agentmemory

目前没有就地迁移。推荐路径：

1. 通过 OpenHuman 现有的导出 RPC（或直接 SQL）从 SQLite 存储中导出你现有的记忆。
2. 遍历导出结果，并将每一行 POST 到 `/agentmemory/remember` 并保持相同的 `project` + `title` + `content`。agentmemory 会分配新的 id；OpenHuman 一侧会在首次 `list`.
3. 将 `backend = "agentmemory"` 和重启时拾取它们。

专门的批量导入路径已作为后续事项提交。

## 实现参考

树内文件：

* [`store/agentmemory/mod.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/store/agentmemory/mod.rs) —— 模块接口
* [`store/agentmemory/backend.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/store/agentmemory/backend.rs) —— `impl Memory for AgentMemoryBackend`
* [`store/agentmemory/client.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/store/agentmemory/client.rs) —— reqwest 封装 + 明文 bearer 守卫
* [`store/agentmemory/mapping.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/store/agentmemory/mapping.rs) —— `MemoryEntry` ↔ agentmemory JSON
* [`tests/agentmemory_backend.rs`](https://github.com/tinyhumansai/openhuman/tree/main/tests/agentmemory_backend.rs) —— 12 个 axum-mock 集成测试

相关上游：

* agentmemory 仓库—— <https://github.com/rohitg00/agentmemory>
* agentmemory REST 契约—— `~/.agentmemory/.env` agentmemory README 中的键 + 端点列表
* v0.9.12 明文 bearer 守卫—— agentmemory PR #315


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tinyhumans.gitbook.io/openhuman/zh/gong-neng/obsidian-wiki/agentmemory-backend.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
