用 AI 处理 28 期完整期刊文献综述:MinerU MCP 与可恢复 Runner 改造记录

前一版流程见这里: 记一次使用agent在一小时内完整调研上百篇文献的工作流 本文记录的是后续工程化改造。 项目背景是 1999-2025 年共 28 期完整英文技术期刊,文章总量在百篇级。目标不是简单翻译全文,而是围绕某一长期主题建立索引、筛选相关文章、拆分文章级摘录,并继续做翻译和横向梳理。 第...
用 AI 处理 28 期完整期刊文献综述:MinerU MCP 与可恢复 Runner 改造记录
用 AI 处理 28 期完整期刊文献综述:MinerU MCP 与可恢复 Runner 改造记录

前一版流程见这里:记一次使用agent在一小时内完整调研上百篇文献的工作流

本文记录的是后续工程化改造。

项目背景是 1999-2025 年共 28 期完整英文技术期刊,文章总量在百篇级。目标不是简单翻译全文,而是围绕某一长期主题建立索引、筛选相关文章、拆分文章级摘录,并继续做翻译和横向梳理。

第一版流程已经能跑通,但后续暴露出两个主要问题:

  1. MinerU 本机运行慢,环境依赖重,Windows 下还会遇到编码、路径和依赖问题。
  2. 翻译阶段任务量大,长文需要分块,源文件更新后还要能定向重跑,不能靠人工记忆维护状态。

这次改造主要解决这两点。

MinerU 改为 MCP 调用

原先做法是本机直接调用 MinerU。这个方式适合单次测试,但不适合长期批处理

主要问题有三个:

  • 本机环境重,依赖变动后排查成本高。
  • 转换耗时较长,中途异常后不容易判断是真失败,还是 Markdown 和图片已经生成。
  • Windows stdout 编码问题会干扰错误判断。

现在改为全局 mineru MCP,项目内只保留一层桥接脚本:

global mineru MCP
  -> .codex/scripts/mineru_mcp_client.py
  -> output/<issue>/<issue>.md
  -> output/<issue>/images/

桥接脚本的职责比较固定:

  • 从全局 Codex MCP 配置读取 mineru server;
  • 调用 parse_documents
  • 强制 UTF-8 环境;
  • 统一返回 extract_pathimages_dir
  • 如果 MCP 没有干净退出,但 Markdown 和 images 已经落盘,则按 fallback_success 处理

这样 MinerU 不再散落在各个 workflow 里,而是变成一个统一的 PDF → Markdown 服务入口。

示例入口:

python .\.codex\scripts\mineru_mcp_client.py `
  --source ".\input.pdf" `
  --output-dir ".\output\issue-name" `
  --language en `
  --enable-ocr

期刊批量转换则通过更上层的管理脚本执行:

powershell -ExecutionPolicy Bypass -File .\.codex\scripts\run-issue-mineru.ps1 -FromVol 49 -Progress

这层只负责选择任务、跳过已有 Markdown、是否覆盖旧输出,实际转换仍然走 MCP 桥接脚本。

Runner 改造

早期流程接近 hooks 模式:agent 开始时写状态,结束时收集输出并推进进度。

后来改成普通 runner。原因很简单:Windows 下 hooks 不稳定,而且复杂任务只靠 start/stop 两个事件不够。

当前 runner 的基本生命周期是:

read progress.json
  -> select next unit
  -> write active-run.json
  -> create runs/<workflow>/<timestamp-id>/
  -> write prompt.txt
  -> codex exec
  -> validate output
  -> finalize official output
  -> update progress.json
  -> clear active-run.json

几个状态文件分工如下:

progress.json      业务队列和完成状态
active-run.json    当前锁,防止并发写入
runs/...           单次运行审计目录
progress.md        人类可读的流水日志

每次运行至少保留:

prompt.txt
stdout.txt
stderr.txt
last-message.md
metadata.json

这样中断后可以明确回答几个问题:

  • 当前跑到哪个 unit;
  • child process 是否真的完成;
  • 输出是否通过校验;
  • finalize 是否写入正式产物;
  • progress 是否推进;
  • 是否可以 repair 或定向重跑。

通用命令保留为固定形态:

node .codex/runner/exec.mjs <workflow> --status
node .codex/runner/exec.mjs <workflow> --dry-run
node .codex/runner/exec.mjs <workflow> --once
node .codex/runner/watch.mjs <workflow> --once
node .codex/runner/exec.mjs <workflow> --repair

实际使用时,一般先 --dry-run 看下一个任务,再 --once 验证一次完整链路,最后才批量跑。

翻译 Workflow

翻译阶段是这套 runner 的主要压力测试。

长文不能直接整篇塞进一个 codex exec,所以按 Markdown 结构切分:

article.md
  -> chunk01.md
  -> chunk02.md
  -> chunk03.md

每个 chunk 单独作为一个 unit:

chunk01.md -> codex exec -> chunk01.zh-CN.md
chunk02.md -> codex exec -> chunk02.zh-CN.md
chunk03.md -> codex exec -> chunk03.zh-CN.md

全部 chunk 完成后,再合并为正式中文稿:

chunk*.zh-CN.md -> article.zh-CN.md

这里重点不是“切块”,而是状态判断。

之前遇到过一个问题:源 Markdown 已经更新,但旧中文稿还在。如果 runner 只判断“译文文件是否存在”,就会把旧译文误判为已完成。

现在的规则改成:

先比较 sourceHash
再判断旧输出是否可复用

也就是说,输入指纹变化优先于输出存在性。

如果源文变了,对应 unit 会重新进入 pending。只有该 unit 自己成功 finalize 后,才更新它的 last successful sourceHash。

这个规则解决了三类问题:

  • 源文更新后旧译文挡住新任务;
  • chunk 边界变化后旧 chunk 被错误复用;
  • 某个 unit 完成时误清理其他 unit 的重跑状态。

定向重跑也固定成三步:

node .codex/runner/exec.mjs component-translate --dry-run
node .codex/runner/exec.mjs component-translate --once
node .codex/runner/exec.mjs component-translate

先确认目标范围,再验证一个前台 unit,最后跑完整队列。

当前状态

当前项目状态如下:

主期刊处理:28/28 completed
文章级/分块翻译:161/161 translated
基础文献翻译:37 translated + 2 skipped_existing_cn
专题来源翻译:54/54 translated

这些数字来自状态文件和 watcher 输出,不依赖对话上下文。

对应状态入口包括:

output/progress.json
output/timeline-progress.json
translation-progress.json
runner watch output

结论

这次改造的核心不是 prompt,而是控制面。

对批量文献任务来说,agent 只应该处理当前最小单元。其余状态必须外置:

  • 队列状态;
  • 当前锁;
  • 输入指纹;
  • 失败记录;
  • 正式输出;
  • 单次运行审计目录。

MinerU MCP 解决输入转换的一致性问题。

Runner 解决批量任务的恢复、监控和重跑问题。

翻译 workflow 则验证了长文分块、输入变更检测和正式产物合并是否可靠。

这套结构跑起来之后,后续扩展新的文献队列或新的处理阶段,主要工作就变成新增 workflow adapter,而不是重新设计整条流水线。

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文