fix: subprocess-based remote sync, pipeline status, skill updates
- Replace thread-based remote state sync with subprocess (sync_remote_state.py) to prevent thread pool exhaustion hanging the API - Fix pipeline showing 'waiting' when a real step is running alongside a gate - Fix watcher scanner path for linux accounts mode - Disable label-master semantic review (Step A.5 + §4.5) per user request - Correct field names: use is_model_correct_dev for accuracy, origin_predict_dev for model output (not cleaned_predict) - Add prohibition against putting test set data in relabel_candidates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,12 @@ when_to_use: |
|
||||
|
||||
# autoresearch-zk — 小爱中控模型自主迭代框架
|
||||
|
||||
## ⛔ 第一步:读 program.md(强制,任何操作之前)
|
||||
|
||||
收到"开始"触发信号后,**第一个动作**必须是读取 `references/program.md`。本文件(SKILL.md)只是概要和 UI 约定,**完整操作规范全在 program.md 里**——包括 bootstrap 流程(clone ai-planning 仓库)、cml 自动安装、评测集定位逻辑等关键步骤。
|
||||
|
||||
**不读 program.md 就开始工作 = 必然跑偏。** 已踩坑:agent 只看 SKILL.md 就开始环境检查,发现评测集文件不在本地后停下来问用户路径——而 program.md 明确写了"文件在 git 仓库里,先 clone"。
|
||||
|
||||
## 🚫 严禁问用户的事(违反任一条 = 违反 skill)
|
||||
|
||||
每次问用户都让用户烦。program.md 已经把"什么时候停"写得很死(17 类 HiTL 信号 + 用户主动打断),**这之外一律不准停下来等确认**。下面是被反复踩坑的"擅自暂停"模式,**全部禁止**:
|
||||
@@ -18,11 +24,11 @@ when_to_use: |
|
||||
| "我先把控制权交回,等你来问跑完了吗" | 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 硬连接")|
|
||||
| "Step 4 augment 完成 ✅,等回调后续做 SFT" | augment=complete 那一刻就是 SFT 启动那一刻——**同一轮 bash** 紧接着跑层1格式校验 + 提交 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`,你不写卡片永远停在上轮。 |
|
||||
| "SFT 用哪个 basemodel?(a) 用户 eval 给的路径 (b) 默认 Qwen3-4B" | program.md §5.0 写死 `--model_path = /mnt/wangsenhao/verl_zk/Qwen3-4B-Instruct-2507`,**每轮强制 basemodel**——这是规则不是选项。eval 阶段的 `model_path_new` 跟 SFT 的 `--model_path` 是两件事,不要混。直接用 Qwen3-4B-Instruct-2507。 |
|
||||
| "zk_trainer 仓库 URL 是什么?" | program.md §5.0 line 1323 写死 `git clone git@git.n.xiaomi.com:wangsenhao/zk_trainer.git`,**直接用**。clone 失败先看 ssh key(program.md 「SSH Key 检查」),**不要**自己脑补 `nlp/`、`xiaoai/`、`autoresearch/` 等命名空间问用户。 |
|
||||
| "label-master 复核 100 条仿写,5 条全 pass,按比例外推 100 条 OK" | **`X/X pass` 缩写禁用**——§4.5 写死「H2 augment_<runDic>.jsonl 必须逐条过 label-master」,review jsonl 行数必须 == augment.jsonl 行数(1:1 覆盖)。抽样外推 = 协议违反,§5.0 准入门会用 `wc -l` 拦住。状态日志里写"100 条复核完成"也必须真的是 100 条 review entry。 |
|
||||
| "格式校验 5 条 pass,按比例外推 100 条 OK" | **抽样外推禁用**——`validate_label_output.py` 必须对全量文件跑,不许抽样 |
|
||||
|
||||
**合法暂停只有**:
|
||||
|
||||
@@ -83,20 +89,19 @@ when_to_use: |
|
||||
- [ ] **runDic 必须在当轮 eval `lark_template.json` 落盘后重新 `eval "$(./scripts/resolve_run_ids.sh)"` 取**——禁止复用之前缓存值或手算,eval 期间 resolve 会得到上一轮 workflow id 导致偏移 -1
|
||||
- [ ] **§4.0 原始训练数据清洗(增强前必做)**——按 4 种动作走完逐 pattern 判定
|
||||
- [ ] §4.0.1 Step A:候选定位输出 csv,**不直接改**
|
||||
- [ ] §4.0.1 Step A.5:**先走 label-master 预审**(用推荐标签覆盖 new_label,剔除 label-master 不认同要改的条目)
|
||||
- [ ] §4.0.1 Step B:量级判定基于 label-master 处理后的**残留候选量**走对应支线(≤50 自动按 label-master 推荐改 / 51-200 全量人审 / >200 触发 #3,全量交付)
|
||||
- [ ] ~~§4.0.1 Step A.5~~ label-master 预审已禁用,跳过
|
||||
- [ ] §4.0.1 Step B:量级判定基于 Step A 候选量走对应支线(≤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+H2 都要)**:H1 `modified_samples.jsonl` **和** H2 `augment_<runDic>.jsonl` 都必须跑两层(`validate_label_output.py` 格式 + Skill 调用 `label-master` 语义),**逐条 1:1 全量覆盖**。§4.0.1 A.5 的预审是粗筛,**不能替代落盘前的 layer2 语义复核**。verdict 写 `results/data_clean_<runDic>/label_master_review.jsonl`,**不通过必须 = 0**(任何不通过必须修正后重新 review 直到全 pass 才能进 Step 5)
|
||||
- [ ] **§4.5 层 2 语义复核必须通过子 agent 调用 `Skill(skill="label-master")`,禁止主 agent 直接调、禁止正则/规则脚本替代**:主 agent 把待审列表写入 `scratchpad/lm_input.jsonl`,起子 agent(`delegate_agent(prompt="...", allow_shell=true)`)逐条调 label-master,结果写 `scratchpad/lm_output.jsonl`,主 agent 读取汇总。子 agent prompt 里给**绝对路径**。**禁止**:主 agent 直接调 Skill(skill="label-master")、纯正则/关键词匹配脚本、"target 都一样所以直接 pass"逻辑、批量写 pass 不看 query 内容。
|
||||
- [ ] ~~§4.5 label-master 标签复核~~ 已禁用,仅保留层 1 格式校验(`validate_label_output.py`)
|
||||
|
||||
### Step 4 → Step 5 边界(**NEVER STOP 硬连接**,反复踩坑)
|
||||
- [ ] 写完 `augment=complete` 那一刻,**同一轮 bash 不许结束**:紧接着跑 §4.5 label-master 复核 → 写 `sft=running` → 调 `submit_sft.sh` → 挂 watcher(评测在 SFT `_SUCCESS` 落盘后的下一轮单独用 `submit_cml_eval.sh` 起,不要在 SFT bg 里串接评测)
|
||||
- [ ] 写完 `augment=complete` 那一刻,**同一轮 bash 不许结束**:紧接着跑层 1 格式校验 → 写 `sft=running` → 调 `submit_sft.sh` → 挂 watcher(评测在 SFT `_SUCCESS` 落盘后的下一轮单独用 `submit_cml_eval.sh` 起,不要在 SFT bg 里串接评测)
|
||||
- [ ] **不许写"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` 存在、行数 = augment 行数(全量不抽样)、不通过 = 0(任何不通过必须修正后重新 review 直到全 pass)
|
||||
- [ ] 层 1 格式校验通过(`validate_label_output.py`)
|
||||
- [ ] 旧 sft_output 已 `mv sft_output sft_output_r{prev}` 备份
|
||||
- [ ] 训练参数从 config.yaml 读,model_path = basemodel(**不从上轮 ckpt 续训**)
|
||||
|
||||
@@ -219,7 +224,7 @@ bg 任务(`bash(run_in_background=true)`)的合法范围是**一个 step 内
|
||||
|---|---|---|
|
||||
| `cml` | 数分钟到数十分钟 | 远端 workflow 启动 + 评测 |
|
||||
| `sft` | 30 分钟以上 | 训练 pod |
|
||||
| `augment` | 数分钟 | GPT 仿写 + sanity + label-master 复核 |
|
||||
| `augment` | 数分钟 | GPT 仿写 + sanity + 格式校验 |
|
||||
| `dist-analysis` | 1-3 分钟 | 读 metric_diff + 跨轮 diff + 写 workflow.md |
|
||||
| `gold-drift` | 1-2 分钟 | drift 检测 |
|
||||
|
||||
@@ -229,7 +234,7 @@ bg 任务(`bash(run_in_background=true)`)的合法范围是**一个 step 内
|
||||
|
||||
**正确做法**:human-review 之后必须按 program.md 逐 step 真跑:
|
||||
1. 写 `hypothesis=running` → 真写假设到 iteration_log → 写 `hypothesis=complete`
|
||||
2. 写 `augment=running` → 真跑 §4.0.1 + §4.1 + §4.2 + §4.3 + §4.5 label-master 复核 → 写 `augment=complete`(**这一步至少 5 分钟**)
|
||||
2. 写 `augment=running` → 真跑 §4.0.1 + §4.1 + §4.2 + §4.3 + 层1格式校验 → 写 `augment=complete`(**这一步至少 5 分钟**)
|
||||
3. 写 `sft=running` → 提交 cml custom_train + 起 watcher
|
||||
|
||||
**自查**:连续两个 step 的 `ts` 间隔 < 60 秒,必然有一个是假的。复盘时 grep program-state.jsonl 看相邻 entry 时间戳。
|
||||
@@ -403,7 +408,7 @@ echo '{"log":{"ts":"11:55:10","iter":"R1","text":"Step 1 dist-analysis: 写 work
|
||||
| 触发信号 | `"开始,<需求集合名>"` |
|
||||
| 评测 workflow | `f-20260408161444-wu3pz`(版本见 `config.yaml`) |
|
||||
| 训练基模 | `/mnt/wangsenhao/verl_zk/Qwen3-4B-Instruct-2507` |
|
||||
| 工作目录 | `$AUTORESEARCH_CHAT_ROOT`(每 chat 独立,由后端注入;位于共享 NFS:`/mnt/wangsenhao/autoresearch-zk-users/<email_prefix>/<chat_session_id>/`,jupyter pod 与训练 pod 都能读写) |
|
||||
| 工作目录 | `$AUTORESEARCH_CHAT_ROOT`(每 chat 独立,由后端注入;位于共享 NFS:`/mnt/<email_prefix>/autoresearch-zk-users/<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/` |
|
||||
|
||||
@@ -412,7 +417,7 @@ echo '{"log":{"ts":"11:55:10","iter":"R1","text":"Step 1 dist-analysis: 写 work
|
||||
每个 chat session 一份独立工作区 `$AUTORESEARCH_CHAT_ROOT`(后端在 jupyter 启动时自动注入这个 env,agent 每次 bash 都能拿到)。该目录位于共享 NFS:
|
||||
|
||||
```
|
||||
/mnt/wangsenhao/autoresearch-zk-users/<email_prefix>/<chat_session_id>/
|
||||
/mnt/<email_prefix>/autoresearch-zk-users/<chat_session_id>/
|
||||
```
|
||||
|
||||
`<email_prefix>` 是用户登录时的小米邮箱前缀(`xxx@xiaomi.com` 取 `xxx`),用作账号根目录;同一用户跨 chat 共享根目录但 chat 之间完全隔离。
|
||||
@@ -506,7 +511,7 @@ assets/
|
||||
写 gate entry / 在 chat 里向用户提问之前,**逐条过这三问**。下面任何一段流程示例都默认你已经过了这三问;过不了,再漂亮的 summary/proposal/ask 也是干扰用户。
|
||||
|
||||
**Q1. 是真 §HiTL 信号吗?**
|
||||
- ✅ 真信号:label-master 预审后残留 > 50/200、Gold drift ≥ 10、跨子集净退步、连续 3 轮无提升 等列表里写明的条件
|
||||
- ✅ 真信号:候选 > 50/200、Gold drift ≥ 10、跨子集净退步、连续 3 轮无提升 等列表里写明的条件
|
||||
- ❌ 凑出来的理由:`第一次跑想让用户校方向` / `我担心副作用` / `想让用户拍板更稳` / `proposal 听起来风险大` —— 这些都是脑补,不是信号
|
||||
|
||||
**Q2. `ask` 是真分叉吗?**
|
||||
@@ -534,10 +539,10 @@ assets/
|
||||
|
||||
| 时机 | gate 类型 | run_id |
|
||||
|---|---|---|
|
||||
| R0 baseline 分析完成、命中 §HiTL 信号要让用户拍板(label-master 预审后残留候选超阈值、目标子集异常、跨子集分歧大 等真实信号;⛔ **不是**"第一次跑想让用户校方向" / "我担心 H1 修标的副作用" / "想让用户在激进/保守里选"这种凑出来的理由——先过上面的自检三问) | `human-check` | `R0` |
|
||||
| R0 baseline 分析完成、命中 §HiTL 信号要让用户拍板(候选超阈值、目标子集异常、跨子集分歧大 等真实信号;⛔ **不是**"第一次跑想让用户校方向" / "我担心 H1 修标的副作用" / "想让用户在激进/保守里选"这种凑出来的理由——先过上面的自检三问) | `human-check` | `R0` |
|
||||
| R{n≥1} analysis 完成、命中 §HiTL 信号(gold drift / regression / 跨子集退步 / 连续 3 轮无提升 等) | `human-review` | `R{n}` |
|
||||
| §4.0.1 Step B:**label-master 预审后**残留候选 > 200 条命中触发条件 #3(原扫数不算数) | `human-check` | 当前 round |
|
||||
| 其他 HiTL 信号(label-master 预审后残留 > 50 条、Gold drift ≥ 10 条 等) | `human-check`(开始前)/ `human-review`(结果后) | 当前 round |
|
||||
| §4.0.1 Step B:候选 > 200 条命中触发条件 #3 | `human-check` | 当前 round |
|
||||
| 其他 HiTL 信号(候选 > 50 条需人审、Gold drift ≥ 10 条 等) | `human-check`(开始前)/ `human-review`(结果后) | 当前 round |
|
||||
|
||||
判断标准就一条:**只要你下一步打算 chat-ask 用户拍板,就先写 gate entry 再问**。
|
||||
|
||||
@@ -550,12 +555,10 @@ assets/
|
||||
# ⛔ ask 必须是真分叉。⛔ "H1+H2 一起 vs 只跑 H1"、"激进 vs 保守"、"先 H1 还是先 H2" 这类
|
||||
# "假设组合"统统不是分叉——是 agent 自己根据信号决定的,决定完写进 proposal 公布即可。
|
||||
# R0 baseline 后命中真实 HiTL 信号(这里是候选量超阈值),让用户拍板更精细 pattern
|
||||
# ⚠️ 给用户排板的候选数量必须是 §4.0.1 Step A.5 label-master 预审之后的残留数(推荐 != old_label 的那部分),
|
||||
# 不是 wide pattern 原始扫出来的数。原始数 label-master 还要剔掉一大半,先用原始数找用户=干扰用户判断。
|
||||
echo '{"step":"human-check","status":"running","run_id":"R0",
|
||||
"summary":"R0 baseline 完成;复杂导航过召专项0511 = 58.89%(53/90);wide pattern 原扫 412 条,label-master 预审后残留 287 条(> 200 阈值)",
|
||||
"proposal":"H1(标签纠错):按 label-master 推荐改这 287 条 complex=true → complex=false。 H2(数据增强):仿写 100 条 complex=false 的简单导航 query 补进训练集",
|
||||
"ask":"残留 287 条仍偏多,是全部按 label-master 推荐改、还是先收窄到 query 含「打开/进入」的子集(约 110 条)单独审一轮?",
|
||||
"summary":"R0 baseline 完成;复杂导航过召专项0511 = 58.89%(53/90);候选 412 条(> 200 阈值)",
|
||||
"proposal":"H1(标签纠错):改这 412 条 complex=true → complex=false。 H2(数据增强):仿写 100 条 complex=false 的简单导航 query 补进训练集",
|
||||
"ask":"412 条偏多,是全部改、还是先收窄到 query 含「打开/进入」的子集(约 110 条)单独审一轮?",
|
||||
"ts":"'$(date -Iseconds)'"}' >> $S
|
||||
|
||||
# R1 评测发现 regression
|
||||
@@ -566,8 +569,7 @@ echo '{"step":"human-review","status":"running","run_id":"R1",
|
||||
"ts":"'$(date -Iseconds)'"}' >> $S
|
||||
|
||||
# fallback:只写 reason 也能跑(前端会启发式切分),但不如结构化清晰
|
||||
# 注意 reason 里的"412"也是 label-master 预审后残留数,不是原扫数
|
||||
echo '{"step":"human-check","status":"running","run_id":"R0","reason":"label-master 预审后残留 412 条仍超阈值,需要人工定更精细 pattern 收窄","ts":"'$(date -Iseconds)'"}' >> $S
|
||||
echo '{"step":"human-check","status":"running","run_id":"R0","reason":"候选 412 条超阈值,需要人工定更精细 pattern 收窄","ts":"'$(date -Iseconds)'"}' >> $S
|
||||
```
|
||||
|
||||
**顺序不能反**:先 echo gate entry → 再 chat reply 给用户。否则用户先看到聊天问话、UI 里却没卡,会困惑"流程是不是卡死了"。
|
||||
@@ -581,7 +583,7 @@ echo '{"step":"human-check","status":"running","run_id":"R0","reason":"label-mas
|
||||
| `run_id` | ✅ | 当前所在轮次(决定 gate 插在哪个 section 后) |
|
||||
| `summary` | 🔼 | **现状一句话**:跑了什么、关键数字。例:`R0 baseline 完成;专项 58.89%(53/90),37 错全为 complex 误判` |
|
||||
| `proposal` | 🔼 | **打算怎么干**:每个 H 单独说"H? (类型):具体做什么"。详见下面规则 |
|
||||
| `ask` | 🔼 | **让用户选什么**:必须是真分叉(用户的判断能改变下一步动作)。例:`label-master 预审后残留 287 条偏多,全改、还是收窄到「打开/进入」子集(~110 条)单独审一轮?` ⚠️ 写候选数量必须是 §4.0.1 Step A.5 label-master 预审后的残留数,不是原扫数。⛔ 反例:`H1+H2 一起 vs 只跑 H1`——假设组合是你定的,不甩给用户 |
|
||||
| `ask` | 🔼 | **让用户选什么**:必须是真分叉(用户的判断能改变下一步动作)。例:`候选 287 条偏多,全改、还是收窄到「打开/进入」子集(~110 条)单独审一轮?` ⛔ 反例:`H1+H2 一起 vs 只跑 H1`——假设组合是你定的,不甩给用户 |
|
||||
| `reason` | ⭕ | 兜底用:没写 summary/proposal/ask 时前端会拿 reason 做启发式切分。但**优先用结构化三段**,别只写 reason |
|
||||
| `ts` | ✅ | ISO 时间戳 |
|
||||
|
||||
@@ -598,7 +600,7 @@ echo '{"step":"human-check","status":"running","run_id":"R0","reason":"label-mas
|
||||
|
||||
**不要写进 proposal 的内容**:
|
||||
- §X.X.X 规则引用(`触发 §4.0.1 Step B`、`命中条件 #3`)—— 用户不关心你按哪条规则做的,只关心你要做什么
|
||||
- 流程自洽说明(`需要人工逐条审 1/0`、`走 sanity check`、`过 label-master 复核`)—— 这些是 agent 内部流程,对用户决策没用
|
||||
- 流程自洽说明(`需要人工逐条审 1/0`、`走 sanity check`、`过格式校验`)—— 这些是 agent 内部流程,对用户决策没用
|
||||
- 候选量区间括号注释(`100~150 触发 51-200 区间`)—— 数量 OK,区间归属归属是规则细节,删
|
||||
|
||||
**`ask` 字段尤其要注意:**
|
||||
|
||||
Reference in New Issue
Block a user