From 4c29ce21f14482059f23b1f359c4e0ec56207d63 Mon Sep 17 00:00:00 2001 From: wuyang6 Date: Mon, 11 May 2026 20:13:08 +0800 Subject: [PATCH] Make product-data generation resumable --- skills/product-data/README.md | 4 +++- skills/product-data/SKILL.md | 37 ++++++++++++++++++++++++------- src/agent_runtime.py | 2 +- src/agent_tool_specs/execution.py | 4 ++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/skills/product-data/README.md b/skills/product-data/README.md index 8ea7ed9..52ed636 100644 --- a/skills/product-data/README.md +++ b/skills/product-data/README.md @@ -50,7 +50,7 @@ cat input.json | python skills/product-data/scripts/.py ## 平台内使用方式 -在 ZK Data Agent 里,前链路输入文本化和 review 状态机仍使用平台注册的 `data_agent_*` 工具;格式转换、校验和导出使用 `python_exec` 直接执行本目录 `scripts/` 下的 portable scripts。 +在 ZK Data Agent 里,前链路输入文本化和 review 状态机仍使用平台注册的 `data_agent_*` 工具;格式转换、校验和导出使用 `python_exec` 的 `script_path` 模式直接执行本目录 `scripts/` 下的 portable scripts。不要在 `python_exec.code` 里通过 subprocess 二次调用这些脚本。 通过 `python_exec` 运行时,脚本会根据 `PYTHON_EXEC_SCRATCHPAD` 自动把逻辑路径 `output/...` 路由到当前会话 output 目录。独立运行时,仍按当前目录下的 `output/` 输出。 @@ -65,3 +65,5 @@ cat input.json | python skills/product-data/scripts/.py ``` 最后从 `scratchpad/normalized_records.jsonl` 统一校验和导出。 + +为避免长时间连续生成导致模型请求超时,单轮最多连续生成 3 批;超过 24 条时先阶段性停顿,用户回复“继续生成”后先统计 `scratchpad/normalized_records.jsonl` 已有行数,再从下一批 append。 diff --git a/skills/product-data/SKILL.md b/skills/product-data/SKILL.md index bb0f066..3513c6c 100644 --- a/skills/product-data/SKILL.md +++ b/skills/product-data/SKILL.md @@ -169,20 +169,25 @@ review 展示必须简短清晰,不要重复解释工具和流程。每次 rev 9. 用户提出修改意见时,调用 `data_agent_update_generation_plan`,再展示计划。 10. 用户明确确认当前计划版本后,调用 `data_agent_confirm_generation_plan`。 11. 分批生成 dataset draft text v1,每批最多 8 条,不要用 `write_file` 保存 `draft_part*.txt` 这类中间草稿。 -12. 每生成一批,立刻使用 `python_exec` 执行 `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`。 -13. 所有批次 normalize 完成后,使用 `python_exec` 执行 `skills/product-data/scripts/validate_dataset_records.py`,传 `records_path="scratchpad/normalized_records.jsonl"`。 -14. 如果用户要求落盘 canonical records,使用 `python_exec` 执行 `skills/product-data/scripts/export_dataset_records.py`,逻辑输出固定为 `output/records.jsonl`,默认导出紧凑 JSONL,并同时生成同目录 `records.csv` 表格,不要用 `write_file` 手写 JSON 或 CSV。 -15. 如果用户已经明确要训练数据,使用 `python_exec` 执行 `skills/product-data/scripts/export_training_jsonl.py`,优先传 `records_path`,输出固定为 `output/training.jsonl`。 -16. 如果用户已经明确要评测 planningPrompt 数据,使用 `python_exec` 执行 `skills/product-data/scripts/export_planning_eval_csv.py`,优先传 `records_path`,输出固定为 `output/eval_planning.csv`。 -17. 如果用户只说“生成数据”但没有说明下游用途,生成 canonical records 和 `records.csv` 后,简短询问用户是否还需要导出训练 jsonl 或评测 csv;不要自己默认生成全部最终格式。 +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. 单轮最多连续生成并 normalize 3 批;如果计划还没完成,先停止并汇报“已生成 X/Y 条,回复继续生成可从下一批继续”,不要在同一轮里继续生成第 4 批。 +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`,再统一校验和导出。 +- 单轮最多推进 3 批,超过 24 条的数据要做阶段性停顿,等待用户回复“继续生成”再继续;这是为了避免模型长时间连续生成导致请求超时。 +- 继续生成前必须先统计 `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 后找不到脚本。 ## 数据生成输出格式 @@ -357,6 +362,14 @@ canonical records 落盘必须使用 `export_dataset_records.py`,默认格式 生成计划确认后,格式转换、校验和导出一律使用 `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="", timeout_seconds=60) +``` + +不要在 `python_exec.code` 里通过 `subprocess` 二次调用这些脚本;`python_exec.code` 的当前目录通常是会话 scratchpad,相对路径容易解析错。 + ### `product_data_normalize_dataset_draft` 脚本: @@ -401,9 +414,17 @@ skills/product-data/scripts/normalize_dataset_draft.py 调用方式示例: ```text -python_exec(script_path="skills/product-data/scripts/normalize_dataset_draft.py", stdin="<上面的 JSON>") +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` 脚本: @@ -521,7 +542,7 @@ skills/product-data/scripts/export_planning_eval_csv.py - `grep_search`:在项目中搜索已有标签定义、历史数据样例或相关文档。 - `glob_search`:按路径模式查找定义文件、样例文件或历史产物。 - `ask_user_question`:需要用户明确选择或补充关键信息时使用;如果不可用,就用普通回复提问并停止。 -- `python_exec`:执行 `skills/product-data/scripts/` 下的 portable scripts;格式转换、校验、导出必须优先用它,不要用 `bash` 执行 Python。 +- `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、预期标签、模型预测、上下文和备注;字段歧义会返回需要确认的问题。 diff --git a/src/agent_runtime.py b/src/agent_runtime.py index 8ee0243..1a12cc6 100644 --- a/src/agent_runtime.py +++ b/src/agent_runtime.py @@ -1602,7 +1602,7 @@ class LocalCodingAgent: f'原因:{exc.msg};参数片段:{preview!r}。' '请回复“继续”,我会要求模型重新生成合法 JSON 参数;' '对于 product-data 生成数据,不要用 write_file 写大段 draft;' - '应每批最多 8 条,直接用 python_exec 调用 ' + '应每批最多 8 条,直接用 python_exec 的 script_path 模式调用 ' 'skills/product-data/scripts/normalize_dataset_draft.py,' '通过 stdin 传入小批量 JSON,并把 normalize 结果追加到 ' 'scratchpad/normalized_records.jsonl。' diff --git a/src/agent_tool_specs/execution.py b/src/agent_tool_specs/execution.py index 76cd163..2a72979 100644 --- a/src/agent_tool_specs/execution.py +++ b/src/agent_tool_specs/execution.py @@ -16,7 +16,8 @@ def build_execution_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentTool '优先用本工具执行小型 Python 代码或项目内 Python 脚本,用于结构化文件分析、' 'JSON/JSONL 处理、批量校验、数据抽样和快速计算。默认使用当前用户独立 Python venv;' '不要用 bash 运行 python/python3/.venv/bin/python。不要用本工具安装依赖。' - '缺包时应向用户确认后再处理依赖。一次性分析优先传 code;如需写临时文件,' + '缺包时应向用户确认后再处理依赖。一次性分析优先传 code;项目或 skill 内已有脚本优先传 script_path,' + '不要在 code 里用 subprocess 二次调用 Python 脚本。如需写临时文件,' '必须写入环境变量 PYTHON_EXEC_SCRATCHPAD 指向的会话隔离目录,' '不要在项目根目录创建临时 .py 脚本。' ), @@ -120,4 +121,3 @@ def build_execution_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentTool handler=resolve_handler(handlers, 'sleep', 'execution'), ), ] -