只改仓库内文件的纯展示品牌名与已随批 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 逐项相同、
三条变异均被咬住且还原后复绿。
31 KiB
三元组抽取:注意力模型方案
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 万 —— 差一个数量级。
这决定了它现在不能训出可用模型。可行的路径:
- 扩充数据源:真库 188 条 + 生产库 1295 条 + 对话归档 + 历史邮件 (预计能到 3000~5000 条句子)
- 或者改任务:既然值抽取已 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 解析的贪婪匹配
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
★ 两个真实问题:
- 训练被拖到 5 分钟还没出 ep10 —— 抢不到 CPU。
- 标注重标 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 个密集样本又把它拽回记忆)。
⇒ 混合两个分布差异巨大的域,比只用一个域更糟。 这是本轮最反直觉的实测结论。
修正下一步方向
不是「继续标更多数据」,而是:
- 分域建模:每个域一个模型,或者用域标记做条件
- 或者只用运维域(当前 67 条),先把这条路走通再扩
- 或者改任务形式:序列标注对「值在句中的位置」要求太严, 而 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 拆分保留作为离线批处理手段 (一次标注、反复使用),不进在线路径。