fix: session resume, config_set schema, metrics chart, subagent logging
- Fix session resume: remove messages.length>1 guard so sessionStorage
ID is used as resumeSessionId on first message (prevents new session
creation when user sends "继续")
- Fix config_set tool schema: add missing `items: {}` to array type in
oneOf (Azure OpenAI strict validation rejects it otherwise)
- Metrics chart: use actual target set filename from trigger text
instead of generic "target_subset" key
- Add sub-agent skill call logging to agent_runtime.py (full
prompt/response, no truncation)
- Various metric enrichment and dedup fixes in server.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -87,8 +87,8 @@ when_to_use: |
|
||||
- [ ] §4.0.1 Step B:量级判定基于 label-master 处理后的**残留候选量**走对应支线(≤50 自动按 label-master 推荐改 / 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` 只跑层 1(格式)—— 语义已在 §4.0.1 Step A.5 完成;H2 `augment_<runDic>.jsonl` 跑两层(`validate_label_output.py` 格式 + Skill 调用 `label-master` 语义),**逐条 1:1 全量覆盖**(`augment` 多少行 `label_master_review.jsonl` H2 部分就多少行,禁止抽样 / spot-check / X/X pass 外推),verdict 写 `results/data_clean_<runDic>/label_master_review.jsonl`,**不通过必须 = 0**(任何不通过必须修正后重新 review 直到全 pass 才能进 Step 5)
|
||||
- [ ] **§4.5 层 2 语义复核必须调用 `Skill(skill="label-master")`,禁止正则/规则脚本替代**:层 2 的本质是"用 label-master 知识体系对每条 (query, label) 做独立语义判定"。label-master 已标记 `repeatable: true`,可在同一 run 内多次调用。**禁止**:纯正则/关键词匹配脚本、"target 都一样所以直接 pass"逻辑、批量写 pass 不看 query 内容。已踩坑:R1 agent 写了个正则脚本充当"layer 2",完全没走语义判断。
|
||||
- [ ] **§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 内容。
|
||||
|
||||
### 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 里串接评测)
|
||||
@@ -128,17 +128,45 @@ when_to_use: |
|
||||
> echo '{"step":"cml","status":"complete","ts":"'$(date -Iseconds)'"}' >> "$SESSION_OUTPUT/program-state.jsonl"
|
||||
> ```
|
||||
|
||||
### R1.5 — 推荐每条 state entry 带 run_id(让 UI 准确归位 round)
|
||||
### R1.5 — 每条 state entry 必须带 run_id(让 UI 准确归位 round)
|
||||
|
||||
新版 UI 按 R0 / R1 / R2 切分 sections。**强烈建议**每行加 `run_id`:
|
||||
新版 UI 按 R0 / R1 / R2 切分 sections。每个 R{n>=1} 同时包含 **Train** 和 **Analysis** 两个 section。**必须**每行带 `run_id`。
|
||||
|
||||
⚠️ **核心规则:run_id 只在 hypothesis=complete 之后递增。** 一个完整迭代(augment → sft → eval → analysis → hypothesis)全程使用同一个 run_id。
|
||||
|
||||
完整示例(一轮完整迭代 R1):
|
||||
|
||||
```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 起点
|
||||
# ─── R0·Baseline ───
|
||||
echo '{"step":"cml","status":"running","run_id":"R0"}' >> $S
|
||||
echo '{"step":"dist-analysis","status":"complete","run_id":"R0"}' >> $S
|
||||
echo '{"step":"hypothesis","status":"complete","run_id":"R0"}' >> $S
|
||||
# ← hypothesis=complete → 递增 run_id
|
||||
|
||||
# ─── R1·Train ───
|
||||
echo '{"step":"augment","status":"running","run_id":"R1"}' >> $S
|
||||
echo '{"step":"augment","status":"complete","run_id":"R1","count":80}' >> $S
|
||||
echo '{"step":"sft","status":"running","run_id":"R1"}' >> $S
|
||||
echo '{"step":"sft","status":"complete","run_id":"R1"}' >> $S
|
||||
|
||||
# ─── R1·Analysis(SFT 后的 eval 仍然是 R1,不是 R2!)───
|
||||
echo '{"step":"cml","status":"running","run_id":"R1"}' >> $S
|
||||
echo '{"step":"cml","status":"complete","run_id":"R1"}' >> $S
|
||||
echo '{"step":"dist-analysis","status":"complete","run_id":"R1"}' >> $S
|
||||
echo '{"step":"hypothesis","status":"complete","run_id":"R1"}' >> $S
|
||||
# ← hypothesis=complete → 递增 run_id
|
||||
|
||||
# ─── R2·Train ───
|
||||
echo '{"step":"augment","status":"running","run_id":"R2"}' >> $S
|
||||
# ...
|
||||
```
|
||||
|
||||
run_id 缺省时后端会按 `iteration_log.jsonl` 行数自动推断(analysis 类 step 含 hypothesis → R{count},train 类 step augment/verify/sft → R{count+1}),但显式写更准——尤其是并发跨轮、补写历史 entry、或要在 R0 强制注入 baseline 时。
|
||||
**错误(禁止)**:eval 后递增 run_id,导致 train 和 analysis 分属不同 round:
|
||||
```
|
||||
R1: augment+sft → R2: cml+analysis+hypothesis → R3: augment+sft → R4: cml+analysis ...
|
||||
```
|
||||
|
||||
**为什么重要**:后端通过 `per_round[R{n}]` 从 R{n}·Analysis 的 watch/log entry 解析 runDic,再用这个 runDic 去找 R{n}·Train 的 augment 产物文件。如果 train 和 analysis 用了不同 run_id,augment 的产物文件找不到。
|
||||
|
||||
### R2 — 写入即生效,最后一行覆盖前面
|
||||
|
||||
|
||||
@@ -465,6 +465,11 @@ def cross_iter_tag(case_h: str, last_runDic: int, registry: dict) -> str:
|
||||
|
||||
#### 4.0 原始训练数据清洗(增强前必做)
|
||||
|
||||
> 🚨 **强制执行:每轮 augment 必须先跑 §4.0→§4.0.1,不得跳过直接做 H2 仿写。**
|
||||
> 即使 hypothesis 归因为"训练集缺数据",也必须先逐 pattern 检索训练集确认是否存在 mislabel。只有当 §4.0.1 Step A 扫描结果 + Step A.5 label-master 预审后残留候选 = 0 时,才能判定"无需 H1 修改"并跳到 H2。**"这轮只需要加数据"不是跳过 H1 检查的合法理由**——上一轮加的新数据可能引入了新的标签冲突,必须每轮重新检索确认。
|
||||
>
|
||||
> 缺少 H1 检查的 augment 视为不完整:`modified_samples.jsonl` 可以为空(代表确认无需修改),但 `data_clean_<runDic>.log` 必须记录"H1 扫描完成,0 候选"的结论,否则 §5.0 准入检查拒绝启动 SFT。
|
||||
|
||||
数据增强不仅仅是加数据,还必须对原数据集中的错误/不一致标签进行清洗,否则新增数据和旧数据矛盾,模型学不好。
|
||||
|
||||
**清洗原则(case 驱动,逐类分析)**:
|
||||
@@ -542,9 +547,12 @@ with open(out_csv, 'w', encoding='utf-8-sig') as fp:
|
||||
具体步骤:
|
||||
|
||||
1. 抽出每条候选的 `(file, line, query, old_label, suspected_new_label)`
|
||||
2. **逐条**调 `Skill(skill="label-master", args=...)`——每次只传**一条** `(query, old_label)`(**不**给 suspected_new_label,避免锚定),让 label-master 按 §决策流程 / §候选召回索引 / §高频混淆边界 输出 → `{verdict: 通过 | 不通过, 推荐标签, 排除理由, 易混淆边界}`。
|
||||
- ⚠️ **禁止把多条 query 塞进同一次 Skill 调用**——批量调用会让 label-master 在多条之间相互锚定,分错率显著升高(已踩坑)
|
||||
- 可在同一轮 assistant response 里并发多次 Skill 调用(建议 ≤ 8 路并发)以提升吞吐
|
||||
2. **用子 agent 调用 label-master**——**禁止在主 agent 上下文里直接调 Skill(skill="label-master")**,避免大量输出污染主 agent 上下文。做法:
|
||||
- 主 agent 把待审列表写入 jsonl 文件(如 `scratchpad/lm_input.jsonl`,每行 `{query, old_label}`)
|
||||
- 起子 agent:`delegate_agent(prompt="读取 <workspace>/scratchpad/lm_input.jsonl,对每条逐一调用 Skill(skill='label-master', args='query: ... label: ...'),把结果逐行 append 到 <workspace>/scratchpad/lm_output.jsonl(每行 {query, verdict, 推荐标签, 理由})。注意:每次 Skill 调用只传一条 query,禁止批量。", allow_shell=true)`
|
||||
- 子 agent 完成后主 agent 读取 `lm_output.jsonl` 汇总结果
|
||||
- ⚠️ **禁止把多条 query 塞进同一次 Skill 调用**——批量调用会让 label-master 在多条之间相互锚定(已踩坑)
|
||||
- ⚠️ 子 agent prompt 里必须给出**完整的 workspace 绝对路径**(`$AUTORESEARCH_CHAT_ROOT/scratchpad/...`),因为子 agent 没有父的环境变量
|
||||
3. 把 label-master 的"推荐标签"**回写到** `relabel_candidates_<runDic>.csv` 覆盖原 `建议新label` 列;新增列 `verdict`、`label_master_理由`,便于回查
|
||||
4. 收尾时按以下规则筛 candidate list(**残留候选 = 真正进入 Step B 的列表**):
|
||||
- `推荐标签 == old_label`:label-master 不认同要改 → 从候选里**剔除**(这条原 label 可能本来就是对的)
|
||||
@@ -571,7 +579,13 @@ done
|
||||
|
||||
同时落归档到 `results/data_clean_<runDic>/`:
|
||||
- `deleted_samples.jsonl`:被删样本原文
|
||||
- `modified_samples.jsonl`:被改样本(含 before/after output)
|
||||
- `modified_samples.jsonl`:被改样本,**必须包含以下字段**:
|
||||
- `line_idx`:原文件行号
|
||||
- `file`:原文件路径
|
||||
- `query`:**完整 query**(用 `extract_query(instruction)` 从原始 instruction 提取,**禁止截断**)
|
||||
- `old_output`:修改前 output
|
||||
- `new_output`:修改后 output
|
||||
- 可选:`instruction_query_excerpt`(仅供人工快速浏览,允许截断,但**不得作为 §4.5 复核的 query 来源**)
|
||||
- `data_clean_<runDic>.log`:摘要 + 影响 pattern + 样本数
|
||||
|
||||
**Step D:修改执行(按文件批量,避免重复读写)**
|
||||
@@ -882,10 +896,35 @@ JSONL 行里的 `sub_cate` 字段仅用于内部路由/去重,归档时丢弃
|
||||
|
||||
| 文件 | 层 1(格式) | 层 2(语义,`Skill(skill="label-master")`) |
|
||||
|---|---|---|
|
||||
| H1 `modified_samples.jsonl` | ✅ `validate_label_output.py --field output_after` | ❌ 已在 §4.0.1 A.5 完成 |
|
||||
| H1 `modified_samples.jsonl` | ✅ `validate_label_output.py --field output_after` | ✅ 逐条调 label-master(1:1 全量)— §4.0.1 A.5 是粗筛,**不能替代落盘前的 layer2** |
|
||||
| H2 `augment_<runDic>.jsonl` | ✅ `validate_label_output.py --field output` | ✅ 逐条调 label-master(1:1 全量) |
|
||||
|
||||
⛔ **层 2 禁止用正则/规则脚本替代**,必须调 `Skill(skill="label-master")`(`repeatable: true`)。每次只传一条 `(query, label)`,禁止批量。
|
||||
⛔ **层 2 禁止用正则/规则脚本替代**,必须通过**子 agent** 调 `Skill(skill="label-master")`(`repeatable: true`)。每次只传一条 `(query, label)`,禁止批量。**禁止主 agent 直接调用 label-master**——上下文污染会导致主 agent 后续推理质量下降。子 agent 通过文件交换结果(主 agent 写 input jsonl → 子 agent 读取并逐条调 Skill → 写 output jsonl → 主 agent 读取汇总),见 §4.0.1 Step A.5 的详细做法。
|
||||
|
||||
⚠️ **层 2 必须校验完整 label(含 tag),不能只校验 complex 维度**。
|
||||
|
||||
子 agent 调用 label-master 时的 args 格式:`query: <完整query> label: <完整output>`,例如:
|
||||
```
|
||||
Skill(skill="label-master", args="query: 顺路再去个加油站 label: Agent(tag=\"地图导航\")")
|
||||
```
|
||||
|
||||
label-master 会按其决策流程判断该 query 的 **tag 归属**是否正确(如应该是"地图导航"还是"充电加油"),同时判断 **complex**(Agent vs ComplexTask)和**输出格式**。
|
||||
|
||||
**禁止只传 complex=true/false 让 label-master 做二分类**——这不是 label-master 的设计用途。必须传完整的 `Agent(tag="xxx")` 或 `ComplexTask(tag="xxx")`,让 label-master 走完整的"候选召回→标签卡片→边界判定→推荐标签"流程。
|
||||
|
||||
label-master 的 verdict 必须同时覆盖:
|
||||
1. **tag 是否正确**:label-master 推荐的 tag 与当前 label 中的 tag 是否一致,不一致则 verdict=不通过
|
||||
2. **complex 是否正确**:Agent vs ComplexTask 是否正确
|
||||
3. **输出格式是否合规**:`Agent(tag="xxx")` / `ComplexTask(tag="xxx")` 格式是否规范
|
||||
|
||||
`label_master_review.jsonl` 每行必须包含字段:`query`、`label`(完整 output,如 `Agent(tag="地图导航")`)、`verdict`(通过/不通过)、`recommended_label`(label-master 推荐的完整 output)、`reason`(判断依据,需说明 tag 判定理由)。
|
||||
|
||||
**以下情况视为不合格 review,§5.0 准入检查拒绝启动 SFT**:
|
||||
- review 行中缺少 `label` 或 `recommended_label` 字段
|
||||
- `label` 字段只含 complex=true/false 而非完整 output
|
||||
- `reason` 中只提及 complex 判定而未提及 tag 归属判断
|
||||
|
||||
⚠️ **H1 query 来源**:review 时传给 label-master 的 `query` **必须**取自 `modified_samples.jsonl` 的 `query` 字段(Step C 已要求写入完整 query)。**禁止**从 `instruction_query_excerpt` 提取——该字段可能被截断导致 `extract_query` 返回空值。如果 `query` 字段缺失(旧格式兼容),必须用 `line_idx` + `file` 回原始训练文件读取完整 instruction 再 `extract_query`。
|
||||
|
||||
**覆盖率硬规则**:`label_master_review.jsonl` H2 行数 = `augment_<runDic>.jsonl` 行数(§5.0 `wc -l` 断言会拦)。不通过 > 0 则必须修正后重新 review 直到全 pass。verdict 文件不存在 → Step 5 拒绝启动。
|
||||
|
||||
@@ -893,6 +932,12 @@ JSONL 行里的 `sub_cate` 字段仅用于内部路由/去重,归档时丢弃
|
||||
|
||||
🚨 **Step 4 → Step 5 硬连接**:`augment=complete` 后同一轮**紧接着**:① 写 `sft=running` ② §4.5 复核 ③ 复核全过 → `submit_sft.sh` + 挂 watcher。禁止 turn 结束、禁止写简报、禁止等回调。评测在 SFT `_SUCCESS` 落盘后的下一轮单独用 `submit_cml_eval.sh` 起,不串进 SFT bg。
|
||||
|
||||
⚠️ **写 `augment=complete` 时必须附带 `"count"` 字段**,值为本轮最终写入 `augment_<runDic>.jsonl` 的样本行数(经 dedup + label-master 过滤后的实际数)。示例:
|
||||
```jsonl
|
||||
{"step":"augment","status":"complete","run_id":"R5","count":49,"ts":"2026-05-26T21:51:32+08:00"}
|
||||
```
|
||||
Pipeline panel 用此字段展示增强条数;缺失则只显示文件名。
|
||||
|
||||
使用 `prepare_and_train_sft.py` 完成数据组装和训练。
|
||||
|
||||
#### 5.0 环境准备
|
||||
@@ -922,6 +967,10 @@ if [ -f "$AUG" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1c. H1 检查日志必须存在(§4.0 强制要求,即使无修改也要记录扫描结论)
|
||||
CLEAN_LOG="$AUTORESEARCH_CHAT_ROOT/results/data_clean_${RUNDIC}/data_clean_${RUNDIC}.log"
|
||||
[ -f "$CLEAN_LOG" ] || { echo "data_clean 日志不存在,说明 §4.0 H1 检查未执行,回 §4.0"; exit 1; }
|
||||
|
||||
# 2. zk_trainer 仓库已 clone(URL 写死,不要换命名空间,clone 失败先看 ssh key)
|
||||
cd "$AUTORESEARCH_CHAT_ROOT"
|
||||
[ -d zk_trainer ] || git clone git@git.n.xiaomi.com:wangsenhao/zk_trainer.git
|
||||
@@ -1029,6 +1078,14 @@ eval "$(./scripts/resolve_run_ids.sh)"
|
||||
|
||||
每轮写入 `results/iteration_log.jsonl` 一行,schema:
|
||||
|
||||
> 🚨 **`results` 字段必须包含以下固定指标(每轮都要,不可遗漏)**:
|
||||
> - `specific_test`:Specific Test 准确率
|
||||
> - `dapan_car`:大盘车载准确率
|
||||
> - `target_subset`:目标集合准确率(= 当前需求子集)
|
||||
> - `icl_test`:ICL Test 准确率
|
||||
>
|
||||
> 这些指标从 `lark_template.json` 的 metric_diff 中提取。如果某个指标在评测结果中确实不存在(如首次评测缺少某子集),写 `null` 而不是省略 key——前端 metrics 折线图依赖每轮 key 的一致性,缺 key 会导致数据点丢失。
|
||||
|
||||
```json
|
||||
{
|
||||
"iteration": 12,
|
||||
@@ -1041,8 +1098,9 @@ eval "$(./scripts/resolve_run_ids.sh)"
|
||||
},
|
||||
"prediction": {"req_set_car": 93.0, "triage_err_rate": 0.15},
|
||||
"results": {
|
||||
"req_set_car": 92.1, "dapan_car": 96.30,
|
||||
"specific_test": 95.10, "triage_err_rate": 0.18
|
||||
"specific_test": 95.10, "dapan_car": 96.30,
|
||||
"target_subset": 92.1, "icl_test": 86.11,
|
||||
"triage_err_rate": 0.18
|
||||
},
|
||||
"verdict": "partial",
|
||||
"root_cause_findings": [
|
||||
|
||||
Reference in New Issue
Block a user