Unify skills under repository root

This commit is contained in:
武阳
2026-05-08 10:54:27 +08:00
parent dcc43adaa6
commit 37fc367304
16 changed files with 967 additions and 6 deletions
+20
View File
@@ -0,0 +1,20 @@
# Local override only — committed profiles.json holds shared team creds
.env
.env.*
# Ad-hoc probes / local experiments
_probe_*.py
scratch_*.py
# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/
venv/
# IDE / OS
.idea/
.vscode/
.DS_Store
Thumbs.db
+140
View File
@@ -0,0 +1,140 @@
# ELK 索引目录(schema reference
> 每张索引一节:**集群 / 账号 / 关键字段 / JSON 字符串字段的内部结构**。纯 schema 参考。
>
> 业务侧"为什么这么查 / 怎么排查问题"在 [`business/`](business/) 目录:
>
> - [`business/keyword-free.md`](business/keyword-free.md) — 免唤醒判决(拒识 / kwfree 两张表的视角差异)
> - [`business/intent-arbitrator.md`](business/intent-arbitrator.md) — 中控仲裁
> - [`business/micar-onetrack.md`](business/micar-onetrack.md) — 小米汽车 OneTrack 端侧埋点(端到端可用性 / 全离线日志 / 离线NLP结果)
## 集群 & 账号
⚠️ **不止一个 ES 集群**。每个 profile 自带 `host` 字段指向所在集群。账号密码(团队共用)已 commit 在仓库的 `profiles.json` 里,clone 即可用。
| Profile Key | ES 集群 | 账号 | 对应 Kibana 前端 |
| ----------- | ------ | --- | ---------------- |
| `default` | `akaiservice.api.es.srv:80` | `ai_service_kibana` | `aiservice.ak.kibana.cloud.mioffice.cn` / `akelk.pt.ai.srv` |
| `reject` | `akaiservice.api.es.srv:80` | `ai_service_kibana` | `akaiservice.kibana.pt.xiaomi.com` |
| `micar` | `c3log.api.es.srv:80` | `xiaoai_micar_kibana` | `c3log.kibana.pt.xiaomi.com` |
`default` / `reject` 共用一个集群和账号;`micar` 是**完全独立的 c3log 集群**,跨集群账号互相不通(403)。
## 索引清单
| preset | 索引 pattern | profile | request id 字段 | 时间字段 | 内置过滤 | 主要业务 |
| ------ | ----------- | ------- | -------------- | ------- | ------- | -------- |
| `main` | `arch-flat-nlp-log-f-*` | default | `request_id` | `timestamp` | — | 主 NLP 日志 / 中控仲裁 |
| `reject` | `aiservice_duplex_rejection_lcs_log*` | reject | `requestId` | `time` | — | 后置拒识 |
| `kwfree` | `nlp_post_processing_lcs-*` | default | `requestId` | `timestamp` | `moduleName=keyword-free-log` | 后处理免唤醒 |
| `micar` | `onetrack_xiaoai_micar*` | micar | `request_id` | `timestamp` | — | 小米汽车 OneTrack 端侧埋点 |
⚠️ **字段命名各表不统一**snake (`request_id`) vs camel (`requestId`);时间字段 `timestamp` vs `time`。Kibana 显示的字段名不一定等于 ES 字段名。
---
## `arch-flat-nlp-log-f-*`preset=`main`
主 NLP 服务端日志,每次请求一条。
**顶层字段**(节选):
`request_id`, `query`, `query_origin`, `domain`, `func`, `intention`, `code`, `latency`,
`user_agent`, `session_id`, `app_id`, `app_name`, `device_id`, `mask_did`,
`arbitrator_dialog_status`, `offline_arbitrate_domains`,
`is_llm`, `is_llm_classify_enable`, `is_llm_classify_success`,
`large_model_info`, `large_model_traceid`,
`llm_intent`, `llm_intent_model_name`, `llm_intent_model_version`, `llm_content`, `llm_knowledge`, `llm_system_prompt`, `llm_history_size`, `llm_reject_intent`, `llm_access_control`, `llm_description`,
`is_continuous_dialog`, `exit_continuous_dialog`, `is_multi_turn`, `is_multi_rewrite`, `multi_rewrite_method`,
`reject_type`, `rejection_hint`, `rejection_info`, `is_shumei_reject`, `is_filtered`, `is_user_cancelled`,
`requestInfo`, `responseInfo`, `text`, `display_text`, `to_read`, `to_speak`, `tts_speaker`, `tts_vendor`,
`car_type`, `car_config`, `car_category`, `vehicle_driving_status`, `vehicle_wakeup_zone`, `is_real_vehicle`, `is_bench_vehicle`, `is_internal`
**关键嵌套对象**
- `intention` (dict) — 意图判决总集,含:
- `intention.intent_arbitrator_info` ⭐ — **中控仲裁对象**(顶层同名字段通常 null,**真东西在这里**),子结构详见 [`business/intent-arbitrator.md`](business/intent-arbitrator.md)
- `intention.score` / `intention.func` / `intention.domain` / `intention.query`
- `intention.dialog_status`, `intention.rewrite_infos`, `intention.domain_judge`, `intention.provider_domains`
---
## `aiservice_duplex_rejection_lcs_log*`preset=`reject`
后置拒识模块日志。**同一 requestId 通常多条(多轮)**,按 `time` 倒序取最新。
**顶层字段**
`requestId`, `time`, `query`, `appId`, `deviceId`, `sessionId`, `env`,
`isDuplex`, `isExit`, `isWakeup`, `costInfo`, `exitInfo`, `instructionPriority`,
`rejectHint`, `rejectInfo`, `rejectType`, `requestInfo`, `strategy_info`
**JSON 字符串字段**(脚本自动深度解析):
- `rejectInfo` — 完整拒识判决,关键路径:
- `rejectInfo.reject_reason` / `rejectInfo.rejection_info` / `rejectInfo.debug_info`
- `rejectInfo.strategy_info.reject.<策略名>` — 各子策略
- 常用子策略 `KEY_WORD_FREE_RESTRIC`(免唤醒):`process` / `skipped_reason` / `keywordFreeType` / `isCodeMatch` / `getProcessedQuery` / `Function List` —— 业务深入见 [`business/keyword-free.md`](business/keyword-free.md)
---
## `nlp_post_processing_lcs-*`preset=`kwfree`
后处理通用日志表。`kwfree` preset 内置 `moduleName=keyword-free-log` 过滤,锁定到免唤醒模块那条。
**顶层字段**
`requestId`, `timestamp`, `moduleName`, `appId`, `env`, `machine`, `maskUid`, `message`
**JSON 字符串字段**(脚本自动深度解析):
- `message` — 免唤醒模块本次处理的入参出参,关键路径:
- `message.process` / `message.skipReason`
- `message.route.flow` / `route.enableFullMigration` / `route.clawEnabled`
- `message.request.query` / `keywordFreeType` / `zone` / `appId` / `maskUid` / `maskDeviceId`
- `message.fallback.oldRejectType` / `fallback.oldDomain`
- 业务深入见 [`business/keyword-free.md`](business/keyword-free.md)
---
## `onetrack_xiaoai_micar*`preset=`micar`
小米汽车小爱端侧 OneTrack 埋点。**c3log 独立集群**。按日 rolling,单日 ~300GB——**强烈建议 `--date YYYYMMDD`**。
⚠️ **同一 request_id 多条记录**,按 `tip` 区分业务点位。**不同 tip 的负载 schema 不统一**。
**顶层公共字段**
`request_id`, `tip`, `tip_id`, `tip_name`, `tip_module_name`, `tip_page_id`, `tip_pos`,
`event_name` (`execute` / `state` / `start`),
`device_id`, `vid`, `instance_id`, `app_id`, `app_package_name`, `pkg`,
`path_id`, `clientTime`, `serverTime`, `timestamp`,
`network`, `region`, `mfrs`, `model`, `car_type`, `car_config`,
`voice_position` (`driver` / `passenger`), `wakeup_origin`,
`srv_env`, `app_ver`, `micar_ver`, `os_ver`,
`is_internal`, `is_bench_vehicle`, `sender`, `distinct_id`, `plugin_id`
**主要 tip 与负载位置**
| tip | tip_name | 负载在 |
| --- | -------- | ------ |
| `1024.1.1.1.23177` | 小爱端到端可用性 | 顶层 `key_value`(性能时序) |
| `1024.1.2.1.23176` | 小爱全离线日志 | 顶层 `request_infos[]` / `response_infos[]` / `other_infos`**不在 `key_value`** |
| `1024.1.2.1.34496` | 离线NLP结果 | `key_value.instructions` |
| `1024.4.1.1.25631` | ASR识别状态 | `key_value` 为 null,看 `event_name` / `other_infos` |
| `1024.1.1.1.33610` | 第三方接口调用 | — |
| `1024.1.3.1.23520` / `23521` | 小爱指令开始 / 结束 | — |
**JSON 字符串字段**(脚本自动深度解析):
- `key_value`(大多数 tip
- `request_infos[]` / `response_infos[]` / `other_infos[]`23176
各 tip 字段语义、模型相关字段(`nlp_debug_info` / `Arbitrate` / `nlp_model_version` 等)业务深入见 [`business/micar-onetrack.md`](business/micar-onetrack.md)。
---
## 新增索引流程
1. 从 Kibana URL 拿:Kibana index UUID、KQL 字段、filter 里的 `match_phrase` 条件
2. 必要时跑临时 `_probe_*.py`(命名已 gitignore):用 `match_phrase` 条件 + `*` 索引跨库搜,从命中文档的 `_index` 反推真实索引名
3. 确认 **request id 字段名**`request_id` / `requestId`)和**时间字段**`timestamp` / `time`
4.`AuthorizationException(403)`,换 profile
5. 字段为 JSON 字符串时(如 `rejectInfo` / `message` / `key_value`)脚本已自动深度解析,把业务路径记到本文档对应索引一节
6.`elk_query.py``PRESETS` 加一条,`extra_filters` 写清楚(如 `moduleName=...`
7. 如果是新业务主题,在 `business/` 下加一份业务深入文档
+37
View File
@@ -0,0 +1,37 @@
# elk-fetch
按 request id 查 ELK / Elasticsearch 日志的小工具 + Claude Code Skill。
**核心能力**
- 绕开 Kibana CAS 登录,直接走 ES HTTP API
- 多账号 profile(不同业务用不同 ES 账号,支持多 ES 集群)
- 多业务 preset(业务语义 → 索引/字段/过滤条件,一键套用)
- 深度 JSON 字段自动解析(如 `rejectInfo` / `message` / `key_value` / `response_infos` 里的嵌套 JSON
- 按点号路径(`a.b.0.c`)提取嵌套字段 / 仅输出指定字段
## 快速开始
```bash
git clone git@git.n.xiaomi.com:zhongsiyao/elk-fetch.git
cd elk-fetch
pip install "elasticsearch<8" urllib3
# 默认查主 NLP 日志
python elk_query.py <your_request_id>
```
账号密码(团队共用)已经在仓库里的 `profiles.json` 中,clone 完即可用。想用自己的账号则 `ELK_FETCH_PROFILES=/path/to/profiles.json` 指向别处。
## 完整用法
- **怎么选 preset / Kibana URL 转参数 / 命令模板** → [`SKILL.md`](SKILL.md)
- **每张表的字段 schema / JSON 字符串内部结构 / 新增索引流程** → [`INDEX_CATALOG.md`](INDEX_CATALOG.md)
- **业务深入**(每个主题一份排查 playbook)→ [`business/`](business/)
- [`business/keyword-free.md`](business/keyword-free.md) — 免唤醒判决(拒识 / kwfree 两张表三个视角)
- [`business/intent-arbitrator.md`](business/intent-arbitrator.md) — 中控仲裁
- [`business/micar-onetrack.md`](business/micar-onetrack.md) — 小米汽车 OneTrack 端侧埋点
## 作为 Claude Code Skill 使用
把本仓库 clone / 软链到 `~/.claude/skills/elk-fetch/`Claude Code 会根据 `SKILL.md` 的触发条件(给出 Kibana 链接、提到 requestId / 某类业务日志等)自动调用。
+200
View File
@@ -0,0 +1,200 @@
---
name: elk-fetch
description: 从小米内网 ELK 按 request id 拉取日志。支持多账号 profile、多 ES 集群、多业务 preset(主 NLP / 拒识表 / 免唤醒后处理日志 / 小米汽车 OneTrack 端侧埋点)、深度 JSON 字段自动解析。绕开 Kibana CAS 登录,直接走 ES HTTP API。触发条件:用户给出 Kibana 链接、提到 requestId / request_id 查日志、想看某次请求的 NLP 日志 / 中控仲裁日志(intent_arbitrator_info / llm_agent_info / hit_rules / score_domains/ 拒识 rejectInfo / KEY_WORD_FREE_RESTRIC / 免唤醒判决(keyword-free-log)/ 小爱端到端可用性 / 小爱全离线日志 / OneTrack 埋点 / micar tip。
when_to_use: 用户给出 Kibana 链接、request_id/requestId,或要求查询 NLP 日志、中控仲裁日志、拒识日志、免唤醒日志、小米汽车 OneTrack 埋点日志时使用。
aliases: elk, elk-query, log-fetch, request-log
allowed_tools: python_exec, python_package, read_file
---
# ELK Fetch Skill
按 request id 查小米内网 ELK。**多个 ES 集群**:`default`/`reject``akaiservice.api.es.srv:80``micar` 走独立的 `c3log.api.es.srv:80`。每个 profile 自带 `host`,脚本会自动选集群。
账号密码(团队共用)已 commit 进仓库的 `profiles.json`,无需配置。需要换账号时用 `ELK_FETCH_PROFILES=/abs/path/to/profiles.json` 指向自己的文件。
## 在本数据 Agent 中使用
本 skill 已安装在项目目录 `skills/elk-fetch/`。在本项目中不要用 `bash` 执行 `python elk_query.py`,也不要用 `pip` 直接安装依赖。
实际查询必须使用 `python_exec`
```json
{
"script_path": "skills/elk-fetch/elk_query.py",
"args": ["<request_id>", "--preset", "main"],
"timeout_seconds": 90,
"max_output_chars": 20000
}
```
如果 `python_exec` 返回缺少 `elasticsearch``urllib3`,先使用 `python_package` 安装:
```json
{
"action": "install",
"packages": ["elasticsearch<8", "urllib3"],
"timeout_seconds": 120
}
```
下面所有 `python elk_query.py ...` 命令模板只表示参数选择;实际执行时都要转换为 `python_exec.script_path = "skills/elk-fetch/elk_query.py"` 和对应的 `args`
## 关键文件
| 文件 | 用途 |
|------|------|
| `elk_query.py` | CLI 入口 |
| `INDEX_CATALOG.md` | **每张表的字段 schema reference**(顶层字段 / JSON 字符串字段内部结构) |
| `business/keyword-free.md` | 免唤醒判决 — 拒识 vs kwfree 两张表三个视角的区分 + 排查 playbook |
| `business/intent-arbitrator.md` | 中控仲裁 — `intention.intent_arbitrator_info` 子字段语义 |
| `business/micar-onetrack.md` | 小米汽车 OneTrack — 23177 端到端可用性 / 23176 全离线日志 / 34496 离线NLP结果 |
## Preset 速查
| preset | profile | 集群 | 索引 pattern | 字段名 | 时间字段 | 内置过滤 |
|--------|---------|------|-------------|--------|---------|---------|
| `main` (默认) | default | akaiservice | `arch-flat-nlp-log-f-*` | `request_id` | `timestamp` | 无 |
| `reject` | reject | akaiservice | `aiservice_duplex_rejection_lcs_log*` | **`requestId`** | **`time`** | 无 |
| `kwfree` | default | akaiservice | `nlp_post_processing_lcs-*` | `requestId` | `timestamp` | `moduleName = keyword-free-log` |
| `micar` | micar | **c3log** | `onetrack_xiaoai_micar*` | `request_id` | `timestamp` | 无(一个 rid 多条 tip |
## 选 preset 的决策表
| 用户想看 | preset | 额外参数 | 业务深入 |
|---------|--------|---------|---------|
| query / domain / func / 设备信息 | `main` | — | — |
| **中控仲裁日志**agent 路由 / score_domains / llm_agent_info / hit_rules | `main` | `--json-path intention.intent_arbitrator_info` | [`business/intent-arbitrator.md`](business/intent-arbitrator.md) |
| **拒识表整体**rejectInfo 全部 / reject_reason / 各子策略) | `reject` | 不加 `--json-path` | [`business/keyword-free.md`](business/keyword-free.md) |
| **拒识里的免唤醒**(后置拒识调到免唤醒那次的出入参) | `reject` | `--json-path rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC` | [`business/keyword-free.md`](business/keyword-free.md) |
| **后处理的免唤醒**(免唤醒模块被后处理调用时自己记的日志) | `kwfree` | — | [`business/keyword-free.md`](business/keyword-free.md) |
| **小米汽车端侧埋点**(端到端可用性 / 全离线日志 / ASR 状态等 OneTrack tip | `micar` | **`--date YYYYMMDD` 强烈建议** | [`business/micar-onetrack.md`](business/micar-onetrack.md) |
## 从 Kibana 链接提取参数
Kibana URL 形如:
```text
...discover#/?...
filters:(...match_phrase:(moduleName:keyword-free-log))
index:d7ff4d65-... ← Kibana index pattern UUID(非 ES 索引名)
query:(language:kuery,query:'requestId : 67f41dff...')
time:(from:now-1d,to:now)
```
反推方法:
- `requestId : xxx` / `request_id : xxx` → request id
- `filters` 里的 `match_phrase:(moduleName:keyword-free-log)`**`kwfree` preset**
- 主机 `aiservice.ak.kibana.cloud.mioffice.cn``default` profile;主机 `akaiservice.kibana.pt.xiaomi.com``reject` profile;主机 `c3log.kibana.pt.xiaomi.com`**`micar` profile(独立集群)**;主机 `akelk.pt.ai.srv``default` profile + `main` preset(中控仲裁的 Kibana 前端,底层就是主表)
- index 名为 `onetrack_xiaoai_micar` 直接走 `micar` presetfilter 里有 `tip` / `tip_name` 也走 `micar`
- `time:(from:now-1d,...)` → 默认近 48 小时,`from:'2026-04-22...'` 这种具体日期 → 传 `--date YYYYMMDD`
Kibana index UUID 不能直接反查 ES 索引名;如遇未知 UUID 跑一次 preset 试,打不中再 probe。
## 命令模板
> 路径形如 `python "C:\Users\Sivan\.claude\skills\elk-fetch\elk_query.py" ...`,下方为简写。每个场景的字段语义和排查思路见 `business/` 对应文档。
### 主 NLP 日志(默认)
```bash
python elk_query.py <request_id>
```
### 中控仲裁日志(→ [`business/intent-arbitrator.md`](business/intent-arbitrator.md)
```bash
# 整个仲裁对象
python elk_query.py <rid> --json-path intention.intent_arbitrator_info
# 命中的 agent + 召回规则 + 仲裁等级
python elk_query.py <rid> \
--fields request_id,query,domain,intention.intent_arbitrator_info.llm_agent_info,intention.intent_arbitrator_info.hit_rules,intention.intent_arbitrator_info.arbitrator_level
# 各候选 domain 的仲裁打分
python elk_query.py <rid> --json-path intention.intent_arbitrator_info.score_domains
```
⚠️ 真东西在 `intention.intent_arbitrator_info`,**顶层同名字段通常 null**。
### 拒识表 / `KEY_WORD_FREE_RESTRIC`(→ [`business/keyword-free.md`](business/keyword-free.md)
```bash
# 整个拒识 rejectInfo
python elk_query.py <rid> --preset reject
# 拒识里的免唤醒子字段
python elk_query.py <rid> --preset reject \
--json-path rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC
```
同一 requestId 在此索引常有多条(多轮),按 `time` 倒序返回。
### 后处理免唤醒(→ [`business/keyword-free.md`](business/keyword-free.md)
```bash
# 整个 message
python elk_query.py <rid> --preset kwfree --json-path message
# 是否实际处理 + 跳过原因
python elk_query.py <rid> --preset kwfree \
--fields requestId,message.process,message.skipReason,message.request.keywordFreeType
```
### 小米汽车 OneTrack(→ [`business/micar-onetrack.md`](business/micar-onetrack.md)
⚠️ 必须传 `--date YYYYMMDD`(按日 rolling,单日 ~300GB)。
```bash
# 这个 request 上报了哪些 tip
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,tip_name,tip_module_name,event_name
# 端到端可用性(23177)的性能时序
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,key_value.state_result_type,key_value.state_exec_result,key_value.wakeup_received_event,key_value.asr_final,key_value.nlp_finish_answer
# 全离线日志(23176)的模型 debug 输出
python elk_query.py <rid> --preset micar --date 20260424 \
--json-path response_infos.0.nlp_debug_info
```
⚠️ 23176 schema 与其他 tip 不同:负载在 `request_infos[]` / `response_infos[]`**不在 `key_value`**。`response_infos[0]``instructions` + `nlp_debug_info` 两块,研究模型行为时都要看,别只取 nlp_debug_info。
### 指定日期 / 自定义索引
```bash
# 老请求超出 48h 窗口
python elk_query.py <rid> --date 20260422
# probe 未知索引
python elk_query.py <rid> \
--index "some-other-*" --field requestId --time-field time --profile reject
```
## 输出格式
始终单个 JSON 对象到 stdout
```json
{
"request_id": "...",
"preset": "reject",
"profile": "reject",
"index": "aiservice_duplex_rejection_lcs_log*",
"field": "requestId",
"time_field": "time",
"date": "past-48h",
"total": 2,
"hits": [ { "...": "_source(嵌套 JSON 字符串已自动解开)" } ]
}
```
出错时 JSON 里会有 `error` 键,退出码非零。
## 常见坑
- **请求 id 过期**:默认查近 48h,老请求必须 `--date YYYYMMDD`
- **字段名驼峰 vs 下划线**`main` / `micar``request_id``reject` / `kwfree``requestId`;Kibana 界面显示的字段名不一定等于 ES 存储字段名。
- **返回大量字段刷屏**:用 `--fields``--json-path` 精简。
- **不要用 WebFetch 抓 Kibana URL**:会被 CAS 拦到登录页,永远走这个 skill。
@@ -0,0 +1,60 @@
# 中控仲裁日志
> "中控"= central intent arbitrator,决定一条 query 路由到哪个 domain/agent`controlCopilot` / `iotCopilot` / `productAgent` / 闲聊 / ...)。
## 在哪查
主 NLP 表 `arch-flat-nlp-log-f-*` 里的 **`intention.intent_arbitrator_info`** 嵌套对象。`main` preset 直接能读,**不需要新 preset**。
⚠️ **坑**:顶层 `intent_arbitrator_info`(不带 `intention.`)通常是 `null`——同名但不同位置,别取错。
> Kibana URL 形如 `akelk.pt.ai.srv/app/discover#/?...index:b0bd62cb-...&query:(...request_id:...)`,这个 Kibana 前端虽然走 akelk 域名,底层 ES 就是 akaiservice 主表。
## 关键字段
路径前缀 `intention.intent_arbitrator_info`
**仲裁结果**
- `predict_result_domain` / `l2_domain` / `l2_func` / `before_final_rule_domain` — 各阶段判出的 domain
- `single_result_domain` / `multi_result_domain` — 单轮 / 多轮结果
- `dialog_status` — 对话状态(`FINISH` / ...
**仲裁打分**
- `score_domains` (dict) — 各候选 domain 的 `score` + `func`,仲裁打分原始数据
- 例:`controlCopilot:1.0`, `soundboxControl:0.97`, `default:0.05`
**仲裁等级**
- `arbitrator_level` — 仲裁等级(如 `Agent_LLM` 表示走 LLM agent 路径)
- `is_llm_allowed` / `is_new_agent_logic`
**LLM agent 命中**
- `llm_agent_info`
- `isEffective` — 是否生效
- `agentType` — 类型(如 `车载控制`
- `agentName` — 名称(如 `controlCopilot|iotCopilot|productAgent`
- `agentSubType`
- `hit_rules` — 命中的召回规则(如 `[agent_llm_recall_controlCopilot]`
**LLM 仲裁子流程**
- `llm_strategy_info``llm_domain` / `llm_intent` / `llm_strategy_stage` / `before_llm_*`
**confidence**
- `intent_confidence_information`
## 命令模板
```bash
# 整个仲裁对象
python elk_query.py <rid> --json-path intention.intent_arbitrator_info
# 命中的 agent + 召回规则 + 仲裁等级(速览)
python elk_query.py <rid> \
--fields request_id,query,domain,intention.intent_arbitrator_info.llm_agent_info,intention.intent_arbitrator_info.hit_rules,intention.intent_arbitrator_info.arbitrator_level
# 各候选 domain 的仲裁打分
python elk_query.py <rid> --json-path intention.intent_arbitrator_info.score_domains
```
## 字段 schema
完整 schema 见 [`../INDEX_CATALOG.md`](../INDEX_CATALOG.md) 中 `arch-flat-nlp-log-f-*` 一节。
+61
View File
@@ -0,0 +1,61 @@
# 免唤醒判决日志
> "免唤醒"指车机上不需要每次说"小爱同学"的免唤醒指令模式(如开/关空调、调温度)。每次请求都会经过免唤醒判决——是否让这条 query 直接执行而不是走闲聊/拒识。
## 三个视角,两张表
| # | 业务视角 | 日志位置 | 命令 |
| - | ------- | ------- | ---- |
| 1 | **拒识模块视角**:每次请求拒识模块整体输出(含所有子策略,含调用免唤醒的结果) | `aiservice_duplex_rejection_lcs_log*` 整条 `rejectInfo` | `--preset reject` |
| 2 | **拒识里的免唤醒子字段**:拒识调免唤醒那一次的入参出参 | 同表,`rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC` | `--preset reject --json-path rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC` |
| 3 | **免唤醒模块视角**:免唤醒模块**被后处理调到时**自己写的日志(独立的另一张表) | `nlp_post_processing_lcs-*``moduleName=keyword-free-log` | `--preset kwfree` |
1 和 2 同表同条记录,2 只是取子字段;3 是完全独立的另一张表。
## 关键字段
### 视角 2:拒识里的免唤醒子字段
路径前缀 `rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC`
- `process` — 是否走到免唤醒判决(true / false
- `skipped_reason` — 被跳过原因(如 `post_processing_takeover` 表示让后处理接管,进入视角 3
- `keywordFreeType`
- `isCodeMatch`
- `getProcessedQuery`
- `Function List`
### 视角 3:后处理免唤醒模块自身
JSON 字段 `message` 顶层:
- `process` — 是否实际走到处理
- `skipReason` — 未处理原因(如 `rejectedByOtherStrategy` 已被其他策略先拒)
- `route.flow` / `route.enableFullMigration` / `route.clawEnabled`
- `request.query`
- `request.keywordFreeType`(如 `INSTRUCTION_KEY_WORD_FREE`
- `request.zone`(如 `DRIVER`
- `request.appId` / `maskUid` / `maskDeviceId`
- `fallback.oldRejectType` / `fallback.oldDomain`
## 排查 Playbook
**问题:免唤醒为什么没生效?**
1. 先查视角 2,看拒识有没有调到免唤醒:
```bash
python elk_query.py <rid> --preset reject \
--json-path rejectInfo.strategy_info.reject.KEY_WORD_FREE_RESTRIC
```
- `process=true` → 走到免唤醒判决了,看 `keywordFreeType` 等子字段
- `process=false` → 没走到,看 `skipped_reason`
- `post_processing_takeover` → 进视角 3 看后处理免唤醒
- 其他原因 → 拒识自己挡了
2. 视角 3——后处理免唤醒模块自身:
```bash
python elk_query.py <rid> --preset kwfree --json-path message
```
- `process=false` 时看 `skipReason``rejectedByOtherStrategy` 表示已被先拒)
## 字段 schema
完整 schema 见 [`../INDEX_CATALOG.md`](../INDEX_CATALOG.md) 中 `aiservice_duplex_rejection_lcs_log*` 和 `nlp_post_processing_lcs-*` 两节。
+127
View File
@@ -0,0 +1,127 @@
# 小米汽车 OneTrack 端侧埋点
> 小米汽车小爱端侧上报的全量埋点(端到端可用性、ASR 状态、全离线日志、性能指标等)。与 NLP 服务端日志正交:从车端上报的客户端事件流。
>
> 索引 `onetrack_xiaoai_micar*`**c3log 独立集群**),单日 ~300GB——**必须传 `--date YYYYMMDD`**。
## tip 体系
一个 request_id 对应多条记录,每条对应一个 `tip` 埋点点位。先 `--fields tip,tip_name` 看一眼有哪些 tip
```bash
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,tip_name,tip_module_name,event_name
```
主要点位:
| tip | tip_name | tip_module_name | event_name | 含义 |
| --- | -------- | --------------- | ---------- | ---- |
| `1024.1.1.1.23177` | 小爱端到端可用性 | 性能类 | execute | 全链路时序埋点(wakeup → asr → nlp → exec |
| `1024.1.2.1.23176` | 小爱全离线日志 | 日志类 | execute | **离线模型相关日志** |
| `1024.1.2.1.34496` | 离线NLP结果 | — | state | 离线 NLP 出参 |
| `1024.4.1.1.25631` | ASR识别状态 | ASR识别 | state | ASR 中间状态 |
| `1024.1.1.1.33610` | 第三方接口调用 | — | execute | — |
| `1024.1.3.1.23520` / `23521` | 小爱指令开始 / 结束 | — | start / — | — |
⚠️ **不同 tip 的负载 schema 不统一**
- 23177 → 负载在顶层 `key_value`
- 23176 → 负载在顶层 `request_infos[]` / `response_infos[]` / `other_infos`**不在 `key_value`**
- 34496 → 负载在 `key_value.instructions`
- 25631 → `key_value` 为 null,看 `event_name` / `other_infos`
---
## 23177 — 端到端可用性
负载在顶层 `key_value`(脚本自动解析)。常用字段:
**唤醒**`wakeup_received_event` / `wakeup_ball_appear`
**ASR 时序**`asr_first_partial` / `asr_first_text` / `asr_first_same_final` / `asr_final` / `asr_first_pack_sent`
**NLP 时序**`nlp_start_answer` / `nlp_finish_answer`
**结果**`state_result_type``success` / .../ `state_exec_result` / `state_cancel_msg`
**指令统计**`state_exec_ins_total` / `state_exec_ins_success` / `state_exec_ins_failed` / `state_exec_ins_filtered`
**异常**`state_nlp_unknown_instructions` (list) / `state_nlp_exceptions` / `state_vad_end_type`
**链路**`state_duplex` / `state_request_id` / `state_asr_final_size`
```bash
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,key_value.state_result_type,key_value.state_exec_result,key_value.wakeup_received_event,key_value.asr_final,key_value.nlp_finish_answer
```
---
## 23176 — 全离线日志 ⭐ 离线模型核心
⚠️ schema 与其他 tip 不同:负载在顶层 `request_infos[]` / `response_infos[]` / `other_infos`(list,长度通常为 1,但每个 list 都有大量信息),**不在 `key_value`**。
### `request_infos[0]` — 送给离线引擎的入参
- 引擎/模型版本:`engine_id` / `engine_model`(如 `3.2.30-claw`/ `offline_model_ver`(如 `2026012201`/ `miai_ver` / `app_version`
- 请求形态:`request_type` / `request_state_type` / `is_wakeup_req` / `is_llm` / `duplex` / `origin`
- 链路环境:`srv_env` / `network` / `network_nlp` / `network_type` / `off_asr_avail`
- 设备:`device_id` / `car_type` / `car_config` / `is_driving` / `zone_info` / `is_login` / `has_token` / `isInternal`
- **`context_offline`** (list) — 离线引擎拿到的全部 context payload(含 `Nlp.OfflineSession` / `Map.MapState` / `UIController.InteractionInfoList` 等 namespace)。**研究模型行为时务必看这一段**
- 其他:`ua` / `transaction_id` / `request_id` / `timeout_reason`
### `response_infos[0]` — 引擎返回的产物
⚠️ 不止 `nlp_debug_info`,三块都要看:
**`instructions[]`** — 引擎下发的全部指令序列。每条 `header.namespace.name` + `header.is_offline`true/false 标识离/在线判决)+ `header.dialog_id` / `id` / `transaction_id` + `payload`。常见 namespace.name
- `System.Heartbeat` / `System.Abort`
- `Offline.CloudStop``current_round_used_audio_duration` / `next_round_rid` / `stop_audio_duration`
- `SpeechRecognizer.RecognizeResult``is_final` / `results[].text` / `confidence` / `is_nlp_request` / `is_key_word_free_request`
- `Nlp.StartAnswer` / `Nlp.FinishAnswer` / `Nlp.IntentsWithRelation``payload.intent`
- `Template.Query``text`
- `Dialog.Reject``query` / `reject_type``NON_HUMAN` / `Dialog.Finish`
**`nlp_debug_info`** ⭐ 离线 NLP 模型自身的调试输出(模型日志打点)。研究模型决策为什么这样做的核心:
- **`Arbitrate`** — 仲裁器决策:`query` / **`logits`** (list[float], 各类目原始打分) / `label_id` / `category`(如 `Info`/ `predict` / `label`(如 `baike#person`/ `domains`(候选 domain/ `time`
- 各阶段耗时:`Rewrite.time` / `PreTrain.time``bertAvailable`/ `EdgeIntentExecutor.time` / `GeneralParse.time` / `lookAndTalk.time`
- 候选 domain`domain_confidence` (list) / `domain_parsers` (list)
- 各 domain parser 子模块(如 `music` / `phonecall` / `mapApp`):每个 parser 块下含 `time` / `score` / `parser_version` + 子模块耗时(如 `music.MusicKvParserCost` / `MusicJsgfParserCost` / `MusicIcsfModelParserCost` / `MusicGlobalCost`
- **`nlp_model_version`** (dict) — 各模型 checkpoint 版本(如 `arbitrator-l1: 20241205_offline` / `phonecall-icsf: 240205_offline` / `mapapp-icsf-single: 241224_offline`
- `nlp_edge_version` — 边端 NLP 引擎整体版本
- `edge_track` — 上下文/能力 capability 追踪:`capabilitiesVersion` / `contexts`(用到的 context namespace list/ `arbitrator_priority`
- `llm_wait` — 是否等待 LLM
**`other_infos`** — 其他辅助信息(list
### 命令模板
```bash
# 整条全离线日志(含 request_infos[0]、response_infos[0]、other_infos
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,tip_name,request_infos,response_infos,other_infos
# 只看模型自身打的 debug 日志
python elk_query.py <rid> --preset micar --date 20260424 \
--json-path response_infos.0.nlp_debug_info
# 模型版本 + 仲裁结果速览
python elk_query.py <rid> --preset micar --date 20260424 \
--fields request_id,tip,response_infos.0.nlp_debug_info.Arbitrate,response_infos.0.nlp_debug_info.nlp_model_version,response_infos.0.nlp_debug_info.nlp_edge_version,request_infos.0.engine_model,request_infos.0.offline_model_ver
# 引擎全部指令(看 is_offline 区分离/在线)
python elk_query.py <rid> --preset micar --date 20260424 \
--json-path response_infos.0.instructions
```
---
## 34496 — 离线NLP结果
`key_value` 顶层就是 `{instructions: [...]}`,结构和 23176 的 `response_infos[0].instructions` 一致,但**只是 NLP 部分的指令子集**(不含 SpeechRecognizer / System / Offline 等)。没有 `nlp_debug_info`、没有 `request_infos`——想看模型决策细节去 23176。
---
## 字段 schema
完整 schema 见 [`../INDEX_CATALOG.md`](../INDEX_CATALOG.md) 中 `onetrack_xiaoai_micar*` 一节。
+300
View File
@@ -0,0 +1,300 @@
"""
ELK / Elasticsearch 日志查询工具。
按 request id 查 ELK。支持:
- 多账号 profile(不同业务用不同 ES 账号)
- 多业务 preset(业务语义 → 索引/字段/过滤条件的映射)
- 深度 JSON 字符串自动解析(rejectInfo / message 等场景)
- 按点号路径提取嵌套字段(--json-path
账号密码从同目录 profiles.json 加载(团队共用账号已 commit 进仓库;可用 ELK_FETCH_PROFILES 指向别处覆盖)。
业务→索引→路径的详细说明见同目录 INDEX_CATALOG.md。
用法:
python elk_query.py <request_id>
[--preset main|reject|kwfree|micar]
[--date YYYYMMDD]
[--index PATTERN] [--field FIELD] [--time-field NAME] [--profile KEY]
[--size N]
[--fields f1,f2,...]
[--json-path a.b.c] # 提取嵌套字段,自动解 JSON 字符串
[--raw] # 关闭自动 JSON 字符串解析
"""
import argparse
import json
import os
import sys
import warnings
from copy import deepcopy
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional
import urllib3
from elasticsearch import Elasticsearch
from elasticsearch.exceptions import (
ConnectionError as ESConnectionError,
ElasticsearchWarning,
NotFoundError,
RequestError,
AuthorizationException,
AuthenticationException,
)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
warnings.filterwarnings("ignore", category=ElasticsearchWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
ES_PORT = int(os.getenv("ES_PORT", "80"))
ES_HOST_FALLBACK = os.getenv("ES_HOST", "")
SCRIPT_DIR = Path(__file__).resolve().parent
PROFILES_PATH = Path(os.getenv("ELK_FETCH_PROFILES", SCRIPT_DIR / "profiles.json"))
def load_profiles() -> Dict[str, Dict[str, str]]:
"""从 profiles.json 读取账号配置。缺文件 / 格式错误时给出可操作的错误信息。"""
if not PROFILES_PATH.exists():
raise FileNotFoundError(
f"profiles.json not found at {PROFILES_PATH}. "
f"Override path via ELK_FETCH_PROFILES=/path/to/profiles.json"
)
try:
with PROFILES_PATH.open("r", encoding="utf-8") as f:
data = json.load(f)
except json.JSONDecodeError as e:
raise ValueError(f"profiles.json is not valid JSON: {e}")
if not isinstance(data, dict) or not data:
raise ValueError("profiles.json must be a non-empty JSON object keyed by profile name")
for key, prof in data.items():
if not isinstance(prof, dict) or "user" not in prof or "password" not in prof:
raise ValueError(f"profile '{key}' missing required fields 'user' / 'password'")
return data
# 业务→索引/字段/额外过滤 的映射。time_field 为毫秒时间戳字段名。
# 每条 preset 对应一个明确的业务语义,详见 INDEX_CATALOG.md。
PRESETS: Dict[str, Dict[str, Any]] = {
"main": {
"profile": "default",
"index": "arch-flat-nlp-log-f-*",
"field": "request_id",
"time_field": "timestamp",
"extra_filters": [],
},
"reject": {
# 后置拒识模块的日志,内含调用免唤醒判决(KEY_WORD_FREE_RESTRIC)的结果
"profile": "reject",
"index": "aiservice_duplex_rejection_lcs_log*",
"field": "requestId",
"time_field": "time",
"extra_filters": [],
},
"kwfree": {
# 后处理阶段调用免唤醒模块自身的日志。message 是 JSON 字符串
"profile": "default",
"index": "nlp_post_processing_lcs-*",
"field": "requestId",
"time_field": "timestamp",
"extra_filters": [{"match_phrase": {"moduleName": "keyword-free-log"}}],
},
"micar": {
# 小米汽车小爱 OneTrack 埋点(端到端可用性 / 性能埋点等 tip)。
# 索引按日 rolling 且非常大(~300GB/天),强烈建议传 --date 收敛日期。
# key_value 是序列化 JSON 字符串,脚本会自动深度解析。
"profile": "micar",
"index": "onetrack_xiaoai_micar*",
"field": "request_id",
"time_field": "timestamp",
"extra_filters": [],
},
}
def build_client(profiles: Dict[str, Dict[str, str]], profile_key: str) -> Elasticsearch:
if profile_key not in profiles:
available = ", ".join(sorted(profiles.keys())) or "(none)"
raise KeyError(f"profile '{profile_key}' not in profiles.json (available: {available})")
prof = profiles[profile_key]
host = prof.get("host") or ES_HOST_FALLBACK
if not host:
raise ValueError(
f"profile '{profile_key}' has no 'host' and ES_HOST env var is not set"
)
return Elasticsearch(
hosts=[f"http://{host}:{ES_PORT}"],
http_auth=(prof["user"], prof["password"]),
timeout=60,
max_retries=3,
)
def build_query(
request_id: str,
field: str,
time_field: str,
date_str: Optional[str],
size: int,
extra_filters: List[Dict[str, Any]],
) -> Dict[str, Any]:
if date_str:
date_obj = datetime.strptime(date_str, "%Y%m%d")
start_ms = int(date_obj.timestamp() * 1000)
end_ms = int((date_obj + timedelta(days=1)).timestamp() * 1000) - 1
else:
end_ms = int(datetime.now().timestamp() * 1000)
start_ms = int((datetime.now() - timedelta(hours=48)).timestamp() * 1000)
filters: List[Dict[str, Any]] = [
{"wildcard": {field: {"value": f"{request_id}*"}}},
]
filters.extend(deepcopy(extra_filters))
return {
"query": {
"bool": {
"must": [{"range": {time_field: {"gte": start_ms, "lte": end_ms}}}],
"filter": filters,
}
},
"size": size,
"sort": [{time_field: {"order": "desc"}}],
}
def deep_parse(v: Any) -> Any:
"""递归把看起来是 JSON 的字符串展开成 dict/list。"""
if isinstance(v, str):
s = v.strip()
if (s.startswith("{") and s.endswith("}")) or (s.startswith("[") and s.endswith("]")):
try:
return deep_parse(json.loads(s))
except Exception:
return v
return v
if isinstance(v, dict):
return {k: deep_parse(val) for k, val in v.items()}
if isinstance(v, list):
return [deep_parse(x) for x in v]
return v
def get_path(obj: Any, dotted: str) -> Any:
cur = obj
for part in dotted.split("."):
if not part:
continue
if isinstance(cur, dict):
cur = cur.get(part)
elif isinstance(cur, list):
try:
cur = cur[int(part)]
except (ValueError, IndexError):
return None
else:
return None
return cur
def project(
source: Dict[str, Any],
fields: Optional[List[str]],
json_path: Optional[str],
) -> Any:
if json_path:
return {json_path: get_path(source, json_path)}
if fields:
return {k: get_path(source, k) for k in fields}
return source
def run(args: argparse.Namespace, profiles: Dict[str, Dict[str, str]]) -> Dict[str, Any]:
preset = PRESETS[args.preset]
profile_key = args.profile or preset["profile"]
index_pattern = args.index or preset["index"]
field_name = args.field or preset["field"]
time_field = args.time_field or preset["time_field"]
extra_filters = preset.get("extra_filters", [])
body = build_query(
args.request_id, field_name, time_field, args.date, args.size, extra_filters
)
try:
client = build_client(profiles, profile_key)
except (KeyError, ValueError) as e:
return {"error": "ProfileError", "message": str(e)}
try:
resp = client.search(index=index_pattern, body=body)
except AuthenticationException as e:
return {"error": "AuthenticationException", "profile": profile_key, "message": str(e)}
except AuthorizationException as e:
return {"error": "AuthorizationException", "profile": profile_key, "index": index_pattern, "message": str(e)}
except NotFoundError as e:
return {"error": "IndexNotFound", "index": index_pattern, "message": str(e)}
except RequestError as e:
return {"error": "QueryError", "message": str(e)}
except ESConnectionError as e:
return {"error": "ConnectionError", "message": str(e)}
hits_raw = resp.get("hits", {}).get("hits", [])
fields = [f.strip() for f in args.fields.split(",")] if args.fields else None
records = []
for h in hits_raw:
src = h.get("_source", {})
if not args.raw:
src = deep_parse(src)
records.append(project(src, fields, args.json_path))
return {
"request_id": args.request_id,
"preset": args.preset,
"profile": profile_key,
"index": index_pattern,
"field": field_name,
"time_field": time_field,
"date": args.date or "past-48h",
"total": len(records),
"hits": records,
}
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(
description="Query ELK by request id (multi-profile, deep-JSON aware)",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="业务→索引映射详见同目录 INDEX_CATALOG.md",
)
p.add_argument("request_id", help="目标 request id(支持前缀通配)")
p.add_argument("--preset", choices=list(PRESETS.keys()), default="main",
help="预置方案:" + " | ".join(PRESETS.keys()))
p.add_argument("--profile", help="覆盖账号 profilekey 来自 profiles.json")
p.add_argument("--index", help="覆盖索引 pattern")
p.add_argument("--field", help="覆盖 request id 字段名")
p.add_argument("--time-field", help="覆盖时间字段名")
p.add_argument("--date", help="指定日期 YYYYMMDD,不填默认近 48 小时")
p.add_argument("--size", type=int, default=20, help="最多返回记录数(默认 20")
p.add_argument("--fields", help="只输出指定字段,逗号分隔,支持 a.b.c 嵌套")
p.add_argument("--json-path", help="只提取某条嵌套路径的值")
p.add_argument("--raw", action="store_true", help="关闭自动 JSON 字符串解析")
return p.parse_args()
def main() -> int:
args = parse_args()
try:
profiles = load_profiles()
except (FileNotFoundError, ValueError) as e:
print(json.dumps({"error": "ConfigError", "message": str(e)}, indent=2, ensure_ascii=False))
return 2
result = run(args, profiles)
print(json.dumps(result, indent=2, ensure_ascii=False))
return 0 if "error" not in result else 1
if __name__ == "__main__":
sys.exit(main())
+17
View File
@@ -0,0 +1,17 @@
{
"default": {
"user": "ai_service_kibana",
"password": "Ox7tLXSorOo5t5KG",
"host": "akaiservice.api.es.srv"
},
"reject": {
"user": "ai_service_kibana",
"password": "Ox7tLXSorOo5t5KG",
"host": "akaiservice.api.es.srv"
},
"micar": {
"user": "xiaoai_micar_kibana",
"password": "aF6pi9XfzXq2h543",
"host": "c3log.api.es.srv"
}
}
+71
View File
@@ -0,0 +1,71 @@
---
name: eval-repair
description: 分析评测错误,并为针对性训练集/评测集补充生成可 review 的数据计划。
when_to_use: 当用户提供评测结果、模型判错样本或带标签错误 case,并希望补充训练/评测数据时使用。
aliases: error-repair, eval-data
allowed_tools: read_file, write_file, edit_file, grep_search, glob_search, ask_user_question
---
使用这个 skill 处理“已有评测错误 -> 错误分析 -> 人工 review 问题类别 -> 数据生成计划 -> canonical records -> 导出”的工作流。
## 输入假设
用户可能会提供一个或多个文件,里面包含:
- query 或多轮对话
- 期望/正确 label
- 模型预测 label
- 可选的 tts
- 可选的原因、垂域、模型版本、设备或其他元数据
输入格式可能每次不同。不要在检查文件前假设列名。
## 必要工作流
1. 读取用户提供的评测/错误文件;如果没有路径,先询问。
2. 识别已有字段,以及缺失的 canonical record 必填字段。
3. 生成错误分析笔记,至少包含:
- top 混淆对
- 反复出现的 query 模式
- 代表性样例
- 缺失或不明确的字段
4. 提出可供人工 review 的问题类别。
5. 如果类别边界不清,先请用户确认,再制定生成计划。
6. 创建生成计划,说明:
- 目标问题类别
- 目标 label
- 计划生成数量
- 要生成的正例、反例、边界例
- 必要元数据
7. 只有在计划清楚后,才生成或请求 canonical records。
8. TODO:工具实现后,在预览或导出前运行 `data_agent_validate_dataset_records`
9. TODO:工具实现后,运行 `render_dataset_preview``export_dataset`
## 当前工具状态
当前先使用已有工具完成文件检查和产物写入:
- `read_file`
- `write_file`
- `edit_file`
- `grep_search`
- `glob_search`
- `ask_user_question`
规划中的数据工具不一定已经实现。除非用户确认当前分支已经实现,否则不要直接调用不存在的工具。
## 推荐产物
```text
tasks/{task_id}/context/eval_errors.*
tasks/{task_id}/artifacts/error_analysis.md
tasks/{task_id}/artifacts/generation_plan.md
tasks/{task_id}/artifacts/generated_candidates.jsonl
tasks/{task_id}/memory/open_questions.md
```
## 约束
- 在 canonical records 达成一致并通过校验前,不要直接导出最终训练/评测格式。
- 不要把自动聚类当成最终事实,聚类和类别命名必须可 review。
- 面向人的总结要简洁,并尽量用样例支撑。
+125
View File
@@ -0,0 +1,125 @@
---
name: online-mining
description: 分析 badcase,并通过可 review 的策略迭代挖掘线上相似 case。
when_to_use: 当用户提供线上 badcase 或产品/标签定义,并希望查找相似线上问题或构建专项集时使用。
aliases: badcase-mining, router-mining
allowed_tools: read_file, write_file, edit_file, grep_search, glob_search, ask_user_question, data_agent_profile_router_sessions, data_agent_search_router_sessions, data_agent_sample_router_candidates, data_agent_convert_router_candidates_to_records, data_agent_export_dataset_records
---
使用这个 skill 处理“badcase 或标签定义 -> 挖掘策略 -> 候选召回 -> 抽样 review -> 策略迭代 -> mined dataset”的工作流。
所有线上挖掘产物必须放在当前用户当前会话的 output 目录下。不要把 records 或 review 结果写到项目根目录的 `output/``tasks/` 或源码目录。canonical records 的稳定输出文件名固定为 `output/records.jsonl`,不要按数据集名创建子目录,不要自定义时间戳、中文专题名或随机文件名。工具会把相对 `output_path` 自动路由到会话 output 目录,并把 records 路径归一到当前会话 `output/records.jsonl`;展示给用户时以工具返回的实际路径为准。
## 两条分支
线上挖掘后必须先判断用户要的是哪条分支。
### 分支 A:线上候选直接作为数据样本
当用户说“把筛选出的数据变成样本”“拿这些线上数据做评测集”“保留这批线上 case”“导出候选样本”时,走这个分支。
流程:
1.`data_agent_profile_router_sessions` 查看数据概貌。
2.`data_agent_search_router_sessions` 按策略召回线上候选。
3.`data_agent_sample_router_candidates` 抽样展示给用户 review。
4. 根据用户 review 意见形成 include/exclude/uncertain 决策和目标标签。
5.`data_agent_convert_router_candidates_to_records` 把线上候选直接转换为 canonical records。
6. 如果用户要求落盘,用 `data_agent_export_dataset_records` 导出紧凑 JSONL`output_path` 固定传 `output/records.jsonl`
这个分支不生成新 query,不调用数据生成计划工具,不调用 dataset draft 归一化工具。
### 分支 B:基于线上问题再生成补充数据
当用户明确说“生成/扩写/构造/造一批类似 case/补充训练数据”时,才走这个分支。
流程:
1. 先完成线上召回、抽样和 review。
2. 总结线上问题模式和需要覆盖的边界。
3. 再切换到数据生成流程,创建待 review 的 generation goal 和 generation plan。
4. 用户确认后才生成 draft,并转换为 canonical records。
如果用户只是要求“把线上数据作为样本”,不要进入这个分支。
## 输入假设
用户可能会提供:
- 产品或标签定义
- 线上 badcase 样例
- 期望/正确 label
- 线上预测 label
- query、tts、agent type、function name、垂域、设备、日期、模型版本或其他元数据
线上字段和允许使用的筛选条件还没有最终确定。
## 必要工作流
1. 读取用户提供的 badcase 或定义文件。
2. 分析 badcase 共性:
- query 模式
- label 混淆
- agent/function 类型
- 垂域
- 如存在,分析设备/日期/模型等元数据
3. 起草带明确筛选条件的挖掘策略。
4. 当筛选条件不清或影响较大时,先请用户 review,再做宽泛线上召回。
5. 只使用批准的工具检索或请求候选数据。
6. 先抽取小批 review 样本,通常约 100 条。
7. 总结样本命中率和主要 false positive 模式。
8. 根据结果调整策略,并按需重复。
9. 最终产出以下一种或多种:
- 线上问题评估报告
- 专项评测集候选
- 专项 badcase 集合
- 训练候选数据
## 当前工具状态
当前先使用已有工具完成本地分析和策略起草:
- `read_file`
- `write_file`
- `edit_file`
- `grep_search`
- `glob_search`
- `ask_user_question`
- `data_agent_profile_router_sessions`:读取 `router_session_parquet/date=YYYYMMDD/` 的小样本概貌,查看 schema、设备、轮次、domain、intent、func 等分布。
- `data_agent_search_router_sessions`:按日期、设备、domain、intent、func、query 关键词/正则、轮次数等条件召回线上 session 候选;输出命中 turn、前文 turn、req_id 和 action_json 解析结果。
- `data_agent_sample_router_candidates`:对召回候选做稳定抽样,并输出 review 需要的基础统计。
- `data_agent_convert_router_candidates_to_records`:把 review 后的线上候选直接转换为 canonical records;用于“线上数据作为样本”的分支。
- `data_agent_export_dataset_records`:后续把 review 后的线上候选转换为 canonical records 后落盘;默认紧凑 JSONL,固定传 `output/records.jsonl`
TODO:后续规划中的专用工具:
- `analyze_badcases`
- `build_mining_strategy`
- `create_annotation_batch`
- `read_annotation_result`
- `evaluate_mining_precision`
- `refine_mining_strategy`
- `export_mined_dataset`
不要手写 raw SQL,也不要用宽泛 shell 命令进行数据检索。
## 推荐产物
```text
tasks/{task_id}/context/badcases.*
tasks/{task_id}/artifacts/badcase_analysis.md
tasks/{task_id}/artifacts/mining_strategy.md
tasks/{task_id}/artifacts/candidate_sample.jsonl
tasks/{task_id}/artifacts/review_report.md
tasks/{task_id}/artifacts/mined_candidates.jsonl
tasks/{task_id}/memory/open_questions.md
tasks/{task_id}/memory/failed_attempts.md
```
## 约束
- 除非批准的工具输出已经脱敏,否则不要导出敏感线上原始字段。
- 不要把第一版挖掘策略当成最终策略。
- 始终让筛选条件和抽样决策可 review。
- 只有用户明确要求生成、扩写或构造新数据时,才进入数据生成分支。
- 如果用户要求把筛选出的线上候选变成样本,必须先使用 `data_agent_convert_router_candidates_to_records`,不要改走 generation goal/plan。
+319
View File
@@ -0,0 +1,319 @@
---
name: product-data
description: 从产品/标签定义、手写边界规则或示例 query 中提取标签边界,并生成可 review 的数据计划、dataset draft text 和 canonical metadata records。
when_to_use: 当用户提供产品定义、标签规则、路由边界文档、示例 query、手写标签边界,并希望生成训练/评测/专项数据时使用。
aliases: definition-data, label-data
allowed_tools: read_file, write_file, edit_file, grep_search, glob_search, ask_user_question, data_agent_load_input_sources, data_agent_render_source_context, data_agent_extract_case_evidence, data_agent_prepare_generation_goal, data_agent_confirm_generation_goal, data_agent_prepare_generation_plan, data_agent_show_generation_plan, data_agent_update_generation_plan, data_agent_confirm_generation_plan, data_agent_normalize_dataset_draft, data_agent_validate_dataset_records, data_agent_export_dataset_records
---
使用这个 skill 作为“产品/标签定义/手写规则/示例 query -> 输入文本化 -> generation goal 草案 -> 人工 review -> 生成计划 review -> dataset draft text -> canonical metadata records”的统一入口。
本 skill 内置数据记录生成协议。其他数据开发 skill 如果需要生成或整理标准数据,可以复用这里的“交互门禁、dataset draft text v1、canonical record v1”规则。
## 输入假设
用户可能会提供:
- 产品定义文档、标签定义文档、路由规则文档。
- 表格、Markdown、JSON、CSV 或普通文本里的标签定义。
- 手写的标签边界规则。
- 一组 example query、badcase、正例或反例。
- 已知边界冲突,例如某类 query 应该进入哪个 Agent/function。
输入可能不完整或存在歧义。保留不确定性,不要自行发明隐藏规则。
## 任务定位
先判断用户输入属于哪一类:
1. **文件定义型**:用户提供文件路径、文档、表格或粘贴的大段定义内容。
2. **手写规则型**:用户直接描述标签边界,例如“找附近美食是餐饮服务,导航去某地是地图导航”。
3. **示例归纳型**:用户只给 query/example/badcase,需要先归纳边界和标签倾向。
三类输入最后都要统一产出:
```text
dataset_label:
target 或 target_definitions:
plan_hint:
coverage:
exclusions:
open_questions:
source_refs:
```
这一步称为 `generation_goal`。它是模型基于输入资料整理出的数据生成目标草案,不是最终生成计划。
`generation_goal` 必须先展示给用户 review。只有用户确认 generation goal 后,才能进入本 skill 内置的 generation plan review 流程。
## 交互门禁
默认不要一步到位生成数据。除非用户已经明确说“开始生成”“确认计划”“按这个计划生成”或同义表达,否则只能做目标对齐、计划草案和问题确认。
为了减少重复确认,优先按下面两种门禁模式选择:
- **一次确认模式**:用户直接给出手写规则、完整 target 表达,并且明确希望生成数据时,直接调用 `data_agent_prepare_generation_plan`,传 `direct_review=true``target_definitions`。这一次 review 同时确认目标、数量、轮次和路径;用户回复“确认,开始生成”后即可调用 `data_agent_confirm_generation_plan`
- **两段确认模式**:用户提供文件、表格、badcase、长文档,或者标签/边界/字段含义有歧义时,先用 `data_agent_prepare_generation_goal` 做目标 review;目标确认后再做 plan review。
所有数据产物必须放在当前用户当前会话的 output 目录下。不要把 records、draft 或 validation 写到项目根目录的 `output/``tasks/` 或其他源码目录。canonical records 的稳定输出文件名固定为 `output/records.jsonl`,不要按数据集名创建子目录,不要自定义时间戳、中文专题名或随机文件名。工具会把相对 `output_path` 自动路由到会话 output 目录,并把 records 路径归一到当前会话 `output/records.jsonl`;展示给用户时以工具返回的实际路径为准。
开始生成前必须确认这些信息:
- `dataset_label`:数据集或专题名称。
- `target` / `target_definitions`:最终监督标签。单标签任务用 `target`,多标签边界任务必须用 `target_definitions` 列出每个标签和判定规则。
- 生成数量:总条数,以及单轮/多轮数量或比例。
- 覆盖范围:需要覆盖哪些 query 类型、意图边界或错误类型。
- 负例/排除项:哪些表达不要生成,或哪些边界容易误判。
- 落盘路径:canonical records 固定使用 `output/records.jsonl`,由工具路由到当前会话 output 目录。
如果任一信息缺失,不要生成数据,不要调用 `data_agent_prepare_generation_plan`,不要调用 `data_agent_normalize_dataset_draft`,不要调用 `data_agent_validate_dataset_records`,只向用户提出需要确认的问题。
两段确认模式下,信息完整后,调用 `data_agent_prepare_generation_goal` 创建 pending goal。这个工具会暂停本轮,必须把返回的 `generation_goal` 展示给用户 review。用户确认 goal 之后,调用 `data_agent_confirm_generation_goal` 获取 `confirmed_goal_id`,再调用 `data_agent_prepare_generation_plan` 创建 pending plan。创建 plan 后也会暂停本轮,必须等待用户 review。
一次确认模式下,不要先创建 generation goal;直接创建 pending plan,并在 plan 里包含 `target_definitions``total_count``turn_mix``coverage``exclusions``output_path``output_path` 固定传 `output/records.jsonl`。不要让用户先确认目标再确认计划。
用户确认后,拿到 `confirmed_plan_id`,才能生成 dataset draft text,并继续调用工具。
`data_agent_normalize_dataset_draft` 对生成数据有代码级门禁:没有 `confirmed_plan_id`,或者计划未确认,会拒绝执行。
如果用户在原始需求里已经写出 `Agent(tag="xxx")`、function 调用或其他完整标签表达,`target` 必须原样保留这个完整表达,不要简化成纯标签名。例如用户说 `Agent(tag="餐饮服务")`,则 `target_definitions[*].target` 和后续 draft 的 `target:` 都必须写 `Agent(tag="餐饮服务")`,不要写成 `餐饮服务`
review 展示必须简短清晰,不要重复解释工具和流程。每次 review 最多展示 6 行,格式优先如下:
```text
我先把生成目标整理好了,先确认边界,暂时不生成数据。
- 数据集:xxx
- 标签:A -> Agent(tag="A")B -> Agent(tag="B")
- 边界:一句话说明核心判定规则
- 覆盖:一句话说明主要 case 类型
- 内部:goal_id `...`revision `...`
你看这个目标是否准确?没问题就回“确认目标”;想改的话直接说哪里不对。
```
计划 review 也最多展示 6 行,只展示数量、轮次、覆盖、输出路径和确认口令。不要把 goal 的完整内容再次复制到 plan review 中,开头必须说明“目标已确认,现在只补充生成参数,标签边界沿用上一步”。确认口令可以自然一点,例如“如果这个数量和路径可以,就回‘确认,开始生成’;想调整就直接说,比如‘改成 20 条,全单轮’。”
多标签边界数据不要拆成多个互不相关的单标签计划。应该创建一个计划,并在 `target_definitions` 中列出所有候选标签。例如:
```json
[
{
"name": "餐饮服务",
"target": "Agent(tag=\"餐饮服务\")",
"rule": "找附近的美食、奶茶、餐厅等,没有明确要求导航。"
},
{
"name": "地图导航",
"target": "Agent(tag=\"地图导航\")",
"rule": "明确出现导航去某地点、带我去某地点、路线规划等。"
}
]
```
生成 draft text 时,每条 case 的 `target:` 必须从已确认计划的 `target``target_definitions[*].target` 中选择。不要临时发明新 target。
## 必须遵守的数据生成协议
不要直接生成 canonical JSON,不要直接导出最终训练/评测格式,不要绕过人类 review。
如果需要生成数据,必须按顺序执行:
1.`data_agent_load_input_sources` 读取文件。
2.`data_agent_render_source_context` 把输入渲染成大模型可读 evidence text。
3. 大模型只基于 evidence text 抽取 `generation_goal`,包括 `dataset_label``target_definitions``plan_hint``coverage``exclusions``open_questions``source_refs`
4. 如果目标标签、边界或字段含义不清楚,先用普通回复向用户提问并停止。
5. 如果是手写规则且信息完整,调用 `data_agent_prepare_generation_plan`,设置 `direct_review=true`,创建一次确认的 pending 计划,并停止等待用户 review。
6. 如果是文件/示例归纳/歧义场景,调用 `data_agent_prepare_generation_goal` 创建 pending goal,并停止等待用户 review。
7. 用户确认 generation goal 后,调用 `data_agent_confirm_generation_goal` 获取 `confirmed_goal_id`,再调用 `data_agent_prepare_generation_plan` 创建 pending 计划。
8. 展示计划后停止本轮,等待用户 review。
9. 用户提出修改意见时,调用 `data_agent_update_generation_plan`,再展示计划。
10. 用户明确确认当前计划版本后,调用 `data_agent_confirm_generation_plan`
11. 生成 dataset draft text v1。
12. 调用 `data_agent_normalize_dataset_draft`,必须传入 `confirmed_plan_id`
13. 调用 `data_agent_validate_dataset_records`
14. 如果用户要求落盘 canonical records,调用 `data_agent_export_dataset_records``output_path` 固定传 `output/records.jsonl`,默认导出紧凑 JSONL,不要用 `write_file` 手写 JSON。
15. 本阶段默认只推进到 canonical metadata records;除非用户另行要求,不做最终训练/评测格式导出。
## 数据生成输出格式
当需要生成数据样本时,默认使用 dataset draft text v1,不要直接输出 JSON、JSONL、CSV 或最终表格格式。除非用户明确要求机器可读格式,否则优先输出便于人工 review 的文本格式。
### 格式
```text
# dataset_label: 数据集或专题名称
### case: case名称
用户: 本轮 query
target: Agent(tag="xxx")
notes: 可选,说明覆盖的问题或边界
### case: 多轮 case 名称
用户: 前一轮 query
小爱: 前一轮 tts
用户: 本轮 query
target: Agent(tag="xxx")
notes: 可选,说明覆盖的问题或边界
```
### 规则
- 每条数据用一个 `### case:` 开始。
- `用户:` 表示用户 query。
- `小爱:` 表示小爱回复 tts。
- 最后一个 `用户:` 是本轮 query。
- `target:` 是本轮 query 对应的监督标签,必须存在。
- 单轮数据只需要写一行 `用户:`,然后写 `target:`
- 多轮数据需要按照时间顺序写多组 `用户:` / `小爱:`
- 多轮数据的最后一轮只写 `用户:``target:`,不要写最后一轮 `小爱:`,因为本轮 query 不包含 tts。
- 如果 `target` 无法确定,不要编造,必须向用户确认。
- 不要手写 `record_id``request_id``timestamp``context`
- 线上挖掘数据如有真实 `request_id``timestamp`,可以附加在 case 中;没有则不写。
## Canonical Record
`data_agent_normalize_dataset_draft` 会把 dataset draft text 转成 canonical records,并统一补齐 `record_id``source``timestamp``context``target_type` 等机械字段。
canonical records 落盘必须使用 `data_agent_export_dataset_records`,默认格式是紧凑 JSONL:一行一个 canonical record,不带外层数组,不手写缩进 JSON。默认 `output_path` 固定传 `output/records.jsonl`;不要传 `output/<数据集名>/records.jsonl`,不要传 `tasks/...`,不要自定义文件名。只有用户明确要求兼容旧文件时,才使用 `output_format="json"` 导出紧凑 JSON 数组,此时工具会归一为 `output/records.json`
当前 canonical record v1 工作格式:
```json
{
"record_id": "gen_aabbccdd_000001",
"source": {
"type": "generated",
"request_id": "aabbccdd",
"timestamp": 1755567930500
},
"turn": {
"query": "本轮 query",
"timestamp": 1755567930500
},
"prev_session": [
{
"query": "前一轮 query",
"tts": "前一轮 tts",
"timestamp": 1755567870500
}
],
"context": {},
"label": {
"dataset_label": "数据集或专题名称",
"target": "Agent(tag=\"xxx\")",
"target_type": "agent"
},
"meta": {
"case_name": "case名称",
"notes": ""
}
}
```
## 场景工作流
### 文件定义型
1. 使用 `data_agent_load_input_sources` 读取用户提供的文件。
2. 如果用户只描述了文件名或主题但没给路径,先询问路径,不要猜。
3. 使用 `data_agent_render_source_context` 文本化输入资料,必要时用 `focus_keywords` 缩小到 query、功能点、标签、badcase 相关内容。
4. 大模型基于 evidence text 总结 query 语义、功能点、边界、正例、反例、冲突点和缺失假设。
5. 把标签边界整理为 `generation_goal.target_definitions`
6. 如果文档里没有明确 target 格式,向用户确认,例如 `Agent(tag="xxx")` 还是 function 调用格式。
7. 调用 `data_agent_prepare_generation_goal`,由工具暂停等待用户 review;用户确认后再调用 `data_agent_confirm_generation_goal``data_agent_prepare_generation_plan`
### 手写规则型
1. 直接从用户描述里抽取标签边界。
2. 多标签边界必须使用 `target_definitions`,不要拆成多个无关单标签计划。
3. 识别规则中的冲突词、优先级和反例。
4. 如果用户已经给出完整 target 表达,直接写入 `target_definitions`;如果 target 表达不明确,先提出问题。
5. 如果数量、轮次或输出路径未指定,可以由模型给出保守建议,走一次确认模式;不要为了这些默认参数单独多问一轮。
6. 调用 `data_agent_prepare_generation_plan`,传入 `direct_review=true`,让用户一次确认目标和生成参数。
### 示例归纳型
1. 先把 example query / badcase 按意图和可能标签分组。
2. 输出边界归纳和不确定点,不要马上生成数据。
3. 如果 query 没有明确正确标签,必须向用户确认标签或允许的 target 集合。
4. 用户确认后,整理为 `generation_goal.target_definitions``generation_goal.coverage`
5. 调用 `data_agent_prepare_generation_goal` 展示 `generation_goal` 给用户 review;用户确认后再调用 `data_agent_confirm_generation_goal``data_agent_prepare_generation_plan`
## Generation Goal 草案格式
大模型完成产品定义或 badcase 分析后,先输出下面的草案给用户 review:
```json
{
"dataset_label": "数据集或专题名称",
"goal_summary": "这批数据要解决什么问题",
"target_definitions": [
{
"name": "标签名称",
"target": "Agent(tag=\"xxx\") 或 function 调用",
"rule": "哪些 query 应该进入这个标签",
"positive_examples": [],
"negative_examples": [],
"boundary_notes": [],
"source_refs": []
}
],
"plan_hint": "建议先生成 50 条单轮,输出到 output/records.jsonl;具体数量和轮次在 generation plan 中确认。",
"coverage": "需要覆盖的 query 语义、功能点、错误类型",
"exclusions": "不要生成或需要排除的表达",
"open_questions": [],
"source_refs": []
}
```
`generation_goal` 只确认“做什么数据、为什么做、标签边界是什么”。不要在 goal 中维护结构化的 `total_count``turn_mix``output_path`;这些字段属于后续 `generation_plan`。如果需要在 goal review 阶段提示执行方向,只写一句 `plan_hint`,例如“建议先生成 50 条单轮,输出到 output/records.jsonl;具体数量和轮次在 generation plan 中确认”。
如果未来增加 `data_agent_validate_generation_goal`,它只做结构校验和缺失字段提示,不做语义判断,不替代用户 review,也不替代 `data_agent_prepare_generation_plan`
现在已经有代码级 goal review 门禁:不要手写 goal 后直接进入 plan,必须先调用 `data_agent_prepare_generation_goal`,并在用户确认后用 `data_agent_confirm_generation_goal` 取得 `confirmed_goal_id`
## 计划中的专用工具
当前前链路只保留三个工具。不要再假设有 `data_agent_load_definition_source``data_agent_extract_target_definitions` 这类更细工具。
### `data_agent_load_input_sources`
读取目录或文件,统一抽取 `xlsx/csv/docx/pdf/txt/md/json/jsonl` 的段落、表格预览、行数据样例和 source refs。
### `data_agent_render_source_context`
`data_agent_load_input_sources` 的结构化结果渲染成大模型可读的 evidence text。产品定义/PRD/走查文档的语义理解应该基于这个文本由大模型完成,不要依赖程序规则直接抽语义。
### `data_agent_extract_case_evidence`
从 badcase、评测表、走查表里识别 query、上下文、预期标签、模型预测、类型和备注。字段不明确时,它会返回 `required_questions`,此时必须向用户确认字段含义。
## 当前可用工具
- `read_file`:读取用户提供的产品定义、标签定义、样例 query 文件。
- `write_file`:在用户确认后落盘 draft、校验结果或说明文档;不要用它手写 canonical records 文件。
- `edit_file`:修改已有的计划、说明文档或生成结果文件。
- `grep_search`:在项目中搜索已有标签定义、历史数据样例或相关文档。
- `glob_search`:按路径模式查找定义文件、样例文件或历史产物。
- `ask_user_question`:需要用户明确选择或补充关键信息时使用;如果不可用,就用普通回复提问并停止。
- `data_agent_load_input_sources`:读取用户给的目录或文件,把 docx/xlsx/pdf 等输入统一抽成段落、表格和 source refs。
- `data_agent_render_source_context`:把结构化输入渲染成大模型可读文本,支持 `max_chars`、表格行数和关键词过滤,用于后续模型语义抽取。
- `data_agent_extract_case_evidence`:从 badcase/评测/走查表中抽取 query、预期标签、模型预测、上下文和备注;字段歧义会返回需要确认的问题。
- `data_agent_prepare_generation_goal`:在已经整理出 `dataset_label``target_definitions``plan_hint``coverage``exclusions``source_refs` 后,创建待 review 的 generation goal;调用后本轮会暂停等待用户 review。
- `data_agent_confirm_generation_goal`:用户明确确认 generation goal 后使用,获取 `confirmed_goal_id`
- `data_agent_prepare_generation_plan`:在 generation goal 已确认后创建待 review 的生成计划;必须传入 `confirmed_goal_id`,调用后本轮会暂停等待用户 review。
- `data_agent_show_generation_plan`:用户要求查看当前计划,或继续上下文时需要恢复计划详情时使用。
- `data_agent_update_generation_plan`:用户对计划提出修改意见后使用,更新计划并重新展示。
- `data_agent_confirm_generation_plan`:用户明确确认当前计划版本后使用,获取 `confirmed_plan_id`
- `data_agent_normalize_dataset_draft`:用户确认计划后,把 dataset draft text v1 转成 canonical records;必须传入 `confirmed_plan_id`
- `data_agent_validate_dataset_records`:对 canonical records 做结构、标签、时间戳和多轮上下文校验。
- `data_agent_export_dataset_records`:校验 canonical records 并落盘;默认写紧凑 JSONL,一行一条,固定传 `output/records.jsonl`,不要再用 `write_file` 手写 records 文件。
## 约束
- 不要静默解决产品或标签歧义。
- 如果 `ask_user_question` 不可用,使用普通回复向用户提问并停止,不要自己替用户确认。
- canonical records 通过校验前,不要生成最终导出格式。
- canonical records 需要落盘时,必须用 `data_agent_export_dataset_records`;不要自己拼接 JSON/JSONL;不要创建数据集子目录或自定义 records 文件名。
- 除非用户明确要求,否则不要把“修改标签定义”和“生成数据”混在一起做。
- 不要因为用户说“生成一些数据”就跳过边界总结和 generation plan review。
+68
View File
@@ -0,0 +1,68 @@
---
name: update-config
description: Configure settings via settings.json - hooks, permissions, env vars.
when_to_use: When the user wants to configure hooks, permissions, or settings.
aliases: config-help
allowed_tools: read_file
---
Help configure the agent settings.
## Settings File Locations
- Global: `~/.claude/settings.json` applies to all projects.
- Project: `.claude/settings.json` is project-specific and committed to git.
- Local: `.claude/settings.local.json` is project-specific and gitignored.
## Configurable Settings
### Hooks
Event-driven shell commands that run on tool use or lifecycle events:
- `PreToolUse` runs before a tool executes and can block with exit code 2.
- `PostToolUse` runs after a tool completes.
- `PreCompact` runs before conversation compaction.
Hook format:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo 'tool: $TOOL_NAME'"
}
]
}
]
}
}
```
### Permissions
Tool permission rules:
```json
{
"permissions": {
"allow": ["Read", "Grep", "Glob"],
"deny": ["Bash(rm:*)"]
}
}
```
### Environment Variables
```json
{
"env": {
"MY_VAR": "value"
}
}
```
+28
View File
@@ -0,0 +1,28 @@
---
name: verify
description: Verify a code change works by running the app and tests.
when_to_use: When the user asks to verify, test, or check that recent changes work.
allowed_tools: read_file, bash, grep_search, glob_search
---
Verify that the recent code changes work correctly.
## Instructions
1. Identify what was changed by checking `git diff` and `git status`.
2. Determine the appropriate verification strategy:
- Unit tests: run existing tests and check for failures.
- Integration tests: run broader test suites if available.
- Manual verification: start the app/server and test the feature when needed.
3. Run the verification.
4. Report the result clearly:
- PASS: all checks passed and the feature works as expected.
- FAIL: describe what failed and why.
- PARTIAL: some checks passed and some still need attention.
## Verification Strategy
- For CLI tools: run the command with test inputs.
- For servers: start the server and make test requests.
- For libraries: run the test suite.
- For config changes: validate that the config loads correctly.