feat(distill): LightMem式逐条事实提取 prompt(P1)— 从整段摘要升级为逐条独立事实,保留全部实体细节+时间区分+推断隐含信息
This commit is contained in:
parent
11162e2f60
commit
0734ffaa5a
|
|
@ -0,0 +1,74 @@
|
|||
# P1: 织忆蒸馏引擎 LightMem 式逐条事实提取改造
|
||||
|
||||
> 2026-08-11 | 小唯 | 目标:把织忆 distill 从「整段摘要式提取」升级为「逐条事实提取」(借鉴 LightMem)
|
||||
> 优先级:P1(织忆 distill 质量的根本提升)
|
||||
|
||||
## 目标(Goal)
|
||||
|
||||
当前 `callLLM5D` 用单 prompt 做整段摘要(decisions/conclusions/actions 各≤3条),对长对话信息密度高的场景丢失细节。
|
||||
改造为 LightMem 式**逐条事实提取**:提取所有可独立成句的事实 + 保留全部实体细节 + 轻量上下文补全。
|
||||
|
||||
## 修改文件(Files to modify)
|
||||
|
||||
| 文件 | 修改 |
|
||||
|------|------|
|
||||
| `/tmp/memoryweave/go/internal/distill/engine.go` | `callLLM5D` prompt 重写 + LLMResponse 结构新增 FactsDetail |
|
||||
| `/tmp/memoryweave/go/internal/distill/engine.go` | `extractFacts` 保留(fallback),新增 facts 组装逻辑 |
|
||||
|
||||
## 实现细节(Implementation details)
|
||||
|
||||
### 1. 重写 `callLLM5D` 的 prompt(第 265-284 行)
|
||||
|
||||
新 prompt 要点(LightMem METADATA_GENERATE_PROMPT 精华移植):
|
||||
- 逐条判断:**"处理每条用户消息,判断是否含事实;除非纯问候/填充,否则都提取"**
|
||||
- 轻量上下文补全:`"My friend John is studying medicine"` → `"User's friend John is studying medicine."`
|
||||
- **保留全部具体细节**:全名/地点/事件/数字/公司名——"The Name of the Wind by Patrick Rothfuss" 不是 "a book"
|
||||
- 推断隐含信息:多个相关条目 → 推断一般模式,独立成条
|
||||
- 时间处理:mention time(说的时间)vs event time(发生时间)
|
||||
- 输出 JSON:`{"facts": [...], "entities": [...], "decisions": [...], "conclusions": [...], "is": 0.8, "su": 0.7, "pa": 0.6, "vd": 0.9, "ru": 0.7}`
|
||||
|
||||
### 2. LLMResponse 结构
|
||||
|
||||
- `Facts` 字段含义升级:从「1条整段摘要」→「多条独立事实」
|
||||
- 保持 `Decisions/Conclusions/ActionsTaken/OpenQuestions` 兼容(下游使用)
|
||||
|
||||
### 3. extractFacts fallback 保留
|
||||
|
||||
LLM 失败时仍走关键词+命名实体启发式(原逻辑不动)。
|
||||
|
||||
## 测试命令(Test commands)
|
||||
|
||||
```bash
|
||||
# 1. 编译
|
||||
cd /tmp/memoryweave/go && go build -o zhiyid-new ./cmd/zhiyid
|
||||
|
||||
# 2. 单元测试(若有)
|
||||
cd /tmp/memoryweave/go && go test ./internal/distill/ -v 2>&1 | tail -20
|
||||
|
||||
# 3. 部署
|
||||
systemctl --user stop zhiyid
|
||||
cp /tmp/memoryweave/go/zhiyid-new /home/muc/bin/zhiyid-new
|
||||
systemctl --user start zhiyid
|
||||
sleep 2
|
||||
|
||||
# 4. 功能测试 — 提交一条含多事实的对话
|
||||
curl -s -X POST -H "X-API-Key: zhiyi-dev-key-2026" -H "Content-Type: application/json" \
|
||||
-d '{"agent_id":"a06","content":"牧尘说小唯今天安装了ffmpeg用于语音转码,昨天研究了LightMem的架构,上周买了新显卡RTX 5080","metadata":{"source":"test"}}' \
|
||||
http://localhost:7821/api/v1/commit
|
||||
|
||||
# 5. 验证蒸馏质量 — 日志出现 facts ≥ 3 条 + entities 含具体实体
|
||||
journalctl --user -u zhiyid --no-pager -n 20 | grep distill
|
||||
|
||||
# 6. recall 命中验证
|
||||
curl -s -X POST -H "X-API-Key: zhiyi-dev-key-2026" -H "Content-Type: application/json" \
|
||||
-d '{"query":"ffmpeg 语音转码","top_k":3}' \
|
||||
http://localhost:7821/api/v1/recall
|
||||
```
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] go build 通过
|
||||
- [ ] 部署后 7821 健康
|
||||
- [ ] 提交多事实内容后,日志显示 `LLM facts: N` 且 N ≥ 3(旧版只有 1)
|
||||
- [ ] recall 能命中具体实体(ffmpeg/RTX 5080/LightMem)
|
||||
- [ ] 蒸馏无 fallback(日志无 `LLMEndpoint empty` / `JSON parse error`)
|
||||
|
|
@ -262,26 +262,48 @@ func (e *Engine) distillOne(input DistillInput) DistillResult {
|
|||
|
||||
// callLLM5D 调用 LLM 进行 5维评估 + 实体/事实提取
|
||||
func (e *Engine) callLLM5D(content string) (LLMResponse, error) {
|
||||
prompt := fmt.Sprintf(`你是一个对话蒸馏器。从以下内容中提取结构化的记忆片段,以JSON格式返回。
|
||||
// LightMem 式逐条事实提取 prompt(2026-08-11 移植)
|
||||
// 精华:逐条判断含事实 → 轻量补全独立句 → 保留全部实体细节 → 推断隐含信息 → 时间区分
|
||||
prompt := fmt.Sprintf(`你是一个个人信息提取器。从以下对话内容中提取所有可能的用户事实信息,以JSON格式返回。
|
||||
|
||||
重点提取:
|
||||
1. **decisions**:明确的决策结论(做了什么决定、选了什么方案、拒绝了什么)
|
||||
2. **conclusions**:最终结论或答案(问题如何解决、最终结论是什么)
|
||||
3. **actions_taken**:采取的具体行动(执行了什么命令、创建了什么文件、修改了什么配置)
|
||||
4. **open_questions**:悬而未决的问题(需要进一步确认的事项)
|
||||
5. **entities**:提到的关键实体(系统名、工具名、人名、技术名词)
|
||||
输入格式:
|
||||
[时间戳, 星期] 说话者: 消息
|
||||
...
|
||||
|
||||
注意:
|
||||
- 对话类内容(用户: xxx / 助手: yyy):提取对话的核心结论和决定,不要逐条总结
|
||||
- 决策类内容:重点记录"做了什么决定、为什么、选择了哪个方案"
|
||||
- 纯对话无结论:提取"讨论了X话题,未得出结论"
|
||||
- 每个decisions/conclusions最多3条,简洁一句话
|
||||
重要指令:
|
||||
1. 必须按顺序逐条处理每条消息。对每条消息,判断是否包含事实信息。
|
||||
- 如果包含 → 提取并改写为独立的完整句子
|
||||
- 如果不包含(纯问候、填充语、无关评论)→ 跳过
|
||||
- 不要因为信息看起来微小、琐碎或不重要就跳过。即使是小细节(如"用户今早喝了咖啡")也必须保留。只有完全无意义的(如"你好"、"哈哈"、"谢谢")才跳过。
|
||||
2. 进行轻量上下文补全,使每个事实成为清晰的独立陈述:
|
||||
- "user: 昨天买了苹果" → "用户昨天买了苹果。"
|
||||
- "user: 我的朋友John在学医" → "用户的朋友John在学医。"
|
||||
3. 保留所有具体实体和细节:
|
||||
- 完整名称: "The Name of the Wind by Patrick Rothfuss"(不是"一本书")
|
||||
- 完整地点: Galway, Ireland; 北京海淀区
|
||||
- 具体事件名: 慈善篮球赛、留学项目
|
||||
- 数字和数量: 4年前、下个月、上周
|
||||
- 公司/组织名: 某饮料公司
|
||||
4. 推断隐含信息:如果多个相关条目提到 → 可以推断一般模式(保留具体事实和推断结论为独立条目)
|
||||
5. 时间处理:区分提及时间(何时说的)和事件时间(何时发生的)
|
||||
- 相对时间(昨天、上周、X前、下个月)→ 保留相对时间并引用消息时间戳
|
||||
- 持续/永久事实 → 无需时间标注
|
||||
6. 额外提取:
|
||||
- decisions: 明确的决策结论(做了什么决定、选了什么方案、拒绝了什么)
|
||||
- conclusions: 最终结论或答案
|
||||
- actions_taken: 采取的具体行动
|
||||
- open_questions: 悬而未决的问题
|
||||
- entities: 提到的关键实体(系统名、工具名、人名、技术名词)
|
||||
|
||||
输出格式(严格JSON):
|
||||
{"facts": ["独立事实1", "独立事实2"], "decisions": ["决定1"], "conclusions": ["结论1"], "actions_taken": ["行动1"], "open_questions": ["问题1"], "entities": ["entity1", "entity2"], "is": 0.8, "su": 0.7, "pa": 0.6, "vd": 0.9, "ru": 0.7}
|
||||
|
||||
评分说明:is/su/pa/vd/ru 是 0.0 到 1.0 之间的浮点数(越高越好),不要用 0-10 整数。
|
||||
要求:除非消息完全无意义,否则提取并输出为事实。facts 要详尽,不要只给1条摘要。
|
||||
|
||||
内容:
|
||||
%s
|
||||
|
||||
只返回JSON: {"decisions": ["决定1", "决定2"], "conclusions": ["结论1"], "actions_taken": ["行动1"], "open_questions": ["问题1"], "entities": ["entity1", "entity2"], "is": 0.8, "su": 0.7, "pa": 0.6, "vd": 0.9, "ru": 0.7}
|
||||
评分说明:is/su/pa/vd/ru 是 0.0 到 1.0 之间的浮点数(越高越好),不要用 0-10 整数。`, truncate(content, 1000))
|
||||
`, truncate(content, 1000))
|
||||
|
||||
body := map[string]interface{}{
|
||||
"model": e.LLMModel,
|
||||
|
|
|
|||
Loading…
Reference in New Issue