Files
HomeAgent/docs/zh/plan.md
root 1cb3e87dde feat: 完整实现 NLP 三元组提取系统 + token budget 上下文分配
- 重写 extractor.go: 分句、17条 POS 模板、依存模板 + COO 链、ATT合并
- parser.go: 分句循环 + TransE 向量验证(h+r≈t)
- fallback.go: jieba POS 降级解析器
- bridge.go: nlp.Triple ↔ memory.Triple 转换
- pipeline.go: extractKeyTriples 改用 NLP 提取器, 删除5条旧前缀规则
- distill.go: docToTriples 改用 NLP 提取器
- reorgGraph: 语义相似度增强检测, 保持纯 LLM 决断
- Provider 接口加 MaxContextTokens() + 模型窗口映射表
- tokenbudget.go: 中文 token 估算器 + budget 分配(80%利用率)
- process.go/buildSystemPrompt: 按 token 预算截断 memory+timeline
2026-07-27 15:26:23 +08:00

378 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# 三元组提取系统 — 施工方案
## 一、背景与目标
### 现状
- HomeAgent 已通过 systemd 托管运行,数据目录 `/home/newqqagent`
- 已积累 **62 万条原始对话记录**125 个 raw TSV 文件)
- 当前三元组提取通过 `extractKeyTriples()` 硬编码 5 条规则完成(姓名/年龄/喜好/居住地/职业)
- `docToTriples()` 用相邻词机械拼接三元组,语义噪音大
### 目标
构建 **"句法定界 + 向量验义"** 双路三元组提取系统:
1. 用本机积累的对话语料训练一个依存句法分析模型
2. 模型以 ONNX 格式发布到 HuggingFaceGo 运行时启动时拉取
3. 依赖Go 侧仅需 `onnxruntime_go`(纯 Go binding无 CGO/Python
4. 降级:模型不可用时退回现有 gojieba POS + 模板方案
---
## 二、整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 训练流水线 (Python一次性) │
│ │
│ /home/newqqagent/memory/raw/*.tsv │
│ │ │
│ ▼ │
│ 数据导出 → 提取 user 语句 → 去重 → 句长过滤 │
│ │ │
│ ▼ │
│ Baidu DDParser (教师模型) → 银标依存树 │
│ │ │
│ ▼ │
│ UD Chinese Treebank (金标) + 银标混合 → supar 训练 │
│ │ │
│ ▼ │
│ ONNX 导出 → 上传 HuggingFace (your-org/chinese-dep-parser) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 推理流水线 (Go运行时) │
│ │
│ HomeAgent 启动 │
│ │ │
│ ▼ │
│ HuggingFace 下载 ONNX 模型 → onnxruntime_go 加载 │
│ │ │
│ ▼ │
│ 用户输入 → gojieba 分词 + POS │
│ │ │
│ ▼ │
│ ONNX 推理 → 依存树解码 (head index + dep label) │
│ │ │
│ ▼ │
│ 句法模板提取三元组 (SBV-VOB / SBV-IOB / ATT-VOB / ...) │
│ │ │
│ ▼ │
│ 融合现有向量验证层 (StaticEmbedder + TransE) → 输出三元组 │
│ │
│ 模型缺失/加载失败 → 降级 gojieba POS + 模板 │
└─────────────────────────────────────────────────────────────┘
```
---
## 三、阶段一:数据导出与探索
### 3.1 数据位置
```
/home/newqqagent/memory/raw/raw_*.tsv
格式: id \t session_id \t role \t content \t timestamp
```
### 3.2 导出脚本
脚本:`tools/export_conversations.py`
功能:
- 扫描所有 raw_*.tsv提取 `role=user` 的语句
- 基础过滤:去除纯标点/极短句(<4 MD5 去重
- 输出 JSONL`{text, session_id, timestamp, length}`
- 统计输出句长分布直方图总句数唯一句数
### 3.3 DDParser 快速验证
在导出后的数据中随机抽 500 DDParser 标注后人工抽样检查
- 依存树的句法合理性主语/谓语/宾语是否能对齐
- 常见错误模式疑问句省略句口语化表达
- 决定需过滤的句式黑名单如有
---
## 四、阶段二:训练流水线搭建
### 4.1 教师模型标注
```python
# 使用 Baidu LAC + DDParser 联合标注
# LAC分词 + 词性标注
# DDParser依存句法分析
文本: "我在杭州读书"
LAC ['我', '在', '杭州', '读书'] / ['r', 'p', 'ns', 'v']
DDParser [{'id':0,'head':2,'deprel':'SBV'}, # 我 → 在(主语)
{'id':1,'head':3,'deprel':'ADV'}, # 在 → 杭州(状语)
{'id':2,'head':3,'deprel':'ADV'}, # 杭州 → 读书(状语)
{'id':3,'head':0,'deprel':'ROOT'}] # 读书 → ROOT
```
产出格式标准 CoNLL-U
```
1 我 _ r _ _ 2 SBV _ _
2 在 _ p _ _ 3 ADV _ _
3 杭州 _ ns _ _ 4 ADV _ _
4 读书 _ v _ _ 0 ROOT _ _
```
### 4.2 训练方案
**框架**: [supar](https://github.com/yzhangcs/parser) (PyTorch, BiLSTM Biaffine)
**数据组成**:
| 来源 | 句数 | 标签 | 用途 |
|------|------|------|------|
| UD_Chinese-GSD | ~4K | 金标 | dev/test 锚点 |
| UD_Chinese-HK | ~1K | 金标 | dev/test 锚点 |
| DDParser 标注本机对话 | 10K-20K | 银标 | train 主体 |
**模型配置**:
| 参数 | |
|------|-----|
| encoder | BiLSTM |
| hidden | 200 |
| layers | 3 |
| embed_dim | 50 |
| dropout | 0.33 |
| epochs | 50 (early stop) |
| batch_size | 32 |
**预期指标**:
- LAS (标注依存): 80 (金标测试集)
- UAS (未标注依存): 85 (金标测试集)
### 4.3 ONNX 导出
```python
torch.onnx.export(
model,
(input_ids, pos_ids, char_ids),
"dep_parser.onnx",
input_names=["input_ids", "pos_ids", "char_ids"],
output_names=["head_logits", "label_logits"],
dynamic_axes={"input_ids": {0: "batch", 1: "seq"}},
)
```
模型包结构
```
dep_parser.onnx # ~15MB
vocab.json # token → id 映射
pos_vocab.json # POS tag → id 映射
config.json # 模型超参 + 版本信息
```
### 4.4 发布到 HuggingFace
```bash
huggingface-cli upload your-org/chinese-dep-parser \
dep_parser.onnx \
vocab.json \
pos_vocab.json \
config.json \
--repo-type model
```
模型页面附加信息
- 训练数据来源UD + HomeAgent 对话语料
- 模型结构与超参
- 已验证的输入/输出格式
- 降级建议
---
## 五、阶段三Go 推理集成
### 5.1 目录结构
```
internal/nlp/
├── dep_parser.go # ONNX 模型管理 + 推理
├── decode.go # 依存解码算法argmax + MST
├── triple_extractor.go # 句法模板 → 三元组
├── fallback.go # gojieba POS + 模板降级
└── model.go # 数据模型定义
```
### 5.2 模型生命周期管理
```go
// 启动时:
// 1. 检查 {dataDir}/models/dep_parser.onnx 是否存在
// 2. 不存在 → 从 HuggingFace 下载
// GET https://huggingface.co/your-org/chinese-dep-parser/resolve/main/dep_parser.onnx
// 3. onnxruntime_go.NewDynamicAdvancedModel() 加载
// 4. 加载失败 → 启用 fallback日志告警
// 5. 检查可选的版本更新(按 config.json 的 version 字段)
```
### 5.3 推理接口
```go
type DepParseResult struct {
Tokens []string // 分词结果
POS []string // 词性标签
Heads []int // 每个词的父节点索引0=ROOT
DepRels []string // 依存关系标签
}
type Triple struct {
Subject string
Relation string
Object string
Score float64
}
type Extractor struct {
parser *DepParser
embed *memory.StaticEmbedder
}
func (e *Extractor) Extract(text string) []Triple {
// 1. DepParser.Parse(text) → DepParseResult
// 2. 句法模板匹配 → 候选三元组
// 3. 向量验证cos(h+r, t))→ 过滤
// 4. 融合打分 → 输出
}
```
### 5.4 句法模板(初版)
| 模板 | 依存模式 | 先验置信度 |
|------|----------|-----------|
| SBV-VOB | `(SBV) → VOB` | 0.9 |
| SBV-IOB | `(SBV) → IOB → VOB` | 0.85 |
| ATT-VOB | `(ATT) → VOB` | 0.8 |
| SBV-POB | `(SBV) → POB` | 0.75 |
| COO | 并列结构扩展 | 0.6 |
### 5.5 降级策略
| 故障场景 | 行为 |
|---------|------|
| ONNX 模型文件不存在 | 启动时下载下载失败则进 fallback |
| onnxruntime_go 加载失败 | 日志告警 + fallback |
| 单句推理超时/panic | 返回空三元组不中断流水线 |
| 全部正常 | 优先 ONNX 模式 |
Fallback 模式沿用现有的 gojieba POS 局部模板提取POS 序列匹配不需要额外依赖
---
## 六、阶段四:集成到现有蒸馏管线
### 6.1 修改点
| 文件 | 改动 |
|------|------|
| `internal/agent/core/distill.go` | `docToTriples()` 改用新 Extractor |
| `internal/memory/pipeline/pipeline.go` | `extractKeyTriples()` 替换为新 Extract |
| `internal/agent/core/process.go` | 系统提示注入时走新提取器可选 |
### 6.2 蒸馏管线的三个触发点
```
1. 实时 (process.go): 用户输入经过 NLU 时,即时提取三元组写入 Graph
2. 周期蒸馏 (pipeline.go): 10 分钟心跳,批量处理 7天前的原始记录
3. 冷文档归档 (distill.go): 72h 未访问的文档 → docToTriples
```
新的 `Extractor` 在三个触发点统一使用上游调用方无需感知底层是 ONNX 还是 fallback
---
## 七、时间线
| 阶段 | 内容 | 预估工时 |
|------|------|----------|
| | 数据导出 + DDParser 快速验证 | 1 |
| | 训练流水线搭建 + v0.1 训练 + ONNX 导出 | 2 |
| | Go 推理集成 + 句法模板 | 2 |
| | 蒸馏管线接入 + 降级测试 | 1 |
| | HuggingFace 发布 + 文档 + 回测 | 1 |
| **总计** | | **7 天** |
---
## 八、模型维护策略
### 8.1 版本迭代
| 版本 | 触发条件 | 训练数据 |
|------|---------|---------|
| v0.1 | 初始版 | UD + 10K 本机对话 |
| v0.2 | 累计 50K 新对话 | 增量合并 retrain |
| v1.0 | 对话域 LAS 85 | 全量 + 人工抽检 |
### 8.2 更新机制
```
HomeAgent 启动 → 检查 HuggingFace 模型版本
├── 本地版本 < 远端版本 → 后台下载新模型,下次重启生效
└── 本地版本 == 远端版本 → 跳过
```
通过 `config.json` 中的 `version` 字段比对采用先下载后原子替换的策略
### 8.3 回滚
```
/data/newqqagent/models/
├── dep_parser.onnx # 当前版本 (symlink)
├── dep_parser_v0.1.onnx # 历史版本
└── dep_parser_v0.2.onnx # 历史版本
```
启动失败时自动 rollback 到上一个可用版本
---
## 九、与现有系统的交互
### 9.1 Context 向量层关联
之前讨论的 **TF-IDF 加权词向量平均** 与三元组提取是两条独立优化线路
```
三元组提取 (本计划) Context 向量 (之前已改完)
───────────────── ────────────────────────
句法定界 + 向量验义 jieba 精确模式 + TF-IDF 加权
输出: (sub, rel, obj) 输出: 300d 语义向量
用于: GraphDB 写入 用于: Context 裁剪评分
```
两者共享 gojieba 分词结果和 StaticEmbedder 词向量但不直接耦合
### 9.2 向量验证层的复用
`StaticEmbedder` `Vectorize()` 可以直接用于 TransE 验证
```go
h := embed.Vectorize(subject)
r := embed.Vectorize(relation) // 谓语子树语义中心
t := embed.Vectorize(object)
score := CosineSimilarity(h + r, t)
```
无需额外加载词向量模型 Context 层在同一向量空间
---
## 十、风险与缓解
| 风险 | 概率 | 影响 | 缓解 |
|------|------|------|------|
| DDParser 标注质量低 | | 模型学偏 | 混入 UD 金标 + 抽检 500 条先行验证 |
| 对话语料句式单一 | | 泛化差 | 数据增强依存树扰动/回译 |
| onnxruntime_go 兼容问题 | | Go 侧无法加载 | fallback 模式独立完整不影响已有功能 |
| 模型体积大 | | 启动慢/占用高 | ~15MB ONNX可接受 |
| HuggingFace 下载失败 | | 首次启动受阻 | 支持本地预下载 + fallback |