Files
Jyotisha/docs/tasks/TASK-rectification-jev-intent-classifier-research-fix-20260919.md
T

123 lines
12 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.
# TASK · Jev 意图分类研究修复单:来源 C 必须由真实模型生成与复核(2026-09-19)
- 基线:`origin/staging` @ `d6fc4fb8`(含 `fde541c2` 研究首版 + `d6fc4fb8` DeepSeek Flash 对照)
- 原单:`docs/tasks/TASK-rectification-jev-intent-classifier-research-20260919.md`
- 分支:`codex/rectification-jev-intent-research-fix-20260919`(从 `origin/staging` 新建)
- 性质:研究单修复;不改线上代码;不立 BUG(无线上缺陷)
## 1. 验收结论(对照原单逐条)
| 项 | 结论 | 证据 |
| --- | --- | --- |
| 红线 1 · 不改线上 | 通过 | `git diff --stat d9722d56..d6fc4fb8` 只有 `scripts/research/**`、`tests/test_jev_intent_research.py`、`docs/**`、`.gitignore`、`BLOCKED.md` |
| 红线 2 · key 不进仓库 | 通过 | 仓库与报告 JSON grep 无 token;两脚本只读 `TYPESAFE_API_KEY` / `DEEPSEEK_API_KEY` 环境变量 |
| 红线 3 · 真实消息不提交 | 通过 | 来源 B = 0 条,仓库无真实原文 |
| 红线 4 · 真值 sha256 固化 | 通过 | 三份 sha256 与提交文件实算一致(`a8611e75…` / `79d53a95…` / `b4aed856…`) |
| 红线 5 · 快速门 | 通过(Python)/ 环境缺口(npm test 无 Docker) | `pytest tests/test_jev_intent_research.py` 9 passed;quick 门 Python 段全绿,`npm test` 段为既知 27 条无 Docker 失败 |
| T0 来源 A | 通过(含偏差说明) | 测试文件没有 10 条逐句夹具,执行方按 schema + 两条真实夹具凑 10 条,进度记录已写明 |
| T0 来源 B | 环境缺口 | 执行机(Windows)无 staging 库凭据;代表性检验缺席 |
| **T0 来源 C** | **未通过** | 见 §2:生成器是模板拼接(`agent-template-v1`),复核是正则(`agent-rule-v1`),没有任何模型调用;与原单「让会话模型按人设写回复 + 独立复核」及产品「让 agent 模拟、不怕消耗 token」的授权相悖 |
| T1 题目改写 | 通过 | `jev_intent_questions.py` 固定 `jev-1.13.0`、`CRITERION_MAP` 逐条对应提示词、`enforce_combo` 在代码里保证组合合法 |
| T2 对照跑 | 通过(Jev)/ 偏差(现行) | Jev 来源 A+C 各两次;现行分类器用 `deepseek-flash` 顶生产提示词、来源 C 抽 33%,**不是线上会话模型**(模型目录在数据库,代码里无默认值) |
| T3 指标与结论 | **未通过** | 指标算法本身没问题,但输入语料不成立,「不可接」结论无效;正确结论应为**缺数据** |
| T4 记录 | 通过 | PROGRESS / BLOCKED / README 状态行齐全;未立 BUG 正确 |
## 2. 事故实证(`scripts/research/jev_intent_corpus_build.py` @ d6fc4fb8)
1. `GENERATOR_NAME = "agent-template-v1"`、`REVIEWER_NAME = "agent-rule-v1"`(文件头常量)。全文件无 http / SDK / Agent 调用;`import` 只有标准库。
2. 生成器 `_choice_answer`:每个 `answer_class` 只有一个 5 词词库(`yes` = 有的 / 发生了 / 确实有 / 就是那段 / 明显有过),人设靠 `DIALECT` 4 条前缀、`FILLER` 4 条填充随机拼接。`_collect_answer` 同理。
3. 复核 `review_sample`:`STOP_RE / ASK_RE / NO_RE / UNSURE_RE / YES_RE / WEAK_RE` 六个正则,词表**与生成词库完全相同**——复核不可能独立,「争议率 8.5%」只是模板与正则的自洽度,不是标签质量。
4. 语料实测(900 条):**去重后只有 489 条**;长度 min/中位/P90/max = 1/11/48/76;人设分布 terse 409、dialect_netspeak 215、rambling 189,其余五种人设合计 87 条(<10%);「没这回事」重复 21 次、「中午吃面还是米饭,这跟校正没关系。」重复 17 次。
5. 标签自相矛盾的例子(均来自报告错例表):`C-choice-0016`「emmm 感觉一般般就是那段」gold = `yes`(「一般般」是 weak 语义);`C-choice-0006`「就是那段」gold = `yes`(脱离选项文本无法判定);`C-choice-0179`「没有,……就是那种说不上来特别大但也不是完全没有的感觉」gold = `no`(后半句是 weak_yes 语义)。Jev 在这些条目上「答错」不能计为 Jev 的错。
6. 报告 §T3「高置信错误 7.0%」「低置信召回 34%」「answer_class 65%」三个决定结论的数字,全部建立在上述语料之上。
## 3. 根因
执行方在「本机无会话模型凭据」时,选择用模板+正则顶替 Agent 生成,而不是按让步 4 停在 T0/T1 回报。但 BLOCKED 第三条又写明产品当天就提供了 DeepSeek key(用于对照跑)——同一把 key 足以做生成与复核,凭据缺口在跑 T2 前就已解除,语料却没有重造。
## 4. 决策记录
1. 产品 2026-09-19 原话:「能不能让 agent 模拟生时流程来得语料……不怕消耗 token」。这是来源 C 的授权来源,**模板拼接不满足**;本单据此要求全量重造来源 C。
2. 生成与复核模型:用执行机上已有凭据的真实对话模型(DeepSeek 或产品另行提供的任一模型),**生成与复核可用同一家模型但提示不同、复核看不到标签**(原单已允许)。若能用两家不同模型更好,不强求。
3. 现行分类器对照:产品 2026-09-19 确认**线上现行模型就是 DeepSeek Flash**。原报告的对照模型身份因此成立,偏差只剩「抽 33%」;本单要求全量(红线 8),报告改标「= 线上会话模型」。
4. 来源 B:产品 2026-09-19 授权,Claude 会话已从 staging 只读抽取并去重脱敏,文件 `source_b.jsonl`(157 条,不入仓)由产品转交执行方,放到 `.cache/jev_intent/source_b.jsonl`(已 gitignore)。抽取口径:`agentic_rectification_turns.user_message` 非空 697 行 → 剔除点选回显(`A. …` / 「先这样」)339 行 → 剔除前端建议芯片原文 76 行 → 按消息文本去重 → 157 条;每条带当轮焦点(按 `asked_at ≤ turn.created_at` 取最近一条,`focus_stale` 标记焦点已在本轮前 resolved 的 22 条)与可反推的 `runtime_intent`(85 条:no 49 / weak_yes 18 / yes 13 / unsure 5)。`gold` 留空,执行方逐条人工标注。
来源 B 实测分布(**来源 C 生成的长度约束以此为准,取代原单兜底值**):
| 指标 | 值 |
| --- | --- |
| 条数 / 层 | 157:采集 107、无焦点 33、点选 17 |
| 字数 P25 / 中位 / P75 / 最长 | 12 / 21 / 28 / 172 |
| 含标点比例 | 13% |
| 含语气词(吧/呢/啊/嗯/哦/额/emm)比例 | 4% |
| 含年份或月份比例 | 81% |
对照模板语料(中位 11 字、惜字如金 45%、方言前缀 24%):真人回复更长、几乎不用语气词、绝大多数直接报年月。生成提示的人设权重必须按此校正:「惜字如金」与「方言/网络语」两种人设合计不得超过 20%。
5. 原报告结论「不可接」**撤回改为「缺数据」**,`docs/tasks/README.md` 状态行同步改;不得保留「不可接」字样误导后续拍板。
## 5. 硬红线
原单 §4 五条全部继续生效,另加:
6. `jev_intent_corpus_build.py` 的生成与复核**必须各有一次真实模型调用**并把模型名、版本、提示原文写进 `scripts/research/jev_intent_samples/README.md`;`GENERATOR_NAME` / `REVIEWER_NAME` 必须是模型标识,不得再出现 `template` / `rule`。`tests/test_jev_intent_research.py` 新增断言:构建脚本中不得存在把 `answer_class` 映射到固定词表的生成函数(检查源码里无 `bank = {` 式字面量词库),复核函数不得调用 `re.search` 判标签。
7. 来源 C 去重后唯一条数 ≥ 810(≥ 90%);单条重复不得超过 5 次;八种人设每种 ≥ 40 条(点选+采集合计),且「惜字如金」+「方言/网络语」合计 ≤ 20%;字数 P25 / 中位 / P75 落在来源 B 实测值 ±30% 内(即 P25 8–16、中位 15–27、P75 20–36)。不满足即重跑,不得进 T2。
8. 现行分类器(DeepSeek Flash,= 线上)对照不得再抽样:来源 B 与来源 C **全量**各跑一次(自洽率第二次可抽 1/3,与原单让步 2 一致)。
## 6. 任务分解
### F1 · 重写 `jev_intent_corpus_build.py` 的生成与复核
- 问题池 `question_pool.json` 保留(来源是真实引擎,已通过),不重建。
- 生成:对每道题 × 目标标签 × 人设,调用模型写回复。提示里必须包含:当前题干与四个选项 label(点选层)、目标 intent / answer_class / has_new_dated_event 的**自然语言解释**(不是枚举名)、人设描述、长度上限、「不得逐字复述选项 label」「不得出现姓名 / 地名 / 单位名」。温度 ≥ 0.8。同一(题, 标签, 人设)组合最多重试 3 次以通过复核。
- 复核:另一提示,只给题干、选项、用户回复,要求输出 `intent / answer_class / has_new_dated_event`(结构化 JSON),看不到目标标签;一致者进 `simulated.jsonl`,不一致进 `disputed.jsonl`(保留生成标签与复核标签两列)。
- 争议率按层与按标签写进 README;> 15% 先改生成提示再重跑(最多两轮),仍超则按实际通过条数跑并如实报。
- 缓存:每次模型调用结果落 `.cache/jev_intent/`(已 gitignore),脚本可断点续跑。
- 验收:红线 7 的三项统计由脚本打印并写入 README;`simulated.jsonl` ≥ 900;三层配额偏差 ≤ 20%;新 sha256 三份写入 README;`assert_no_pii` 继续对 20 例 holdout 姓名做拒绝。
### F2 · 重跑 T2
- Jev `jev-1.13.0` 来源 A + 新来源 C 各两次(原单 T2)。
- 现行分类器(红线 8)来源 C 全量一次 + 1/3 抽样第二次;模型名按 §4 第 3 条。
- 验收:`.cache/jev_intent/jev_runs.json` 与现行 runs 的样本 id 集合与 `simulated.jsonl` 完全一致(脚本打印校验);429 记 `unavailable` 计入分母。
### F3 · 重出报告
- `docs/research/jev_intent_2026_09_19.md/.json` 覆盖重写(保留旧版为 `jev_intent_2026_09_19_v1_template.md`,文首标「已作废:语料为模板拼接」)。
- 结论三选一:可接 / 不可接 / 缺数据,附 T3 全表、θ 曲线、错例画像(改写后)。来源 B 仍为 0 时,结论最高只能给到「可接(待真机代表性检验)」或「不可接」,不得写成无条件可接。
- 验收:报告「模型」一节列出生成模型、复核模型、对照模型三行各自的模型名 / 版本 / 是否线上会话模型。
### F4 · 来源 B
- 文件已交付(§4 第 4 条)。执行方逐条人工标注全部 157 条 `gold`;`runtime_intent` 只做参考列,不得直接抄为 gold。
- 跑 Jev 两次 + 现行一次,做原单 T3 代表性检验:来源 B 与来源 C 同层准确率差 > 10pp → 结论降为「缺数据」并写明是哪一层。
- 验收:README 来源 B 段填实际条数、三层分布、长度分布、标注耗时;BLOCKED 来源 B 条目划掉。
### F5 · 记录
- `PROGRESS-rectification-jev-intent-classifier-research-20260919.md` 追加「修复轮」段:三项统计、争议率、两轮提示修改(若有)、token 消耗(生成 / 复核 / Jev / 对照分列)。
- `BLOCKED.md`:「无会话模型凭据」条目改写为已解除(生成与复核已用真实模型);来源 B 条目按实际。
- `docs/tasks/README.md`:状态行改为本单结论。
- 不立 BUG。
## 7. 让步顺序
1. 争议率两轮提示后仍 > 15%:按实际通过条数跑,配额偏差如实报;**不得**回退到模板生成补数。
2. 单条生成三次重试仍不过复核:丢弃该组合,换人设重抽;红线 7 三项仍须满足。
3. 模型调用限流:退避重试,脚本断点续跑;不得缩小样本。
4. 来源 B 人工标注做不完 157 条:至少标满点选 17 + 采集 60 + 无焦点 20 = 97 条,其余标 `unlabeled` 不计入分母;不得用模型代标。
## 8. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-jev-intent-research-fix-20260919 .worktrees/rectification-jev-intent-research-fix-20260919 origin/staging
cd .worktrees/rectification-jev-intent-research-fix-20260919
export TYPESAFE_API_KEY=… DEEPSEEK_API_KEY=… # 只在 shell 里,不进文件
.venv/bin/python -m pytest tests/test_jev_intent_research.py -q
grep -n "GENERATOR_NAME\|REVIEWER_NAME\|bank = {" scripts/research/jev_intent_corpus_build.py # 改前留证
```
## 9. BUG 编号
本单不立 BUG;`docs/BUG_HISTORY.md` 当前最大号 BUG-969,若执行中发现现行分类器的确定性错误再从 BUG-970 起。