概述
PAI 的记忆系统为每位用户维护独立的长期记忆库,实现跨会话的信息持久化。无论用户在哪个平台、哪次对话中提及的偏好、事实或目标,都会被自动提取并存储,在后续对话中按相关性注入上下文。跨会话持久化
记忆存储在数据库中,不随会话结束而丢失
双通道写入
Agent 显式工具调用 + 异步后台提取,双管齐下
相关性检索
基于 token 重叠 + 重要度 + 时效性的综合打分
语义去重
自动识别并合并语义相似的记忆条目
记忆类型
系统支持以下 5 种记忆类型(profile 类型保留给用户档案,不进入长期记忆):
| 类型 | 说明 | 示例 |
|---|---|---|
preference | 用户偏好与习惯 | 喜欢川菜、偏好简洁回复风格 |
fact | 客观事实信息 | 生日 1999-02-07、住在武汉 |
goal | 用户的目标与计划 | 年底前考过 N1 |
project | 项目相关信息 | 正在开发 PAI 项目 |
constraint | 约束规则 | 回复不超过 200 字 |
数据模型
记忆存储在long_term_memories 表中:
memory_key 采用点分路径或下划线命名,如 profile.birthday、preference.sport,保证同一语义槽位的记忆可被覆盖更新而非重复创建。双通道写入
记忆通过两个独立通道写入系统:通道一:Agent 显式工具
Agent 在对话过程中可通过以下工具直接操作记忆:| 工具 | 功能 |
|---|---|
memory_save | 创建或覆盖一条记忆 |
memory_append | 追加内容到已有记忆 |
memory_delete | 删除指定记忆 |
memory_list | 查询用户记忆列表 |
chat_manager、schedule_manager、ledger_manager)均可使用记忆工具。
通道二:异步提取管道
后台memory_worker 进程定期扫描未处理的用户消息,自动从对话中提取记忆候选。
三阶段提取流程
异步提取管道分为三个阶段:候选提取 (extract_memory_candidates)
LLM 分析用户输入与完整会话上下文,输出结构化候选记忆数组。每条候选包含:
op:操作类型(save / delete)memory_type:记忆类型key:稳定槽位键content:记忆值importance:重要度 1-5confidence:置信度 0-1ttl_days:可选过期天数
精炼去重 (_llm_refine_memory_candidates)
第二轮 LLM 调用,结合用户已有记忆做最终裁决:
- 判断每条候选是否保留(
keep) - 语义等价的候选与已有记忆合并(
merge_target_id) - 归并表述不同但语义相同的候选为规范形式
- 复用已有
memory_key避免重复创建
提取规则
会被提取的内容:- 用户明确表达的偏好和习惯
- 30 天后仍有价值的事实信息
- 用户说”请记住”或”以后按此执行”的规则
- 用户的长期目标和计划
- 短期状态和一次性任务进度
- 天气快照、日/周汇总
- 运维/系统/工具内部信息
- 身份档案信息(昵称、姓名、助手名、emoji)
- 原始数据库日志
相关性检索
retrieve_relevant_long_term_memories 函数在每次对话开始时,根据用户输入检索最相关的记忆注入上下文。
打分公式
| 分量 | 计算方法 |
|---|---|
token_overlap | 查询 tokens 与记忆 tokens 的重叠比例:overlap / len(query_tokens) |
importance_score | min(5, importance) / 5,归一化到 0-1 |
recency_score | 1 / (1 + days_since_update / 30),越新分数越高 |
- 扫描用户所有未过期记忆(上限
retrieve_scan_limit条) - 按 importance 和 updated_at 降序排列
- 对每条记忆计算相关性得分
- 优先取词法匹配分 >= 0.12 的条目
- 不足时用剩余高分条目补充
- 过滤掉身份类记忆
- 返回 top-20 条,注入对话上下文
Token 匹配策略
系统同时支持 ASCII 和中文的 token 化:- ASCII 词:正则
[a-zA-Z0-9_]{2,}匹配英文单词 - 中文 bigram:连续中文字符串按 2-gram 切分(如”武汉大学” -> “武汉”, “汉大”, “大学”)
- Jaccard 系数:
|A & B| / |A | B| - Containment 系数:
max(|A & B|/|A|, |A & B|/|B|) * 0.92
生命周期管理
TTL 过期
- 默认 TTL:730 天(约 2 年)
- 可通过
ttl_days字段自定义每条记忆的过期时间 - 检索时自动过滤
expires_at已过期的记忆
语义去重
两个阈值控制去重行为:| 阈值 | 值 | 用途 |
|---|---|---|
SEMANTIC_DUPLICATE_THRESHOLD | 0.82 | 查找语义重复项时的匹配阈值 |
SEMANTIC_MERGE_STRICT_THRESHOLD | 0.9 | 写入后清理严格重复项的阈值 |
合并 API
系统提供consolidate API,可批量扫描用户记忆(上限 160 条),通过 LLM 判断是否合并、保留或删除。
消息级追踪
每条用户消息在messages 表中包含记忆处理状态字段:
| 字段 | 说明 |
|---|---|
memory_status | 处理状态 |
memory_processed_at | 处理完成时间 |
memory_worker 后台进程
memory_worker 是独立运行的后台进程,负责定期扫描未处理消息并执行记忆提取:
配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
long_term_memory_enabled | true | 总开关 |
long_term_memory_min_confidence | 0.5 | 最低置信度阈值 |
long_term_memory_max_write_items | 6 | 单次最大写入条数 |
long_term_memory_retrieve_limit | 20 | 检索返回上限(top-K) |
long_term_memory_retrieve_scan_limit | 80 | 检索扫描上限 |
long_term_memory_default_ttl_days | 730 | 默认过期天数 |
long_term_memory_debounce_sec | 0 | 写入防抖间隔 |
long_term_memory_extract_timeout_sec | 90 | 提取超时 |
long_term_memory_upsert_timeout_sec | 90 | 写入超时 |
long_term_memory_extract_context_max_chars | 24000 | 提取上下文最大字符数 |
long_term_memory_extract_message_max_chars | 200 | 单条消息最大字符数 |
long_term_memory_scan_enabled | true | 后台扫描开关 |
long_term_memory_scan_run_in_api | false | 是否在 API 进程内运行扫描 |
long_term_memory_scan_interval_sec | 120 | 扫描间隔(秒) |
long_term_memory_scan_max_conversations | 80 | 每轮最大扫描会话数 |
long_term_memory_scan_max_messages_per_conversation | 30 | 每会话最大扫描消息数 |