- 新增 context_rewrite 工具:允许 AI 在单轮内压缩工具调用上下文 - 清理 persona_query_max 和 task_query_max 死配置(commit 9be60ba 引入) - 同步全栈:后端/前端/文档/测试 18 个文件 - 测试:70/70 通过
245 lines
6.8 KiB
Markdown
245 lines
6.8 KiB
Markdown
# TrulyMEM 记忆机制
|
||
|
||
本文档详细说明 TrulyMEM 内部的记忆工作机制。
|
||
|
||
## 核心设计理念
|
||
|
||
### 区别于传统上下文系统
|
||
|
||
传统 AI 对话系统使用 messages 数组存储对话历史:
|
||
- 每次请求携带全部历史消息
|
||
- 随着对话轮次增加,上下文逐渐膨胀
|
||
- 最终触发记忆压缩或滑动窗口,造成记忆丢失
|
||
|
||
TrulyMEM 的解决思路:
|
||
- **摒弃** messages 数组上下文
|
||
- **唯一** 记忆载体:图数据库
|
||
- 全部记忆以三元组(节点)- 关系 → (节点)形式存储
|
||
|
||
### 图数据库作为唯一记忆源
|
||
|
||
所有记忆必须通过以下方式写入图数据库:
|
||
- `memory_commit` - 写入新记忆
|
||
- `memory_purge` - 删除/修正记忆
|
||
|
||
所有记忆必须通过以下方式读取:
|
||
- `memory_recall` - 检索记忆
|
||
|
||
### 工作记忆管理(实验性)
|
||
|
||
`context_rewrite` 允许 AI 在单轮对话内主动压缩工具调用的临时上下文:
|
||
- 将冗长的 JSON 工具结果提炼为简洁的自然语言摘要
|
||
- 摘要必须包含调用了哪些工具、对几次调用的总结
|
||
- 系统验证格式后,替换 `messages_history` 为 `[用户消息, 摘要]`
|
||
- 确保 LLM 保留元认知(知道"我调用过工具"),同时减少 JSON 噪音
|
||
|
||
---
|
||
|
||
## 强制执行流程(每轮对话)
|
||
|
||
由于没有传统上下文系统,每轮对话必须按以下顺序执行:
|
||
|
||
### 步骤 1:查询人设图(最高优先级)
|
||
|
||
```python
|
||
memory_recall(
|
||
query_intent="AI,人设,角色,性格,语气,说话风格",
|
||
depth=2
|
||
)
|
||
```
|
||
|
||
**目的**:获取当前人设,确保角色一致性。
|
||
|
||
**处理逻辑**:
|
||
- 找到人设 → 严格按照人设的语气、风格、特征回复
|
||
- 未找到 → 使用默认 TrulyMEM 身份
|
||
|
||
### 步骤 2:查询工作记忆链
|
||
|
||
```python
|
||
memory_recall(
|
||
query_intent="TaskNode,工作记忆,任务链",
|
||
depth=2
|
||
)
|
||
```
|
||
|
||
**目的**:获取之前的任务上下文,了解对话历史。
|
||
|
||
### 步骤 3:处理对话
|
||
|
||
- 理解用户意图
|
||
- 根据人设和工作记忆链生成回复
|
||
- 执行其他必要的记忆操作
|
||
|
||
### 步骤 4:更新工作记忆链
|
||
|
||
```python
|
||
task_create(
|
||
task_id="Task_当前轮次ID",
|
||
description="本轮对话概述",
|
||
info_nodes=["相关记忆节点"]
|
||
)
|
||
```
|
||
|
||
**目的**:记录本轮对话,维持时间链。
|
||
|
||
---
|
||
|
||
## 记忆写入规则
|
||
|
||
### 必须写入的情况
|
||
|
||
以下信息**必须**写入图数据库:
|
||
|
||
| 场景 | 示例 | 写入方式 |
|
||
|------|------|----------|
|
||
| 用户明确偏好 | "我喜欢摇滚" | `memory_commit` |
|
||
| 用户分享信息 | "我在做X项目" | `memory_commit` |
|
||
| 用户制定计划 | "我打算X" | `memory_commit` |
|
||
| 用户描述状态 | "我现在在X" | `memory_commit` |
|
||
|
||
### 禁止写入的情况
|
||
|
||
以下信息**禁止**写入:
|
||
|
||
| 场景 | 原因 | 处理方式 |
|
||
|------|------|----------|
|
||
| AI 推断的用户偏好 | 未经证实 | 不写入或标注[推测] |
|
||
| AI 猜测的用户意图 | 未经证实 | 不写入或标注[推测] |
|
||
| AI 推导的结论 | 未经证实 | 不写入或标注[推测] |
|
||
|
||
### 标注规则
|
||
|
||
| 类型 | 标注方式 | 示例 |
|
||
|------|----------|------|
|
||
| 推理内容 | 必须标注 **[猜测]** | 用户[推测]喜欢音乐 |
|
||
| 明确内容 | 直接陈述 | 用户喜欢音乐 |
|
||
|
||
---
|
||
|
||
## 节点与边类型
|
||
|
||
### 节点类型
|
||
|
||
| 节点类型 | 说明 | 存储内容 |
|
||
|----------|------|----------|
|
||
| `PersonaNode` | 人设节点 | AI 角色、性格、语气 |
|
||
| `TaskNode` | 任务节点 | 任务概述 |
|
||
| `StateNode` | 状态节点 | 任务状态 |
|
||
| `InfoNode` | 信息节点 | 具体信息 |
|
||
| `EntityNode` | 实体节点 | 通用实体 |
|
||
|
||
### 边类型
|
||
|
||
| 边类型 | 说明 | 连接关系 |
|
||
|----------|------|----------|
|
||
| `HAS_PERSONA` | 人设 | AI → PersonaNode |
|
||
| `NEXT_TASK` | 时间链 | TaskNode → TaskNode |
|
||
| `HAS_STATE` | 状态 | TaskNode → StateNode |
|
||
| `CONTAINS_INFO` | 信息 | TaskNode → InfoNode |
|
||
| `RELATES_TO` | 关联 | EntityNode → EntityNode |
|
||
|
||
---
|
||
|
||
## 必须查询工作记忆链的场景
|
||
|
||
### 强制查询场景
|
||
|
||
以下情况**必须**查询工作记忆链:
|
||
|
||
| 场景 | 示例 |
|
||
|------|------|
|
||
| 每轮对话开始 | 执行步骤 2 |
|
||
| 用户提到"刚才" | "刚才我们聊了什么?" |
|
||
| 用户提到"之前" | "继续刚才的话题" |
|
||
| 用户提到"上次" | "上次说的X" |
|
||
| 用户询问历史 | "我们之前说了什么?" |
|
||
| 连续性任务恢复 | 用户回到之前的话题 |
|
||
| 上下文引用 | "那个东西" |
|
||
|
||
---
|
||
|
||
## 自主性原则
|
||
|
||
在强制执行流程之外,AI 可自主决定:
|
||
|
||
### 查询决策
|
||
|
||
- 用户询问历史 → 查询
|
||
- 涉及之前内容 → 查询
|
||
- 不确定时 → 可查询
|
||
|
||
### 写入决策
|
||
|
||
- 用户明确提到 → 必须写入
|
||
- AI 推理得到 → 可以写入,标注[推测]
|
||
|
||
### 工具使用决策
|
||
|
||
- 根据上下文灵活选择
|
||
- 避免过度使用
|
||
- 保持自然对话
|
||
|
||
---
|
||
|
||
## 完整示例:成语接龙游戏
|
||
|
||
### 第一轮:用户发起游戏
|
||
|
||
```
|
||
用户: 咱来玩成语接龙吧,我先开始,为所欲为
|
||
|
||
AI操作:
|
||
1. 查询人设图 → 获取当前人设(如:猫娘)
|
||
2. 查询工作记忆链 → 无进行中任务
|
||
3. 记录游戏状态:
|
||
memory_commit(triplets=[
|
||
{"subject": "成语接龙_当前成语", "relation": "内容", "object": "为所欲为"},
|
||
{"subject": "成语接龙_当前成语", "relation": "游戏", "object": "成语接龙"}
|
||
])
|
||
4. 创建任务:
|
||
task_create(task_id="Task_成语接龙", description="成语接龙游戏,当前成语:为所欲为", info_nodes=["成语接龙_当前成语"])
|
||
5. 回复: "好的喵!我接:为虎作伥喵!"
|
||
```
|
||
|
||
### 第二轮:话题被打断
|
||
|
||
```
|
||
用户: 长门有希
|
||
|
||
AI操作:
|
||
1. 查询人设图 → 获取当前人设(猫娘)
|
||
2. 查询工作记忆链 → 发现 Task_成语接龙 状态为"进行中"
|
||
3. 暂停任务:
|
||
task_set_state(task_id="Task_成语接龙", state="已暂停")
|
||
4. 创建新任务:
|
||
task_create(task_id="Task_长门有希", description="讨论长门有希")
|
||
5. 回复关于长门有希的内容
|
||
```
|
||
|
||
### 第三轮:用户要求继续游戏
|
||
|
||
```
|
||
用户: 关于刚才的成语接龙,我并不知道应该怎么接你的成语,请帮我接一下
|
||
|
||
AI操作:
|
||
1. 查询人设图 → 获取当前人设(猫娘)
|
||
2. 查询工作记忆链 → 发现 Task_成语接龙 状态为"已暂停"
|
||
3. 恢复任务:
|
||
task_set_state(task_id="Task_成语接龙", state="进行中")
|
||
4. 查询信息节点 → 获取当前成语"为虎作伥"
|
||
5. 回复: "好的喵!上一个成语是'为虎作伥',我帮你接:伥鬼害人喵!"
|
||
```
|
||
|
||
---
|
||
|
||
## 执行检查清单
|
||
|
||
每轮对话必须检查:
|
||
|
||
- [ ] 步骤 1:是否查询了人设图?
|
||
- [ ] 步骤 2:是否查询了工作记忆链?
|
||
- [ ] 步骤 3:是否根据人设和工作记忆链生成回复?
|
||
- [ ] 步骤 4:是否更新了工作记忆链?
|
||
- [ ] 涉及上下文引用时是否查询了工作记忆链?
|
||
- [ ] 用户提到"刚才/之前/上次"时是否查询了工作记忆链? |