memoryweave/IMPLEMENTATION-FIVE.md

479 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 织忆五步实施计划
> 创建时间2026-05-30
> 状态E1/E2/E3/E4/E5 全部完成 ✅
> 禁止:偷懒、随意更改变动设计语言
---
## 总览
| 阶段 | 内容 | 优先级 | 预计工期 | 状态 |
|------|------|--------|---------|------|
| **E1** | 图谱导航激活 | 🔴 高 | 1-2 天 | ✅ 完成 |
| **E2** | 多 agent 命名空间激活 | 🔴 高 | 1 天 | ✅ 完成 |
| **E3** | 增量 embedding | 🟡 中 | 1 天 | ✅ 完成 |
| **E4** | 图谱推理 | 🟡 中 | 2-3 天 | ✅ 完成 |
| **E5** | 产品 UI | 🔵 低 | 长期 | ⏳ |
---
## E1图谱导航激活
### 目标
改造 `/recall` 链路,向量搜索后接图谱 BFS 扩展,让搜索结果融合结构化知识。
### 现状
- 图谱数据1269 节点 / 31248 边(`graph/stats` 确认)
- `navigate.go` BFS 导航代码已写好,但 `/recall` 没调用
- `/recall` 当前纯语义搜索,不走图谱
### 实施步骤
#### E1.1 确认 navigate.go 接口
- 找到 `navigate.go` 的函数签名和参数
- 确认 BFS 扩展是 1 跳还是 2 跳
- 确认输出格式(节点列表还是边列表)
#### E1.2 改造 recall 链路
修改 `internal/api/routes/recall.go`
```
语义搜索 → 向量 top-K →
图谱 BFS 扩展navigate 1跳邻居
合并去重 → bge-reranker 重排 →
返回最终结果
```
#### E1.3 测试验证
```bash
# 验证图谱有数据
curl http://localhost:7821/api/v1/graph/stats
# 验证 recall 走图谱(找有图谱关联的记忆)
curl "http://localhost:7821/api/v1/recall?query=织忆图谱导航&top_k=5"
# 对比:图谱激活前 vs 激活后的召回结果差异
# 激活后应该出现更多 1 跳邻居相关结果
```
### 验收标准
- `graph/stats` node_count > 1000已有确认不变
- recall 结果中包含图谱扩展内容(通过日志或响应标记确认)
- 延迟 < 500ms可接受范围
---
## E2多 agent 命名空间激活
### 目标
Hermes OpenClaw 使用独立 namespace数据物理隔离互不串味
### 现状2026-05-31 验证)
- Hermes`agent_id="hermes-a06"` `namespace="hermes-main"`Go `deriveNamespace` 自动推导
- OpenClaw`agent_id="openclaw"``namespace="openclaw-main"`zhiyi client 显式传递
- Rust sidecar`search()` / `scan_all()` 均有 `only_if("namespace = '{}'", ns)` namespace 过滤
- 验证同一 query hermes-main default-main 返回不同 ID 的记忆隔离生效
- 历史数据`agent_id=default`)在 `default-main` hermes-main/openclaw-main 物理隔离
### 实施步骤
#### E2.1 确认当前 namespace 配置(已验证)
- Hermes 插件`agent_id="hermes-a06"` 硬编码于 `ZhiYiClient.commit/recall``~/.hermes/plugins/zhiyi/__init__.py`
- OpenClaw 插件`agent_id="openclaw"`, `namespace="openclaw-main"` 硬编码于 `ZhiYiClient``/persistent/.../memory-zhiyi/src/client.ts`
#### E2.2 验收2026-05-31 通过)
```bash
# Hermes recall → hermes-main
curl -X POST http://localhost:7821/api/v1/recall \
-H "X-API-Key: zhiyi-dev-key-2026" \
-d '{"query":"牧尘 小唯","top_k":3,"namespace":"hermes-main"}'
# → 返回 hermes-main 专属记忆
# OpenClaw recall → openclaw-main
curl -X POST http://localhost:7821/api/v1/recall \
-H "X-API-Key: zhiyi-dev-key-2026" \
-d '{"query":"牧尘 小唯","top_k":3,"namespace":"openclaw-main"}'
# → 返回 openclaw-main 专属记忆
```
### 验收标准
- [x] `namespace=hermes-main` 查询不到 `namespace=openclaw-main` 的记忆
- [x] `namespace=openclaw-main` 查询不到 `namespace=hermes-main` 的记忆
- [x] Rust sidecar commit/recall 时正确使用 namespace 过滤
---
## E3增量 embedding
### 目标
commit 时同步调用 vLLM embedding不等 Rust sidecar batch延迟从分钟级降到毫秒级
### 现状2026-05-31 验证)
- **已实现**Go `core.go:79` `Commit()` 中调用 `a.Embedder.EncodeSingle(req.Content)`
- **BGE HTTP**连接 `localhost:8000/v1/embeddings`17ms/L2 归一化
- **IPC 传输**Go vector + 文本一起发往 Rust `lancedb_insert`Rust 直接存储不重编码
- **验证**commit 后立即 recall 测试记忆排第一score=0.843),无需等待 batch
- embedder.go `MOLIFANG_API_KEY` fallback当前未启用环境无此 key
### 实施步骤
#### E3.1 确认当前 embedding 流程(已验证)
- Go `core.go:79` `a.Embedder.EncodeSingle(req.Content)` BGE HTTP 8000 1024-dim vector
- Go `core.go:123` `Vector: vector` 写入 `models.MemoryRecord`
- Go `core.go:129` `a.LanceDB.InsertMemory(mem)` IPC `lancedb_insert` 发送到 Rust
- Rust `lancedb_ops.rs:160-163` JSON 读取 vector直接写入 LanceDB FixedSizeListArray
#### E3.2 验证结果2026-05-31
```bash
# commit 后立即 recall
curl -X POST http://localhost:7821/api/v1/commit \
-H "X-API-Key: zhiyi-dev-key-2026" \
-d '{"content":"E3增量embedding测试验证commit时同步生成vector","namespace":"default-main"}'
# → memory_id: mem_1780211630617301197
curl -X POST http://localhost:7821/api/v1/recall \
-H "X-API-Key: zhiyi-dev-key-2026" \
-d '{"query":"E3增量embedding测试","top_k":3,"namespace":"default-main"}'
# → mem_1780211630617301197 排第一score=0.843 ✅
```
### 验收标准
- [x] commit 响应包含 memory_id写入成功
- [x] commit 后立即 recall 能搜到无需等待 Rust sidecar batch
- [x] recall 得分 > 0.8(证明向量有效,非零向量)
---
## E4图谱推理
### 目标
图谱真正参与推理:矛盾检测、跨 agent 共享、遗忘决策参考图谱结构。
### 现状
- 图谱存了 1269 节点/31248 边只搜不用2026-05-31 修复了 fmt.Printf debug
- E4.1: Commit 路径已接入 ConflictDetector检测率依赖 dedup 搜索覆盖
- E4.2: Recall 结果 < 3 时自动补充 shared namespace
- E4.3: Forgetter.ShouldForget 支持 graphDegree 可变参数
### 实施步骤
#### E4.1 矛盾检测(✅ 已实现2026-05-31
**实现位置**
- `governance/governance.go`: `IsContradiction()` + `DetectContradiction()` 方法导出供 routes 包调用
- `routes/core.go:API`: 添加 `ConflictDetector` 字段构造函数签名更新
- `server.go`: ConflictDetector 提到 NewAPI 之前创建避免作用域错误
**Commit 流程**
```
Commit() → dedup 搜索 top-5 相似记忆
→ 3a: exact match → merge
→ 3b: near-dup (cos ≥ 0.98) → merge
→ 3c: 对其余相似记忆运行 DetectContradiction()
→ 有矛盾 → {"status":"ok","conflicts":["矛盾内容"]}
```
**限制**依赖 dedup 搜索结果的覆盖率语义相反的陈述在向量空间中未必是 top-5 最近邻2026-05-31 修复了 `IsContradiction` 中文感知问题原用 `strings.Fields` 对中文无效改用 `unicode.Han` 字符级切分 + `containsNegCN` 否定词检测)。
#### E4.2 跨 agent 知识共享(✅ 已实现2026-05-31
**实现位置**`routes/core.go:Recall()` `pipeline.Recall()` 结果 < 3 补充 shared namespace 搜索
**流程**
```
Pipeline.Recall(query, ns, limit) → len < 3 && ns != "shared"
→ EncodeSingle(query) → LanceDB.Search("memories", vec, 5, "shared")
→ 去重已出现在 own ns 的记忆 → 追加到 resultsScore 降权 0.5
```
#### E4.3 遗忘决策参考图谱(✅ 已实现2026-05-31
**实现位置**`governance/governance.go:ShouldForget()`
**逻辑**向后兼容不传 graphDegree 则行为不变
```go
// 节点度 > 5 时,每超 1 度 + 0.03 保留分
if len(graphDegree) > 0 && graphDegree[0] > 5 {
score += float64(graphDegree[0]-5) * 0.03
}
```
**调用方待接入**`routes/admin.go:71`、`server.go:904` 需要在遗忘循环中查 GraphStore.GetEntityDegree() 注入 graphDegree 参数
### 验收标准
- [x] E4.1: Commit 响应包含 `conflicts` 字段如有矛盾)— 逻辑已接入
- [x] E4.2: Recall < 3 结果时自动补充 shared 已实现
- [x] E4.3: ShouldForget 签名支持 graphDegree 已实现调用方待接
---
## E5产品 UI
> 规划版本v1.0 | 2026-06-02
> 目标给织忆构建三层可视化入口Obsidian 插件 / 增强 CLI / Web UI覆盖日常快速查询和图谱深度探索两种场景。
---
### E5.1Obsidian 插件(优先级最高)
**为什么先做 Obsidian**
- 牧尘的笔记和记忆本来就在 Obsidian 界面切换成本最低
- 插件形式天然接入 vault 工作流不需要另外打开窗口
- 图谱可以直接嵌入笔记界面实体关系和笔记内容联动
**目标功能**
- [ ] E5.1.1 插件骨架`manifest.json` + `main.ts` + `styles.css`Obsidian 加载并注册 `ZhiYiPlugin`
- [ ] E5.1.2 记忆侧边栏面板展示最近记忆列表支持按 namespace 过滤支持分页
- [ ] E5.1.3 实体图谱视图基于 D3.js force-directed graph `/api/v1/graph/entity/{entity}/neighbors` 获取数据节点颜色区分 categoryhover 显示关系标签
- [ ] E5.1.4 记忆搜索模态框输入查询词调用 `/api/v1/search/recall`显示 top-20 结果点击跳转到记忆详情
- [ ] E5.1.5 实体详情视图选中图谱节点后 `/api/v1/memories/by-entity/{entity}` 获取关联记忆列表
- [ ] E5.1.6 蒸馏状态面板展示 `/api/v1/distill/status` `/api/v1/distill/quota`队列为非空时高亮提醒
**技术方案**
- 开发目录`~/projects/memoryweave/plugins/obsidian/`
- 插件通过 `fetch()` 调用 Go API端口 7821Go 服务需添加 CORS `Access-Control-Allow-Origin: app://obsidian.md`
- 图谱渲染D3.js v7 CDN 加载`https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js`不用本地打包
- 构建esbuild 打包 `main.ts` `main.js``npx esbuild main.ts --bundle --outfile=main.js`
- Obsidian 开启第三方插件插件文件夹挂载到 `~/.obsidian/plugins/zhiyi-memory/`
**CORS 适配Go 服务改动)**
```go
// api/middleware/cors.go — 新增
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "app://obsidian.md")
c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Header("Access-Control-Allow-Headers", "X-API-Key, Content-Type")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
// server.go — 注册 middleware
server.Use(apiMiddleware.CORS())
```
**文件结构**
```
plugins/obsidian/
├── manifest.json # Obsidian 插件清单
├── styles.css # 插件样式
├── main.ts # 插件入口,注册侧栏、图谱视图、搜索模态框
├── src/
│ ├── api.ts # 调用 Go APIfetch 封装baseURL = http://localhost:7821
│ ├── MemoryView.ts # 记忆侧边栏面板
│ ├── GraphView.ts # D3 图谱渲染
│ └── SearchModal.ts # 搜索弹窗
├── esbuild.config.mjs # 构建配置
└── README.md
```
**验收标准**
- [ ] Obsidian 加载插件后左侧出现织忆侧边栏
- [ ] 侧边栏显示最近 20 条记忆namespace=default点击展开内容
- [ ] 图谱视图能渲染至少 3 层邻居节点节点可拖拽
- [ ] 搜索模态框输入关键词返回结果<500ms
- [ ] 蒸馏队列非空时侧边栏顶部出现红色提示
---
### E5.2:增强 CLI第二优先级
**目标功能**
- [ ] E5.2.1 `zhiyi tree` 命令树形展示 namespace 下记忆结构 category 分组每条记忆显示前 60 字符摘要
- [ ] E5.2.2 `zhiyi graph` 命令ASCII art 渲染 ego-network 图谱中心节点 + 一跳邻居 + 关系标签
- [ ] E5.2.3 `zhiyi recall <query>` 命令语义搜索返回 top-10 结果显示 relevance score 和摘要
- [ ] E5.2.4 `zhiyi stats` 命令显示记忆总数namespace 分布今日新增蒸馏队列状态
- [ ] E5.2.5 `zhiyi entity <name>` 命令查询实体详情出现次数关联实体列表记忆片段
**技术方案**
- CLI 命令入口`~/projects/memoryweave/go/cmd/zhiyi-cli/`
- 使用 `cobra` 或原生 `flag` 解析子命令
- 图谱 ASCII 渲染 Unicode box-drawing 字符`┌─┬┐│├┼┤└┴┘`中心节点用 `◉`邻居用 `○`
- 调用现有 Go API 端点不直接操作存储
**文件结构**
```
go/cmd/zhiyi-cli/
├── main.go
├── cmd/
│ ├── root.go
│ ├── tree.go
│ ├── graph.go
│ ├── recall.go
│ ├── stats.go
│ └── entity.go
└── output/
├── ascii_graph.go # ASCII 图谱渲染
└── formatter.go # 格式化输出
```
**验收标准**
- [ ] `zhiyi tree` 输出格式正确树形分组摘要
- [ ] `zhiyi graph <entity>` 渲染 ASCII 图谱实体数 5 时换行正确
- [ ] `zhiyi recall` 输出 relevance score 排序正确
- [ ] `zhiyi stats` 显示记忆数namespace 分布蒸馏配额used/limit
---
### E5.3Web UI第三优先级
**目标功能**
- [x] E5.3.1 React 项目骨架Vite + React + TypeScript路由 `/memories` `/graph` `/search` `/distill`
- [x] E5.3.2 记忆列表页分页表格每页 20 id / content_preview / category / created_at / namespace支持点击展开完整内容
- [x] E5.3.3 图谱探索页全屏 D3.js force-directed graph支持缩放/拖拽/筛选category / namespace点击节点弹出详情 drawer
- [x] E5.3.4 语义搜索页输入框 + 实时结果debounce 300ms显示 relevance 和摘要高亮匹配片段
- [x] E5.3.5 蒸馏监控页进度条显示 daily used / limit队列列表episode_id / category / content_preview
- [x] E5.3.6 响应式布局支持 1280px+ 宽屏
**技术方案**
- 项目目录`~/projects/memoryweave/web-ui/`
- 技术栈Vite + React 18 + TypeScript + TailwindCSS + D3.js v7
- API Axios 调用 Go API响应式状态用 React Query 管理缓存
- 图谱 E5.1 共用 `/api/v1/graph/navigate` 和邻居接口数据结构一致
- 部署Go 服务新增静态文件中间件`/static/*` `web-ui/dist/``make build-web` 构建后自动同步
**文件结构**
```
web-ui/
├── index.html
├── package.json
├── vite.config.ts
├── tailwind.config.js
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── api/
│ │ └── zhiyi.ts # API 客户端封装
│ ├── pages/
│ │ ├── MemoriesPage.tsx
│ │ ├── GraphPage.tsx
│ │ ├── SearchPage.tsx
│ │ └── DistillPage.tsx
│ └── components/
│ ├── GraphCanvas.tsx # D3 图谱组件
│ ├── MemoryTable.tsx
│ └── DistillStatus.tsx
└── dist/ # 构建输出,由 Go 静态中间件托管
```
**Go 服务静态文件中间件**
```go
// api/middleware/static.go — 新增
func StaticFile(root string) gin.HandlerFunc {
fs := http.FileServer(http.Dir(root))
return func(c *gin.Context) {
if _, err := os.Stat(filepath.Join(root, c.Request.URL.Path)); err == nil {
fs.ServeHTTP(c.Writer, c.Request)
c.Abort()
} else {
c.Next()
}
}
}
// server.go — 注册
if opt.Mode == "dev" {
server.Use(apiMiddleware.StaticFile("../web-ui/dist"))
}
```
**验收标准**
- [ ] Web UI 能加载并显示记忆列表分页正常
- [ ] 图谱页渲染实体节点 10 缩放拖拽流畅
- [ ] 搜索页输入关键词后 1 秒内显示结果高亮匹配文字
- [ ] 蒸馏监控页显示正确的 used/limit 进度条
- [ ] 各页面在 1920×1080 1366×768 下布局正常
---
### E5 总体依赖关系
```
E5.1 (Obsidian 插件)
└── Go API 需添加 CORS 中间件
└── 构建系统需新增 esbuild 步骤
E5.2 (增强 CLI)
└── 复用 E5.1 的 CORS 无关紧要
└── 直接调用 Go API无需其他依赖
E5.3 (Web UI)
└── 复用 E5.1 的 CORS 中间件
└── Go 服务新增静态文件中间件
└── 需要独立的 Vite 构建流程
```
### 实施顺序
**第一波E5.1 Obsidian 插件)**
1. 添加 Go CORS 中间件构建部署
2. 创建 `plugins/obsidian/` 目录结构
3. 实现 `ZhiYiPlugin` 骨架注册侧边栏
4. 实现 MemoryView记忆列表
5. 实现 GraphViewD3 图谱
6. 实现 SearchModal搜索
7. 本地测试Obsidian 加载插件验证全部功能
8. 提交WORKLOG 同步
**第二波E5.2 增强 CLI**
1. 创建 `go/cmd/zhiyi-cli/` 项目结构
2. 实现 tree / graph / recall / stats / entity 命令
3. 本地测试所有子命令
4. 提交WORKLOG 同步
**第三波E5.3 Web UI**
1. 初始化 Vite + React + TypeScript 项目
2. 实现 MemoriesPage
3. 实现 GraphPage基于 E5.1 相同的 D3 数据源
4. 实现 SearchPage
5. 实现 DistillPage
6. Go 服务添加静态文件中间件
7. `make build-web` 集成到 Makefile
8. 完整测试提交
### 附录外部调研GitHub 开源参考)
| 方向 | 参考项目 | 关键技术 |
|------|---------|---------|
| Obsidian 插件 | `obsidianmd/obsidian-sample-plugin` | manifest.json, Plugin class, CustomView |
| 图谱可视化 | `react-force-graph` (底层 D3) | force-directed layout, zoom/pan |
| Web UI 图谱 | `vis-network` / `react-vis` | alternative to raw D3 |
| CLI 图谱 | `dogmap`Mastodon ASCII 工具 | box-drawing 字符布局 |
---
---
## 阶段推进规则
1. **必须按顺序完成**E1 E2 E3 E4 E5
2. **每个阶段必须测试验证后才能进入下一阶段**
3. **禁止跳过测试验证步骤**
4. **禁止偷懒:实施步骤必须逐条执行**
5. **禁止随意更改变动设计语言**阶段目标和验收标准锁定
6. **如有阻塞,记录到 BLOCKED 章节,继续下一个阶段**
---
## BLOCKED阻塞记录
| 时间 | 阶段 | 阻塞原因 | 尝试方案 |
|------|------|---------|---------|
| - | - | 无阻塞 | - |
---
## 进度追踪
| 阶段 | 开始时间 | 完成时间 | 状态 |
|------|---------|---------|------|
| E1 图谱导航激活 | 2026-05-30 | 2026-05-30 | |
| E2 agent 命名空间 | 2026-05-30 | 2026-05-30 | |
| E3 增量 embedding | 2026-05-30 | 2026-05-30 | |
| E4 图谱推理 | 2026-05-31 | 2026-05-31 | E4.1/E4.2/E4.3 已实现E4.3 调用方已接入extractTopEntityDegree
| E5 产品 UI | 2026-06-02 | - | 🔨 | E5.1 E5.2 待启动 |
---
*最后更新2026-06-02E5 规划完成*