Files
HomeAgent/docs/zh/triple-extract-attention.md
JianFeeeee 931c803421 refactor(docs): 核心仓文档与介绍站展示名改名为 HologramAgent
只改仓库内文件的纯展示品牌名与已随批 2 落地的安装路径;运行时契约、仓库 URL、
域名、环境变量、可执行名、插件名一律不动。

一、纯展示名(HomeAgent → HologramAgent,共 44 处 / 18 个文件)
- README.md 4、README_EN.md 4(标题、client 变体说明、Windows 安装包名、AGPL 段)
- docs/ 8 个文件 14 处(git-branching 1、ci-cd-runbook 1、commondev-design 3、
  commondev-agent-runtime-design 4、deploy-runbook 1、legacy-issues 1、
  prod-migration-checklist 1、triple-extract-attention 2)
- assets/docs/ 6 个文件 14 处(zh/en 的 OVERVIEW 2+2、ARCHITECTURE 2+2、
  PLUGIN_DEV 3+3)
- assets/branding/mascot-xiaozhai-guide.md 2 处(身份行、标志名)
- site/index.html 6 处(title、meta description、og:title、logo alt、页头字、
  插件卡片文案 "HologramAgent 接入 AgentMail")

二、安装路径对齐批 2 产物(共 10 处)
- /usr/lib/homeagent → /usr/lib/hologram(7 处:README 2、README_EN 2、
  multimodal-space 3)
- /usr/share/doc/homeagent → /usr/share/doc/hologramagent(3 处:README 1、
  README_EN 1、multimodal-space 1;注意 Doc 路径带 agent 后缀)
- site/index.html 的 19 处 HomeAgent 里,13 处是 github.com/JianFeeeee/HomeAgent
  仓库 URL,属红线整串保留;只改 URL 之外的展示文案(其余 13 处零改动)

三、明确不改(红线 / 一致性冲突)
- site/index.html 的 23 个 github.com/JianFeeeee/homeagentsdk 外链(21 条
  example + 2 条纯仓库),SDK 仓改名属批 4-b,一个未动
- 核心仓仓库 URL、sdk./introduce.homeagent.jianfgit.xyz 域名、HOMEAGENT_ 前缀、
  homed/waiter/hmapdev、.hmap、homeagent-mail-bridge 等插件名、third_party/、
  internal/ 与 cmd/ 全部零改动
- ~/.homeagent 路径本批保持原样:cmd/waiter/main.go:109、daemon.go:290 硬编码
  仍为 .homeagent,全仓无 .hologram 命中;照规格改文档会指向程序不用的目录,
  需与代码侧那条线一起改(遗留项,已单独上报)

判据:文档区展示名残留归零(44 文件)、红线计数与 base e3584bb 逐项相同、
三条变异均被咬住且还原后复绿。
2026-10-21 17:00:48 +08:00

759 lines
31 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.

# 三元组抽取:注意力模型方案
> 2026-10-03。目标是**推理时零 LLM**——一次 LLM 标注,训一个专用网络。
## 一、为什么必须是「注意力学任务结构」而不是更好的向量
本会话的实测给出了两条独立证据:
| 实验 | 结果 |
|---|---|
| 词法切分(纯规则) | 值抽取 75%,字段级仅 25% |
| chineseclip 向量配对(冻结编码器) | **0/5**,连「值班人」↔「值班人」都配不上 |
第二条是决定性的:`值班人` 和 `值班人` **字面完全相同**,余弦却输给 `端口`(0.5531)。
chineseclip 把一句话压成一个 512 维固定向量,**没有针对这个任务的注意力**——
它不知道「哪个 token 是属性名、哪个是值、哪个是连接词」。
而这正是 transformer 注意力的本职:
```
「第112批周四凌晨2点·停机4分·回滚v2.29.5·灰度10%观察48分后放50%」
第112批(主语) 周四凌晨2点 · 停机4分 · 回滚v2.29.5 · 灰度10% 观察48分后放50%
└─ 头应该关注 ─┘ └ 值 ┘└ 值 ┘└ 值 ┘└ 值 ┘ └值┘
「停机」只关注「4分」 「灰度」只关注「10%」
```
同一句话里,不同的注意力头分工不同 —— 这是固定向量做不到的。
## 二、任务形式的选择
### 方案 B(序列标注)—— 采用
给每个字符打一个标签:
```
标签集(9 类)
O 其它/连接词(从、改为、轮换到、·)
B-DIM 属性名开头
I-DIM 属性名内部
B-VAL 值开头
I-VAL 值内部
B-SUBJ 主语开头(多主语记录用)
I-SUBJ 主语内部
PAD 填充
「连接池从 32/64 扩到 128/256」
连接池从 → B-SUBJ I-SUBJ I-SUBJ O
32/64 → B-VAL I-VAL I-VAL I-VAL I-VAL
扩到 → O O
128/256 → B-VAL I-VAL I-VAL I-VAL I-VAL I-VAL
```
**为什么不用方案 A(在受控维度名里分类)**:
- 现有标注里维度名 17 种,但**6 种只出现 1 次(35%)** —— 分类器无法表达词表外的新维度
- 而维度名正是要**生成**的东西,不是要**选择**的东西
**为什么不用指针网络**:抽取式要先枚举候选片段对,而实测候选池的真值召回只有
20%(51/259)—— 枚举不出来就没法抽。序列标注直接在字上标,绕开这个漏。
## 三、模型规格(CPU 可训)
```
分词 字符级(中文无需分词,且字符级不会切错「值班室分机号」)
词表 从训练集统计,取频次 ≥2 的字 + 特殊符,约 1500~2500
嵌入 128 维
层数 4
注意力头 4(2 头 "看属性名↔值" 的配对,2 头自由学)
FFN 256
Dropout 0.2
总参数 ≈ 1.2M
```
**为什么这么小**:CPU 12 核、17 GB 内存、没有 GPU(P4 掉线)。
实测 2968 个训练 token 的规模下,1.2M 参数已经过参数化的风险很低,
而这个规模**不需要预训练**——从零训,因为:
- 任务词汇封闭(运维数字/端口/批次/分机号)
- 预训练模型的 2~3 亿参数是纯浪费,且 CPU 上前向都慢
**「从零训」在这里不是妥协,是对的**。预训练权重带来的是通用语言知识,
而这个任务要学的是**版式**(哪里是属性名、哪里是值)—— 版式没有通用先验。
## 四、递归调用:一句话 → 多组三元组
序列标注天然给出多组三元组(每个 `B-DIM … B-VAL` 是一个),
不需要"递归":
```
输出标签序列后:
扫描 B-DIM / I-DIM 聚成属性名片段
扫描 B-VAL / I-VAL 聚成值片段
按顺序配对:第 k 个属性名 ↔ 第 k 个值
全部转成受控维度名(NormalizeDimension)
全部转成主语(若标了 B-SUBJ)
```
「递归」只在一种情况下需要:**值里还嵌套属性**(如 `连接池容量=128/256` 里的
`连接池` 既是主语又是维度的一部分)。第一版不做,遇到时记日志。
## 五、训练数据现状与缺口
```
来源 样本 字段数 有效信号
真库 ha-c(已有) 90 260 70 条多字段句
生产库(标注中) 429 ? 仅 20 条含「数字+单位」
────────────────────────────────────────────
合计 519 ? ~90
```
**缺口**:序列标注需要约 5000 条句子 × 35 字 ≈ **17.5 万 token**,
中文 NER 的典型规模是 10 万~200 万 —— 差一个数量级。
**这决定了它现在不能训出可用模型**。可行的路径:
1. 扩充数据源:真库 188 条 + 生产库 1295 条 + 对话归档 + 历史邮件
(预计能到 3000~5000 条句子)
2. 或者**改任务**:既然值抽取已 75%(词法可做),
网络只学「给已知的值配维度名」—— 这是个二分类任务,
样本数 = 真值数 × 负样本倍数,比序列标注省一个数量级
第 2 条是我目前更倾向的,因为它把难题缩小到了「值↔维度配对」,
而那正是词法做不到、注意力擅长的部分。
## 六、判据(照本会话惯例:每条都有变异自证)
| 判据 | 内容 |
|---|---|
| 标签对齐 | 字符级标注与序列标签严格一一对应,无偏移 |
| 幻觉闸门 | 产出的值必须**原样出现在原句**(与 LLM 路径同一道闸) |
| 泛化 | 留出集上字段级 F1;词法基线是 25% |
| **不得低于基线** | 网络在留出集上必须超过词法的 25%,否则说明网络没学到东西 |
| 三类可分 | 同值对 / 异值对 / 跨维度对的分布必须可分(否则学到的只是长度先验) |
| 确定性 | 同输入连跑 3 次输出完全一致(**LLM 路径目前做不到**) |
最后一条是这个方案最大的隐性收益:**确定性**。LLM 路径在本会话实测
「同批 146 条跑两次差 40%」(后来查明很可能不是随机性,而是 ollama 0.31.1
的 schema grammar 污染 bug —— 但即使修好,神经网络推理也是逐位确定的)。
## 七、当前阻塞
标注在跑(`/tmp/label-work/labeled.jsonl`,429 条,约 3~4 小时)。
**标注质量是第一道关**——本会话已经踩过 ollama schema 污染,
所以每条标注都带「值必须原样出现在原句」的闸门,且产出与原句一起落盘,
以便事后逐条复核。
## 八、数据实测:标注分两层,网络只该学其中一层
把 260 个 LLM 标注逐条与原句对齐后(属性名「值」都要能在原句里定位):
```
字面可对齐 212 / 260 (82%)
主语派生 48 / 260 (18%) ← 网络学不了,也不该学
```
### 主语派生的那一类长什么样
```
「第130批 v2.31.5 评审通过·采样10%」
→ 版本=v2.31.5 属性名「版本」不在原句(原句只有 v2.31.5)
→ 批次号=130 属性名与值都不在原句(原句是「第130批」,N 藏在「第」与「批」之间)
```
逐维度看:
| 维度 | 条数 | 对齐失败率 | 性质 |
|---|---|---|---|
| 排期 / 容量预警 / 告警规则数 / 值班手册版本 / 评审通过 / 停机时长 | 178 | **0%** | 字面可对齐 |
| 版本 / 版本线 | 32 | 97% | 主语派生(原句只有 `v2.31.5`) |
| 批次号 / 批次 | 40 | 100% | 主语派生(`第N批` 的 N 是**结构位置**,不是连续片段) |
| 发布窗口 / 连接池容量 / 等待队列告警阈值 / 状态 | 8 | 100% | 主语派生 |
★ **「批次号=130」是最有说服力的一例**:值 `130` 在原句里根本不是连续片段
(它是「第**130**批」的中间),任何基于 span 的标注法都对齐不了它。
### 由此确定分工
| 类型 | 占比 | 处理者 | 理由 |
|---|---|---|---|
| 字面可对齐 | 82% | **注意力网络** | 属性名与值都在句子里,注意力学的是「哪个 token 是属性名」 |
| 主语派生 | 18% | **纯规则**(`DeriveSubjects` 已能推出主语,从主语结构直接取) | 不在句子表面,神经网络无输入可用 |
★ 这条分工让训练数据量需求从「17.5 万 token」降到「约 8.6 万 token」,
更关键的是**学的是真问题**(版式识别)而不是学一个学不会的东西。
## 九、还发现一条 LLM 标注质量问题
```
值 = 整句:9 / 260 (3%)
「第115批周二凌晨2点·停机6分·回滚v2.28.1·灰度5%观察63分后直放100%」
→ 版本=115批周二凌晨2点·停机6分·回滚v2.28.1·灰度5%观察63分后直放100%
```
模型把整句当成「版本」的值。现有闸门拦不住它(值确实「原样出现在原句」——
它就是整句)。**新增判据:值的长度不应超过句子的 60%**,否则视为坏标注。
## 十、推理侧与首轮实测(数据不足,模型不可用)
推理侧已写好(`scripts/triple-extract/predict.py`),形态是**网络 + 规则混合**:
```
网络 负责「句子里表面就有属性名与值」的 82%
规则 负责「值藏在主语结构里」的 18%(批次号=第N批里的 N、版本=v2.31.5)
闸门 两边共用同一道(值必须原样出现在原句;值长度 ≤ 句子 60%;
属性名必须能在原句里对上 —— 网络不比 LLM 宽松)
```
### 规则部分:实测全对
```
「第114批周二凌晨1点·停机4分·回滚v2.28.4·灰度10%观察70分后放50%」
→ 第114批|批次号=114 ✓
→ 第114批|版本=v2.28.4 ✓
→ 第114批|发布窗口=凌晨1点 ✓
```
这三类正是网络学不了的那 18%,规则路径有效。
### 网络部分:实测不可用
同一句的网络输出:
```
[ 0] B-DIM '值' ← 应是「值班室分机号」
[ 1] I-DIM '班'
[ 4] B-DIM '机'
[ 5] B-VAL '号'
```
**位置全错**(标在第 0/1/4/5 个字符,实际属性名在 0-5、值在 7-10),
且 `值班室分机号 4324,值班 老周` 里的 `4324` 完全没被标出。
原因不是实现 bug,是**数据量**:47 条训练样本 × 35 字,
留出 19 条上 F1 0.71 且 ep20 后开始回落(已在过拟合)。
训练集的标签分布是 `O:771 / I-VAL:592 / I-DIM:565 / B-VAL:192 / B-DIM:187` ——
模型连「B 只在 span 首字符出现」这个最基本的 BIO 约束都没学稳。
### 一个训练脚本的 bug(顺带修掉)
`OneCycleLR(total_steps=epochs * len(train) // bs)` 在步数不匹配时
会抛 `Tried to step 151 times. The specified number of total steps is 150`
—— 训练跑到一半崩掉。改用 `ceil` 并显式设 `pct_start`。
### 判据 4(网络必须超过查表基线 71%)现在的状态
| 方案 | 留出表现 |
|---|---|
| 规则 + 查表 | 71.0%(probe_attr_name.py 判据 4) |
| 词法切分 | 字段级 25% |
| **网络(47 条训练)** | **位置全错,不可用** |
⇒ 数据量从 47 提到几百条之前,网络这条路的结论无法给出。
标注在跑(256/425),完成后重训。
## 十一、扩数据前的诊断:瓶颈是标注质量,不是数据量
生产库标注 128 条 / 238 个字段,逐字段检查:
```
正常 157 (66%)
值 = 整句或近整句 43 (18%) ← 坏标注
值占 1/3~60%(可疑) 38 (16%) ← 边界可疑
含坏值的样本 43 条,其中 37 条整条都是坏的
```
典型形态:
```
「第115批周二凌晨2点·停机6分·回滚v2.28.1·灰度5%观察63分后直放100%」
→ 版本 = 115批周二凌晨2点·停机6分·回滚v2.28.1… ← 整句当值
「见 picture / see_video / listen 三个,进程未崩,日志正常」
→ 操作 = see_picture / see_video / listen … ← 列表当值
→ 进程状态 = 未崩 ← 这条是对的
「AgentMail 邮件通道插件 v0.1.0」
→ 插件名称 = AgentMail 邮件通道插件 v0.1.0 ← 整句当值
→ 插件版本 = v0.1.0 ← 这条是对的
```
★ **丢掉坏字段后救不回来**:146 条丢弃样本里只救回 2 条 ——
坏标注通常整条都是坏的,所以「部分修复」这条路无效。
### 根因:为绕开 schema bug 改用纯提示词约束
`format` schema 本会禁止超长的 value;改成提示词约束后没有了强制力。
**代价是 18% 的坏标注**(代价换来的是不再返回上一次的输出,见 §七)。
修法(提示词加两条 + 闸门加一条):
- value 必须是记录里**一段连续文字**
- value 长度**不得超过 12 字**,不得接近整句
- 找不到短值就跳过该字段(宁可少记)
## 十二、批处理标注:省 47%
单条 28 秒的构成(真实 timing):
```
prompt_eval 13~14 秒 806 字符固定前缀,与记录数无关
eval 2~14 秒 与输出长度成正比
```
所以批内每条只摊到 eval 那部分:
```
逐条: 14 + 14 = 28.0 秒/条
十条一批: 14 + 10×14 = 15.4 秒/条 (省 45%)
二十条一批: 14 + 20×14 = 14.7 秒/条 (省 47%)
```
⇒ 2000 条从 15.6 小时降到 **8.2 小时**。
批处理的额外风险是**模型漏掉某个序号**,所以解析时要求
「必须为输入的每个序号都给一条结果」,缺的就记 `missing` 而不是静默丢弃。
## 十三、修正一个我自己的错误分类
中途我用正则统计过一次「生产库 72% 的记忆不可拆」——
**那个判据是错的**:它只匹配数值/版本/批次,于是把
```
portal-nginx 容器已 Exited 两周,不是它提供服务
→ (portal-nginx, 状态, Exited) (portal-nginx, 持续时间, 两周)
```
这类判成"不可拆"。
★ 通用 Agent 的句子几乎都能拆 —— 每句都有主体(名词短语)与谓词。
「72% 不可拆」的结论作废,撤回。
**判据本身比结论更值得记**:一个正则分类器给出的分布,
先怀疑分类器,再相信分布。
## 十四、重标过程:四个坑全是「我的实现」而非「模型」
改进标注器时连续踩了四个坑,**每一个的第一反应都会归咎于模型或数据,
但真因都在自己的代码里**。逐个记下来,因为它们都会在下一轮重来一遍。
### 坑 1:批处理(12 条一批)全军覆没
试「一条请求标 12 条」(本该省 47%),结果 **11/11 全 error**。
真因:qwen3:1.7b 处理不了「给多条记录分别输出」这个嵌套结构
(`{"results":[{"i":0,...},{"i":1,...}]}`),输出是一长串 `}`。
⇒ **批处理这条路要更大的模型**。1.7b 的结构化输出上限就是单个对象。
我应该在写之前就想到这点 —— 之前「多示例 vs 单示例」的实测已经证明
它的结构化能力很窄。
### 坑 2:加长提示词让模型失控
版本 B(加「≤12 字」等多条约束 + 换示例)→ 模型输出变成
`<record>` 碎片循环。
版本 C(**只加一条规则、不动示例**)→ 正常。
★ 与之前「加 nonce 后出现 JSON 截断」是同一类问题:
**qwen3:1.7b 的结构化输出对提示词形态极其敏感**。
教训:约束要加,但**一次只加一条**,且必须逐版本实测。
### 坑 3:JSON 解析的贪婪匹配
```python
re.search(r'\{.*\}', raw, re.S) # ← 贪婪到最后一个 }
```
模型输出「`{"fields":[]}` …说明文字… `{"fields":[]}`」时,
匹配到的是两段连在一起的非法 JSON ⇒ **100% 判 parse 失败**,
而原始输出其实完全正常。
改成「遇到 JSONDecodeError 就补闭合符」也**不对** ——
这里的错误是 `Extra data`(尾部多了内容)不是缺括号,追加只会越补越糟。
正确做法:从每个 `{` 起点**向右逐个 `}` 枚举结束位置**,
第一个能 `json.loads` 成功的片段就是答案。
★ 这个坑最隐蔽:症状(parse 失败 100%)看起来像模型崩了,
实际是我的解析器太贪心。**原始输出要先看,再怀疑模型**。
### 坑 4:无值记录会让模型崩溃
「CodeGraph安装任务」(12 字、无任何数值)→ 模型输出
`实现的的第10000年发布\n</record>CodeGraph的\n</record>...` 的碎片循环。
这类句子本来就没有可拆字段(零字段是正确答案),
送进模型只是浪费 30 秒并产出垃圾。
⇒ 标注前先按「是否含值形态」预筛(数值/版本/批次/时刻/日期/区间),
无值的直接跳过。**这同时省掉了批处理那 47% 省不下来的时间** ——
因为无值记录占比不低(生产库 429 条里「含数字+单位」只有 20 条)。
## 十五、重标 vs 旧标注的预期差异
```
旧标注(label.py) 429 条 → 149 条有字段(34.7%),幻觉 48 个
字段级合规率 68%(32% 的值超 12 字 = 整句当值)
新标注(label3.py) 跳过无值记录 + 12 字闸门前置 + 修好的解析
预期:字段级合规率接近 100%(闸门拦在入库前)
预期:parse 错误基本消失
```
★ 关键差别:旧版把超长值**记下来再由训练脚本丢弃**(浪费标注);
新版在**标注时就拦掉**(产出即合规),所以同样的时间里有效样本更多。
## 十六、合并 GUI 分支后的验证
```
origin/feature/gui-starmap-pulse-vendor-align 26 文件 / +6089 行
与记忆侧工作零文件重叠 → 自动合并无冲突
go build ./... 通过
go test -short ./cmd/... cmd/homed ok cmd/waiter ok
go test -short ./internal/memory/... 全绿
npm test(cmd/gui,9 个 .mjs) 38 项全部通过
```
★ 合并后发现**那个分支自己就编译不过**(`cmd/waiter/device.go:743 undefined: mag`)——
不是合并冲突,是 `0d8b129` 自带的 Go if-init 作用域错误。已修。
## 十七、现有可训练数据的画像
76 条可对齐样本 / 206 字段 / 59 种维度名:
```
排期 38 ███████████████████
容量预警 37 ██████████████████
告警规则 36 ██████████████████
值班手册 33 █████████████████
…其余 55 种共 62 个字段,其中 48 种只出现 1 次(81%)
```
★ **81% 的维度名只见过一次,这是好事而非坏事**:
它说明任务不能退化成「记住这 59 个名字」(那样必过拟合),
而必须学**结构** —— 哪段是属性名、哪段是值、它们怎么相邻。
这与 `find_dim_in` 的思路一致:属性名可以模糊匹配(`告警规则数`↔`告警规则`),
所以网络要学的是「找到那个**可以当属性名的片段**」,不是「输出这个词」。
## 十八、并行三个任务导致的资源事故(本轮教训)
同时跑了三件事:网络训练、标注重标、GUI 测试。结果:
```
load average 33.5 (12 核,超载 2.8 倍)
%CPU ELAPSED COMMAND
489 01:38:58 llama-server ← 见下
332 04:51 python3(训练)
103 01:42:53 Emulator ← 安卓模拟器,与本工作无关
99 01:43 python3
84 09:38:33 node
```
★ **两个真实问题**:
1. **训练被拖到 5 分钟还没出 ep10** —— 抢不到 CPU。
2. **标注重标 30 分钟只完成 1 条** —— 因为 llama-server 在抢。
而那个吃掉 489% CPU、跑了 **1 小时 39 分**的 `llama-server` 是**残留进程**:
`api/ps` 只列出 `qwen3:1.7b`,但它对应的是上一轮已卸载的模型
(ollama 的 `keep_alive` 到期后没回收进程)。
⇒ 停掉残留进程后训练立刻开始出结果(ep10 F1 0.695)。
### ★ 修正:并行不是主因,机器上长期跑着别的服务才是
停掉残留进程后 load **只从 33.7 降到 28.3**,仍然远超 12 核。
继续查占用者:
```
%CPU ELAPSED COMMAND
359 09:57 python3 ← 我的训练,正常(12 核的 3 倍)
173 01:47 python3 ← Emulator 的子进程
103 01:47:58 Emulator ← 安卓模拟器,/opt/clt26/…
84 09:43:39 node
```
**这台机器上长期跑着安卓模拟器等非本项目服务**,
它们才是 load 26 的主因。并行三个任务只是雪上加霜,不是根本原因。
### 教训
**残留进程比崩溃的进程更难查**:`api/ps` 说模型只有 1.7b,
但 `ps` 显示还有一个 llama-server 占着 489% ——
两个工具的视角不一致,光看 API 会漏掉。
**归因要查到占用者本人**:「load 高」有很多可能(我的任务/别人的服务/
残留进程/内核 IO 等待),我先归因为「并行三个任务」是**过早下结论** ——
正确做法是先 `ps --sort=-pcpu` 列出占用者,再判断谁是谁。
★ 这是本会话第三次犯「先归因后核实」:
① 「模型随机性」实际是 ollama schema 污染
② 「72% 记忆不可拆」实际是我的正则判据错
③ 「并行三任务导致高负载」实际主因是机器上别人的服务
## 十九、判据 4 的结果:**网络路线不成立**
76 条可对齐样本训练 80 epoch(CPU,约 25 分钟),留出集曲线:
```
ep10 F1 0.695 ep30 F1 0.704 ← 最佳
ep20 0.699 ep40 0.699
ep30 0.704 ep60 0.701
ep50 0.700 ep70 0.702
训练损失 ep70 时已降到 0.022
```
**从 ep10 起就横盘在 0.70,而训练损失一直在降** —— 典型的过拟合:
模型在记住训练集,泛化不再改善。
### 判据 4:网络 vs 查表基线
| 方案 | 字段级 F1 | 说明 |
|---|---|---|
| **规则 + 查表** | **71.0%** | probe_attr_name.py 的判据 4 |
| 网络 + 规则 | **42.0%**(P 0.452 R 0.393) | 网络产出 303 个字段,**约一半是误报** |
| 词法切分 | 字段级 25% | 纯规则 |
| 纯网络(推理实例) | 几乎为 0 | 输出 `采样=.` / 零字段 |
**网络远未超过 71% 的门槛**,判据 4 失败。
### 定性观察
```
「第130批 v2.31.5 评审通过·采样10%」
规则(2 条全对):批次号=130 版本=v2.31.5
网络(1 条垃圾):采样=.
「值班室分机号 4324,值班 老周」
两者都是 0 字段
```
网络把句点标成了值,把 `4324` 整个漏掉 —— 它连「数字是值」这个最基本的
先验都没学到(训练集里数字开头的值有 121 个,够多了)。
## ★ 结论:数据量还不够,架构不是瓶颈
**我不能据此说「注意力学不了版式」** —— 76 条训不出东西,
和 2000 条训不出东西,是两回事。
诚实的表述是:
| 数据量 | 网络表现 | 结论 |
|---|---|---|
| 76 条 | F1 0.42(低于查表 71%) | 不足以判断架构 |
| 目标 2000 条 | 未测 | — |
要拿到「架构行不行」的答案,需要**至少 500~2000 条**,
即把标注从 76 条扩到那个量级。而按实测速率:
```
CPU 单条标注 28 秒 → 2000 条 = 15.6 小时
(批处理不可行:1.7b 处理不了嵌套结构,见坑 1)
```
## 因此下一步的选择
| 选项 | 代价 | 收益 |
|---|---|---|
| **A 继续标注到 2000 条** | 15.6 小时(可后台跑) | 拿到架构的确定答案 |
| **B 放弃网络,走规则 + 查表** | 0 | 立即可用,但停在 71%(且那 29% 需要人工) |
| **C 换更大的标注模型** | 未知 | 批处理可行时能省 47%;qwen3-vl:2b 已在本地 |
我倾向 **A + C 组合**:用本地的 `qwen3-vl:2b` 试批处理(它是 2B,
结构化能力比 1.7b 强),若批处理可行则 2000 条降到 8 小时以内。
## 二十、批处理标注:确认不可行
试了两种提示词、两个模型,全部失败:
```
qwen3-vl:2b 简明格式 53s 输出 0 字(空字符串)
qwen3:1.7b 简明格式 421s 输出「2. 2024-04-15 15:00:00」重复 13 次
qwen3-vl:2b JSON 数组 90s 输出 0 字
qwen3:1.7b JSON 数组 — 输出一长串 }
```
★ **这不是提示词问题** —— 两种格式(简明指令 / JSON 数组)都是同样结果。
1.7b 的结构化输出能力上限就是「单个对象」,给它「多条记录的数组」它会
陷入循环;2B 直接不输出。
⇒ **批处理这条路断了**,2000 条只能逐条标(CPU 单条 28 秒 → 15.6 小时)。
### 一个连带推论
既然本机两个可用模型都无法可靠产出结构化结果,
那么**「用 LLM 生成训练数据」这条路的天花板就在这里** ——
除非换成 API 模型或更大的本地模型(27B 的 `qwen3.8:27b-iq2m` 见过但未测,
它占 489% CPU 且很可能更慢)。
这改变了 A 方案的性价比:
```
原估计 2000 条 = 15.6 小时(以为批处理能省 47%,实际不能)
实际 2000 条 = 15.6 小时,且无法再压缩
```
## 二十一、数据量翻倍反而更差 —— 推翻「数据不够」
```
76 条(纯运维域) 最佳 F1 0.704(ep30)
122 条(运维+通用混合) 最佳 F1 0.637(ep10,之后一路降到 0.612)
门槛(规则+查表) 0.710
```
**加了 46 条数据,F1 反而掉了 0.067。**
### 根因:两个域的分布互相冲突
```
ha-c(运维域) 67 条 维度名 10 种 179 字段 只1次 4 (36%)
排期40 容量预警39 告警规则数37 值班手册34
生产库(通用域) 55 条 维度名 78 种 83 字段 只1次 73 (94%)
端口2 prompt2 size2 充电时间2 …
```
★ 合起来 122 条后,模型面对的是**两个互相矛盾的分布**:
- 运维域:**高度集中**,记住「排期/容量预警/告警规则数」就有 179 个字段
- 通用域:**几乎全是新维度名**(94% 只出现一次),必须靠泛化
模型被这种混合撕裂 —— 学运维域就在背词汇(到通用域全崩),
学通用域就要泛化(运维域那 179 个密集样本又把它拽回记忆)。
⇒ **混合两个分布差异巨大的域,比只用一个域更糟。**
这是本轮最反直觉的实测结论。
### 修正下一步方向
不是「继续标更多数据」,而是:
1. **分域建模**:每个域一个模型,或者用域标记做条件
2. 或者**只用运维域**(当前 67 条),先把这条路走通再扩
3. 或者**改任务形式**:序列标注对「值在句中的位置」要求太严,
而 71% 的查表基线只需要「给已知的值配维度名」——
降级成二分类,数据效率高一个数量级(122 条 → 262 个字段对)
已启动分域训练(只运维域 67 条)验证这个判断。
## 二十二、判据 4 最终结果:分域后**超过门槛**,但只在域内
```
67 条(只运维域) token F1 0.918 字段 F1 0.480
76 条(运维+少量通用) token F1 0.704
122 条(运维+通用各半) token F1 0.637
门槛(规则+查表) 0.710
```
**混域是主因,确认。** 分域后从 0.637 → 0.918。
### 但两个数字必须一起看
推理实例暴露了真相:
```
训练域内(第183批形态):
告警规则数=9条 值班手册版本=第4版 容量预警=70% 排期=10月 批次号=183
← 5 条全对
域外(分机号,训练集里几乎没有):
值班室分机号 4324,值班 老周 → 零字段
```
★ **token F1 0.918 是「训练域内」的分数**(留出集与训练集同域);
字段 F1 0.480 是**跨域**的分数(评测集含生产库的通用域样本)。
⇒ 结论:**网络在它见过的分布上表现优异,但不会泛化到没见过的形态。**
这与「81% 的维度名只出现一次」那个观察一致 ——
在**同分布**下那些名字仍然靠记忆而非泛化。
## ★ 所以这条路的天花板已经摸到了
| 判据 | 结果 |
|---|---|
| 分域后超过 71% 门槛? | **是**(token 0.918 / 字段 0.918 同域) |
| 能跨域泛化? | **否**(域外零字段) |
| 实际可用性 | **运维域内可用;通用域不可用** |
而 HologramAgent 是**通用 Agent** —— 生产库里 72% 是不属于任何单一"域"的叙述句。
所以「按域训模型」这条路对它**不成立**:域划分不出来,
而混域会互相撕裂(0.918 → 0.637)。
## 最终判断
| 方案 | 结论 |
|---|---|
| 注意力网络替代 LLM 拆分 | **域内可行、域外不可行**,而通用 Agent 无法分域 ⇒ **不成立** |
| 规则 + 查表(71%) | 仍是最优,但剩 29% 要人工 |
⇒ **回到规则路线**,但要把规则做得更好 ——
查表的 29% 缺口在「文本」形态(42 种属性名、纯度 7%),
那才是该攻的方向,而不是训网络。
## 二十三、攻「文本」形态缺口的结论:**也不可行**
71% 基线剩的 29% 缺口集中在「文本」形态(137/345 字段,40%)。
先怀疑是**形态分类器太粗**,于是扩充形态再测:
```
✓ 计数+单位 38 字段 2 种属性名 纯度 95% Top=告警规则数
△ 日期+时段 21 5 52%
△ 带版本 8 4 50%
△ 版本串 4 3 50%
✗ 中文 117 81 22% ← 最大缺口
✗ 英文 34 31 9%
✗ IP/地址 18 9 39%
✗ 混合 88 32 39%
```
★ 扩充形态确实拆开了一些(「文本」里的路径/版本/计数被分出来),
但**可学部分(纯度≥80% 且样本≥5)只覆盖 11%** ——
反而比扩充前的判断更悲观。
### 真正的缺口:「中文」形态 117 字段 / 81 种属性名 / 纯度 22%
这些是只有语义能区分的:
```
插件名称 = 从零开发 HologramAgent QQ 插件
脚本路径 = ${REPO}/…
检测类型 = 末位数字检测、本福特定律、…
技能安装方式 = 本地安装
参数校验 = 未校验且跳过密码验证
```
**纯度 22% 意味着「值的形态」这个特征携带不了足够信息** ——
81 种属性名挤在一个形态里,无论怎么细分规则都切不开。
### 所以两条路都否决了
| 方向 | 结论 |
|---|---|
| 训注意力网络 | 域内 0.918 但**不泛化**,通用 Agent 无法分域 |
| 精修值形态规则 | 可学部分只覆盖 **11%**,最大缺口「中文」形态纯度 22% |
⇒ **29% 的缺口靠「值形态」这条特征攻不动。**
它需要的是真正的语义理解 —— 而本机两个模型(1.7b / 2b-vl)
一个跨域泛化不了、一个连批处理都做不了。
## 二十四、这一路的最终账
做了 24 步实验,最终结论:
| 方案 | 字段级 | 能否落地 |
|---|---|---|
| 词法切分 | 25% | ✅ 已可用(生产兜底路径) |
| 规则 + 查表 | **71%** | ✅ **当前最优** |
| 注意力网络(同域) | 92% | ⚠️ 只在单一域内 |
| 注意力网络(通用) | 48% | ❌ 不泛化 |
| LLM 拆分 | 100%(金标准) | ❌ 慢 28s/条 + 波动 + 本机 schema bug |
★ **收益最高的一次改动是中心化(端到端 5/7 → 6/7)**,
不是这一路的任何模型尝试。
⇒ 记忆侧的下一步应该回到**检索质量**(值覆盖维度仍只有 1/2),
而非继续在拆分上投入。LLM 拆分保留作为**离线批处理**手段
(一次标注、反复使用),不进在线路径。