run-batch-codex-research 是一个与具体 CLI 无关的批量扇出(fan-out)技能,核心作用是把一个提示词模板套用到 N 个输入上,通过 LLM 命令行工具(codex、claude、gemini、ollama 等)并行运行,最终为每个输入生成一个答案文件。其工作单元是 (template, input) → answers/<slug>.md。
【适用时机】 当请求中出现以下描述的意图时适用:对 urls.txt 中每个 URL 运行 codex;对列表中每个输入运行同一提示词;把模板扇出到 N 个 ID;把提示词批量跑在 CSV 条目上,每行一个答案文件;跨多个主题并行研究;对每个条目执行并保存输出;断点续跑,只重试失败项;跳过已完成输入保持幂等;把 codex 换成 claude/gemini/ollama 重跑同一批;限制最多 N 个并发 CLI 进程。不适用情形:单次独立的 codex 调用(应使用 run-codex-exec);单个技术研究问题与网页搜索(应使用 run-research);5 个以上实体、多维度证据语料(应使用 run-corpus-research);需要 worktree、manifests、Monitor 事件或 rescue 模式的纯 codex 集群(应使用 run-codex-2)。触发形态为:输入列表 + 提示词模板 + PATH 中的 LLM CLI,三者缺一不可。
【核心规则】
- 每个输入一个答案文件,路径 <workdir>/answers/<slug>.md,slug 由输入确定性生成。
- 默认重试策略为跳过已存在,非空答案文件永不覆盖。
- 模板必须包含单一占位符令牌,禁止用字符串拼接内联输入。
- 并发必须有界,默认上限 8,硬上限 32,禁止裸 & 扇出或 xargs -P 0。
- 每个 CLI 进程拥有自己的已渲染提示词文件 <workdir>/prompts/<slug>.md。
- 每个任务日志写入 <workdir>/logs/<slug>.log,STDERR 重定向,绝不并入答案文件。
- 仅重试失败和从未启动的任务,已完成任务不自动重试。
- 空(0 字节)或仅含空白的答案文件视为失败而非完成。
- CLI 命令只参数化一次并复用于所有输入,仅通过环境变量覆盖。
- 每次运行由 run_id(UTC 紧凑时间 + 短哈希)标识,工作目录默认 ./batch-runs/<run_id>/。
【CLI 支持】 codex exec(位置参数或 stdin,stdout,可加 --skip-git-repo-check 与沙箱参数);claude -p(stdin,stdout,无头模式);gemini / gemini-cli(stdin 或 --prompt-file,stdout);ollama run <model>(stdin,stdout,本地模型,并发受 GPU/内存限制);其他(mistral、cohere、自定义包装器)。必须在开始前选定一个 CLI,中途更换意味着新 run_id 与新工作目录。
【工作目录布局】 固定为契约式目录树:run.json(run_id、cli、template_path、concurrency、起止时间)、inputs.txt、template.md(冻结副本)、prompts/<slug>.md、answers/<slug>.md(下游唯一关心的文件)、logs/<slug>.log、status/<slug>.status(queued | running | done | failed | skipped)、audit.txt。
【七个步骤】
- 解析输入:来自文件或内联列表,每行一个,去空白、去注释、去重,生成稳定 slug 并写入 inputs.txt。
- 冻结模板:复制到 template.md,占位符为 {{INPUT}}(首选)或 XXXXX。占位符为零或多个时在渲染前停止。
- 渲染提示词:在进程内替换占位符并写入 prompts/<slug>.md,渲染幂等,不会覆盖答案文件。
- 规划并发:默认 8,可通过 BATCH_CONCURRENCY 或 --concurrency 覆盖,超过 32 拒绝。可用 xargs -n1 -P 或带信号量的脚本,禁止裸扇出。
- 带跳过已存在地运行:worker 先检查答案文件是否存在且非空(则标记 skipped),运行时 stdout 捕获到 .tmp,stderr 写入日志;非零退出或空文件则标记 failed 并保留 .tmp;成功后原子重命名 .tmp 为答案文件并标记 done。
- 运行后审计:统计 done/failed/skipped/never-started,给出大小分布与低于下限(默认 200 字节)清单;仅当 failed + never-started == 0 才算完成。
- 仅重试失败与未启动:相同 run_id 与工作目录重跑即为重试,强制重试需先归档答案文件再删除状态文件。
【命令形态】 每次 CLI 调用统一包裹 timeout(默认 1500 秒)与 stdin/stdout/stderr 重定向;CLI_BIN 与 CLI_FLAGS 在运行开始时从 run.json 导出一次。
【失败分类】 配额/限流(429、503)→ 等待后重跑;进程挂起 → timeout 杀进程标记失败;空答案 → 读日志;答案截断 → 审计暴露由人工检查;CLI 未找到 → 停止整个运行;工作目录冲突 → 拒绝启动;模板畸形 → 渲染前停止;磁盘满 → 停止清空后恢复。
【反模式】 禁止原地覆盖答案文件、用 tee 吞掉退出码、用 shell 替换内联输入、无 timeout 运行、无理由把并发提高到 32 以上、同一 run_id 混用两个 CLI、自动重试低于下限条目、以日志作为事实来源、运行中修改 inputs.txt、把 prompts/answers/logs 合并为单一目录。