feat: 新增label快慢分流skill全套配置与校验脚本

This commit is contained in:
zhukai8
2026-07-23 18:09:01 +08:00
parent 017743a415
commit 9067914ecc
7 changed files with 1386 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
---
name: label-fast-slow-routing
description: 依据 XA4.1 2026.6 新标准,对单条或批量 query、带会话上下文的请求和快慢分流评测数据进行“快 / 慢 / 模糊”三档可解释打标,给出默认路由、原因码、证据、置信度和复核标记,并在需要时兼容旧 FAST_DIRECT / SLOW_* 评测标签。用于快慢系统策略标注、旧评测集重标、边界 case 仲裁、标注 Prompt 编写、标签一致性检查和 badcase 分析;可结合相邻 label-master skill 判断业务领域、候选标签、复杂度、多指令和自动任务,但不得把这些辅助维度直接等同于快慢标签。
---
# 快慢分流打标
## 目标
按用户对响应速度和结果质量的预期判断语义标签,不按当前系统能力反推标签。始终分开输出:
- `label``快``慢``模糊`
- `default_route`:三档标签的默认系统策略。
- `fast_system_pending`:已确认的快系统能力缺口。
- 业务标签及 `complex`、多指令、自动任务等辅助维度。
## 加载知识
1. 每次打标前读取 [references/policy.md](references/policy.md)。
2. 处理边界 case、校准标注员或编写 Prompt 时,再读取 [references/golden-examples.md](references/golden-examples.md)。
3. 迁移旧评测集、输出 `FAST_DIRECT` / `SLOW_*`、设计评测集或做质检时,再读取 [references/legacy-eval-and-quality.md](references/legacy-eval-and-quality.md)。
4. 需要业务领域、业务标签、候选标签或结构维度时,按“结合 label-master”一节渐进加载相邻知识;不要一次读取整个知识库。
## 遵守真源优先级
按以下顺序处理冲突:
1. 用户在当前任务中明确给出的已批准新口径。
2. 《【XA4.1】快慢分流新标准-2026.6》最终汇总(物理页 7–10)。
3. 同文档的垂域调研细则(物理页 2–7),仅补充最终汇总未覆盖的场景。
4. 《快慢系统分发-评测集构建》的旧八类标签,仅用于原因解释、旧数据迁移和评测方法。
5. `label-master` 的业务标签与正交维度,仅用于辅助理解。
明确处理以下冲突:
- 把“导航去附近评分 4.5 以上的泰国菜”标为 `快`,不要沿用旧的筛选即慢。
- 把“那个压线噔噔的声音关掉”标为 `快`,前提是功能可唯一识别且动作明确。
- 把纯静态产品知识、故障诊断、设备或环境状态查询标为 `模糊`,不要直接沿用旧的快或慢。
- 把多个全部明确的设备控制动作标为 `快`;不要仅因多指令就标慢。
- 不要使用“长度不超过 5 字默认快”。它只是旧候选挖掘捷径,不是语义规则。
## 执行工作流
### 1. 整理输入
提取并保留:
- 当前 `query`
- 已提供的前轮、系统回复、时间窗口、设备、端侧和场景。
- 用户要求的输出格式和旧标签兼容要求。
- 已提供的快系统能力清单;没有清单时不要猜测能力支持状态。
只使用可见上下文。缺少会改变结论的上下文时,保留不确定点并设置 `review_required=true`
### 2. 判断业务与结构辅助信息
需要时使用 `label-master` 判断:
- 业务领域和 2–5 个候选业务标签。
- 是否为多指令、自动任务或 `complex`
- 当前轮是否真正依赖上文。
保持这些结果与快慢标签正交。特别注意:
- `complex=true` 不自动等于 `慢`
- 多指令不自动等于 `慢`
- 自动任务要结合端侧专项策略。
- 业务标签不自动决定快慢。
### 3. 按三档策略裁决
使用 [references/policy.md](references/policy.md) 的完整决策树。执行以下最小顺序:
1. 先区分纯信息获取和带副作用的执行请求。
2. 对执行请求,先检查是否必须反推意图、映射场景、消歧、补槽、依赖上下文或规划多步;命中则倾向 `慢`
3. 再检查动作和目标能否直接识别、是否无需先决定“做什么”;满足则标 `快`
4. 对纯闲聊、开放问答、产品知识、故障诊断、设备或环境状态查询标 `模糊`
5. 无法可靠归入快或慢、或速度和质量预期同时明显存在时标 `模糊`,不要把“模型自己不确定”伪装成确定标签。
6. 聚合多个子请求时使用 `慢 > 模糊 > 快`;多个全部明确的设备控制动作使用 `快`特例。
同时做反证检查:说明为什么没有选择另外两档。不要只凭关键词命中一个规则。
### 4. 确定默认路由和能力状态
按固定映射填写:
- `快``default_route="快系统"`
- `慢``default_route="慢系统"`
- `模糊` → 默认 `default_route="慢系统"`;只有用户另给细分部署策略时才在下游覆盖,语义标签保持 `模糊`
仅在有明确能力清单或验证结果证明“语义上应为快,但当前快系统尚不支持”时填写 `fast_system_pending=true`。没有检查能力时填 `null`,不要填 `false` 假装已验证。
### 5. 生成可审查结果
默认每条样本输出一个符合 [schemas/label-record.schema.json](schemas/label-record.schema.json) 的 JSON 对象:
```json
{
"query": "太冷了,调一下空调",
"label": "快",
"default_route": "快系统",
"primary_reason_code": "DIRECT_ACTION",
"reason_codes": ["DIRECT_ACTION"],
"reason": "用户明确给出调节动作和空调对象,无需反推要操作什么。",
"evidence": ["“调一下空调”同时包含动作和可识别对象"],
"counterevidence": ["含感受词“太冷”,但感受词不覆盖明确执行目标"],
"domain_label": "设备控制",
"candidate_domain_labels": ["设备控制", "车载控制"],
"structural_signals": {
"complex": null,
"multi_instruction": false,
"auto_task": false
},
"context_used": false,
"fast_system_pending": null,
"legacy_eval_label": "FAST_DIRECT",
"confidence": "high",
"review_required": false,
"uncertainties": [],
"policy_version": "XA4.1-2026.6"
}
```
使用 `null` 表示未评估,不要用猜测值补齐辅助字段。用户只要求简表时至少保留 `query``label``primary_reason_code``reason``review_required`;落盘数据仍使用完整契约。
### 6. 校验后再落盘
对单条结果执行:
```bash
python skills/label-fast-slow-routing/scripts/validate_label.py \
--record '{"query":"开空调","label":"快","default_route":"快系统","primary_reason_code":"DIRECT_ACTION","reason_codes":["DIRECT_ACTION"],"reason":"动作和对象明确。","evidence":["开+空调"],"context_used":false,"fast_system_pending":null,"confidence":"high","review_required":false,"uncertainties":[],"policy_version":"XA4.1-2026.6"}'
```
对 JSON、JSONL 文件执行:
```bash
python skills/label-fast-slow-routing/scripts/validate_label.py \
--file output/fast_slow_labels.jsonl
```
出现 `valid=false` 时先修正结构和不变量,再写入训练集或评测集。校验器只保证结构和跨字段一致性,不能替代语义复核。
## 结合 label-master
需要业务标签时按以下最短路径读取:
1. 读取 `../label-master/knowledge/决策流程.md`
2. 读取 `../label-master/knowledge/索引/候选召回索引.md``../label-master/knowledge/标签总览.md`
3.`../label-master/knowledge/索引/标签索引.md` 召回 25 个候选。
4. 命中高频混淆时读取对应边界卡,再读取候选标签卡。
5. 只在任务需要时读取复杂度、多指令或自动任务文件。
复用它的“候选召回 → 边界比较 → 排除理由 → 不确定点”方法,不复用它的 `Agent` / function target 作为快慢输出。
`label-master` 与本策略冲突时:
- 保留其业务标签与结构维度结论。
- 按本 skill 的真源优先级独立重算快慢标签。
-`counterevidence` 中记录冲突,不静默覆盖。
## 批量与评测集要求
- 保留原始 ID、query、上下文和来源,不修改原始数据。
- 逐条执行完整语义判断,不按字数、关键词或既有类别比例直接定标。
- 对同一 session 使用相同的上下文截取规则;缺上下文样本单独标记。
-`模糊` 保留为语义标签;需要二值评测时另派生默认路由,不要覆盖原标签。
- 旧八类标签只写入 `legacy_eval_label`;无法无损映射时填 `null` 并解释。
- 对高流量、高分歧、高风险误分样本优先双人盲标并仲裁。
- 分别统计三档标签、垂域、端侧和方向性错误;不要只看总准确率。
## 复核门禁
满足任一情况时设置 `review_required=true`
- 缺少会改变标签的前轮、设备或端侧信息。
- 快慢规则同时有强证据,且优先级无法消解。
- 只能依赖未确认的系统能力或白名单。
- 旧标签无法无损迁移到新三档。
- `confidence="low"`
`模糊` 不自动等于需要人工复核。产品知识或状态查询可以高置信地标为 `模糊`
## 禁止事项
- 不要因快系统当前不支持而把 `快`改成 `慢`
- 不要把 `complex=true`、长 query、联网、搜索、排序、模糊词或多轮中的任一项当成单独硬触发器。
- 不要把非标准叫法等同于真实歧义;先判断对象能否唯一识别。
- 不要脑补前轮、设备位置、说话人位置或能力清单。
- 不要把旧评测集的样本占比当成先验配额。
- 不要输出没有证据或排除理由的裸标签。
@@ -0,0 +1,4 @@
interface:
display_name: "快慢分流打标"
short_description: "依据 2026.6 新标准完成快、慢、模糊三档可解释打标"
default_prompt: "Use $label-fast-slow-routing to label these queries as 快、慢或模糊并给出可复核依据。"
@@ -0,0 +1,61 @@
# 黄金边界样例
使用这些样例校准规则,不要把词面模板机械迁移到所有场景。`来源`中的页码为 PDF 物理页码;“推导”表示按规则组合得到,不是文档逐字给出的标签。
| # | query / 上下文 | label | 主原因码 | 边界说明 | 来源 |
| --- | --- | --- | --- | --- | --- |
| 1 | 开空调 | 快 | `DIRECT_ACTION` | 动作和对象明确 | 新标准 p8 |
| 2 | 太冷了,调一下空调 | 快 | `DIRECT_ACTION` | 感受词不覆盖明确动作和对象 | 新标准 p8,规则推导 |
| 3 | 我感觉好冷快冻死了,帮我调一下 | 慢 | `INTENT_INFERENCE` | 未说明要调什么,需反推动作 | 新标准 p9 |
| 4 | 调一个浪漫点的氛围灯 | 快 | `DIRECT_ACTION` | 风格是简单修饰,功能目标明确 | 新标准 p8–9 |
| 5 | 帮我把房间布置得舒适温馨一些 | 慢 | `SCENE_GOAL_MAPPING` | 需把目标状态映射成动作组合 | 新标准 p9 |
| 6 | 导航去附近评分 4.5 以上的泰国菜 | 快 | `DIRECT_ACTION` | 最终汇总覆盖调研版“筛选即慢” | 新标准 p2 与 p8 |
| 7 | 导航去附近最大的停车场 | 快 | `DIRECT_ACTION` | 单目标、单一简单排序可直接满足 | 旧评测 p3 与新标准 p8,规则推导 |
| 8 | 推荐一家安静、亲子、评分高、停车方便且别排太久的酒店 | 慢 | `COMPLEX_REASONING` | 多个主观/实时条件需权衡 | 旧评测 p3,新标准规则推导 |
| 9 | 导航去大裤衩,再沿途找充电站,并看看终点附近哪里好停车 | 慢 | `MULTI_STEP_PLANNING` | 多地点、多步骤路线编排 | 新标准 p9 |
| 10 | 删除途经点 | 快 | `DIRECT_OPERATION_FLOW` | 导航操作流中的直接动作 | 新标准 p8 |
| 11 | 把空调调到 24 度,然后打开座椅加热 | 快 | `EXPLICIT_DEVICE_MULTI_ACTION` | 多个动作全部为明确设备控制 | 新标准 p8 |
| 12 | 查天气再决定要不要开窗 | 慢 | `MULTI_STEP_PLANNING` | 查询结果决定后续控制 | 旧评测 p4 |
| 13 | 那个压线噔噔的声音关掉 | 快 | `DIRECT_ACTION` | 名称不标准但目标可唯一识别且动作明确 | 新标准 p4 与 p9 |
| 14 | 打开那个灯(场景中有多个灯) | 慢 | `TRUE_DISAMBIGUATION` | 存在真实多候选,需澄清 | 旧评测 p3,新标准规则推导 |
| 15 | 导航到那个像大裤衩一样的楼 | 慢 | `TRUE_DISAMBIGUATION` | 需先识别隐含地标再定位 | 旧评测 p3 |
| 16 | 播放周杰伦的歌 | 快 | `DIRECT_ACTION` | 明确播放意图和资源 | 新标准 p8 |
| 17 | 播放最近短视频里很火的那首歌 | 慢 | `TRUE_DISAMBIGUATION` | 资源身份不确定且依赖外部热点 | 旧评测 p3,新标准内容细则 |
| 18 | 找王菲在春晚与别人合唱的歌,再用网易云播放 | 慢 | `MULTI_STEP_PLANNING` | 外部检索后跨步骤播放 | 新标准 p9 |
| 19 | 现在几点了 | 快 | `DIRECT_DETERMINISTIC_QUERY` | 高频、唯一、确定性工具查询 | 旧评测 p3 |
| 20 | 帮我定明天早上 9 点的闹钟 | 快 | `DIRECT_ACTION` | 时间和动作明确 | 新标准 p8 |
| 21 | 小米 SU7 支持哪些驾驶模式 | 模糊 | `PRODUCT_KNOWLEDGE` | 纯静态产品知识 | 新标准 p10 |
| 22 | 我的车怎么突然没声音了 | 模糊 | `FAULT_DIAGNOSIS` | 纯故障原因/诊断请求 | 新标准 p4 与 p10 |
| 23 | 现在胎压是多少 | 模糊 | `DEVICE_OR_ENV_STATE_QUERY` | 最终模糊档覆盖调研版简单动态查询快 | 新标准 p4 与 p10,规则推导 |
| 24 | 家里哪个房间最热 | 模糊 | `DEVICE_OR_ENV_STATE_QUERY` | 环境状态查询,无控制副作用 | 新标准 p10 |
| 25 | 给我讲个笑话 | 模糊 | `OPEN_CHAT_OR_QA` | 闲聊/开放内容 | 新标准 p10 |
| 26 | 做一个介绍 AI4S 的 PPT | 慢 | `MULTI_STEP_PLANNING` | 复杂产物生成与内容组织 | 新标准 p9 |
| 27 | 音箱端:每天晚上 8 点开灯开空调 | 慢 | `AUTOMATION_SLOW_POLICY` | 音箱家庭自动化专项默认慢 | 新标准 p7 |
| 28 | 车载端:车速超过 70 提醒我减速 | 快 | `DIRECT_ACTION` | 车载闹钟/提醒专项走快 | 新标准 p7 |
| 29 | 前轮慢系统询问“确认开启湿滑模式吗”;当前轮“帮我开启” | 慢 | `CONTEXT_REQUIRED` | 继承慢确认链路 | 新标准 p4 |
| 30 | 仅当前轮“帮我开启”,无任何前轮 | 慢 | `MISSING_REQUIRED_SLOT` | 无法确定开启对象;低置信并复核 | 新标准规则推导 |
| 31 | 自动帮我设置一下(端侧未知) | 模糊 | `POLICY_UNCERTAIN` | 端侧会改变正式自动化策略;默认慢并复核 | 新标准 p7,规则推导 |
## 最小对照组
校准时至少同时检查以下紧邻对:
```text
太冷了,调一下空调 -> 快
我感觉好冷快冻死了,帮我调一下 -> 慢
调一个浪漫点的氛围灯 -> 快
帮我把房间布置得浪漫一点 -> 慢
导航去附近评分 4.5 以上的泰国菜 -> 快
帮我综合挑一家不踩雷、少排队、停车方便的餐厅 -> 慢
那个压线噔噔的声音关掉 -> 快
打开那个灯(存在多个候选) -> 慢
现在几点 -> 快
现在胎压多少 -> 模糊
开空调并打开座椅加热 -> 快
查天气再决定是否开窗 -> 慢
```
@@ -0,0 +1,153 @@
# 旧评测标签迁移与质量控制
## 目录
- [文档定位](#文档定位)
- [旧八类标签](#旧八类标签)
- [迁移到新三档](#迁移到新三档)
- [关键口径变化](#关键口径变化)
- [评测集构建](#评测集构建)
- [质检门禁](#质检门禁)
- [禁止固化的旧信息](#禁止固化的旧信息)
## 文档定位
《快慢系统分发-评测集构建》是早期策略、评测集项目计划和一次候选挖掘实验的混合文档。其项目日期早于 2026.6 新标准。
使用它:
- 解释复杂性原因。
- 迁移旧 `FAST_DIRECT` / `SLOW_*` 数据。
- 复用候选挖掘、人工盲标、主动学习和流量回放方法。
不要使用它覆盖新三档语义标准。
## 旧八类标签
| 旧标签 | 旧定义 |
| --- | --- |
| `FAST_DIRECT` | 当前轮可直接、安全、确定性满足 |
| `SLOW_FILTER_RANK` | 候选筛选、排序、优化或推荐 |
| `SLOW_DISAMBIGUATE` | 目标身份不确定,需先消歧 |
| `SLOW_GOAL_STATE` | 给目标状态,需反推参数 |
| `SLOW_WORKFLOW` | 多动作、多工具或显式多步骤 |
| `SLOW_CONTEXT_DEPENDENT` | 复杂性主要来自上文或记忆 |
| `SLOW_UNCLEAR` | 结合必要上下文仍不明确 |
| `SLOW_ADVANCED_CAPABILITY` | 不在旧快系统白名单的高级能力 |
旧文档没有给多原因冲突优先级。迁移时允许先保留多个原因,再选择决定性主因。
## 迁移到新三档
不要做固定的一对一映射。先按 2026.6 语义重标,再按需要回填最接近的旧标签。
| 旧标签 | 新标准处理 |
| --- | --- |
| `FAST_DIRECT` | 通常为快;仍检查它是否属于新标准明确列出的模糊信息型 |
| `SLOW_FILTER_RANK` | 简单单目标条件搜索可改为快;复杂权衡、推荐、外部验证仍慢 |
| `SLOW_DISAMBIGUATE` | 非标准称呼但可唯一识别且动作明确可改为快;真实多候选仍慢 |
| `SLOW_GOAL_STATE` | 只有目标状态、需反推动作时慢;动作和对象已明确时可能改为快 |
| `SLOW_WORKFLOW` | 多步/跨工具仍慢;多个全部明确的设备控制动作改为快 |
| `SLOW_CONTEXT_DEPENDENT` | 真正依赖上文推理时慢;已建立快操作流中的简单直接续接可快 |
| `SLOW_UNCLEAR` | 执行意图需澄清时慢;若缺端侧导致政策无法确定,可标模糊并复核 |
| `SLOW_ADVANCED_CAPABILITY` | 废除按能力白名单直接定语义;按用户预期重判,能力缺口另记 |
兼容输出规则:
- 只有新结论能被旧标签无损表达时才填写 `legacy_eval_label`
- `模糊`通常无法无损映射到旧二值标签,默认填 `null`
- 业务方明确要求旧二值路由时,可从 `default_route` 派生,但不要覆盖新 `label`
- 不要把 `complex_task=true``慢`画等号。
## 关键口径变化
| 维度 | 旧评测文档 | 2026.6 新标准 |
| --- | --- | --- |
| 核心视角 | 快系统白名单与能力边界 | 用户等待预期 |
| 标签空间 | 1 个 FAST + 7 个 SLOW | 快 / 慢 / 模糊 |
| 简单筛选 | 普遍视为 `SLOW_FILTER_RANK` | 目标明确、无需多步时可快 |
| 模糊称呼 | 普遍视为 `SLOW_DISAMBIGUATE` | 可唯一识别且动作明确时可快 |
| 多动作 | 普遍视为 `SLOW_WORKFLOW` | 全部明确设备控制是快特例 |
| 产品知识/故障/状态 | 快慢二分 | 正式标为模糊 |
| 能力不支持 | `SLOW_ADVANCED_CAPABILITY` | 标签不变;快能力缺口标待承接 |
| 边界 case | 短期一律慢 | 保留模糊语义,默认路由慢 |
| 短 query | 实验中 ≤5 字默认快 | 禁止作为语义规则 |
内部冲突也要处理:
- 新标准物理页 2 把“评分 4.5 以上的泰国菜”列入导航慢,物理页 8 又明确列为快;使用最终汇总的快。
- 新标准垂域调研把“压线噔噔的声音关掉”归入模糊功能指代慢,物理页 9 明确改为快;使用最终汇总的快。
- 新标准调研把静态知识、故障和部分状态查询直接分快慢,物理页 10 统一为模糊;保留模糊标签。
## 评测集构建
复用旧文档的闭环:
```text
候选挖掘
-> LLM 预打标
-> 人工背靠背盲标
-> 分歧仲裁与规则回灌
-> 种子模型
-> 主动学习
-> 真实流量回放
-> 覆盖分析
```
候选来源:
1. 线上真实流量。
2. 明确功能点 TopQuery。
3. 现有快慢评测集。
4. LLM 生成或改写的紧邻边界对。
建议数据记录至少保留:
- 原始 ID、query、session 和上下文。
- 端侧、设备、来源和时间。
- 新三档语义标签及默认路由。
- 原因码、证据、置信度、复核状态。
- 可选旧标签和结构维度。
- 标注员、规则版本和仲裁结果。
## 质检门禁
### 标注一致性
- 使用同一份边界基准集校准 Prompt 和标注员。
- 对高分歧样本执行两名标注员背靠背盲标。
- 计算 IAA,并把分歧原因回灌规则和黄金样例。
- 把人机分歧与人人分歧分开分析。
### 数据完整性
- 对同一 session 使用一致的上下文窗口。
- 区分真实流量、旧集迁移和合成改写来源。
- 对 session 去重,并隔离训练集和评测集。
- 保留 `模糊`,不要在语义真值中提前二值化。
### 覆盖与指标
- 按快/慢/模糊、原因码、垂域、端侧和来源分层统计。
- 单独统计慢误分为快的高风险错误,以及快误分为慢的延迟损失。
- 同时报告宏平均、各类召回和混淆矩阵,不只报告总准确率。
- 不使用旧实验分布作为目标配额。
### 两道门
1. 语义门:人工或模型是否依据当前规则得出正确结论。
2. 结构门:记录是否通过 `scripts/validate_label.py`
结构校验通过不代表语义正确。
## 禁止固化的旧信息
不要把以下内容写成新策略:
- “≤5 字默认快”。
- “上轮慢则次轮必慢”的无条件继承。
- “边界一律慢”的永久语义规则。
- 旧快系统白名单或 `SLOW_ADVANCED_CAPABILITY`
- 旧实验中任一类别占比。
- 2K/3K 种子规模、5K/1W 最终规模等互相冲突的项目目标。
- 95%/98% 等未统一口径的验收数字。
@@ -0,0 +1,332 @@
# XA4.1 2026.6 快慢分流策略
## 目录
- [真源与概念](#真源与概念)
- [三档标签](#三档标签)
- [决策树](#决策树)
- [快的规则](#快的规则)
- [慢的规则](#慢的规则)
- [模糊的规则](#模糊的规则)
- [上下文、多指令和聚合](#上下文多指令和聚合)
- [垂域补充](#垂域补充)
- [原因码](#原因码)
- [能力与默认路由](#能力与默认路由)
## 真源与概念
使用《【XA4.1】快慢分流新标准-2026.6》的最终汇总作为主真源。该文档物理页 7–10 的最终汇总高于物理页 2–7 的垂域调研草案;更早的《快慢系统分发-评测集构建》只提供旧原因分类和评测方法。
判断核心问题:
> 用户说出这句话时,心里能接受等多久?
不要用“当前快系统能不能做”代替这个问题。
保持以下概念正交:
- 语义标签:快、慢、模糊。
- 默认路由:快系统或慢系统。
- 能力承接:当前快系统是否已支持。
- 业务标签:导航、车控、产品问答等。
- 结构维度:complex、多指令、自动任务、上下文依赖。
## 三档标签
| 标签 | 用户预期 | 默认策略 |
| --- | --- | --- |
| 快 | 立即响应,通常期望 2–3 秒内得到动作或确定结果 | 快系统 |
| 慢 | 愿意等几秒换取规划、推理或高质量结果 | 慢系统 |
| 模糊 | 对速度和质量都有要求,或难以可靠归入快/慢 | 默认慢系统,可由明确细分策略覆盖 |
`模糊` 是正式语义标签,不是模型置信度。高置信产品知识问答仍可标为 `模糊`;低置信执行请求不一定因此成为 `模糊`,还要看是否需要澄清或补上下文。
## 决策树
按顺序执行:
```text
1. query 是否包含要产生副作用的执行请求?
否:
a. 闲聊/开放问答/产品静态知识/故障诊断/
设备或环境状态查询 -> 模糊
b. 高频、唯一、确定性的工具或导航流查询 -> 快
c. 需要多步分析、换算、聚合或生成复杂产物 -> 慢
是:
a. 只有感受/意向,无法确定要做什么 -> 慢
b. 只有目标状态/场景,需映射动作组合 -> 慢
c. 真实歧义、缺关键槽位、需依赖上文才可决定 -> 慢
d. 需规划、多步、多工具、复杂比较或跨源推理 -> 慢
e. 动作与目标直接可识别,无需先决定“做什么” -> 快
2. 多个子请求:
a. 全部是明确设备控制动作 -> 快
b. 其他情况按 慢 > 模糊 > 快 聚合
3. 仍无法归类,或端侧缺失导致策略确实不同 -> 模糊,
default_route=慢系统,review_required=true
```
“快系统当前不支持”不出现在语义决策树中。
## 快的规则
命中以下一类且没有更高优先级慢条件时标 `快`
### 明确直接动作
- 有明确操作目标。
- 动作和设备、对象或资源可直接识别。
- 不需要先推断用户到底想做什么。
例:
- 开空调。
- 把温度调到 24 度。
- 关车窗。
- 播放周杰伦的歌。
- 帮我定明天早上 9 点的闹钟。
感受词不覆盖明确目标:
- “太冷了,调一下空调”仍是快。
- “调一个浪漫点的氛围灯”仍是快。
### 简单条件与非标准称呼
- 单一明确目标可以带位置、距离、评分、附近、风格等简单修饰。
- 非标准称呼只要能唯一映射到对象,并且动作明确,仍为快。
例:
- 导航去附近评分 4.5 以上的泰国菜。
- 导航去附近最大的停车场。
- 那个压线噔噔的声音关掉。
不要把“有筛选词”或“名称不标准”直接当成慢。只有需要权衡多个方案、外部事实验证、真实澄清或多步规划时才转慢。
### 操作流中的直接动作
- 删除途经点。
- 切换导航播报。
- 切换路线。
- 下一首、暂停、声音调大。
- 高频且答案唯一的工具查询,例如“现在几点”。
### 多个明确设备控制
多个动作全部为明确设备控制时仍标快:
- 把空调调到 24 度,然后打开座椅加热。
- 关客厅灯并打开空调。
跨工具编排、查询后再决定、先推荐再执行不适用该特例。
## 慢的规则
命中以下任一决定性条件时标 `慢`
### 意向反推
用户只表达主观感受、身体状态、情绪、不满或潜在需求,没有给出可直接识别的操作目标,系统必须反推要做什么。
例:
- 我感觉好冷快冻死了,帮我调一下。
- 脚底下好冷,腿快冻麻了。
- 屏幕太亮有点刺眼,帮我弄一下。
对照:
- “太冷了,调一下空调”有明确对象和动作,标快。
### 场景或目标状态映射
用户描述结果应是什么样,而不是要执行什么动作;系统必须把场景映射为一个或多个功能。
例:
- 后排宝宝睡着了,把车里调成适合睡觉的状态。
- 今天情人节,帮我营造点节日氛围。
- 帮我把房间布置得舒适温馨。
### 规划、推理和多步编排
需要先决定方案,再执行;或需要多步计算、数值换算、跨源聚合、复杂比较、多工具承接。
例:
- 导航到主目的地,路上找充电站,再看看终点附近哪里好停车。
- 找王菲在春晚与别人合唱的歌,再用网易云播放。
- 查天气再决定要不要开窗。
- 做一个介绍 AI4S 的 PPT。
### 真实消歧、补槽和上下文推理
- 存在多个可行对象,必须追问才能选择。
- 缺少执行所必需且不能稳定默认的槽位。
- 当前短句必须结合前轮才能确定对象或字段。
- 上一轮慢系统已发起确认,当前轮为确认、取消或继续。
例:
- “打开那个灯”,且场景里存在多个灯。
- 前轮慢系统询问是否开启湿滑模式;当前轮“帮我开启”。
不要把非标准称呼、口语改口或简单补充一律视为真实消歧。
### 复杂选择
单个简单筛选条件可快;需要综合主观体验、实时状态、风险、外部事实或多个相互权衡的条件时标慢。
例:
- 推荐一家安静、适合亲子、评分高、停车方便且别排太久的酒店。
- 找一个冷门但不能踩雷、出餐快且人不多的餐厅。
## 模糊的规则
### 纯信息型
以下纯信息请求标 `模糊`
- 闲聊、聊天和开放问答。
- 产品功能、规格、概念、规则、使用方法等静态知识。
- 报异常、问故障、问原因、要排查建议。
- 设备状态或环境信息查询,无控制副作用。
例:
- 给我讲个笑话。
- 为什么天空是蓝色的?
- 小米 SU7 支持哪些驾驶模式?
- 哨兵模式是什么意思?
- 我的车怎么突然没声音了?
- 家里哪个房间最热?
- 空调已经开了多久?
### 不要扩大模糊档
- 现在几点、高频导航状态等唯一且期望即时的工具查询可标快。
- 需要制作复杂产物或多步分析的信息任务标慢。
- 执行对象不明且需要澄清时通常标慢,不要仅因“有歧义”就标模糊。
### 策略不完整
如果缺失的端侧或场景信息会导致正式规则给出不同标签,可标 `模糊`,并设置低置信和人工复核。不要把普通的模型犹豫都标成模糊。
## 上下文、多指令和聚合
### 上下文
- 只在输入真实提供前轮时使用。
- 区分“继承已建立的直接操作流”和“必须推理上文才能理解”。
- 上一轮慢系统主动追问后的确认/取消继续走慢。
- 上一轮快时,当前简单直接动作可继续快;复杂补充转慢。
- 缺少前轮而当前短句不可独立理解时,标慢并复核;若端侧政策本身不确定,可标模糊并复核。
### 多指令
- 全部为明确设备控制动作:快。
- 含查询后判断、跨 App、跨工具、规划或生成:慢。
- 可独立拆分路由时先对子请求分别标注,再保留聚合标签。
### 聚合
默认使用风险主导顺序:
```text
慢 > 模糊 > 快
```
只对“全部为明确设备控制动作”应用快特例。
## 垂域补充
### 导航和生活服务
- 单地点、目标明确、简单条件筛选:快。
- 地图操作和高频导航问答:快。
- 多地点、多动作路线编排、复杂综合决策、外部事实定位:慢。
- “评分 4.5 以上的泰国菜”按最终汇总标快,覆盖调研草案中的慢标签。
### 车控、系统控制和 IoT
- 明确设备、功能和动作:快。
- 只有感受、场景目标、复杂排除或位置集合推理:慢。
- 非标准功能名但可唯一识别且动作明确:快。
- 设备或环境状态查询:模糊。
### 内容和媒体
- 精确播放、明确资源、直接播控:快。
- 描述性搜歌、歌词片段、场景推荐、歌单编排、收藏搜索、复杂内容承接:慢。
- 纯内容知识问答按信息型处理,通常模糊。
### 通用工具
- 原子化且结果唯一的查询或控制:快。
- 多步换算、结合外部信息再加工、缺关键槽位、开放生成:慢。
- 不要仅因需要联网或 parser 暂不支持就标慢。
### 自动化
- 音箱端家庭自动化默认慢。
- 音箱定时任务和意向化自动化慢。
- 车载闹钟或提醒按文档专项策略可快。
- 缺端侧且端侧会改变标签时,标模糊并复核。
## 原因码
每条记录选择一个决定性的 `primary_reason_code`,并在 `reason_codes` 中补充其他命中原因。
### 快
| 原因码 | 含义 |
| --- | --- |
| `DIRECT_ACTION` | 动作和目标直接可识别 |
| `DIRECT_DETERMINISTIC_QUERY` | 高频、唯一、确定性的直接查询 |
| `EXPLICIT_DEVICE_MULTI_ACTION` | 多个动作全部为明确设备控制 |
| `DIRECT_OPERATION_FLOW` | 已建立操作流中的直接操作或播控 |
### 慢
| 原因码 | 含义 |
| --- | --- |
| `INTENT_INFERENCE` | 从感受或不满反推动作 |
| `SCENE_GOAL_MAPPING` | 将场景或目标状态映射为动作组合 |
| `MULTI_STEP_PLANNING` | 多步骤、多工具或任务编排 |
| `COMPLEX_REASONING` | 换算、聚合、外部验证或复杂比较 |
| `TRUE_DISAMBIGUATION` | 存在真实多候选,必须消歧 |
| `CONTEXT_REQUIRED` | 必须依赖前轮才能决定 |
| `MISSING_REQUIRED_SLOT` | 缺关键且不可默认的执行槽位 |
| `AUTOMATION_SLOW_POLICY` | 命中明确的慢自动化专项策略 |
### 模糊
| 原因码 | 含义 |
| --- | --- |
| `OPEN_CHAT_OR_QA` | 闲聊或开放问答 |
| `PRODUCT_KNOWLEDGE` | 产品静态知识 |
| `FAULT_DIAGNOSIS` | 故障、异常、原因或排查建议 |
| `DEVICE_OR_ENV_STATE_QUERY` | 无副作用的设备或环境状态查询 |
| `POLICY_UNCERTAIN` | 缺少会改变正式策略的端侧或规则信息 |
## 能力与默认路由
固定派生:
| label | default_route |
| --- | --- |
| 快 | 快系统 |
| 慢 | 慢系统 |
| 模糊 | 慢系统 |
`fast_system_pending`
- `true`:已有证据证明本应为快,但快系统尚未承接。
- `false`:已有能力清单或验证证明已承接。
- `null`:未评估能力;默认使用该值。
不得因为 `fast_system_pending=true` 修改 `label``default_route`
@@ -0,0 +1,293 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://local.skills/label-fast-slow-routing/label-record.schema.json",
"title": "快慢分流打标记录",
"type": "object",
"additionalProperties": false,
"required": [
"query",
"label",
"default_route",
"primary_reason_code",
"reason_codes",
"reason",
"evidence",
"context_used",
"fast_system_pending",
"confidence",
"review_required",
"uncertainties",
"policy_version"
],
"properties": {
"id": {
"type": ["string", "number", "null"]
},
"query": {
"type": "string",
"minLength": 1
},
"label": {
"enum": ["快", "慢", "模糊"]
},
"default_route": {
"enum": ["快系统", "慢系统"]
},
"primary_reason_code": {
"$ref": "#/$defs/reasonCode"
},
"reason_codes": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/reasonCode"
}
},
"reason": {
"type": "string",
"minLength": 1
},
"evidence": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
},
"counterevidence": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"domain_label": {
"type": ["string", "null"]
},
"candidate_domain_labels": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
},
"structural_signals": {
"type": "object",
"additionalProperties": false,
"required": ["complex", "multi_instruction", "auto_task"],
"properties": {
"complex": {
"type": ["boolean", "null"]
},
"multi_instruction": {
"type": ["boolean", "null"]
},
"auto_task": {
"type": ["boolean", "null"]
}
}
},
"context_used": {
"type": "boolean"
},
"fast_system_pending": {
"type": ["boolean", "null"]
},
"legacy_eval_label": {
"enum": [
"FAST_DIRECT",
"SLOW_FILTER_RANK",
"SLOW_DISAMBIGUATE",
"SLOW_GOAL_STATE",
"SLOW_WORKFLOW",
"SLOW_CONTEXT_DEPENDENT",
"SLOW_UNCLEAR",
"SLOW_ADVANCED_CAPABILITY",
null
]
},
"confidence": {
"enum": ["high", "medium", "low"]
},
"review_required": {
"type": "boolean"
},
"uncertainties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"policy_version": {
"const": "XA4.1-2026.6"
}
},
"allOf": [
{
"if": {
"properties": {
"label": {
"const": "快"
}
},
"required": ["label"]
},
"then": {
"properties": {
"default_route": {
"const": "快系统"
},
"primary_reason_code": {
"enum": [
"DIRECT_ACTION",
"DIRECT_DETERMINISTIC_QUERY",
"EXPLICIT_DEVICE_MULTI_ACTION",
"DIRECT_OPERATION_FLOW"
]
},
"reason_codes": {
"items": {
"enum": [
"DIRECT_ACTION",
"DIRECT_DETERMINISTIC_QUERY",
"EXPLICIT_DEVICE_MULTI_ACTION",
"DIRECT_OPERATION_FLOW"
]
}
}
}
}
},
{
"if": {
"properties": {
"label": {
"const": "慢"
}
},
"required": ["label"]
},
"then": {
"properties": {
"default_route": {
"const": "慢系统"
},
"fast_system_pending": {
"enum": [false, null]
},
"primary_reason_code": {
"enum": [
"INTENT_INFERENCE",
"SCENE_GOAL_MAPPING",
"MULTI_STEP_PLANNING",
"COMPLEX_REASONING",
"TRUE_DISAMBIGUATION",
"CONTEXT_REQUIRED",
"MISSING_REQUIRED_SLOT",
"AUTOMATION_SLOW_POLICY"
]
},
"reason_codes": {
"items": {
"enum": [
"INTENT_INFERENCE",
"SCENE_GOAL_MAPPING",
"MULTI_STEP_PLANNING",
"COMPLEX_REASONING",
"TRUE_DISAMBIGUATION",
"CONTEXT_REQUIRED",
"MISSING_REQUIRED_SLOT",
"AUTOMATION_SLOW_POLICY"
]
}
}
}
}
},
{
"if": {
"properties": {
"label": {
"const": "模糊"
}
},
"required": ["label"]
},
"then": {
"properties": {
"default_route": {
"const": "慢系统"
},
"fast_system_pending": {
"enum": [false, null]
},
"primary_reason_code": {
"enum": [
"OPEN_CHAT_OR_QA",
"PRODUCT_KNOWLEDGE",
"FAULT_DIAGNOSIS",
"DEVICE_OR_ENV_STATE_QUERY",
"POLICY_UNCERTAIN"
]
},
"reason_codes": {
"items": {
"enum": [
"OPEN_CHAT_OR_QA",
"PRODUCT_KNOWLEDGE",
"FAULT_DIAGNOSIS",
"DEVICE_OR_ENV_STATE_QUERY",
"POLICY_UNCERTAIN"
]
}
}
}
}
},
{
"if": {
"properties": {
"confidence": {
"const": "low"
}
},
"required": ["confidence"]
},
"then": {
"properties": {
"review_required": {
"const": true
}
},
"required": ["review_required"]
}
}
],
"$defs": {
"reasonCode": {
"enum": [
"DIRECT_ACTION",
"DIRECT_DETERMINISTIC_QUERY",
"EXPLICIT_DEVICE_MULTI_ACTION",
"DIRECT_OPERATION_FLOW",
"INTENT_INFERENCE",
"SCENE_GOAL_MAPPING",
"MULTI_STEP_PLANNING",
"COMPLEX_REASONING",
"TRUE_DISAMBIGUATION",
"CONTEXT_REQUIRED",
"MISSING_REQUIRED_SLOT",
"AUTOMATION_SLOW_POLICY",
"OPEN_CHAT_OR_QA",
"PRODUCT_KNOWLEDGE",
"FAULT_DIAGNOSIS",
"DEVICE_OR_ENV_STATE_QUERY",
"POLICY_UNCERTAIN"
]
}
}
}
@@ -0,0 +1,352 @@
#!/usr/bin/env python3
"""Validate fast/slow/ambiguous label records without external dependencies."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
POLICY_VERSION = "XA4.1-2026.6"
REASON_CODES_BY_LABEL: dict[str, set[str]] = {
"": {
"DIRECT_ACTION",
"DIRECT_DETERMINISTIC_QUERY",
"EXPLICIT_DEVICE_MULTI_ACTION",
"DIRECT_OPERATION_FLOW",
},
"": {
"INTENT_INFERENCE",
"SCENE_GOAL_MAPPING",
"MULTI_STEP_PLANNING",
"COMPLEX_REASONING",
"TRUE_DISAMBIGUATION",
"CONTEXT_REQUIRED",
"MISSING_REQUIRED_SLOT",
"AUTOMATION_SLOW_POLICY",
},
"模糊": {
"OPEN_CHAT_OR_QA",
"PRODUCT_KNOWLEDGE",
"FAULT_DIAGNOSIS",
"DEVICE_OR_ENV_STATE_QUERY",
"POLICY_UNCERTAIN",
},
}
DEFAULT_ROUTE_BY_LABEL = {
"": "快系统",
"": "慢系统",
"模糊": "慢系统",
}
LEGACY_LABELS = {
"FAST_DIRECT",
"SLOW_FILTER_RANK",
"SLOW_DISAMBIGUATE",
"SLOW_GOAL_STATE",
"SLOW_WORKFLOW",
"SLOW_CONTEXT_DEPENDENT",
"SLOW_UNCLEAR",
"SLOW_ADVANCED_CAPABILITY",
}
CONFIDENCE_VALUES = {"high", "medium", "low"}
REQUIRED_FIELDS = {
"query",
"label",
"default_route",
"primary_reason_code",
"reason_codes",
"reason",
"evidence",
"context_used",
"fast_system_pending",
"confidence",
"review_required",
"uncertainties",
"policy_version",
}
OPTIONAL_FIELDS = {
"id",
"counterevidence",
"domain_label",
"candidate_domain_labels",
"structural_signals",
"legacy_eval_label",
}
def _is_nonempty_string(value: Any) -> bool:
return isinstance(value, str) and bool(value.strip())
def _check_string_list(
value: Any,
*,
field: str,
errors: list[str],
require_nonempty: bool = False,
unique: bool = False,
) -> None:
if not isinstance(value, list):
errors.append(f"{field} 必须是数组")
return
if require_nonempty and not value:
errors.append(f"{field} 至少包含一项")
if any(not _is_nonempty_string(item) for item in value):
errors.append(f"{field} 的每一项都必须是非空字符串")
if unique and all(isinstance(item, str) for item in value):
if len(value) != len(set(value)):
errors.append(f"{field} 不允许重复项")
def validate_record(record: Any, index: int) -> dict[str, Any]:
errors: list[str] = []
warnings: list[str] = []
if not isinstance(record, dict):
return {
"index": index,
"valid": False,
"errors": ["记录必须是 JSON 对象"],
"warnings": [],
}
if "__parse_error__" in record:
return {
"index": index,
"valid": False,
"errors": [str(record["__parse_error__"])],
"warnings": [],
}
missing = sorted(REQUIRED_FIELDS - set(record))
if missing:
errors.append(f"缺少必填字段: {', '.join(missing)}")
unknown = sorted(set(record) - REQUIRED_FIELDS - OPTIONAL_FIELDS)
if unknown:
errors.append(f"存在未知字段: {', '.join(unknown)}")
query = record.get("query")
if not _is_nonempty_string(query):
errors.append("query 必须是非空字符串")
label = record.get("label")
if label not in REASON_CODES_BY_LABEL:
errors.append("label 必须是 快、慢、模糊 之一")
default_route = record.get("default_route")
expected_route = DEFAULT_ROUTE_BY_LABEL.get(label)
if expected_route is not None and default_route != expected_route:
errors.append(f"label={label} 时 default_route 必须是 {expected_route}")
primary_reason_code = record.get("primary_reason_code")
reason_codes = record.get("reason_codes")
allowed_reasons = REASON_CODES_BY_LABEL.get(label, set())
if primary_reason_code not in allowed_reasons:
errors.append(
f"primary_reason_code={primary_reason_code!r} 与 label={label!r} 不兼容"
)
if not isinstance(reason_codes, list):
errors.append("reason_codes 必须是数组")
else:
if not reason_codes:
errors.append("reason_codes 至少包含一项")
if all(isinstance(code, str) for code in reason_codes):
if len(reason_codes) != len(set(reason_codes)):
errors.append("reason_codes 不允许重复")
invalid_reason_codes = [
code
for code in reason_codes
if not isinstance(code, str) or code not in allowed_reasons
]
if invalid_reason_codes:
rendered_codes = ", ".join(repr(code) for code in invalid_reason_codes)
errors.append(f"reason_codes 包含与 label 不兼容的值: {rendered_codes}")
if primary_reason_code not in reason_codes:
errors.append("primary_reason_code 必须同时出现在 reason_codes 中")
if not _is_nonempty_string(record.get("reason")):
errors.append("reason 必须是非空字符串")
_check_string_list(
record.get("evidence"),
field="evidence",
errors=errors,
require_nonempty=True,
)
if "counterevidence" in record:
_check_string_list(
record.get("counterevidence"),
field="counterevidence",
errors=errors,
)
if "domain_label" in record:
domain_label = record.get("domain_label")
if domain_label is not None and not _is_nonempty_string(domain_label):
errors.append("domain_label 必须是非空字符串或 null")
if "candidate_domain_labels" in record:
_check_string_list(
record.get("candidate_domain_labels"),
field="candidate_domain_labels",
errors=errors,
unique=True,
)
structural_signals = record.get("structural_signals")
if structural_signals is not None:
if not isinstance(structural_signals, dict):
errors.append("structural_signals 必须是对象")
else:
expected_keys = {"complex", "multi_instruction", "auto_task"}
actual_keys = set(structural_signals)
if actual_keys != expected_keys:
errors.append(
"structural_signals 必须且只能包含 "
"complex、multi_instruction、auto_task"
)
for key in expected_keys:
value = structural_signals.get(key)
if value is not None and not isinstance(value, bool):
errors.append(f"structural_signals.{key} 必须是布尔值或 null")
if not isinstance(record.get("context_used"), bool):
errors.append("context_used 必须是布尔值")
fast_system_pending = record.get("fast_system_pending")
if fast_system_pending is not None and not isinstance(fast_system_pending, bool):
errors.append("fast_system_pending 必须是布尔值或 null")
if fast_system_pending is True and label != "":
errors.append("只有 label=快 时 fast_system_pending 才能为 true")
legacy_eval_label = record.get("legacy_eval_label")
if (
"legacy_eval_label" in record
and legacy_eval_label is not None
and legacy_eval_label not in LEGACY_LABELS
):
errors.append("legacy_eval_label 不是受支持的旧八类标签")
if label == "模糊" and legacy_eval_label is not None:
warnings.append("模糊档通常无法无损映射到旧二值标签,请确认迁移依据")
confidence = record.get("confidence")
if confidence not in CONFIDENCE_VALUES:
errors.append("confidence 必须是 high、medium、low 之一")
review_required = record.get("review_required")
if not isinstance(review_required, bool):
errors.append("review_required 必须是布尔值")
if confidence == "low" and review_required is not True:
errors.append("confidence=low 时 review_required 必须为 true")
_check_string_list(
record.get("uncertainties"),
field="uncertainties",
errors=errors,
)
uncertainties = record.get("uncertainties")
if isinstance(uncertainties, list) and uncertainties and review_required is False:
warnings.append("uncertainties 非空但 review_required=false,请确认不确定点不影响标签")
if record.get("policy_version") != POLICY_VERSION:
errors.append(f"policy_version 必须是 {POLICY_VERSION}")
return {
"index": index,
"id": record.get("id"),
"valid": not errors,
"errors": errors,
"warnings": warnings,
}
def _load_records_from_file(path: Path) -> list[Any]:
text = path.read_text(encoding="utf-8-sig")
if path.suffix.lower() == ".jsonl":
records: list[Any] = []
for line_number, line in enumerate(text.splitlines(), start=1):
if not line.strip():
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError as exc:
records.append(
{
"__parse_error__": (
f"JSONL 第 {line_number} 行解析失败: {exc.msg}"
)
}
)
return records
payload = json.loads(text)
if isinstance(payload, list):
return payload
if isinstance(payload, dict) and isinstance(payload.get("records"), list):
return payload["records"]
return [payload]
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="校验 XA4.1-2026.6 快慢分流打标 JSON/JSONL"
)
source_group = parser.add_mutually_exclusive_group(required=True)
source_group.add_argument("--record", help="单条 JSON 对象字符串")
source_group.add_argument("--file", type=Path, help="JSON 或 JSONL 文件路径")
parser.add_argument(
"--strict",
action="store_true",
help="把 warning 也视为校验失败",
)
return parser.parse_args()
def main() -> int:
args = _parse_args()
try:
if args.record is not None:
records = [json.loads(args.record)]
else:
records = _load_records_from_file(args.file)
except (OSError, json.JSONDecodeError) as exc:
result = {
"valid": False,
"total": 0,
"invalid": 1,
"warning_count": 0,
"errors": [str(exc)],
"results": [],
}
print(json.dumps(result, ensure_ascii=False, indent=2))
return 1
results = [validate_record(record, index) for index, record in enumerate(records)]
invalid = sum(not result["valid"] for result in results)
warning_count = sum(len(result["warnings"]) for result in results)
valid = invalid == 0 and (not args.strict or warning_count == 0)
summary = {
"valid": valid,
"total": len(records),
"invalid": invalid,
"warning_count": warning_count,
"results": results,
}
print(json.dumps(summary, ensure_ascii=False, indent=2))
return 0 if valid else 1
if __name__ == "__main__":
sys.exit(main())