This commit is contained in:
hupenglong1
2026-05-22 19:48:47 +08:00
parent 20690cdef3
commit 5311e6d97c
31 changed files with 4620 additions and 472 deletions
+180 -44
View File
@@ -20,20 +20,22 @@ when_to_use: |
|---------|---------|
| "要不要我做训练集近邻检索?" | 这是 §2.3 分析必做项,**直接做**,做完把结果落 report |
| "下一步可以继续吗?" | 永远不问。看 program.md 流程图自己判断 |
| "我先把控制权交回,等你来问跑完了吗" | watcher 接手 UI 同步,你还是要在 bash 内 sleep+poll 等 cml 完,看到 complete **直接进 Step 1** |
| "我先把控制权交回,等你来问跑完了吗" | watcher 接手 UI 同步,长任务用 `bash(run_in_background=true, wait_for_completion=true)` 提交后本轮主动结束;后端会在产物落盘后自动起新一轮把结果送回,**直接进 Step 1** |
| "这一步是关键决策点,需要你拍板" | program.md 没写就不是。**自主决策 + 落 iteration_log** |
| "我把现状停在这里,把决策摘要给你 review" | 不准。摘要可以写,但不准停 |
| "Step 4 augment 完成 ✅,等回调后续做 SFT" | augment=complete 那一刻就是 SFT 启动那一刻——**同一轮 bash** 紧接着跑 §4.5 label-master 复核 + 提交 SFT,不许写简报、不许等回调(详见 program.md "Step 4 → Step 5 硬连接"|
| "R1 评测发现 regression,先把诊断给你看,等你拍板再决定要不要回滚" | dist-analysis 发现 regression **也算 dist-analysis 完成**——**同轮 bash** 紧接着写 `results/workflow<runDic>.md`(包含完整 delta 表 + new_fail/new_fix 子集 + 病灶定位 + 回滚建议)+ append `iteration_log.jsonl` R{n} entry。文件落完了再用 chat reply 给人提回滚选项。**不许把诊断只写在 chat 里、不落盘**——前端「分层结果分析」卡片读的是 `results/workflow<runDic>.md`,你不写卡片永远停在上轮。 |
**合法暂停只有**
1. program.md `## Human-in-the-Loop 时机` 章节的 7 类迭代级信号
2. `### 训练集调整专项 H-i-T-L 信号` 的 T1-T10
3. **§4.0.1 Step B 量级判定的 HiTL 分支**
- 候选量 51-200 条 → **必须**抽样 30-50 条到飞书 sheet 让人审 1/0,按比例外推**这就是一次合法暂停**,不是擅自停)
- 候选量 >200 条 → 命中触发条件 #3,强制 HiTL 介入让人定更精细 pattern 收窄
- 候选量 51-200 条 → **必须**全量导出到飞书 sheet 让人逐条审 1/0**这就是一次合法暂停**,不是擅自停**不再抽样外推**
- 候选量 >200 条 → 命中触发条件 #3,强制 HiTL 介入让人定更精细 pattern 收窄(仍然全量交付)
4. 用户主动发消息打断
这之外**所有**"我觉得这事大、我先停"的本能都要压下来。命中合法暂停时,**不要只是说"等你确认"**——按 §4.0.1 把抽样/精细 pattern 的具体输出 dump 到飞书 / scratchpad 给人具体可审的东西,再停。
这之外**所有**"我觉得这事大、我先停"的本能都要压下来。命中合法暂停时,**不要只是说"等你确认"**——按 §4.0.1 把**全量候选清单**(不是抽样/精细 pattern 的具体输出 dump 到飞书 / scratchpad 给人具体可审的东西,再停。
## 📋 每个 Step 的强制准入条件(少一项不准进下一步)
@@ -44,13 +46,13 @@ when_to_use: |
- [ ] runDic 已分配(扫 `run_history_dir` 取 max+1
- [ ] CML workflow 已提交(拿到 Execution ID
- [ ] watcher 声明已写入 program-state.jsonl
- [ ] 在 bash sleep+poll 等到 cml=complete
- [ ] cml workflow + 等 metric_diff 落盘已用 `bash(run_in_background=true, wait_for_completion=true)` 提交(**不要**在 bash sleep+poll
### Step 1 准入(`dist-analysis`
- [ ]`metric_diff/lark_template.json` 提分层指标(specific / 大盘车载 / 目标专项)
- [ ]`metric_diff/specific_comparison.csv` baseline 错误分布
- [ ]`metric_diff/specific_comparison.csv` 提取 baseline 错误分布
- [ ] **§2.3 失败 case 与训练数据的关联**:每条错例在 `train_set/zk_intent/*.jsonl` 做近邻检索(前 3 近邻),输出"有近邻 / 无近邻"分类
- [ ] **§2.3 Reward 对齐检查**抽 10 条失败 case 用 `zk_reward_fn` 验 reward 方向
- [ ] **§2.3 Reward 对齐检查**全量失败 case 用 `zk_reward_fn` 验 reward 方向(不抽样)
- [ ] §0.1 Gold drift 检查(如未做)
### Step 2 准入(`report`
@@ -58,18 +60,33 @@ when_to_use: |
- [ ] 写入 `results/workflow<runDic>.md`
- [ ] error_registry 追加本轮错误
### Step 3 准入(`hypothesis`
### Step 3 准入(`hypothesis` — analysis 收尾,不是 train 开头
- [ ] 写本轮假设到 iteration_log.jsonl 的 hypothesis 字段
- [ ] 假设必须有依据(指向 §2.3 / §2.4 的具体发现)
- [ ] **`step:"hypothesis"` entry 写在当前 roundR{n})名下**——不要写成 R{n+1}。后端把 hypothesis 归类为 analysis 类,是 R{n}·Baseline / R{n}·Analysis 的最后一张卡,不是 R{n+1}·Train 的开头。这样 gateHuman Check / Review)会插在 hypothesis 卡之后、R{n+1}·Train 之前,**用户看到假设内容再拍板是否进 train**。
### Step 1 → Step 2 → Step 3 边界(**NEVER STOP 硬连接**R0 / R1+ regression 都适用)
- [ ] dist-analysis 算完 deltanew_fail / new_fix / persistent / 子集分布)那一刻起,**同一轮 bash 不许结束**:紧接着写 `results/workflow<runDic>.md`(包含完整指标表 + 病灶定位 + 假设 + 回滚/继续建议)→ append `iteration_log.jsonl` R{n} entry`results` 字段填本轮 metric`hypothesis` 字段填下一动作)
- [ ] **regression 场景同样适用**:哪怕 R1 出现导航bvt 纯劣化、可聊可控大幅 -25 这种"必须回滚"信号,**先把分析落到 workflow<runDic>.md 和 iteration_log,再用 chat reply 给人回滚选项**。文件先落、聊天再发——顺序不能反。
- [ ] **不许"诊断只写聊天回复 / 等用户拍板再补盘"**:前端「分层结果分析」卡片读的是 `chat_root/results/workflow<runDic>.md`;你不写盘卡片永远显示上一轮,看不到 R1 结论。
- [ ] **runDic 推进**:每轮新评测结果落盘后,append `iteration_log.jsonl` 一条新 entryrunDic 写新轮的值,比如 R0 是 17793R1 评测的 metric_diff 在 workflow17794,这条 entry 的 runDic 就写 17794),UI 卡片才会切到新一行。
- [ ] **凡是要让用户拍板的检查点(R0 baseline 之后、R{n} regression 之后、>200 候选要定 pattern、Gold drift 复核 等),必须先 append `step:"human-check"` 或 `step:"human-review"` running entry,再用 chat reply 提问**。**禁止只在 chat 里问而不写 gate entry**——UI 看不到拦截 = 视为没做这步,用户面板上看不到任何卡。详见下方 "Human-in-the-Loop 信号 → 写 gate 节点"。
### Step 4 准入(`augment`,需要修改训练集才进)
- [ ] **§4.0 原始训练数据清洗(增强前必做)**——按 4 种动作走完逐 pattern 判定
- [ ] §4.0.1 Step A:候选定位输出 csv**不直接改**
- [ ] §4.0.1 Step B:量级判定走对应支线(≤50 自动 / 51-200 抽样审 / >200 触发 #3
- [ ] §4.0.1 Step B:量级判定走对应支线(≤50 自动 / 51-200 全量人审 / >200 触发 #3,全量交付
- [ ] §4.0.1 Step C:备份 `.bak` 文件
- [ ] §4.1 输入准备 / §4.2 GPT 调用 / §4.3 sanity check / §4.4 写入
- [ ] **§4.5 label-master 标签复核(落盘后必做,强制)**:对 H1 `modified_samples.jsonl` + H2 `augment_<runDic>.jsonl` 跑两层(`validate_label_output.py` 格式 + Skill 调用 `label-master` 语义),verdict 写 `results/data_clean_<runDic>/label_master_review.jsonl`,不通过比例 ≤ 5% 才能进 Step 5
### Step 4 → Step 5 边界(**NEVER STOP 硬连接**,反复踩坑)
- [ ] 写完 `augment=complete` 那一刻,**同一轮 bash 不许结束**:紧接着跑 §4.5 label-master 复核 → 写 `sft=running` → 调 `submit_sft_via_cml.sh` → 挂 watcher
- [ ] **不许写"Step 4 完成"进度简报后 turn 结束**,不许"等回调后续做 SFT"
- [ ] H2 后台任务回调(`[system] 后台任务 ... exit_code=0`)**不是 turn 结束信号**——它只是 augment 子流程的一个中间节拍,agent 必须在同轮里继续走完 §4.5 → Step 5
### Step 5 准入(`sft`
- [ ] §4.5 label-master 复核报告 `label_master_review.jsonl` 存在且不通过 ≤ 5%
- [ ] 旧 sft_output 已 `mv sft_output sft_output_r{prev}` 备份
- [ ] 训练参数从 config.yaml 读,model_path = basemodel**不从上轮 ckpt 续训**
@@ -101,6 +118,18 @@ when_to_use: |
> echo '{"step":"cml","status":"complete","ts":"'$(date -Iseconds)'"}' >> "$SESSION_OUTPUT/program-state.jsonl"
> ```
### R1.5 — 推荐每条 state entry 带 run_id(让 UI 准确归位 round
新版 UI 按 R0 / R1 / R2 切分 sections。**强烈建议**每行加 `run_id`
```bash
echo '{"step":"cml","status":"running","run_id":"R0","ts":"'$(date -Iseconds)'"}' >> $S
echo '{"step":"hypothesis","status":"complete","run_id":"R0","ts":"..."}' >> $S # hypothesis 跟当前轮(R0),不是 R1
echo '{"step":"augment","status":"running","run_id":"R1","ts":"..."}' >> $S # augment 才是 R1·Train 起点
```
run_id 缺省时后端会按 `iteration_log.jsonl` 行数自动推断(analysis 类 step 含 hypothesis → R{count}train 类 step augment/verify/sft → R{count+1}),但显式写更准——尤其是并发跨轮、补写历史 entry、或要在 R0 强制注入 baseline 时。
### R2 — 写入即生效,最后一行覆盖前面
后端按 `step` 字段取**最后一条**状态。如果你之前写过 failed、现在恢复了,**必须 append 一条新的 `running`**,否则 UI 永远停在 failed。
@@ -122,18 +151,24 @@ UI 卡底部进度条会跟着动;不写就一直显示初始进度。
### State 文件位置
```
<当前会话目录>/output/program-state.jsonl
<远端 workspace>/output/program-state.jsonl
```
会话目录在系统提示的 `[当前会话目录]` 里给了绝对路径。**直接用那个值**,不要拼。**绝对不要**写到 `/mnt/wangsenhao/...` 或项目根目录——多会话互相覆盖
bash 命令是在 **远端 workspace** 里跑的(一般 `/root/zk_agent_workspaces/LOCALID_xxx`),系统提示里的「当前会话目录」是 **后端宿主机的本地路径****不能直接拿来当 SESSION_OUTPUT**——拿了等于在远端凭空建一条同名死路径,后端 sync 永远读不到,前端卡片就一直不动
每次写之前先确保目录存在:
正确做法:用远端 cwd 派生(`pwd` 就是当前远端 workspace),目录确保存在:
```bash
SESSION_OUTPUT="<从系统提示拷过来>/output"
SESSION_OUTPUT="$(pwd)/output"
mkdir -p "$SESSION_OUTPUT"
```
或者显式写远端绝对路径 `SESSION_OUTPUT="/root/zk_agent_workspaces/LOCALID_<本会话 id>/output"`。**绝对不要**
- 拷系统提示里的 `/home/mi/zk-data-agent-wsh/.port_sessions/.../output`(那是后端本地,远端没这条)
- 写到 `/mnt/wangsenhao/...` 或项目根目录(多会话互相覆盖)
后端 `_sync_remote_program_state` 只读远端 `{workspace_cwd}/output/program-state.jsonl` —— 路径写错 = 前端死锁。
### Step key 表(必须用这套 key,否则匹配不到卡片)
| step key | 对应卡片 |
@@ -147,7 +182,9 @@ mkdir -p "$SESSION_OUTPUT"
| `verify` | 修改返回验证 |
| `sft` | SFT 训练 |
| `log` | 记录迭代日志 |
| `next-round` | 下一轮评测 |
| `next-round` | 下一轮评测(边界标记,**不生成卡片**,仅推进 UI 的 run_id 推断) |
| `human-check` | Human CheckHiTL 网关;命中 HiTL 时 agent 显式写,UI 在对应 round section 后插入 gate 卡) |
| `human-review` | Human Review(同上,多用于"建议人审 1/0"或"建议回滚"等需要人拍板的检查点) |
### KPI(你不要写,后端读权威源)
@@ -159,7 +196,7 @@ mkdir -p "$SESSION_OUTPUT"
| VERSION | `skills/model-iteration/assets/config.yaml``cml_eval.version` |
| 目标集合 | 从 session 最新 user 消息里解析 `"开始, <name>"` 中的 name |
| 大盘车载 / SPECIFIC TEST | `RUN_HISTORY_DIR/workflow<runDic>/metric_diff/lark_template.json` |
| ITERATION | `<AUTORESEARCH_ROOT>/results/iteration_log.jsonl` 行数 |
| ITERATION | `$AUTORESEARCH_CHAT_ROOT/results/iteration_log.jsonl` 行数(每 chat 独立) |
| 耗时 | program-state.jsonl 第一条 `{step:"cml",status:"running"}``ts` 到现在 |
**你不要再写 `{"kpi":...}` 行**——写了也会被后端忽略,不显示。如果想让用户看到某个 KPI 的当前值,把它落到对应的权威源(比如改 config.yaml、写 iteration_log),后端会自动读到。
@@ -172,7 +209,8 @@ CML 评测 / SFT 训练这类几分钟到几十分钟的长任务,声明 watch
- **不许**给用户回"控制权交回 / 我先停下 / 等你来问"这种话
- **不许**主动结束本轮(除非命中 program.md 里写明的 H-i-T-L 阻断信号)
- 提交完任务、声明完 watcher 后,**继续在 bash 里轮询 state file** 等 cml=complete,看到就**直接进入 Step 1**(读 metric_diff、做分层分析、写报告等),一气呵成跑完整轮迭代
- 长任务(cml 评测、SFT 训练)**必须**用 `bash(run_in_background=true, wait_for_completion=true)` 提交:把"提交远端任务 + 等产物落盘"打包成一个 bg 命令;本轮 agent 主动结束(**不是**交回控制权——是把等待这件事 detach 给后端),产物落盘后后端自动起新一轮,agent 收到 stdout 直接进 Step 1
- **不许**在 bash 里写 `while true; do sleep 60; done` 这种轮询循环——bash 命令有 30s 硬超时,会被切成几十个 step,把本轮 50 step 上限耗尽留不出做分析的预算
watcher 解决的是 **UI 与 agent 异步**UI 不用等 agent 写状态,watcher 替 agent 把 complete 落盘。它不是替你"放假"。
@@ -202,31 +240,41 @@ echo '{"watch":{"step":"cml","kind":"file_exists","path":"/mnt/xiaoai-zk-model-t
- step 已经有 complete/failed 行后,watcher 声明会被忽略
- 后端 scanner 5 秒扫一次所有会话的 state file
**典型用法**CML workflow**NEVER STOP**):
**典型用法**CML workflow**用 bg 模式 + watcher**):
长任务必须用 `bash(run_in_background=true, wait_for_completion=true)` 提交。bg 命令在远端 detach 跑、立刻返回 task_id;产物落盘后后端自动起新一轮把 stdout 作为 system 消息送回 agent**不要**在 bash 里 sleep+poll。watcher 仍然要声明——它驱动 UI 卡片状态,跟 bg 任务是两条独立的通道。
```bash
# 1. 提交 cml 任务(异步,立刻返回 runDic
RUNDIC=$(cml workflow run --workflow_id "$WORKFLOW_ID" ...)
# 2. 写 running + watcher 声明
# 1. 写 running + watcher 声明(UI 卡片靠它
echo '{"step":"cml","status":"running","ts":"'$(date -Iseconds)'"}' >> $S
# 2. 用 bg 模式:把"提交 cml + 等 metric_diff 落盘"一次封装
# wait_for_completion=true(默认):进程结束后后端自动起新一轮把 stdout 送回
bash(run_in_background=true, command='''
set -e
RUNDIC=$(cml workflow run --workflow_id "$WORKFLOW_ID" ...)
echo "RUNDIC=$RUNDIC"
TARGET=/mnt/xiaoai-zk-model-train-tj5/workflow5/workflow$RUNDIC/metric_diff/lark_template.json
while [ ! -f "$TARGET" ]; do sleep 60; done
echo "EVAL_DONE runDic=$RUNDIC target=$TARGET"
''')
# 3. 紧接着声明 watcherUI 卡片切到 complete 靠它,runDic 在 bg 任务 stdout 里)
# watcher path 用稍后从 bg stdout 拿到的 runDic 拼;如果先不知道 runDic
# 可以等 bg 任务返回 RUNDIC 后这一轮里再补声明 watcher。
echo '{"watch":{"step":"cml","kind":"file_exists","path":"'$ROOT/workflow$RUNDIC/metric_diff/lark_template.json'","interval":30,"timeout":7200,"run_id":"R'$N'"}}' >> $S
# 3. 跟用户简报一句状态,**不要说"交回控制权"**,紧接着开始等
echo "已提交评测 runDic=$RUNDIC,开始轮询 state file 等 watcher 写 complete..."
# 4. 跟用户简报一句"已提交,等产物落盘后会自动续",agent 这一轮主动结束
# (不是交回控制权——是把等待这件事 detach 给后端)
# 4. 在 bash 里轮询 state file(不阻塞你下一步思考
while true; do
status=$(grep -E '"step":"cml"' $S | tail -1 | python3 -c 'import json,sys; print(json.loads(sys.stdin.read()).get("status",""))')
if [ "$status" = "complete" ]; then break; fi
if [ "$status" = "failed" ]; then echo "cml failed"; exit 1; fi
sleep 60
done
# 5. cml 完成 → 立刻进入 Step 1,读 metric_diff、做分析、写下一步 running...
# 整轮迭代不交回控制权,直到达标 / 命中 H-i-T-L 阻断 / 用户主动打断
# 5. 产物落盘后,后端起新一轮,agent 收到 stdout(含 RUNDIC + EVAL_DONE
# → 立刻进入 Step 1,读 metric_diff、做分层分析、写报告……整轮 50 step 全用在分析上
```
**为什么不能在 bash 里 sleep+poll**bash 工具有 30s 硬超时,`sleep 60` 会被切成 `sleep 25 + sleep 5` 两个 step;评测跑 25 分钟 = ~50 step 全耗在等待上,留不出做分析的预算。bg 模式只占 1 个 step。
**bg 任务排查**agent 中途想看进度,可以 `bash_status(task_id)` 拿当前 stdout/exit_code 快照;想终止远端进程要 `bash_kill(task_id)`(前端按 stop 不会杀 bg 任务,那是 detach 的本意)。
### Live log(可选)
要把日志推到 UI 底部那块黑色 Live Log 区,追加:
@@ -249,10 +297,42 @@ echo '{"log":{"ts":"11:55:10","iter":"R3","text":"Step 3 开始: 问题分析 &
| 触发信号 | `"开始,<需求集合名>"` |
| 评测 workflow | `f-20260408161444-wu3pz`(版本见 `config.yaml` |
| 训练基模 | `/mnt/wangsenhao/verl_zk/Qwen3-4B-Instruct-2507` |
| 工作目录 | `/mnt/wangsenhao/autoresearch-zk` |
| 工作目录 | `$AUTORESEARCH_CHAT_ROOT`(每 chat 独立,由后端注入;位于共享 NFS:`/mnt/wangsenhao/autoresearch-zk-users/<email_prefix>/<chat_session_id>/`jupyter pod 与训练 pod 都能读写) |
| 历史记录目录 | `/mnt/xiaoai-zk-model-train-tj5/workflow5/` |
| 需求集合位置 | `https://git.n.xiaomi.com/ai-service/ai-planning/-/tree/autoresearch-v1``ai-planning/data/specific_test_set/` |
## 工作空间隔离(按用户 + 每 chat 二级隔离,落在 NFS)
每个 chat session 一份独立工作区 `$AUTORESEARCH_CHAT_ROOT`(后端在 jupyter 启动时自动注入这个 envagent 每次 bash 都能拿到)。该目录位于共享 NFS:
```
/mnt/wangsenhao/autoresearch-zk-users/<email_prefix>/<chat_session_id>/
```
`<email_prefix>` 是用户登录时的小米邮箱前缀(`xxx@xiaomi.com``xxx`),用作账号根目录;同一用户跨 chat 共享根目录但 chat 之间完全隔离。
**为什么放 NFS 而不是 jupyter pod 私有路径**SFT 训练在独立的 pytorch pod 里跑,那个 pod 不挂载 jupyter pod 的 workspace;放共享 NFS 是双方都能 `cd` 进去的唯一选择。
**所有写入路径都必须以 `$AUTORESEARCH_CHAT_ROOT` 开头**,不要写 hard-coded `/mnt/wangsenhao/autoresearch-zk/...` 全局路径,也不要写 jupyter pod 本地路径(`/root/zk_agent_workspaces/...`),训练 pod 看不到。
| 资产 | 位置 | 隔离方式 |
|---|---|---|
| `prepare_and_train_sft.py` | `$AUTORESEARCH_CHAT_ROOT/scripts/` | 后端 bind 时从 skill bundle 推过来,每次刷新最新版 |
| `ai-planning/`(含 corpus + augment | `$AUTORESEARCH_CHAT_ROOT/ai-planning/` | **首次访问前你自己 git clone**(见 program.md 「ai-planning bootstrap」) |
| `zk_trainer/` | `$AUTORESEARCH_CHAT_ROOT/zk_trainer/` | **Step 5 首次 SFT 前你自己 git clone**(见 program.md §5.0 |
| `results/iteration_log.jsonl` | `$AUTORESEARCH_CHAT_ROOT/results/` | 每 chat 独立累积;后端 KPI 也读这里 |
| `results/error_registry.jsonl` | 同上 | 每 chat 独立 |
| `results/workflow<runDic>.md / data_clean_<runDic>/ / augment_raw/` | 同上 | 每 chat 独立 |
| `sft_output/` | `$AUTORESEARCH_CHAT_ROOT/sft_output/` | 每 chat 独立,互不覆盖;训练 pod 走 NFS 直接写 |
| `augment_<runDic>.jsonl` | `$AUTORESEARCH_CHAT_ROOT/ai-planning/data/train_set/zk_intent/` | 写在 chat 自己的 ai-planning clone 内,下一次 SFT prepare 只合并本 chat 的增量 |
**保持全局共享的资产**(不要按 chat 拆):
- runDic 计数:`/mnt/xiaoai-zk-model-train-tj5/workflow5/`max+1 是平台级唯一标识)
- 训练基模:`/mnt/wangsenhao/verl_zk/Qwen3-4B-Instruct-2507`(只读模型权重)
- metric_diff 输出:`/mnt/xiaoai-zk-model-train-tj5/workflow5/workflow<runDic>/metric_diff/`cml workflow 写)
**每个 step 落产物时务必用 `$AUTORESEARCH_CHAT_ROOT` 作前缀**(例如 `cd $AUTORESEARCH_CHAT_ROOT && python scripts/prepare_and_train_sft.py ...`,或 `echo ... >> $AUTORESEARCH_CHAT_ROOT/results/iteration_log.jsonl`)。
## 核心原则
1. **评测优先**`"开始"` 信号的第一个动作永远是评测当前模型,绝不直接训练
@@ -343,6 +423,58 @@ assets/
- 跨子集净退步(#4
- 连续 3 轮目标子集净提升 ≤ 0.5pp(#5
### 写 gate 节点(让 UI 显示 Human Check / Review 卡)
凡是要让用户拍板的检查点,**必须** append 一条 gate entry 到 `program-state.jsonl`**再** 用 chat reply 提问。UI 会在当前 round section 之后插入「Human Check」或「Human Review」横向卡片(IN PROGRESS 状态)。
> ⛔ **禁止**:只在 chat 里问用户、不写 gate entry。UI 看不到拦截 = 视为这一步没做——用户在面板里看不到任何卡,会以为流程卡死或还在自动推进。**这是反复踩坑点**:以前 agent 经常在 R0 baseline 跑完后聊天里问"要不要进 R1 train",但 program-state 一直在写 augment runningUI 里完全看不到 gate。
#### 什么时候要写
需要用户拍板就写——不管 R0 还是 R{n≥1},触发条件都按 §HiTL 信号判:
| 时机 | gate 类型 | run_id |
|---|---|---|
| R0 baseline 分析完成、要让用户确认是否进 R1 train(命中 HiTL 信号 / 候选量大 / 目标子集偏低 / 第一次跑想让用户校方向 等) | `human-check` | `R0` |
| R{n≥1} analysis 完成、命中 §HiTL 信号(gold drift / regression / 跨子集退步 / 连续 3 轮无提升 等) | `human-review` | `R{n}` |
| §4.0.1 Step B:候选量 > 200 条命中触发条件 #3 | `human-check` | 当前 round |
| 其他 HiTL 信号(要批改旧标签 > 50 条、Gold drift ≥ 10 条 等) | `human-check`(开始前)/ `human-review`(结果后) | 当前 round |
判断标准就一条:**只要你下一步打算 chat-ask 用户拍板,就先写 gate entry 再问**。
#### 写法
```bash
# R0 baseline 之后想让用户拍板是否进 train
echo '{"step":"human-check","status":"running","run_id":"R0","reason":"R0 baseline 分析完成,目标子集 X.X% 偏低,确认是否按当前 H1 候选进 R1 train","ts":"'$(date -Iseconds)'"}' >> $S
# R1 评测发现 regression,需要用户决定是否回滚
echo '{"step":"human-review","status":"running","run_id":"R1","reason":"R1 vs R0:导航bvt -3.2pp, 可聊可控 -25pp,建议回滚","ts":"'$(date -Iseconds)'"}' >> $S
# 候选量超 200 条命中触发条件 #3
echo '{"step":"human-check","status":"running","run_id":"R0","reason":"候选量 412 条命中触发条件 #3","ts":"'$(date -Iseconds)'"}' >> $S
```
**顺序不能反**:先 echo gate entry → 再 chat reply 给用户。否则用户先看到聊天问话、UI 里却没卡,会困惑"流程是不是卡死了"。
#### 字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
| `step` | ✅ | `human-check``human-review` |
| `status` | ✅ | 卡进入时写 `running`;用户回复后写 `complete` |
| `run_id` | ✅ | 当前所在轮次(决定 gate 插在哪个 section 后) |
| `reason` | ⭕ | 触发原因——直接展示给用户看,写具体一些("Gold drift 12 条,触发 #1" 比 "需要人审" 强得多) |
| `ts` | ✅ | ISO 时间戳 |
**用户拍板回复后**append `status:"complete"` 同 step + run_id 一行,gate 卡变 COMPLETED;然后才继续往下走(继续/回滚/调参)。
**两种 gate 的区分(约定)**
- `human-check`:偏"开始前的检查"——baseline → train 网关、>200 条候选定 pattern、Gold drift 复核
- `human-review`:偏"结果出来要复核"——R{n} 评测出来发现 regression 要不要回滚、目标集合连续 3 轮无提升要不要换思路
实际差别在 UI 上不大(都是横向 dashed 卡),区分主要为了让用户从标题就大致知道是开始前还是结果后的检查点。
## CML 配置(快速查阅)
```yaml
@@ -357,13 +489,17 @@ xiaomi_cloudml:
## 结果文件
| 文件 | 用途 |
|---|---|
| `results/iteration_log.jsonl` | 每轮假设/干预/判定完整记录 |
| `results/error_registry.jsonl` | 跨轮错误追踪(case_hash → 出错轮次) |
| `results/workflow<runDic>.md` | 每轮回归分析报告 |
| `results/augment_raw/augment_<runDic>_raw.jsonl` | GPT-5.4 原始生成产物 |
| `ai-planning/data/train_set/zk_intent/augment_<runDic>.jsonl` | 清洗后的增量训练数据 |
| `results/data_clean_<runDic>/` | 旧数据清洗存档(必须在覆盖原文件前写入) |
| `results/gold_drift/drift_<runDic>.json` | Gold drift 检测结果 |
| `results/label_rules.md` | 已确立的标签规则集(R1~RN |
| 文件 | 用途 | 前端卡片 |
|---|---|---|
| `results/iteration_log.jsonl` | 每轮假设/干预/判定完整记录 | hypothesis / log |
| `results/error_registry.jsonl` | 跨轮错误追踪(case_hash → 出错轮次) | log |
| `results/workflow<runDic>.md` | 每轮回归分析报告 | dist-analysis / report |
| `output/relabel_candidates_<runDic>.csv` | **阶段一**:预计修改训练集候选清单(complex 翻转候选等) | **dist-analysis(分层结果分析)** |
| `results/data_clean_<runDic>/modified_samples.jsonl` | **阶段二**:确认修改最终落盘(用户审核 1/0 后实际生效的改动) | **augment(数据增强)** |
| `results/data_clean_<runDic>/deleted_samples.jsonl` | 旧数据清洗存档(删除项) | — |
| `results/augment_raw/augment_<runDic>_raw.jsonl` | GPT-5.4 仿写原始产物 | augment |
| `ai-planning/data/train_set/zk_intent/augment_<runDic>.jsonl` | 本轮合入训练集的增量(H1 重标 + H2 仿写) | augment |
| `results/gold_drift/drift_<runDic>.json` | Gold drift 检测结果 | gold-drift |
| `results/label_rules.md` | 已确立的标签规则集(R1~RN) | — |
**两阶段训练集修改的展示约定**:候选阶段(量级判定 + 人审 1/0 之前)写在 `output/relabel_candidates_<runDic>.csv`,前端「分层结果分析」卡片读取展示供人审;确认阶段(人审后落盘)写在 `results/data_clean_<runDic>/modified_samples.jsonl`,前端「数据增强」卡片读取展示最终改动。详见 [references/program.md §4.0.1 Step A](references/program.md)。