Files
zk-data-agent/skills/intervention-data/SKILL.md
T
2026-06-10 21:20:15 +08:00

222 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.
---
name: intervention-data
description: 生成和校验单句精确干预、正则干预候选 TSV,复用标签大师的 target 语法校验,并从历史干预表推断 code 到下发 domain 的固定映射。
when_to_use: 当用户希望新增、检查或整理干预规则,例如给某个 query 或多轮正则生成干预条目、确认设备和标签是否合法、推断下发 domain 时使用。
aliases: intervention, rule-intervention, 干预数据
allowed_tools: read_file, write_file, edit_file, grep_search, glob_search, ask_user_question, python_exec
---
使用这个 skill 处理“query/多轮表达 -> 干预候选 -> 合并 + 上传 HDFS”的任务。
干预数据目前有两类,**文件无扩展名**(HDFS 上文件名是 `260610` 这种纯日期),内容是真实 TAB 字符分隔的纯文本,**绝不是字面 `\t` 两字符**。下文所有 `\t` 都表示一个真实的 TAB。
- **单句精确干预(codeTopQuery**:一行 4 列,`设备\tquery\tcode\t下发domain`
示例:
```
unify 播放音乐 Agent(tag="音乐") contentCopilot|music
unify 小爱同学 Chat(type="wakeup") dialogCopilot|michat
```
- **正则干预(codeIntervene**:一行 3 列,`设备\t正则\t标签`
示例:
```
unify ^query#播放歌曲关机又关机$ Agent(tag="音乐")
unify ^beforeQuery#播放歌曲#query#关机又关机$ Agent(tag="音乐")
glass ^query#这是什么$ QA()
```
精确干预的 `code` 和正则干预的 `标签` 使用同一套 target 语法,和 `label-master` 的输出一致,但不包含复杂度判断。校验时必须复用 `skills/label-master/scripts/validate_label_output.py` 的逻辑。
## 关键约定
- `unify` 表示全部设备。
- 设备必须优先从下载下来的最新干预文件中归纳,不要随手发明设备名。
- **单句精确干预的 `下发domain` 必须由人拍板**:脚本基于最新干预文件按 `code → 下发domain` 频次给出 top-N 候选 + 计数,agent 列出来让用户从用户给的 tag 信息出发选一个,**不要默认用多数派直接落盘**。
- 正则干预支持多轮。多轮正则使用 `beforeQuery#上一轮#query#当前轮` 这种片段表达,最终通常包在 `^...$` 中。
- 默认只生成候选行 + 校验结果,不直接追加到历史干预文件。上传 HDFS 必须等用户明确确认。
- 所有产物优先写到当前会话的 `$AUTORESEARCH_CHAT_ROOT/output/` 或 `scratchpad/` 下,不要写到项目根目录的 `output/`。
## HDFS 干预文件路径
线上干预的权威源在 HDFS。两类文件 × 四个环境(p4t = 测试,ptr = 灰度,preview = 预发,production = 生产):
| 类型 | HDFS 目录 |
|---|---|
| 单句精确干预(codeTopQuery | `hdfs://zjyprc-hadoop/user/s_ai_service/nlp/PlanningPredict/all/<env>/strategy/codeTopQuery` |
| 正则干预(codeIntervene | `hdfs://zjyprc-hadoop/user/s_ai_service/nlp/PlanningPredict/all/<env>/strategy/codeIntervene` |
`<env>` 取值:`p4t` / `ptr` / `preview` / `production`。每个目录下文件名"形式"按日期 `yymmdd` 命名(**纯数字、无扩展名**,例:`260609`),但实际值不要求严格等于当天日期——新增一份就是上一份**按一天 +1**(要正确处理月末/年末进位),最大值仍须落在合法 `yymmdd` 范围内(最大不超过今天)。**最新文件 = 文件名尾号最大的那一个**。
## 推荐流程
0. **先确认链接到远端**(强制第一步,不通过就停下)。
- "远端"概念同 [model-iteration](../model-iteration/SKILL.md)bash 实际跑在远端 jupyter pod 工作区,`$AUTORESEARCH_CHAT_ROOT` 是后端注入的远端 chat 工作区路径(位于共享 NFS:`/mnt/<email_prefix>/autoresearch-zk-users/<chat_session_id>/`)。系统提示里的"当前会话目录"是后端宿主机本地路径,**不是**远端,不能拿来跑脚本或读历史文件。
- 起手第一条 bash 必须是预检(同时验远端 + HDFS 工具):
```bash
pwd && echo "AUTORESEARCH_CHAT_ROOT=$AUTORESEARCH_CHAT_ROOT" && uname -a && which hls hget hput
```
- 通过条件:`pwd` 返回远端绝对路径(一般 `/root/zk_agent_workspaces/LOCALID_xxx` 或类似 Linux 路径)、`$AUTORESEARCH_CHAT_ROOT` 非空、`hls/hget/hput` 都能找到。
- 不通过的情形(任一命中就停下问用户):
- `$AUTORESEARCH_CHAT_ROOT` 为空 → 后端没注入,会话没绑定 chat。
- `pwd` 是 Windows 路径(如 `D:\...`、`/d/...`)或后端本地路径(如 `/home/mi/zk-data-agent-wsh/...`)→ 当前在本地,不是远端。
- `hls/hget/hput` 找不到 → 远端环境没装 HDFS 客户端,下一步拉文件直接卡死。
- bash 报命令找不到 / 没权限 → 远端 pod 没起来。
1. **获取最新的干预文件**(强制第二步,所有后续校验和上传都基于这一步拉下来的文件)。
- 先确认 (a) 干预类型:精确干预 → `codeTopQuery`,正则干预 → `codeIntervene`(b) 目标环境(`p4t/ptr/preview/production`,用户没说就问,建议先在 `p4t` 验证)。
- 用 `hls` 列目录,按文件名尾号取最大的那个作为最新文件,然后用 `hget` 拉下来:
```bash
ENV=p4t # 或 ptr / preview / production
TYPE=codeTopQuery # 或 codeIntervene
HDFS_DIR=hdfs://zjyprc-hadoop/user/s_ai_service/nlp/PlanningPredict/all/$ENV/strategy/$TYPE
hls "$HDFS_DIR"
LATEST=$(hls "$HDFS_DIR" | awk '{print $NF}' | grep -v '^$' | sort -V | tail -1)
echo "LATEST=$LATEST"
WORK="$AUTORESEARCH_CHAT_ROOT/intervention/${ENV}_${TYPE}"
mkdir -p "$WORK"
# base 不带扩展名(HDFS 上原文件名就是日期),保持原貌
hget "$LATEST" "$WORK/base"
wc -l "$WORK/base"
# 抽 1-2 行肉眼校验是不是真 TAB 分隔(cat -A 把 TAB 显成 ^I
head -2 "$WORK/base" | cat -A
```
- **设备枚举、`code → 下发domain` 频次、重复/冲突检查全部基于这份刚拉下来的 `base`**,不要再用本地历史快照(旧快照可能已过时)。文件是 TAB 分隔纯文本,无扩展名,不要当 csv/json 处理。
2. 先确认用户要哪种干预方式(如果第 1 步还没定就在这里定,确定后回到第 1 步拉对应文件):
- 单句精确干预:query 必须完全命中。
- 正则干预:适合一类表达、多轮上下文或需要泛化的高频 case。
3. 确认设备。用户没说设备时,必须问设备;可以提示 `unify` 表示全部设备。设备枚举从 base.tsv 第一列归纳。
4. 确认目标标签/code。它必须是完整 target,例如 `Agent(tag="地图导航")`、`QA()`、`Chat()` 或合法 function 调用。
5. 如果是单句精确干预:
- 确认 `query`。
- **下发domain 必须人选**:基于 base 文件统计该 `code`target)历史上配过的 `下发domain` 频次,给出 top-N 候选 + 计数,让用户结合自己提的 tag 信息选一个。例:
```bash
# 用 awk 抽该 target 的下发domain 分布
awk -F'\t' -v t='Agent(tag="音乐")' '$3==t {print $4}' "$WORK/base" \
| sort | uniq -c | sort -rn | head -5
```
把结果(如 `12345 contentCopilot|music / 87 contentCopilot|audio_book / ...`)作为候选给用户,**不要默认取第一个直接落盘**。
6. 如果是正则干预:
- 确认当前轮 query。
- 如果有多轮,确认前序 query 顺序。
- 用户给了原始正则就保留;没给时由脚本生成 `^query#...$` 或 `^beforeQuery#...#query#...$`。
7. 调用 `scripts/generate_intervention_candidate.py` 生成候选 TSV 和校验报告(产物写到 `$AUTORESEARCH_CHAT_ROOT/output/` 或 `scratchpad/`)。
8. **合并 + diff 给人看**。把候选行追加进 `base` 副本,跑 unified diff
```bash
cp "$WORK/base" "$WORK/merged"
cat candidate >> "$WORK/merged" # candidate 是上一步生成的候选行文件,纯 TAB 分隔
diff -u "$WORK/base" "$WORK/merged" | tee "$WORK/diff.patch"
```
9. 展示给用户:干预类型、设备、候选行(原文 TAB 分隔)、校验结果、diff 摘要、目标 HDFS 路径。**用户没明确说"上传 / 推到 X 环境 / hput"前不要执行 hput**——这是写远端 + 影响线上的不可逆动作,必须等确认。
10. **用户确认后 hput 上传**。新文件名规则:**取 `LATEST` 的尾号「按一天」+1**——必须正确处理月末 / 年末进位(260131 +1 = 260201261231 +1 = 270101260228 在闰年 +1 = 260229、平年 +1 = 260301),**不是**数值 `+1`(那样 260131+1=260132 直接失效)。用 `date` 做日期算术:
```bash
OLD_NUM=$(basename "$LATEST") # 例:260609 或 260131
NEW_NUM=$(date -d "20${OLD_NUM} + 1 day" +%y%m%d)
TODAY=$(date +%y%m%d)
# 校验:date 解析失败(OLD_NUM 本身就不合法)会直接报错;新尾号也必须是合法 yymmdd
if ! [[ "$NEW_NUM" =~ ^[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ ]]; then
echo "ERR: $NEW_NUM 不符合 yymmdd 形式,停下问用户"; exit 1
fi
if [ "$NEW_NUM" -gt "$TODAY" ]; then
echo "ERR: $NEW_NUM=$NEW_NUM 超过今天 $TODAY(说明上一份就是今天的,已经发过一版),停下问用户"; exit 1
fi
hput "$WORK/merged" "$HDFS_DIR/$NEW_NUM"
hls "$HDFS_DIR" | tail -3 # 校验新文件已落、尾号最大
```
说明:HDFS 上文件名"形式"按日期来,实际值是「上一份按一天 +1」而不是严格当天日期;只要 +1 后仍是合法 yymmdd 且不超过今天就行。如果上一份已经是今天(OLD_NUM == TODAY),`date +1 day` 会算出明天 → 命中"超过今天"分支,停下问用户怎么处理(通常是当天已经发过一版,要不要并入现有版本,或推迟到明天再发)。
11. 展示最终结果:上传后的 HDFS 路径 + 新文件尾号。
## 脚本能力
```text
skills/intervention-data/
SKILL.md
knowledge/
type_match_agent.json
scripts/
intervention_common.py
analyze_intervention_sources.py
audit_intervention_sources.py
generate_intervention_candidate.py
validate_intervention_records.py
```
### analyze_intervention_sources.py
读取历史干预文件,输出设备枚举、`code -> 下发domain` 多数映射、常见标签,以及 `knowledge/type_match_agent.json` 中的下发表。
示例:
```bash
python skills/intervention-data/scripts/analyze_intervention_sources.py
```
### generate_intervention_candidate.py
从 JSON 输入生成候选 TSV,并自动校验。输入可以来自文件或 stdin。
### audit_intervention_sources.py
审计现有两个历史干预文件,只输出摘要,不修改文件。适合验证当前 skill 的校验规则和存量数据是否贴合。
示例:
```bash
python skills/intervention-data/scripts/audit_intervention_sources.py
```
单句精确干预示例:
```json
{
"mode": "exact",
"device": "unify",
"query": "播放周杰伦的歌",
"target": "Agent(tag=\"音乐\")"
}
```
正则多轮干预示例:
```json
{
"mode": "regex",
"device": "miCar",
"before_queries": ["第一个"],
"query": "红绿灯少的路线",
"target": "Agent(tag=\"地图导航\")"
}
```
### validate_intervention_records.py
校验候选 TSV。支持 `--mode exact` 或 `--mode regex`。
规则包括:
- 列数正确。
- 设备合法。
- `code` / `标签` 优先通过 label-master 校验;如果 label-master 未收录但历史干预表中已经稳定出现,允许作为候选通过,并给出 warning 让用户确认知识库是否需要补齐。
- 单句精确干预的 `下发domain` 与历史多数映射一致,默认不一致为 warning,`--strict-domain` 时为 error。
- 单句精确干预还会用 `type_match_agent.json` 做配置校对;配置值可以是更宽泛前缀,例如 `音乐 -> contentCopilot` 可以兼容历史里的 `contentCopilot|music`。
- 正则可编译,且包含 `query#` 片段。
- 与历史文件重复或冲突时给出 warning/error。
## 展示格式
生成候选后,优先用这个格式回复:
```text
我生成了一条候选干预,先不写入历史文件。
- 类型:单句精确干预 / 正则干预
- 设备:xxx
- 标签:xxx
- 校验:通过 / 有问题
候选 TSV`设备 query code 下发domain`
需要确认:是否写入某个文件,或是否调整设备/标签/正则。
```
如果校验失败,先解释失败原因,不要要求用户直接落盘。