---
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, python_exec, 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
---

使用这个 skill 作为“产品/标签定义/手写规则/示例 query -> 输入文本化 -> generation goal 草案 -> 人工 review -> 生成计划 review -> dataset draft text -> canonical metadata records”的统一入口。

本 skill 内置数据记录生成协议。其他数据开发 skill 如果需要生成或整理标准数据，可以复用这里的“交互门禁、dataset draft text v1、canonical record v1”规则。

## 能力组织

本 skill 按 portable skill contract 组织，能力本体在 skill 目录内：

```text
skills/product-data/
  SKILL.md
  tools.yaml
  requirements.txt
  knowledge/
    dataset_draft_v1.md
    canonical_record_v1.md
    portable_skill_contract.md
  schemas/
  scripts/
    normalize_dataset_draft.py
    validate_dataset_records.py
    export_dataset_records.py
    export_dataset_table.py
    export_training_jsonl.py
    export_planning_eval_csv.py
```

在 ZK Data Agent 平台里，前链路输入文本化和 review 状态机仍使用已注册的 `data_agent_*` 工具；数据格式转换、校验和导出一律使用 `python_exec` 执行本 skill 的 `scripts/`。

迁移到其他 Agent 或没有平台注册能力时，可以直接执行 `scripts/` 下的 portable scripts。脚本支持 `--input input.json` 或 stdin JSON，stdout 只输出一个 JSON 对象。`tools.yaml` 描述这些脚本如何被其他平台注册为工具；当前 ZK Data Agent 主流程不再依赖这些格式转换类平台注册工具。

脚本能力和平台注册的关系是：

```text
脚本能力是本体
平台注册是适配层
```

portable scripts 不包含人类 review 状态机。生成式数据仍必须先由 Agent 按本 skill 的 review 门禁获得用户确认，再调用脚本做格式转换、校验和导出。

## 输入假设

用户可能会提供：

- 产品定义文档、标签定义文档、路由规则文档。
- 表格、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` 列出每个标签和判定规则。
- `complex` 判定规则：复杂度是独立维度，必须和标签边界一起确认；如果用户没有说明复杂/不复杂，默认按“原子化单步操作=false，需要规划、分析、组合或多步骤推理=true”给出建议，并在 review 中让用户确认。
- 生成数量：总条数，以及单轮/多轮数量或比例。
- 覆盖范围：需要覆盖哪些 query 类型、意图边界或错误类型。
- 负例/排除项：哪些表达不要生成，或哪些边界容易误判。
- 落盘路径：canonical records 固定使用 `output/records.jsonl`，由工具路由到当前会话 output 目录。

如果任一信息缺失，不要生成数据，不要调用 `data_agent_prepare_generation_plan`，不要执行 portable scripts，只向用户提出需要确认的问题。

两段确认模式下，信息完整后，调用 `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，并继续调用工具。

portable scripts 不维护平台 review 状态。生成式数据必须先通过 `data_agent_confirm_generation_plan` 取得 `confirmed_plan_id`，再执行格式转换脚本；调用 normalize 脚本时在输入 JSON 中携带 `confirmed_plan_id` 方便追踪。

如果用户在原始需求里已经写出 `Agent(tag="xxx")`、function 调用或其他完整标签表达，`target` 必须原样保留这个完整表达，不要简化成纯标签名。例如用户说 `Agent(tag="餐饮服务")`，则 `target_definitions[*].target` 和后续 draft 的 `target:` 都必须写 `Agent(tag="餐饮服务")`，不要写成 `餐饮服务`。

如果用户给的是训练格式里的两行输出，例如 `complex=false\nAgent(tag="地图导航")`，需要拆开处理：`complex=false` 进入复杂度维度，`Agent(tag="地图导航")` 才是 `target`。不要把 `complex=...` 作为 target 的一部分写入 generation plan。

review 展示必须简短清晰，不要重复解释工具和流程。每次 review 最多展示 6 行，格式优先如下：

```text
我先把生成目标整理好了，先确认边界，暂时不生成数据。
- 数据集：xxx
- 标签：A -> Agent(tag="A")；B -> Agent(tag="B")
- 复杂度：默认 false；复杂任务按规则单独标 true
- 边界：一句话说明核心判定规则
- 覆盖：一句话说明主要 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，不要在 canonical records 校验通过前导出训练/评测格式，不要绕过人类 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，每批最多 8 条，不要用 `write_file` 保存 `draft_part*.txt` 这类中间草稿。
12. 如果是继续一个已确认但中断/超时的生成任务，先检查 `scratchpad/normalized_records.jsonl` 是否已存在；存在时先用 `python_exec` 统计已有记录数，然后从下一批继续追加，禁止覆盖已有记录。
13. 每生成一批，立刻使用 `python_exec` 的 `script_path` 模式执行 `skills/product-data/scripts/normalize_dataset_draft.py`：通过 `stdin` 传入小批量 JSON，必须带 `confirmed_plan_id`、`records_output_path="scratchpad/normalized_records.jsonl"`、`append=true`、`return_records=false`；draft 中每条 case 都必须有 `complex: true/false`。
14. 批次数超过 1 时，每批工具调用前必须用一句阶段说明标记进度，格式固定为：`进度：批 i/n，已生成 x/y，主题`。例如：`进度：批 3/7，已生成 16/50，路线偏好追加`。摘要行会优先展示这个格式。
15. 所有批次 normalize 完成后，使用 `python_exec` 执行 `skills/product-data/scripts/validate_dataset_records.py`，传 `records_path="scratchpad/normalized_records.jsonl"`。
16. 如果用户要求落盘 canonical records，使用 `python_exec` 执行 `skills/product-data/scripts/export_dataset_records.py`，逻辑输出固定为 `output/records.jsonl`，默认导出紧凑 JSONL，并同时生成同目录 `records.csv` 表格，不要用 `write_file` 手写 JSON 或 CSV。
17. 如果用户已经明确要训练数据，使用 `python_exec` 执行 `skills/product-data/scripts/export_training_jsonl.py`，优先传 `records_path`，输出固定为 `output/training.jsonl`。
18. 如果用户已经明确要评测 planningPrompt 数据，使用 `python_exec` 执行 `skills/product-data/scripts/export_planning_eval_csv.py`，优先传 `records_path`，输出固定为 `output/eval_planning.csv`。
19. 如果用户只说“生成数据”但没有说明下游用途，生成 canonical records 和 `records.csv` 后，简短询问用户是否还需要导出训练 jsonl 或评测 csv；不要自己默认生成全部最终格式。

生成阶段要避免一次性把大量数据塞进工具参数：

- 单次 `normalize_dataset_draft.py` 最多处理 8 条 case；计划数量更多时，分批生成、分批 normalize 到同一个 `scratchpad/normalized_records.jsonl`，再统一校验和导出。
- 不要因为批次数多就中途停止等待用户继续；用户确认计划后，除非遇到工具错误、模型错误、用户取消或标签/边界歧义，否则在同一轮里把所有批次生成、校验和导出跑完。
- 每个批次工具调用前的自然语言进度必须使用 `进度：批 i/n，已生成 x/y，主题`，让 Web 摘要行能展示当前进度。
- 继续生成前必须先统计 `scratchpad/normalized_records.jsonl` 的现有行数；如果已有记录，后续 normalize 必须 `append=true`，不要重新从第 1 批覆盖。
- dataset draft 中的 Agent target 推荐写成单引号形式，例如 `target: Agent(tag='地图导航')`。工具会规范化为 `Agent(tag="地图导航")`，这样可以降低 tool call JSON 里双引号转义失败的概率。
- `complex:` 独立写一行，不要写进 `target:`；工具会在导出训练数据和流转表格时自动组合成 `complex=false\nAgent(...)`。
- 不要调用 `write_file` 保存模型新生成的 dataset draft；只有用户已经提供 draft 文件时，才使用 `draft_path`。
- 校验和导出 records 时，优先传 `records_path="scratchpad/normalized_records.jsonl"`，不要把大量 records 数组塞进工具参数。
- 不要在 `python_exec.code` 中再用 `subprocess.run([...normalize_dataset_draft.py])` 调脚本；执行 product-data 脚本时直接使用 `python_exec` 的 `script_path` 参数，避免相对路径落到 scratchpad 后找不到脚本。

## 数据生成输出格式

当需要生成数据样本时，默认使用 dataset draft text v1，不要直接输出 JSON、JSONL、CSV 或最终表格格式。除非用户明确要求机器可读格式，否则优先输出便于人工 review 的文本格式。

### 格式

```text
# dataset_label: 数据集或专题名称

### case: case名称
用户: 本轮 query
complex: false
target: Agent(tag="xxx")
notes: 可选，说明覆盖的问题或边界

### case: 多轮 case 名称
用户: 前一轮 query
小爱: 前一轮 tts
用户: 本轮 query
complex: false
target: Agent(tag="xxx")
notes: 可选，说明覆盖的问题或边界
```

### 规则

- 每条数据用一个 `### case:` 开始。
- `用户:` 表示用户 query。
- `小爱:` 表示小爱回复 tts。
- 最后一个 `用户:` 是本轮 query。
- `complex:` 是复杂度维度，必须独立填写 `true` 或 `false`；无法确定时先问用户。
- `target:` 是本轮 query 对应的监督标签，必须存在。
- 为避免工具参数 JSON 转义失败，dataset draft 里 Agent 标签优先写 `target: Agent(tag='xxx')`；转换工具会统一规范为 `Agent(tag="xxx")`。
- 单轮数据只需要写一行 `用户:`，然后写 `complex:` 和 `target:`。
- 多轮数据需要按照时间顺序写多组 `用户:` / `小爱:`。
- 多轮数据的最后一轮只写 `用户:`、`complex:` 和 `target:`，不要写最后一轮 `小爱:`，因为本轮 query 不包含 tts。
- 如果 `target` 无法确定，不要编造，必须向用户确认。
- 不要手写 `record_id`、`request_id`、`timestamp`、`context`。
- 线上挖掘数据如有真实 `request_id` 和 `timestamp`，可以附加在 case 中；没有则不写。

## Canonical Record

`normalize_dataset_draft.py` 会把 dataset draft text 转成 canonical records，并统一补齐 `record_id`、`source`、`timestamp`、`context`、`target_type` 等机械字段。

canonical records 落盘必须使用 `export_dataset_records.py`，默认格式是紧凑 JSONL：一行一个 canonical record，不带外层数组，不手写缩进 JSON。默认 `output_path` 固定传 `output/records.jsonl`；不要传 `output/<数据集名>/records.jsonl`，不要传 `tasks/...`，不要自定义文件名。脚本默认同时在同目录生成 `records.csv`，用于同事之间流转和表格查看。只有用户明确要求兼容旧文件时，才使用 `output_format="json"` 导出紧凑 JSON 数组，此时输出为 `output/records.json`，但表格仍然是同目录 `records.csv`。

派生格式也必须从 canonical records 转换，不要让模型手写：

- 训练数据：执行 `export_training_jsonl.py`，输出 `training.jsonl`，每行包含 `system`、`instruction`、`output`。
- 评测数据：执行 `export_planning_eval_csv.py`，输出 `eval_planning.csv`，字段为 `request_id,newPrompt,query,类别真实标签,code标签,complex`。
- 训练 `output` 和流转表格 `function` 列都会自动组合为两行：第一行 `complex=true/false`，第二行原监督标签；评测 `complex` 列直接来自 canonical record 的 `dimensions.complex`，按评测表习惯输出 `TRUE/FALSE`。
- 训练 `instruction` 和评测 `newPrompt` 复用相同 prompt 主体：`[知识注入]`、`[系统状态]`、`[对话历史]`、`[当前query]`、`[function]`；评测 `newPrompt` 还会额外包上 `<|im_start|>system`、`<|im_start|>user`、`<|im_start|>assistant` chat template。
- 对话历史默认最多取 5 轮，且相邻轮间隔不超过 5 分钟；`context` 默认注入 `location` 和 `rag` 两个字段。

当前 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"
  },
  "dimensions": {
    "complex": false
  },
  "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`，此时必须向用户确认字段含义。

## Portable Scripts

生成计划确认后，格式转换、校验和导出一律使用 `python_exec` 执行这些脚本。不要用 `bash` 执行 Python。脚本在平台内会自动把逻辑路径 `output/...` 路由到当前会话的 output 目录；独立运行时则写入当前目录下的 `output/`。

调用本目录脚本时，必须优先使用 `python_exec` 的 `script_path` 模式，例如：

```text
python_exec(script_path="skills/product-data/scripts/normalize_dataset_draft.py", stdin="<JSON>", timeout_seconds=60)
```

不要在 `python_exec.code` 里通过 `subprocess` 二次调用这些脚本；`python_exec.code` 的当前目录通常是会话 scratchpad，相对路径容易解析错。

### `product_data_normalize_dataset_draft`

脚本：

```text
skills/product-data/scripts/normalize_dataset_draft.py
```

输入示例：

```json
{
  "draft_text": "# dataset_label: 地图餐饮边界\n\n### case: 找附近美食\n用户: 附近有什么好吃的\ncomplex: false\ntarget: Agent(tag='餐饮服务')",
  "batch_id": "aabbccdd",
  "source_type": "generated",
  "confirmed_plan_id": "data_plan_000001"
}
```

如果草稿已经落盘，也可以传：

```json
{"draft_path": "scratchpad/dataset_draft.txt", "batch_id": "aabbccdd", "source_type": "generated", "confirmed_plan_id": "data_plan_000001"}
```

用途：把 dataset draft text v1 转成 canonical records。每条 case 推荐包含 `complex: true/false`；旧草稿缺失时会按 `false` 兼容。

大批量生成时必须把每批 normalize 结果追加到同一个 records 文件，避免大段 records 在模型上下文和工具参数里来回传递：

```json
{
  "draft_text": "# dataset_label: 地图导航\n\n### case: ...",
  "batch_id": "map_nav_batch_01",
  "source_type": "generated",
  "confirmed_plan_id": "data_plan_000001",
  "records_output_path": "scratchpad/normalized_records.jsonl",
  "append": true,
  "return_records": false
}
```

调用方式示例：

```text
python_exec(script_path="skills/product-data/scripts/normalize_dataset_draft.py", stdin="<上面的 JSON>", timeout_seconds=60)
```

继续中断任务时，先统计已有 records：

```text
python_exec(code="import os\nfrom pathlib import Path\np=Path(os.environ.get('PYTHON_EXEC_SCRATCHPAD', '.'))/'normalized_records.jsonl'\nprint(sum(1 for _ in p.open(encoding='utf-8')) if p.exists() else 0)")
```

如果已有记录数大于 0，下一批必须设置 `append=true`，并从下一批覆盖点继续生成。

### `product_data_validate_dataset_records`

脚本：

```text
skills/product-data/scripts/validate_dataset_records.py
```

输入支持：

```json
{"records": []}
```

或：

```json
{"records_path": "output/records.jsonl"}
```

用途：校验 canonical records。

### `product_data_export_dataset_records`

脚本：

```text
skills/product-data/scripts/export_dataset_records.py
```

输入支持：

```json
{
  "records": [],
  "output_dir": "output",
  "output_format": "jsonl"
}
```

用途：导出紧凑 JSONL/JSON。默认文件名是 `records.jsonl` 或 `records.json`。

默认还会生成同目录 `records.csv` 表格，字段固定为：

```text
request_id,timestamp,query,prev_session,context,label,是否迁移Function,function
```

其中 `function` 列会自动组合为 `complex=true/false` 和原监督标签两行。

### `product_data_export_dataset_table`

脚本：

```text
skills/product-data/scripts/export_dataset_table.py
```

输入支持：

```json
{
  "records_path": "output/records.jsonl",
  "output_dir": "output"
}
```

用途：已有 canonical records 时，只补生成同事流转表格 `records.csv`，不改写元数据文件。主流程仍优先用 `product_data_export_dataset_records`，因为它会一次性导出元数据和表格。

### `product_data_export_training_jsonl`

脚本：

```text
skills/product-data/scripts/export_training_jsonl.py
```

输入支持：

```json
{
  "records_path": "output/records.jsonl",
  "output_dir": "output"
}
```

用途：把 canonical records 转成训练 JSONL，默认文件名 `training.jsonl`。
输出的 `output` 字段会自动组合 `complex=true/false` 和 `label.target` 两行。

### `product_data_export_planning_eval_csv`

脚本：

```text
skills/product-data/scripts/export_planning_eval_csv.py
```

输入支持：

```json
{
  "records_path": "output/records.jsonl",
  "output_dir": "output"
}
```

用途：把 canonical records 转成含 `newPrompt` 的评测 CSV，默认文件名 `eval_planning.csv`。
`newPrompt` 会使用评测侧标准 chat template；`complex` 列来自 canonical records 的 `dimensions.complex`，输出为 `TRUE/FALSE`，不要用固定默认值覆盖。

## 当前可用工具

- `read_file`：读取用户提供的产品定义、标签定义、样例 query 文件。
- `write_file`：只用于写少量说明、用户明确要求保存的人工 review 备注或非数据型文档；禁止用它写 `draft_part*.txt`、`records.jsonl`、`records.csv`、`training.jsonl`、`eval_planning.csv` 等生成数据或派生格式。
- `edit_file`：修改已有的计划、说明文档或生成结果文件。
- `grep_search`：在项目中搜索已有标签定义、历史数据样例或相关文档。
- `glob_search`：按路径模式查找定义文件、样例文件或历史产物。
- `ask_user_question`：需要用户明确选择或补充关键信息时使用；如果不可用，就用普通回复提问并停止。
- `python_exec`：执行 `skills/product-data/scripts/` 下的 portable scripts；格式转换、校验、导出必须优先用它的 `script_path` 模式，不要用 `bash` 执行 Python，也不要在 `python_exec.code` 里用 subprocess 二次调用脚本。
- `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`。
- `normalize_dataset_draft.py`：用户确认计划后，把 dataset draft text v1 转成 canonical records；输入 JSON 必须带 `confirmed_plan_id`；模型新生成的数据优先用小批量 `draft_text` 直接 stdin 输入，设置 `records_output_path`、`append=true`、`return_records=false`；只有用户已提供草稿文件时才使用 `draft_path`。
- `validate_dataset_records.py`：对 canonical records 做结构、标签、时间戳和多轮上下文校验；支持 `records` 或 `records_path`。
- `export_dataset_records.py`：校验 canonical records 并落盘；支持 `records` 或 `records_path`；默认写紧凑 JSONL，一行一条，固定传 `output/records.jsonl`，并同时生成 `output/records.csv` 表格，不要再用 `write_file` 手写 records 或表格文件。
- `export_training_jsonl.py`：把 canonical records 转成训练 JSONL；支持 `records` 或 `records_path`；默认写 `output/training.jsonl`；`output` 自动包含 `complex` 行。
- `export_planning_eval_csv.py`：把 canonical records 转成评测 CSV；支持 `records` 或 `records_path`；默认写 `output/eval_planning.csv`；`complex` 列从元数据生成。

## 约束

- 不要静默解决产品或标签歧义。
- 如果 `ask_user_question` 不可用，使用普通回复向用户提问并停止，不要自己替用户确认。
- canonical records 通过校验前，不要生成最终导出格式。
- canonical records 需要落盘时，必须用 `python_exec` 执行 `export_dataset_records.py`；不要自己拼接 JSON/JSONL/CSV；不要创建数据集子目录或自定义 records 文件名。
- 训练/评测派生格式必须从 canonical records 通过工具导出；不要让模型自己拼 prompt、手写 jsonl 或 csv。
- 如果使用 portable scripts，它们只负责格式转换、校验和导出，不替代 `generation_goal` / `generation_plan` 的用户 review。
- 除非用户明确要求，否则不要把“修改标签定义”和“生成数据”混在一起做。
- 不要因为用户说“生成一些数据”就跳过边界总结和 generation plan review。
