Fix: F3 tool details toggle, sidebar width, and sync docs with code
- Fix F3 tool details: use display:none/block instead of re-compose - Increase sidebar width from 40 to 70 for readability - Sync 一键启动指南.md with actual entry points (trulymem_entry.py) - Rewrite 工作记忆链机制说明.md with actual task_* tool APIs - Rewrite 架构.md with actual file structure (38 Python files) - Update docs/README.md with complete feature list
This commit is contained in:
@ -1,25 +1,28 @@
|
|||||||
# TrulyMEM 文档
|
# TrulyMEM 文档
|
||||||
|
|
||||||
欢迎来到TrulyMEM项目文档。
|
欢迎来到 TrulyMEM 项目文档。
|
||||||
|
|
||||||
## 文档目录
|
## 文档目录
|
||||||
|
|
||||||
- [架构设计](架构.md) - 系统架构和技术设计
|
- [架构设计](架构.md) - 系统架构和技术设计
|
||||||
- [快速开始](一键启动指南.md) - 一键启动指南
|
- [快速开始](一键启动指南.md) - 一键启动指南
|
||||||
|
- [工作记忆链机制说明](工作记忆链机制说明.md) - 连续性任务处理
|
||||||
|
|
||||||
## 项目简介
|
## 项目简介
|
||||||
|
|
||||||
TrulyMEM是一个让AI拥有长期记忆能力的图记忆系统,通过图数据库存储实体关系,让AI能够像人类一样记忆、回忆和管理信息。
|
TrulyMEM (TrueHumanMEM) 是一个让 AI 拥有长期记忆能力的图记忆系统,通过图数据库存储实体关系,让 AI 能够像人类一样记忆、回忆和管理信息。
|
||||||
|
|
||||||
## 核心特性
|
## 核心特性
|
||||||
|
|
||||||
- 长期记忆存储
|
- **长期记忆存储**: 基于 SQLite 内嵌图数据库,开箱即用
|
||||||
- 人设图机制
|
- **人设图机制**: 支持角色扮演和性格设定
|
||||||
- 流式消息显示
|
- **工作记忆链**: 维持对话连贯性的任务跟踪机制
|
||||||
- 跨平台支持
|
- **流式消息显示**: 实时显示 AI 响应
|
||||||
- 独立部署
|
- **键盘驱动 TUI**: 无需鼠标,全键盘操作
|
||||||
|
- **跨平台支持**: Windows / Linux / macOS
|
||||||
|
- **独立部署**: 支持打包为可执行文件
|
||||||
|
|
||||||
## 快速链接
|
## 快速链接
|
||||||
|
|
||||||
- [GitHub仓库](https://github.com/yourusername/trulymem)
|
- [GitHub 仓库](https://github.com/yourusername/trulymem)
|
||||||
- [问题反馈](https://github.com/yourusername/trulymem/issues)
|
- [问题反馈](https://github.com/yourusername/trulymem/issues)
|
||||||
|
|||||||
113
docs/一键启动指南.md
113
docs/一键启动指南.md
@ -1,6 +1,6 @@
|
|||||||
# 一键启动指南
|
# TrulyMEM 一键启动指南
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 快速开始
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
@ -12,72 +12,69 @@
|
|||||||
start.bat
|
start.bat
|
||||||
|
|
||||||
# 方法3: Python
|
# 方法3: Python
|
||||||
python start.py
|
python trulymem_entry.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linux / macOS
|
### Linux / macOS
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 方法1: Shell脚本
|
# 方法1: Python直接运行
|
||||||
chmod +x start.sh
|
python3 trulymem_entry.py
|
||||||
./start.sh
|
|
||||||
|
|
||||||
# 方法2: Python
|
# 方法2: 打包后的可执行文件
|
||||||
python3 start.py
|
chmod +x TrulyMEM
|
||||||
|
./TrulyMEM
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📋 启动流程
|
## 启动流程
|
||||||
|
|
||||||
```
|
```
|
||||||
[Step 1/3] 检查 Python
|
[Step 1/3] 检查 Python
|
||||||
↓
|
↓
|
||||||
[Step 2/3] 设置虚拟环境
|
[Step 2/3] 安装依赖
|
||||||
- 创建venv(如果不存在)
|
- 安装 requirements.txt 中的依赖
|
||||||
- 安装依赖
|
|
||||||
↓
|
↓
|
||||||
[Step 3/3] 启动应用
|
[Step 3/3] 启动应用
|
||||||
↓
|
↓
|
||||||
✅ 系统初始化成功!
|
✅ 系统初始化成功!
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⏱️ 启动时间
|
## 启动时间
|
||||||
|
|
||||||
- **首次启动**: 约1-2分钟(安装依赖)
|
- **首次启动**: 约1-2分钟(安装依赖)
|
||||||
- **后续启动**: 约30秒
|
- **后续启动**: 约30秒
|
||||||
|
|
||||||
## 🔧 系统要求
|
## 系统要求
|
||||||
|
|
||||||
### 必需
|
### 必需
|
||||||
|
|
||||||
- **Python 3.8+**
|
- **Python 3.8+**
|
||||||
- **DeepSeek API Key**
|
- **DeepSeek API Key** (或 OpenAI 兼容 API Key)
|
||||||
|
|
||||||
### 可选
|
### 可选
|
||||||
|
|
||||||
- **Git** - 用于克隆仓库
|
- **Git** - 用于克隆仓库
|
||||||
|
|
||||||
## 📝 首次使用
|
## 首次使用
|
||||||
|
|
||||||
### 1. 启动应用
|
### 1. 启动应用
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
start.bat # Windows
|
python trulymem_entry.py
|
||||||
./start.sh # Linux/macOS
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 配置 API Key
|
### 2. 配置 API Key
|
||||||
|
|
||||||
1. 按 **F2** 展开侧边栏
|
1. 按 **F2** 展开侧边栏
|
||||||
2. 点击 **"配置"**
|
2. 在 API Key 输入框输入密钥
|
||||||
3. 输入 **DeepSeek API Key**
|
3. 按 **Enter** 保存
|
||||||
4. 按 **Enter** 保存
|
|
||||||
|
|
||||||
### 3. 开始对话
|
### 3. 开始对话
|
||||||
|
|
||||||
- 输入消息
|
- 在底部输入框输入消息
|
||||||
- 按 **Enter** 发送
|
- 按 **Enter** 发送
|
||||||
|
|
||||||
## 🌐 获取 API Key
|
## 获取 API Key
|
||||||
|
|
||||||
### DeepSeek
|
### DeepSeek
|
||||||
|
|
||||||
@ -89,9 +86,9 @@ start.bat # Windows
|
|||||||
### 其他兼容API
|
### 其他兼容API
|
||||||
|
|
||||||
- OpenAI
|
- OpenAI
|
||||||
- 其他兼容OpenAI格式的API
|
- 其他兼容OpenAI格式的API(如 Azure OpenAI)
|
||||||
|
|
||||||
## ⚠️ 常见问题
|
## 常见问题
|
||||||
|
|
||||||
### Q: Python 未找到
|
### Q: Python 未找到
|
||||||
|
|
||||||
@ -105,60 +102,98 @@ start.bat # Windows
|
|||||||
**A:** 手动安装
|
**A:** 手动安装
|
||||||
```bash
|
```bash
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
venv\Scripts\activate # Windows
|
|
||||||
source venv/bin/activate # Linux/macOS
|
source venv/bin/activate # Linux/macOS
|
||||||
|
venv\Scripts\activate # Windows
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Q: API Key 无效
|
### Q: API Key 无效
|
||||||
|
|
||||||
**A:** 检查API Key格式
|
**A:** 检查API Key格式
|
||||||
- 应以 `sk-` 开头
|
- 应以 `sk-` 开头(DeepSeek/OpenAI)
|
||||||
- 无多余空格
|
- 无多余空格
|
||||||
- 正确复制
|
- 正确复制
|
||||||
|
|
||||||
## 🎯 快捷键
|
### Q: 提示"请先配置API Key"
|
||||||
|
|
||||||
|
**A:** 按 F2 展开侧边栏,在 API Key 输入框中输入密钥后按 Enter
|
||||||
|
|
||||||
|
## 快捷键
|
||||||
|
|
||||||
| 快捷键 | 功能 |
|
| 快捷键 | 功能 |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| F1 | 帮助 |
|
| F1 | 帮助 |
|
||||||
| F2 | 侧边栏 |
|
| F2 | 切换侧边栏 |
|
||||||
| F3 | 工具详情 |
|
| F3 | 工具详情 |
|
||||||
|
| F4 | 聚焦查询框 |
|
||||||
| F5 | 清屏 |
|
| F5 | 清屏 |
|
||||||
| F6 | 退出 |
|
| F6 | 退出 |
|
||||||
|
|
||||||
## 📊 数据存储
|
## 数据存储
|
||||||
|
|
||||||
- **数据库**: `graph_memory.db`
|
- **数据库**: `graph_memory.db`
|
||||||
- **位置**: 应用目录
|
- **位置**: 应用目录
|
||||||
- **格式**: SQLite
|
- **格式**: SQLite(内嵌,无需外部数据库)
|
||||||
- **可备份**: 是
|
- **可备份**: 是,直接复制文件即可
|
||||||
|
|
||||||
## 🔄 更新应用
|
## 更新应用
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 拉取最新代码
|
# 拉取最新代码
|
||||||
git pull
|
git pull
|
||||||
|
|
||||||
|
# 重新安装依赖(如有更新)
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# 重新启动
|
# 重新启动
|
||||||
start.bat
|
python trulymem_entry.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🗑️ 清理数据
|
## 清理数据
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 删除数据库
|
# 删除数据库
|
||||||
rm graph_memory.db
|
rm graph_memory.db
|
||||||
|
|
||||||
# 重新启动会创建新数据库
|
# 重新启动会创建新数据库
|
||||||
start.bat
|
python trulymem_entry.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## 💡 提示
|
## 提示
|
||||||
|
|
||||||
- 首次启动需要安装依赖,请耐心等待
|
- 首次启动需要安装依赖,请耐心等待
|
||||||
- API Key 只需配置一次
|
- API Key 只需配置一次(自动保存)
|
||||||
- 数据自动保存,无需手动操作
|
- 数据自动保存,无需手动操作
|
||||||
- 可以随时按F6退出
|
- 可以随时按 F6 退出
|
||||||
|
|
||||||
|
## 从源码运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 克隆仓库
|
||||||
|
git clone https://github.com/yourusername/trulymem.git
|
||||||
|
cd trulymem
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 运行
|
||||||
|
python -m graph_memory_tui.main
|
||||||
|
# 或
|
||||||
|
python trulymem_entry.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包应用
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build_windows.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash build_linux.sh
|
||||||
|
```
|
||||||
|
|
||||||
开始使用吧!
|
开始使用吧!
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
# 工作记忆链机制说明
|
# 工作记忆链机制说明
|
||||||
|
|
||||||
## 问题背景
|
## 概述
|
||||||
|
|
||||||
当前工程的AI助手在处理连续性任务时存在以下问题:
|
TrulyMEM 通过工作记忆链机制维持对话连贯性。由于系统没有传统的消息历史数组,图数据库是唯一的记忆载体,工作记忆链是维持对话上下文的关键机制。
|
||||||
|
|
||||||
1. **没有工作记忆链**: AI无法记住当前正在进行的任务状态
|
## 核心问题
|
||||||
2. **任务上下文丢失**: 当话题被打断后,AI无法恢复之前的任务
|
|
||||||
|
传统 AI 对话系统在处理连续性任务时存在以下问题:
|
||||||
|
|
||||||
|
1. **没有工作记忆链**: AI 无法记住当前正在进行的任务状态
|
||||||
|
2. **任务上下文丢失**: 当话题被打断后,AI 无法恢复之前的任务
|
||||||
3. **缺乏任务状态管理**: 没有明确标注任务的完成状态
|
3. **缺乏任务状态管理**: 没有明确标注任务的完成状态
|
||||||
|
|
||||||
### 问题示例
|
### 问题示例
|
||||||
@ -18,171 +22,50 @@ AI: 好的喵!我接:为虎作伥喵!
|
|||||||
AI: (讨论长门有希的内容)
|
AI: (讨论长门有希的内容)
|
||||||
|
|
||||||
用户: 关于刚才的成语接龙,我并不知道应该怎么接你的成语,请帮我接一下
|
用户: 关于刚才的成语接龙,我并不知道应该怎么接你的成语,请帮我接一下
|
||||||
AI: [猜测] 看起来我们之前应该没有进行过成语接龙游戏,因为记忆中没有找到相关记录。
|
AI: [猜测] 看起来我们之前应该没有进行过成语接龙游戏...
|
||||||
```
|
```
|
||||||
|
|
||||||
**问题**: AI完全忘记了之前进行的成语接龙游戏,无法恢复任务上下文。
|
**问题**: AI 完全忘记了之前的成语接龙游戏。
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 解决方案
|
## 解决方案
|
||||||
|
|
||||||
### 工作记忆链机制
|
### 专用工具
|
||||||
|
|
||||||
通过图数据库实现一个**时间序列的任务链**,用于跟踪连续性任务的状态和上下文。
|
系统提供 4 个专用任务工具:
|
||||||
|
|
||||||
### 图数据库结构
|
| 工具 | 功能 | 使用场景 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `task_create` | 创建任务节点 | 开始新任务 |
|
||||||
|
| `task_set_state` | 设置任务状态 | 更新进行中/已完成/已暂停/已取消 |
|
||||||
|
| `task_delete` | 删除任务 | 清理完成任务 |
|
||||||
|
| `task_link_info` | 关联信息节点 | 连接任务与具体信息 |
|
||||||
|
|
||||||
#### 节点类型
|
### 任务状态
|
||||||
|
|
||||||
1. **TaskNode (任务节点)**: 存储任务概述
|
- **进行中**: 任务正在执行
|
||||||
- 实体名称: `Task_当前轮次ID`
|
- **已完成**: 任务成功完成
|
||||||
- 类型: `TaskNode`
|
- **已暂停**: 任务被中断,可恢复
|
||||||
- 属性:
|
- **已取消**: 任务被取消
|
||||||
- `description`: 任务概述(精简)
|
|
||||||
- `created_at`: 创建时间
|
|
||||||
- `turn_id`: 对话轮次
|
|
||||||
|
|
||||||
2. **StateNode (状态节点)**: 存储任务状态
|
## 使用流程
|
||||||
- 实体名称: `State_进行中` / `State_已完成` / `State_已暂停` / `State_已取消`
|
|
||||||
- 类型: `StateNode`
|
|
||||||
|
|
||||||
3. **普通记忆节点**: 通过memory_commit正常插入的记忆节点
|
### 每轮对话必须执行
|
||||||
- 就是普通的实体节点,不需要特殊类型
|
|
||||||
- 例如: `成语接龙_当前成语`、`成语接龙_上一个成语`等
|
|
||||||
- 通过CONTAINS_INFO边与任务节点关联
|
|
||||||
|
|
||||||
#### 边类型
|
1. **查询人设图** (最高优先级)
|
||||||
|
|
||||||
1. **NEXT_TASK**: 连接任务节点,形成时间链
|
|
||||||
```
|
```
|
||||||
(Task_N) -[NEXT_TASK]-> (Task_N+1)
|
调用 memory_recall
|
||||||
|
参数: {"query_intent": "AI,人设,角色,性格,语气,说话风格", "depth": 2}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **HAS_STATE**: 任务节点指向状态节点
|
2. **查询工作记忆链**
|
||||||
```
|
```
|
||||||
(Task_N) -[HAS_STATE]-> (State_进行中)
|
调用 memory_recall
|
||||||
|
参数: {"query_intent": "TaskNode,工作记忆,任务链", "depth": 2}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **CONTAINS_INFO**: 任务节点指向普通记忆节点
|
3. **根据上下文生成回复**
|
||||||
```
|
|
||||||
(Task_N) -[CONTAINS_INFO]-> (普通记忆节点)
|
|
||||||
```
|
|
||||||
例如: `(Task_001) -[CONTAINS_INFO]-> (成语接龙_当前成语)`
|
|
||||||
|
|
||||||
4. **SUB_TASK**: 任务节点指向子任务节点
|
4. **更新工作记忆链** (如有必要)
|
||||||
```
|
|
||||||
(Task_N) -[SUB_TASK]-> (SubTask_M)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 强制执行规则
|
|
||||||
|
|
||||||
### 每轮对话开始时
|
|
||||||
|
|
||||||
**必须**执行以下操作:
|
|
||||||
|
|
||||||
1. **查询工作记忆链**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"query_intent": "TaskNode,工作记忆,任务链",
|
|
||||||
"depth": 2
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **检查是否有进行中的任务**:
|
|
||||||
- 如果有进行中的任务,检查是否与当前对话相关
|
|
||||||
- 如果相关,恢复任务上下文并继续
|
|
||||||
- 如果不相关,询问用户是否要暂停当前任务
|
|
||||||
|
|
||||||
### 每轮对话结束时
|
|
||||||
|
|
||||||
**必须**执行以下操作:
|
|
||||||
|
|
||||||
1. **创建任务节点**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_当前轮次ID", "relation": "is_type", "object": "TaskNode"},
|
|
||||||
{"subject": "Task_当前轮次ID", "relation": "has_description", "object": "任务概述(精简)"},
|
|
||||||
{"subject": "Task_当前轮次ID", "relation": "created_at", "object": "当前时间"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **连接到时间链**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "上一个Task节点", "relation": "NEXT_TASK", "object": "Task_当前轮次ID"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **设置任务状态**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_当前轮次ID", "relation": "HAS_STATE", "object": "State_进行中"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **如果任务包含具体信息,通过memory_commit创建普通记忆节点,并用CONTAINS_INFO边连接**:
|
|
||||||
- 先用memory_commit正常写入记忆(如成语接龙的当前成语)
|
|
||||||
- 再用CONTAINS_INFO边将任务节点指向这些记忆节点
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_当前轮次ID", "relation": "CONTAINS_INFO", "object": "记忆节点名称"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 连续性任务处理
|
|
||||||
|
|
||||||
### 识别连续性任务
|
|
||||||
|
|
||||||
以下情况属于连续性任务,**必须**维护工作记忆链:
|
|
||||||
|
|
||||||
- 游戏(成语接龙、猜谜等)
|
|
||||||
- 多步骤任务(项目开发、学习计划等)
|
|
||||||
- 需要上下文的对话(故事创作、问题讨论等)
|
|
||||||
- 被打断的对话(需要恢复上下文)
|
|
||||||
|
|
||||||
### 任务状态转换
|
|
||||||
|
|
||||||
1. **进行中 → 已完成**: 任务完成时
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_N", "relation": "HAS_STATE", "object": "State_已完成"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **进行中 → 已暂停**: 任务被打断时
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_N", "relation": "HAS_STATE", "object": "State_已暂停"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **已暂停 → 进行中**: 任务恢复时
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_N", "relation": "HAS_STATE", "object": "State_进行中"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 完整示例: 成语接龙游戏
|
## 完整示例: 成语接龙游戏
|
||||||
|
|
||||||
@ -192,35 +75,24 @@ AI: [猜测] 看起来我们之前应该没有进行过成语接龙游戏,因
|
|||||||
用户: 咱来玩成语接龙吧,我先开始,为所欲为
|
用户: 咱来玩成语接龙吧,我先开始,为所欲为
|
||||||
|
|
||||||
AI操作:
|
AI操作:
|
||||||
1. 查询工作记忆链 → 无进行中任务
|
1. 查询人设图 → 获取当前人设(如:猫娘)
|
||||||
2. 创建任务节点:
|
2. 查询工作记忆链 → 无进行中任务
|
||||||
{
|
3. 记录游戏状态:
|
||||||
"triplets": [
|
调用 memory_commit
|
||||||
{"subject": "Task_001", "relation": "is_type", "object": "TaskNode"},
|
参数: {
|
||||||
{"subject": "Task_001", "relation": "has_description", "object": "成语接龙游戏"},
|
|
||||||
{"subject": "Task_001", "relation": "created_at", "object": "2024-01-01 11:02:13"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
3. 设置状态:
|
|
||||||
{
|
|
||||||
"triplets": [
|
|
||||||
{"subject": "Task_001", "relation": "HAS_STATE", "object": "State_进行中"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
4. 创建普通记忆节点(通过memory_commit):
|
|
||||||
{
|
|
||||||
"triplets": [
|
"triplets": [
|
||||||
{"subject": "成语接龙_当前成语", "relation": "内容", "object": "为所欲为"},
|
{"subject": "成语接龙_当前成语", "relation": "内容", "object": "为所欲为"},
|
||||||
{"subject": "成语接龙_当前成语", "relation": "游戏", "object": "成语接龙"}
|
{"subject": "成语接龙_当前成语", "relation": "游戏", "object": "成语接龙"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
5. 连接任务节点到记忆节点:
|
4. 创建任务节点:
|
||||||
{
|
调用 task_create
|
||||||
"triplets": [
|
参数: {
|
||||||
{"subject": "Task_001", "relation": "CONTAINS_INFO", "object": "成语接龙_当前成语"}
|
"task_id": "Task_成语接龙",
|
||||||
]
|
"description": "成语接龙游戏,当前成语:为所欲为",
|
||||||
|
"info_nodes": ["成语接龙_当前成语"]
|
||||||
}
|
}
|
||||||
6. AI回复: "好的喵!我接:为虎作伥喵!"
|
5. 回复: "好的喵!我接:为虎作伥喵!"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 第二轮: 话题被打断
|
### 第二轮: 话题被打断
|
||||||
@ -229,15 +101,15 @@ AI操作:
|
|||||||
用户: 长门有希
|
用户: 长门有希
|
||||||
|
|
||||||
AI操作:
|
AI操作:
|
||||||
1. 查询工作记忆链 → 发现Task_001(成语接龙)进行中
|
1. 查询人设图 → 获取当前人设(猫娘)
|
||||||
2. 暂停Task_001:
|
2. 查询工作记忆链 → 发现 Task_成语接龙 状态为"进行中"
|
||||||
{
|
3. 暂停任务:
|
||||||
"triplets": [
|
调用 task_set_state
|
||||||
{"subject": "Task_001", "relation": "HAS_STATE", "object": "State_已暂停"}
|
参数: {"task_id": "Task_成语接龙", "state": "已暂停"}
|
||||||
]
|
4. 创建新任务:
|
||||||
}
|
调用 task_create
|
||||||
3. 创建新任务节点Task_002(讨论长门有希)
|
参数: {"task_id": "Task_长门有希", "description": "讨论长门有希"}
|
||||||
4. AI回复关于长门有希的内容
|
5. 回复关于长门有希的内容
|
||||||
```
|
```
|
||||||
|
|
||||||
### 第三轮: 用户要求继续游戏
|
### 第三轮: 用户要求继续游戏
|
||||||
@ -246,62 +118,65 @@ AI操作:
|
|||||||
用户: 关于刚才的成语接龙,我并不知道应该怎么接你的成语,请帮我接一下
|
用户: 关于刚才的成语接龙,我并不知道应该怎么接你的成语,请帮我接一下
|
||||||
|
|
||||||
AI操作:
|
AI操作:
|
||||||
1. 查询工作记忆链 → 发现Task_001(成语接龙)已暂停
|
1. 查询人设图 → 获取当前人设(猫娘)
|
||||||
2. 恢复Task_001:
|
2. 查询工作记忆链 → 发现 Task_成语接龙 状态为"已暂停"
|
||||||
{
|
3. 恢复任务:
|
||||||
"triplets": [
|
调用 task_set_state
|
||||||
{"subject": "Task_001", "relation": "HAS_STATE", "object": "State_进行中"}
|
参数: {"task_id": "Task_成语接龙", "state": "进行中"}
|
||||||
]
|
4. 查询信息节点 → 获取当前成语"为虎作伥"
|
||||||
}
|
5. 回复: "好的喵!上一个成语是'为虎作伥',我帮你接:伥鬼害人喵!"
|
||||||
3. 查询Task_001的信息节点 → 获取当前成语"为虎作伥"
|
|
||||||
4. AI回复: "好的喵!上一个成语是'为虎作伥',我帮你接:伥鬼害人喵!"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## API 参考
|
||||||
|
|
||||||
## 修改的文件
|
### task_create
|
||||||
|
|
||||||
1. **graph_memory_tui/core/optimized_operations.py**
|
创建任务节点,用于跟踪连续性任务。
|
||||||
- 添加了工作记忆链机制的完整说明
|
|
||||||
- 添加了图数据库结构定义
|
|
||||||
- 添加了强制执行规则
|
|
||||||
- 添加了连续性任务处理逻辑
|
|
||||||
- 添加了完整的示例说明
|
|
||||||
|
|
||||||
2. **graph_memory_demo.py**
|
```json
|
||||||
- 同步添加了工作记忆链机制
|
{
|
||||||
- 保持了人设图机制的最高优先级
|
"task_id": "Task_成语接龙",
|
||||||
- 确保了与optimized_operations.py的一致性
|
"description": "任务概述",
|
||||||
|
"info_nodes": ["关联的信息节点名称"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
### task_set_state
|
||||||
|
|
||||||
## 验证结果
|
设置任务状态。
|
||||||
|
|
||||||
所有关键机制已成功添加到提示词中:
|
```json
|
||||||
|
{
|
||||||
|
"task_id": "Task_成语接龙",
|
||||||
|
"state": "进行中" // 进行中/已完成/已暂停/已取消
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
- ✅ 工作记忆链机制
|
### task_delete
|
||||||
- ✅ TaskNode节点定义
|
|
||||||
- ✅ 强制执行规则
|
|
||||||
- ✅ 连续性任务处理
|
|
||||||
- ✅ 任务状态转换
|
|
||||||
- ✅ 完整示例说明
|
|
||||||
|
|
||||||
---
|
删除任务节点。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"task_id": "Task_成语接龙",
|
||||||
|
"delete_info_nodes": true // 是否删除关联的信息节点
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### task_link_info
|
||||||
|
|
||||||
|
关联信息节点到任务。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"task_id": "Task_成语接龙",
|
||||||
|
"info_node_names": ["成语接龙_当前成语", "成语接龙_上一个成语"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **人设图优先级**: 工作记忆链机制与人设图机制并存,人设图仍然保持最高优先级
|
1. **人设图优先级最高**: 每轮必须首先查询人设图
|
||||||
2. **强制执行**: 每轮对话必须维护工作记忆链,这是强制要求
|
2. **工作记忆链是唯一上下文载体**: 没有传统消息历史
|
||||||
3. **任务概述精简**: 任务节点的description应该是精简的概述,不要包含过多细节
|
3. **任务状态必须及时更新**: 确保状态转换正确
|
||||||
4. **信息节点**: 具体的任务信息应该存储在InfoNode中,通过CONTAINS_INFO边连接
|
4. **使用专用工具**: 优先使用 task_* 工具而非 memory_commit 处理任务相关操作
|
||||||
5. **状态管理**: 任务状态必须及时更新,确保状态转换的正确性
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 未来优化方向
|
|
||||||
|
|
||||||
1. **任务优先级**: 可以为任务节点添加优先级属性
|
|
||||||
2. **任务依赖**: 可以通过SUB_TASK边实现任务依赖关系
|
|
||||||
3. **任务超时**: 可以为任务添加超时机制,自动暂停长时间未更新的任务
|
|
||||||
4. **任务归档**: 可以实现任务归档机制,将已完成的任务移到归档区
|
|
||||||
5. **任务查询优化**: 可以优化任务查询算法,提高查询效率
|
|
||||||
|
|||||||
234
docs/架构.md
234
docs/架构.md
@ -1,43 +1,103 @@
|
|||||||
---
|
# TrulyMEM 架构设计
|
||||||
|
|
||||||
第一部分:完整架构设计文档
|
|
||||||
|
|
||||||
将此内容保存为 架构.md。
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Graph Memory TUI 架构设计
|
|
||||||
|
|
||||||
## 核心原则
|
## 核心原则
|
||||||
|
|
||||||
- 键盘驱动,零鼠标依赖
|
- 键盘驱动,零鼠标依赖
|
||||||
- 极简视觉,信息密度优先
|
- 极简视觉,信息密度优先
|
||||||
- 工具痕迹默认隐藏,需要时可展开
|
- 工具痕迹默认隐藏,需要时可展开
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
TrulyMEM-TrueHumanMEM/
|
||||||
|
├── trulymem_entry.py # 打包入口(独立可执行文件)
|
||||||
|
├── graph_memory_tui/ # 核心应用包
|
||||||
|
│ ├── app.py # TUI 主应用 (GraphMemoryApp)
|
||||||
|
│ ├── main.py # 模块入口
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── core/ # 核心逻辑
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── imports.py # 动态导入(内嵌DB vs Neo4j)
|
||||||
|
│ │ ├── embedded_db.py # SQLite 图数据库实现
|
||||||
|
│ │ ├── graph_client.py # Neo4j 客户端(可选)
|
||||||
|
│ │ ├── optimized_operations.py
|
||||||
|
│ │ ├── prompts/ # 提示词管理
|
||||||
|
│ │ │ ├── __init__.py
|
||||||
|
│ │ │ ├── prompt_manager.py
|
||||||
|
│ │ │ └── templates/
|
||||||
|
│ │ │ └── system_prompt.md
|
||||||
|
│ │ └── tools/ # 工具定义与执行
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── memory_tools.py # 6个记忆工具 + 人设/任务工具
|
||||||
|
│ │ ├── tool_executor.py # 工具执行器
|
||||||
|
│ │ └── tool_limiter.py # 调用限制器
|
||||||
|
│ ├── models/ # 数据模型
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── message.py # Message, ToolCall, ToolResult
|
||||||
|
│ │ ├── config.py # AppConfig
|
||||||
|
│ │ └── log_entry.py # LogEntry
|
||||||
|
│ ├── services/ # 服务层
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── config_manager.py # 配置持久化
|
||||||
|
│ │ ├── config_service.py
|
||||||
|
│ │ ├── chat_service.py
|
||||||
|
│ │ └── tool_service.py
|
||||||
|
│ ├── handlers/ # 事件处理
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── focus_handler.py
|
||||||
|
│ │ ├── key_handler.py
|
||||||
|
│ │ └── message_handler.py
|
||||||
|
│ ├── widgets/ # TUI 组件
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── left_panel.py # 左侧主对话区
|
||||||
|
│ │ ├── right_panel.py # 右侧边栏
|
||||||
|
│ │ ├── message_history.py # 消息历史列表
|
||||||
|
│ │ ├── message_widget.py # 单条消息组件
|
||||||
|
│ │ ├── input_box.py # 底部输入框
|
||||||
|
│ │ ├── config_section.py # 配置区
|
||||||
|
│ │ ├── operation_log.py # 图操作日志
|
||||||
|
│ │ ├── cypher_query_box.py # Cypher 查询
|
||||||
|
│ │ └── status_bar.py # 状态栏
|
||||||
|
│ └── styles/ # 样式文件
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── app.css
|
||||||
|
│ ├── components.css
|
||||||
|
│ └── messages.css
|
||||||
|
├── tests/ # 测试
|
||||||
|
├── docs/ # 文档
|
||||||
|
├── requirements.txt # 依赖
|
||||||
|
└── build_*.{bat,sh} # 打包脚本
|
||||||
|
```
|
||||||
|
|
||||||
## 布局结构
|
## 布局结构
|
||||||
|
|
||||||
### 默认视图(右侧展开)
|
### 默认视图(右侧展开)
|
||||||
|
|
||||||
┌─────────────────────────────────────┬───────────────┐
|
```
|
||||||
│ │ F2:隐藏侧边栏 │
|
┌─────────────────────────────────────┬─────────────────────┐
|
||||||
│ ┌─────────────────────────────┐ │ ───────────── │
|
│ │ F2:隐藏侧边栏 │
|
||||||
│ │ 🟠 用户: 量子力学是什么? │ │ API Key: *** │
|
│ ┌─────────────────────────────┐ │ ────────────────────│
|
||||||
│ └─────────────────────────────┘ │ 模型: deepseek▼│
|
│ │ 🟠 用户: 量子力学是什么? │ │ API Key: *** │
|
||||||
│ │ Base URL: ... │
|
│ └─────────────────────────────┘ │ 模型: deepseek-chat ▼│
|
||||||
│ ┌─────────────────────────────┐ │ ───────────── │
|
│ │ Base URL: ... │
|
||||||
│ │ 🔵 模型: 根据记忆... │ │ [图操作日志] │
|
│ ┌─────────────────────────────┐ │ ────────────────────│
|
||||||
│ │ │ │ ▼ recall │
|
│ │ 🔵 模型: 根据记忆... │ │ [图操作日志] │
|
||||||
│ │ 是的!根据记忆... │ │ 实体:... │
|
│ │ │ │ ▼ 14:32:15 recall │
|
||||||
│ │ │ │ 关系:... │
|
│ │ 是的!根据记忆... │ │ 实体: 量子力学... │
|
||||||
│ │ [工具:2次] (F3展开) │ │ ───────────── │
|
│ │ │ │ 关系: 3条 │
|
||||||
│ └─────────────────────────────┘ │ >[查询图...] │
|
│ │ [工具:2次] (F3展开) │ │ ───────────────────│
|
||||||
│ │ F4:执行Cypher │
|
│ └─────────────────────────────┘ │ >[查询图...] │
|
||||||
│ ┌─────────────────────────────┐ └───────────────┘
|
│ │ F4:执行 │
|
||||||
|
│ ┌─────────────────────────────┐ └─────────────────────┘
|
||||||
│ │ 🟠 [输入框...] │
|
│ │ 🟠 [输入框...] │
|
||||||
│ └─────────────────────────────┘
|
│ └─────────────────────────────┘
|
||||||
│ F1:帮助 F2:隐藏侧边栏 F5:清屏 F6:退出 │
|
│ F1:帮助 F2:隐藏侧边栏 F5:清屏 F6:退出│
|
||||||
└─────────────────────────────────────┴───────────────┘
|
└─────────────────────────────────────┴─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
### F2后(右侧折叠)
|
### F2后(右侧折叠)
|
||||||
|
|
||||||
|
```
|
||||||
┌─────────────────────────────────────┐
|
┌─────────────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ ┌─────────────────────────────┐ │
|
│ ┌─────────────────────────────┐ │
|
||||||
@ -49,7 +109,7 @@
|
|||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ 是的!根据记忆... │ │
|
│ │ 是的!根据记忆... │ │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ [工具:2次] (F3展开) │ │
|
│ │ [工具:2次] (F3展开) │ │
|
||||||
│ └─────────────────────────────┘ │
|
│ └─────────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ ┌─────────────────────────────┐ │
|
│ ┌─────────────────────────────┐ │
|
||||||
@ -58,6 +118,7 @@
|
|||||||
│ F1:帮助 F2:展开侧边栏 F5:清屏 F6:退出│
|
│ F1:帮助 F2:展开侧边栏 F5:清屏 F6:退出│
|
||||||
│ │
|
│ │
|
||||||
└─────────────────────────────────────┘
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
## 快捷键映射
|
## 快捷键映射
|
||||||
|
|
||||||
@ -69,103 +130,88 @@
|
|||||||
| `F4` | 右侧 Cypher 查询框获得焦点(若侧边栏折叠则自动展开) |
|
| `F4` | 右侧 Cypher 查询框获得焦点(若侧边栏折叠则自动展开) |
|
||||||
| `F5` | 清屏(保留记忆,清空显示) |
|
| `F5` | 清屏(保留记忆,清空显示) |
|
||||||
| `F6` / `Ctrl+C` | 退出程序 |
|
| `F6` / `Ctrl+C` | 退出程序 |
|
||||||
| `Tab` | 在左侧输入框和右侧可聚焦控件间循环切换焦点 |
|
|
||||||
| `↑/↓` | 浏览历史消息(仅在输入框聚焦时生效) |
|
|
||||||
| `Enter` | 发送消息(输入框聚焦时) |
|
|
||||||
| `Shift+Enter` | 输入框换行 |
|
|
||||||
|
|
||||||
## 焦点管理
|
## 焦点管理
|
||||||
|
|
||||||
- **默认焦点**:启动后焦点自动位于左侧底部输入框。
|
- **默认焦点**: 启动后焦点自动位于底部输入框
|
||||||
- **焦点切换**:`Tab` 键在输入框、配置区控件(输入框/下拉框)、Cypher 查询框之间循环切换。
|
- **F4 特殊行为**: 按下 F4 时,若侧边栏处于折叠状态,先自动展开侧边栏,再将焦点移至 Cypher 查询框
|
||||||
- **焦点指示**:当前聚焦的输入框边框高亮(例如亮橙色或加亮背景),状态栏最左侧显示当前焦点位置标识(如 `[Input]`、`[Config]`、`[Cypher]`)。
|
|
||||||
- **F4 特殊行为**:按下 `F4` 时,若侧边栏处于折叠状态,先自动展开侧边栏,再将焦点移至右侧的 Cypher 查询输入框。
|
|
||||||
- **非输入组件按键处理**:当焦点位于消息历史等非输入区域时,按下字母键可自动将焦点切回输入框并插入字符(可选实现,提升体验)。
|
|
||||||
|
|
||||||
## 视觉样式
|
## 视觉样式
|
||||||
|
|
||||||
| 元素 | 样式 |
|
| 元素 | 样式 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 用户消息 | 橙色边框 (`border: solid orange`) |
|
| 用户消息 | 🟠 橙色头部标识 |
|
||||||
| 模型消息 | 蓝色边框 (`border: solid blue`) |
|
| 模型消息 | 🔵 蓝色头部标识 |
|
||||||
| 底部输入框 | 橙色边框,聚焦时加亮显示 |
|
| 底部输入框 | 橙色边框,聚焦时高亮 |
|
||||||
| 工具调用摘要 | 灰色括号 `[工具:N次]` |
|
| 工具调用摘要 | 灰色括号 `[工具:N次]` |
|
||||||
| 右侧配置区 | 可折叠,默认折叠 |
|
| 右侧边栏 | 深色背景,宽度 70 字符 |
|
||||||
| 右侧日志区 | 格式化文本,最新在上 |
|
| 右侧日志区 | 格式化文本,最新在上 |
|
||||||
|
|
||||||
## 组件职责
|
## 组件职责
|
||||||
|
|
||||||
### 左侧区域(主对话区)
|
### 左侧区域(主对话区)
|
||||||
- **消息历史**:滚动容器,支持 Markdown 渲染。
|
- **MessageHistory**: 滚动容器,显示消息历史
|
||||||
- **输入框**:固定底部,橙色边框,支持焦点切换与高亮指示。
|
- **InputBox**: 固定底部,消息输入
|
||||||
- **状态栏**:底部显示当前快捷键提示,以及焦点位置标识(如 `[Input]`)。
|
- **LeftPanel**: 左上面板容器
|
||||||
|
|
||||||
### 右侧区域(侧边栏)
|
### 右侧区域(侧边栏)
|
||||||
- **标题栏**:显示 `F2:隐藏侧边栏`。
|
- **RightPanel**: 侧边栏容器,可折叠(宽度 70)
|
||||||
- **配置区**:可折叠,包含 API Key、模型选择、Base URL 输入框。
|
- **ConfigSection**: 配置区(API Key、模型选择、Base URL)
|
||||||
- **图操作日志**:只读,显示最近 N 次工具调用结果(格式化文本),支持滚动。
|
- **OperationLog**: 图操作日志,只读
|
||||||
- **快捷查询**:输入框 + 执行按钮,支持直接执行 Cypher 语句。
|
- **CypherQueryBox**: 直接执行 Cypher 查询
|
||||||
|
|
||||||
## 交互流程
|
|
||||||
|
|
||||||
1. 用户输入 → 左侧底部输入框 → `Enter` 发送。
|
|
||||||
2. 模型回复 → 左侧消息区,蓝色边框包裹。
|
|
||||||
3. 工具调用 → 后台异步执行,左侧显示 `[工具:N次]` 摘要。
|
|
||||||
4. 工具详情 → 按 `F3` 展开/折叠,显示完整调用参数和结果。
|
|
||||||
5. 右侧日志 → 实时更新,显示格式化后的图操作记录,最新记录置顶。
|
|
||||||
6. 侧边栏切换 → `F2` 折叠后完全消失,左侧全屏。
|
|
||||||
7. 焦点切换 → `Tab` 循环移动焦点,`F4` 快速定位到 Cypher 查询框。
|
|
||||||
|
|
||||||
## 数据流
|
## 数据流
|
||||||
|
|
||||||
```
|
```
|
||||||
|
用户输入 → InputBox.on_input_box_send_message
|
||||||
用户输入 → ChatInput → GraphMemoryClient.send_message()
|
↓
|
||||||
↓
|
GraphMemoryClient.send_message_with_history()
|
||||||
模型响应 ← OpenAI API
|
↓
|
||||||
↓
|
OpenAI API / DeepSeek API
|
||||||
有 tool_calls? → execute_tool() → Neo4j
|
↓
|
||||||
↓
|
检查 tool_calls → execute_tool() → EmbeddedGraphDB / Neo4jGraph
|
||||||
|
↓
|
||||||
|
循环调用 API 直到无 tool_calls
|
||||||
|
↓
|
||||||
最终回复 → MessageHistory (左侧)
|
最终回复 → MessageHistory (左侧)
|
||||||
↓
|
↓
|
||||||
工具记录 → GraphOpsLog (右侧)
|
工具记录 → OperationLog (右侧)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 技术实现
|
## 技术实现
|
||||||
|
|
||||||
- **框架**:textual(Python TUI 框架)
|
- **框架**: textual(Python TUI 框架)
|
||||||
- **布局**:响应式 CSS-like,右侧固定宽度 40 列
|
- **布局**: CSS-like 样式,右侧固定宽度 70 字符
|
||||||
- **状态管理**:全局状态对象,包含当前会话、消息历史、工具调用记录
|
- **状态管理**: App 实例持有配置、数据库连接、API 客户端
|
||||||
- **Neo4j 操作**:复用现有 `Neo4jGraph` 类,异步执行
|
- **数据库**: SQLite(默认内嵌)或 Neo4j(可选)
|
||||||
- **LLM 接口**:复用现有 `GraphMemoryClient`,支持流式响应
|
- **LLM 接口**: OpenAI SDK,兼容 DeepSeek 等
|
||||||
|
|
||||||
## 文件结构
|
## 数据库双模式
|
||||||
|
|
||||||
|
```python
|
||||||
|
# core/imports.py
|
||||||
|
if USE_EMBEDDED_DB:
|
||||||
|
from .embedded_db import EmbeddedGraphDB as Neo4jGraph # SQLite
|
||||||
|
else:
|
||||||
|
from .graph_client import Neo4jGraph # Neo4j
|
||||||
```
|
```
|
||||||
|
|
||||||
graph-memory-tui/
|
## 工具系统
|
||||||
├── main.py # 程序入口
|
|
||||||
├── app.py # Textual App 主类
|
|
||||||
├── widgets/
|
|
||||||
│ ├── chat_history.py # 左侧消息历史
|
|
||||||
│ ├── chat_input.py # 底部输入框
|
|
||||||
│ ├── sidebar.py # 右侧边栏容器
|
|
||||||
│ ├── config_panel.py # 配置区(可折叠)
|
|
||||||
│ ├── ops_log.py # 图操作日志
|
|
||||||
│ ├── cypher_input.py # 快捷查询输入
|
|
||||||
│ └── help_modal.py # 帮助弹窗
|
|
||||||
├── core/
|
|
||||||
│ ├── neo4j_graph.py # 现有 Neo4jGraph 类迁移
|
|
||||||
│ ├── llm_client.py # 现有 GraphMemoryClient 迁移
|
|
||||||
│ └── state.py # 全局状态管理
|
|
||||||
└── styles.css # Textual 样式定义
|
|
||||||
|
|
||||||
```
|
### 记忆工具 (6个)
|
||||||
|
- `memory_recall` - 检索记忆
|
||||||
|
- `memory_commit` - 写入记忆
|
||||||
|
- `memory_purge` - 删除记忆
|
||||||
|
- `memory_introspect` - 查看状态
|
||||||
|
- `memory_archive` - 归档记忆
|
||||||
|
- `memory_cleanup` - 清理数据
|
||||||
|
|
||||||
## 与现有 Demo 的关系
|
### 人设工具 (2个)
|
||||||
|
- `persona_update` - 更新人设
|
||||||
|
- `persona_clear` - 清除人设
|
||||||
|
|
||||||
- 保留所有核心逻辑:`Neo4jGraph`、`GraphMemoryClient`、`TOOLS` 定义、`system_prompt`。
|
### 任务工具 (4个)
|
||||||
- 移除:命令行 `input`/`print` 交互,改为 TUI 组件。
|
- `task_create` - 创建任务
|
||||||
- 新增:异步事件循环、组件状态管理、键盘快捷键处理、焦点管理系统。
|
- `task_set_state` - 设置状态
|
||||||
- 适配:`execute_tool` 返回结果同时更新右侧日志和左侧消息。
|
- `task_delete` - 删除任务
|
||||||
|
- `task_link_info` - 关联信息
|
||||||
|
|||||||
@ -22,7 +22,7 @@ LeftPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RightPanel {
|
RightPanel {
|
||||||
width: 40;
|
width: 70;
|
||||||
dock: right;
|
dock: right;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
@ -57,6 +57,18 @@ OperationLog {
|
|||||||
height: 1fr;
|
height: 1fr;
|
||||||
margin: 1;
|
margin: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
padding: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
OperationLog .log-entry {
|
||||||
|
color: $text;
|
||||||
|
margin: 0 0 1 0;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
OperationLog .log-empty {
|
||||||
|
color: $text-muted;
|
||||||
|
text-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
CypherQueryBox {
|
CypherQueryBox {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
from textual.containers import Container, Vertical
|
from textual.containers import Container, Vertical
|
||||||
from textual.widgets import Static
|
from textual.widgets import Static
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
|
from textual.css.query import NoMatches
|
||||||
from ..models.message import Message as MessageModel
|
from ..models.message import Message as MessageModel
|
||||||
|
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ class MessageWidget(Container):
|
|||||||
self._message = message
|
self._message = message
|
||||||
self._show_tool_details = False
|
self._show_tool_details = False
|
||||||
self._content_widget = None # 保存内容组件的引用
|
self._content_widget = None # 保存内容组件的引用
|
||||||
|
self._tool_details_container = None # 保存工具详情容器引用
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
"""构建消息组件"""
|
"""构建消息组件"""
|
||||||
@ -35,37 +37,42 @@ class MessageWidget(Container):
|
|||||||
# 工具调用指示器
|
# 工具调用指示器
|
||||||
if self._message.tool_calls:
|
if self._message.tool_calls:
|
||||||
tool_count = len(self._message.tool_calls)
|
tool_count = len(self._message.tool_calls)
|
||||||
|
toggle_hint = "(F3折叠)" if self._show_tool_details else "(F3展开)"
|
||||||
yield Static(
|
yield Static(
|
||||||
f"[工具:{tool_count}次] (F3展开)",
|
f"[工具:{tool_count}次] {toggle_hint}",
|
||||||
classes="tool-indicator"
|
classes="tool-indicator"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 工具调用详情(默认折叠)
|
# 工具调用详情容器 - 始终创建,但根据状态显示/隐藏
|
||||||
if self._show_tool_details:
|
self._tool_details_container = Vertical(classes="tool-details")
|
||||||
with Vertical(classes="tool-details"):
|
with self._tool_details_container:
|
||||||
for i, tool_call in enumerate(self._message.tool_calls, 1):
|
for i, tool_call in enumerate(self._message.tool_calls, 1):
|
||||||
yield Static(
|
yield Static(
|
||||||
f"工具 {i}: {tool_call.name}",
|
f"工具 {i}: {tool_call.name}",
|
||||||
classes="tool-name"
|
classes="tool-name"
|
||||||
)
|
)
|
||||||
yield Static(
|
yield Static(
|
||||||
f"参数: {tool_call.arguments}",
|
f"参数: {tool_call.arguments}",
|
||||||
classes="tool-args"
|
classes="tool-args"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 显示执行结果
|
# 显示执行结果
|
||||||
if self._message.tool_results:
|
if self._message.tool_results:
|
||||||
for result in self._message.tool_results:
|
for result in self._message.tool_results:
|
||||||
if result.tool_call_id == tool_call.id:
|
if result.tool_call_id == tool_call.id:
|
||||||
# 显示完整结果,不截断
|
# 显示完整结果,不截断
|
||||||
result_text = result.result
|
result_text = result.result
|
||||||
# 如果结果太长,只显示前1000字符,但提供完整信息
|
# 如果结果太长,只显示前1000字符,但提供完整信息
|
||||||
if len(result_text) > 1000:
|
if len(result_text) > 1000:
|
||||||
result_text = result_text[:1000] + f"\n... (共{len(result.result)}字符,按F3查看完整内容)"
|
result_text = result_text[:1000] + f"\n... (共{len(result.result)}字符,按F3查看完整内容)"
|
||||||
yield Static(
|
yield Static(
|
||||||
f"结果: {result_text}",
|
f"结果: {result_text}",
|
||||||
classes="tool-result"
|
classes="tool-result"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 根据状态设置初始显示/隐藏
|
||||||
|
if not self._show_tool_details:
|
||||||
|
self._tool_details_container.styles.display = "none"
|
||||||
|
|
||||||
def update_content(self, new_content: str) -> None:
|
def update_content(self, new_content: str) -> None:
|
||||||
"""更新消息内容"""
|
"""更新消息内容"""
|
||||||
@ -75,6 +82,27 @@ class MessageWidget(Container):
|
|||||||
|
|
||||||
def toggle_tool_details(self) -> None:
|
def toggle_tool_details(self) -> None:
|
||||||
"""切换工具详情显示状态"""
|
"""切换工具详情显示状态"""
|
||||||
if self._message.tool_calls:
|
if self._message.tool_calls and self._tool_details_container:
|
||||||
self._show_tool_details = not self._show_tool_details
|
self._show_tool_details = not self._show_tool_details
|
||||||
self.refresh()
|
|
||||||
|
# 切换显示/隐藏
|
||||||
|
if self._show_tool_details:
|
||||||
|
self._tool_details_container.styles.display = "block"
|
||||||
|
else:
|
||||||
|
self._tool_details_container.styles.display = "none"
|
||||||
|
|
||||||
|
# 更新指示器文字
|
||||||
|
self._update_indicator()
|
||||||
|
|
||||||
|
# 刷新布局
|
||||||
|
self.refresh(layout=True)
|
||||||
|
|
||||||
|
def _update_indicator(self) -> None:
|
||||||
|
"""更新工具调用指示器文字"""
|
||||||
|
try:
|
||||||
|
indicator = self.query_one(".tool-indicator", Static)
|
||||||
|
tool_count = len(self._message.tool_calls)
|
||||||
|
toggle_hint = "(F3折叠)" if self._show_tool_details else "(F3展开)"
|
||||||
|
indicator.update(f"[工具:{tool_count}次] {toggle_hint}")
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class RightPanel(Container):
|
|||||||
self.styles.width = 0
|
self.styles.width = 0
|
||||||
self.styles.display = "none"
|
self.styles.display = "none"
|
||||||
else:
|
else:
|
||||||
self.styles.width = 40
|
self.styles.width = 70
|
||||||
self.styles.display = "block"
|
self.styles.display = "block"
|
||||||
|
|
||||||
def is_collapsed(self) -> bool:
|
def is_collapsed(self) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user