> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneee.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 记忆系统

> PAI 长期记忆系统：PostgreSQL 真值库、Milvus 检索层、双通道写入与异步索引同步

## 概述

PAI 为每位用户维护独立的长期记忆库，实现跨会话持久化。当前实现采用 `PostgreSQL` 保存业务真值，`Milvus` 保存向量索引；写入时先落真值，再由独立 `memory_index_worker` 异步同步索引，读取时优先做相关记忆召回，再回 PostgreSQL 取真值内容。

<CardGroup cols={2}>
  <Card title="真值与索引解耦" icon="database">
    PostgreSQL 保存正式记忆，Milvus 只负责召回，不承担业务真值职责。
  </Card>

  <Card title="双通道写入" icon="code-branch">
    Agent 显式工具调用 + 对话后异步提取，最终统一写入长期记忆表。
  </Card>

  <Card title="异步索引同步" icon="arrows-rotate">
    记忆写入后先标记为 DIRTY，再由 `memory_index_worker` 批量生成 embedding 并写入 Milvus。
  </Card>

  <Card title="检索模式可切换" icon="magnifying-glass">
    支持 `full_inject` 和 `dense` 两种模式，可按环境配置切换。
  </Card>
</CardGroup>

## 记忆类型

系统支持以下 5 种长期记忆类型：

| 类型           | 说明      | 示例                 |
| ------------ | ------- | ------------------ |
| `preference` | 稳定偏好与习惯 | 喜欢川菜、偏好简洁回复风格      |
| `fact`       | 持久事实信息  | 常住武汉、喜欢乌龙茶         |
| `goal`       | 长期目标与计划 | 年底前考过 N1           |
| `project`    | 长期项目上下文 | 正在开发 PAI 项目        |
| `constraint` | 长期规则约束  | 回复尽量中文、输出不超过 200 字 |

<Note>
  `profile` 类型用于用户档案字段，不进入长期记忆表，避免昵称、AI 名称、emoji 等身份信息与记忆系统混用。
</Note>

## 数据模型

长期记忆保存在 `long_term_memories` 表：

```sql theme={null}
CREATE TABLE long_term_memories (
    id                SERIAL PRIMARY KEY,
    user_id           INTEGER NOT NULL REFERENCES users(id),
    conversation_id   INTEGER REFERENCES conversations(id),
    source_message_id INTEGER REFERENCES messages(id),

    memory_key        VARCHAR(160) NOT NULL,
    memory_type       VARCHAR(40) DEFAULT 'fact',
    content           VARCHAR(1000),
    importance        INTEGER DEFAULT 3,
    confidence        FLOAT DEFAULT 0.8,

    is_active         BOOLEAN DEFAULT TRUE,
    last_accessed_at  TIMESTAMPTZ,
    expires_at        TIMESTAMPTZ,

    vector_status     VARCHAR(20) DEFAULT 'DIRTY',
    vector_synced_at  TIMESTAMPTZ,
    vector_error      TEXT,
    vector_model      VARCHAR(120),
    vector_version    INTEGER,
    vector_text_hash  VARCHAR(64),

    created_at        TIMESTAMPTZ NOT NULL,
    updated_at        TIMESTAMPTZ NOT NULL,

    UNIQUE(user_id, memory_key)
);
```

消息表同时追踪每条用户消息的记忆处理状态：

```text theme={null}
PENDING -> PROCESSED
        -> FAILED
        -> SKIPPED
```

## 双通道写入

### 通道一：Agent 显式工具

Agent 在对话过程中可直接调用以下工具：

| 工具              | 功能        |
| --------------- | --------- |
| `memory_save`   | 创建或覆盖一条记忆 |
| `memory_append` | 追加内容到已有记忆 |
| `memory_delete` | 删除指定记忆    |
| `memory_list`   | 查询用户当前记忆  |

这条路径直接写 PostgreSQL 真值，并同步标记向量状态。

### 通道二：异步提取管道

每轮对话结束后，系统会调度长期记忆提取管道；独立 `memory_worker` 也会补扫 `PENDING / FAILED` 的用户消息，确保最终一致性。

## 三阶段提取流程

<Steps>
  <Step title="候选提取 (extract_memory_candidates)">
    LLM 以当前用户消息为触发源，结合会话摘要与完整上下文提取结构化候选记忆。助手回复仅作上下文，不作为事实来源。候选包含 `op`、`memory_type`、`key`、`content`、`importance`、`confidence`、`ttl_days` 等字段。
  </Step>

  <Step title="精炼去重 (_llm_refine_memory_candidates)">
    第二轮 LLM 调用结合用户已有记忆，对候选逐条裁决：保留、合并到已有记忆、复用既有 key，或直接丢弃。
  </Step>

  <Step title="持久化写入 (upsert_long_term_memories)">
    最终将候选写入 `long_term_memories`，并根据 key 匹配或语义重复阈值执行覆盖、合并、删除和去重。
  </Step>
</Steps>

### 提取边界

**会被保留为长期记忆的内容：**

* 稳定偏好和习惯
* 30 天后仍有价值的事实信息
* 长期目标、长期项目、长期约束
* 用户明确要求“以后都按这个来”的长期规则

**不会进入长期记忆的内容：**

* 短期提醒、待办和执行步骤
* 今晚、明天、这周、本月这类明确时间窗要求
* 仅在短期窗口内成立的条件规则
* 天气快照、日/周/月汇总、短期财务统计
* 可用工具清单、系统状态、技术实现细节、流式开关等内部信息
* 昵称、AI 名称、emoji 等身份档案字段
* 明显错误的档案槽位值，例如把纯数字写进城市、国家、省份等字段

<Note>
  例如“下雨时提醒我带伞”可视为长期约束；“明后天有雨时提醒我带伞”属于短期规则，不会写入长期记忆。
</Note>

### 事实来源与硬过滤

* 提取由 `user` 消息驱动；完整上下文和会话摘要只用于判断这条信息是否稳定、是否覆盖旧记忆
* `assistant` 回复不会被当作新的事实来源，不会单独生成长期记忆
* 当前实现会对一批已知噪声 key 做硬过滤，例如 `weather-date_*`、`tool-*`、`system-*`、`reminder-last_*`、`finance-monthly_* / weekly_* / daily_*`
* 对工具清单、天气快照、流式输出设置、系统格式说明等内容，即使误进入候选，也会在写入和读取阶段被再次过滤

<Tip>
  这意味着即便历史库里遗留了旧的脏记忆，`memory_list`、召回注入和后台清洗也会优先把它们排除在外。
</Tip>

## 向量同步

长期记忆写入 PostgreSQL 后，会统一标记为 `vector_status = DIRTY`。独立 `memory_index_worker` 定时扫描 `DIRTY / FAILED` 记录并执行：

1. 构造索引文本：`[{memory_type}] {memory_key}: {content}`
2. 调用 embedding 模型生成向量
3. `upsert` 到 Milvus collection
4. 成功后将对应行更新为 `SYNCED`

删除或失效记忆时，也会同步删除或刷新 Milvus 中的索引项。

## 检索流程

系统支持两种读取模式，由 `LONG_TERM_MEMORY_RETRIEVE_MODE` 控制：

| 模式            | 行为                         |
| ------------- | -------------------------- |
| `full_inject` | 直接注入当前用户全部有效长期记忆           |
| `dense`       | 先向量召回，再回 PostgreSQL 取真值并重排 |

### `dense` 模式流程

```text theme={null}
1. 将 query = 用户当前问题 + 会话摘要
2. 调用 embedding 模型生成 query vector
3. 去 Milvus 搜索 top-N memory_id
4. 回 PostgreSQL 取这些 memory_id 的真值内容
5. 应用层按 retrieval_score / importance / confidence / recency / exact_key_bonus 重排
6. 返回 top-k 注入 prompt
```

这意味着：

* `PostgreSQL` 决定“这条记忆真实是什么”
* `Milvus` 决定“当前最该召回哪几条记忆”

如果向量检索不可用，系统会自动回退到词法扫描，不会中断主对话链路。

## 生命周期管理

### TTL 过期

* 默认 TTL：`730` 天
* 支持按条目设置 `ttl_days`
* 检索时自动过滤已过期记忆

### 语义去重

| 阈值     | 用途               |
| ------ | ---------------- |
| `0.82` | 判定为语义重复，优先复用已有记忆 |
| `0.90` | 写入后强制合并高相似度冗余副本  |

### 后台清洗

系统提供 consolidate 能力，可按用户批量审查长期记忆，执行保留、删除、合并和规范化更新。

## 后台进程

### `memory_worker`

扫描 `PENDING / FAILED` 消息并重新执行提取管道，保证消息侧最终一致性。

### `memory_index_worker`

扫描 `DIRTY / FAILED` 记忆并同步 Milvus，保证向量索引最终一致性。

## 配置参数

| 参数                                           | 默认值                      | 说明                                |
| -------------------------------------------- | ------------------------ | --------------------------------- |
| `LONG_TERM_MEMORY_ENABLED`                   | `true`                   | 总开关                               |
| `LONG_TERM_MEMORY_RETRIEVE_MODE`             | `full_inject`            | 记忆读取模式，支持 `full_inject` / `dense` |
| `LONG_TERM_MEMORY_MIN_CONFIDENCE`            | `0.75`                   | 写入最低置信度                           |
| `LONG_TERM_MEMORY_MAX_WRITE_ITEMS`           | `6`                      | 单轮最多写入条数                          |
| `LONG_TERM_MEMORY_RETRIEVE_LIMIT`            | `6`                      | 注入 prompt 的记忆条数上限                 |
| `LONG_TERM_MEMORY_RETRIEVE_SCAN_LIMIT`       | `80`                     | 检索候选扫描上限                          |
| `LONG_TERM_MEMORY_DEFAULT_TTL_DAYS`          | `180`                    | 默认过期天数                            |
| `LONG_TERM_MEMORY_DEBOUNCE_SEC`              | `12`                     | 异步提取延迟                            |
| `LONG_TERM_MEMORY_EXTRACT_TIMEOUT_SEC`       | `90`                     | 提取超时                              |
| `LONG_TERM_MEMORY_UPSERT_TIMEOUT_SEC`        | `90`                     | 写入超时                              |
| `LONG_TERM_MEMORY_EXTRACT_CONTEXT_MAX_CHARS` | `8000`                   | 提取上下文窗口                           |
| `LONG_TERM_MEMORY_SCAN_ENABLED`              | `true`                   | `memory_worker` 开关                |
| `LONG_TERM_MEMORY_SCAN_INTERVAL_SEC`         | `120`                    | 后台扫描间隔                            |
| `MEMORY_INDEX_WORKER_ENABLED`                | `false`                  | `memory_index_worker` 开关          |
| `MEMORY_INDEX_WORKER_INTERVAL_SEC`           | `30`                     | 向量同步轮询间隔                          |
| `MEMORY_INDEX_WORKER_BATCH_SIZE`             | `32`                     | 每轮同步批量大小                          |
| `MEMORY_EMBEDDING_MODEL`                     | `text-embedding-3-small` | 记忆 embedding 模型                   |
| `MEMORY_EMBEDDING_DIM`                       | `1536`                   | 向量维度                              |
| `MEMORY_MILVUS_ENABLED`                      | `false`                  | 是否启用 Milvus 检索                    |
| `MEMORY_MILVUS_URI`                          | -                        | Milvus 连接地址                       |
| `MEMORY_MILVUS_COLLECTION`                   | `memory_text_v1`         | 记忆向量 collection                   |

## 关键文件

| 文件                                            | 职责                                                                |
| --------------------------------------------- | ----------------------------------------------------------------- |
| `backend/app/models/memory.py`                | 记忆真值模型与向量状态字段                                                     |
| `backend/app/services/memory.py`              | 提取、精炼、写入、检索、去重、清洗                                                 |
| `backend/app/services/memory_embeddings.py`   | embedding 调用封装                                                    |
| `backend/app/services/memory_vector_store.py` | Milvus collection / upsert / search                               |
| `backend/app/services/message_handler.py`     | 记忆注入与异步提取调度                                                       |
| `backend/app/services/tool_executor.py`       | `memory_save` / `memory_append` / `memory_delete` / `memory_list` |
| `backend/app/memory_worker.py`                | 后台消息补扫                                                            |
| `backend/app/memory_index_worker.py`          | 后台索引同步                                                            |
| `backend/app/core/config.py`                  | 记忆与 Milvus 配置参数                                                   |
