PaperFlow项目进展记录:MinerU 全文精析与 Editor Pro 进展记录
1. 设计背景与阶段目标
Daily Update 打通后,系统已经可以从首页触发论文发现、审核和初步入库。但这个阶段生成的内容主要来自论文元数据、标题、摘要和 Agent 判断,还不能充分利用论文全文。对于一个论文阅读系统来说,只把标题和摘要入库是不够的:Sage 回答需要正文证据,PDF 阅读器需要页码和位置,公式翻译需要 LaTeX,Editor Pro 需要图片和图注,后续可视化也需要更细粒度的结构化资源。
因此 MinerU 在 PaperFlow 中的定位不是“把 PDF 转成一段文本”,而是全文资源层。它应该把 PDF 中的正文、标题、公式、图片、表格、markdown 和 layout/json 都变成可索引、可访问、可复用的知识资源。Editor Pro 则建立在这一层之上:它不再只基于摘要写一张卡片,而是在全文解析和 MinerU 资产准备好之后,选择合适的论文插图,并生成更适合详情页展示的图文导读。
这也是为什么系统区分 Editor Lite 和 Editor Pro。Lite 负责快,适合 Daily Update 当场刷新和用户上传后的即时反馈;Pro 负责深,适合在 PDF 下载、MinerU 精析、资源入库之后异步补强。两者不是重复功能,而是服务于不同延迟要求的两层编辑机制。
2. MinerU 精准解析 API 接入
当前实现已经把 MinerU 解析从本地命令行优先改成远程精准解析 API。Knowledge Engine 的 MinerUAdapter 会根据环境变量决定解析模式,当 MINERU_PARSE_MODE=api 时,会直接走 MinerU precise API,不再尝试本地运行 MinerU。
实际流程是:先调用 MinerU 的批量上传 URL 接口,为当前 PDF 申请上传地址;然后把本地 PDF 二进制上传到返回的 URL;上传完成后轮询 batch 解析结果;解析成功后下载 full_zip_url 指向的 zip;最后将 zip 解压到当前论文的解析目录。这个目录在服务器上位于:
/opt/paperflow-knowledge/data/parsed/<paper_id>/mineru_precise
以已验证论文 2605.30345v1 为例,MinerU 解包后能看到 full.md、content_list.json、content_list_v2.json、layout.json、model.json 和 images/*.jpg。这些文件是后续全文 chunks、公式资产、图片资产和 Editor Pro 插图选择的原始来源。
这里有一个重要取舍:系统没有把 MinerU 原始 zip 或全部 JSON 原封不动塞进 PostgreSQL。PDF 解析结果可能包含大量图片和结构文件,如果直接塞进 pf_paper.metadata,会让论文主表变重,也不利于前端按需加载。当前采用的是“文件留磁盘,数据库存索引”的方式。原始文件继续放在 data/parsed,数据库只保存资源类型、路径、页码、bbox、caption、latex 等结构化索引。
3. 正文 blocks 归一化与 chunks 入库
MinerU 输出格式并不是直接等于 PaperFlow 的知识库格式,所以中间增加了 flatten_blocks 归一化步骤。这个步骤会优先读取 MinerU 输出中的 pdf_info/preproc_blocks,把其中的 text、title、interline_equation 转换为统一 block:
{ "id": "...", "page": 1, "bbox": [x0, y0, x1, y1], "text": "...", "type": "text|title|formula" }
如果某些 MinerU API 结果没有 pdf_info,则 fallback 到 content_list、blocks 或 pages。对于 content-list 中缺少坐标的文本,系统会保留文本并生成兜底 bbox,这样至少不影响全文检索和 RAG 入库。这个策略的核心是:坐标准确时用于 PDF 定位,坐标缺失时仍然保留语义文本,避免解析结果因为少字段而被全部丢弃。
归一化后的 blocks 会通过 save_parsed_paper 写入 pf_paper_chunk。这些 chunk 的 metadata 标记为:
{ "source": "parsed-original", "page": 1, "bbox": [x0, y0, x1, y1], "source_type": "text", "section_title": "...", "paper_title": "..." }
这一步和 Editor Lite 的 agent-workflow chunks 是区分开的。agent-workflow 保存 Agent 对论文的总结和判断,parsed-original 保存 MinerU 从论文全文中解析出的原文内容。Sage 检索时会优先奖励 parsed-original,因为它更接近论文原始证据;但 Agent summary 仍然保留,用于快速理解和补充语义召回。
4. 公式资源的处理方式
公式目前有两条入库路径。第一条是作为 type=formula 的 parsed-original chunk 写入 pf_paper_chunk,这样 Sage 和全文搜索可以检索到公式上下文。第二条是作为 asset_type=formula 写入 pf_paper_asset,这样后续 PDF 阅读器和前端渲染可以按页码、bbox 和 LaTeX 精确定位公式。
formula asset 不是独立图片文件,而是从归一化 blocks 中提取出来的结构化资源。它会保存 page、bbox、latex/content 和原始 block id。这个设计是为后续公式渲染准备的:当用户在 PDF 中选中含公式区域时,系统可以先根据 page + bbox 找到对应公式 asset,再把 LaTeX 交给翻译模型或前端 KaTeX/MathJax 渲染,而不是只依赖 OCR 后的纯文本。
目前公式资产已经能进入资源表,但前端 LaTeX 渲染还没有最终完成。也就是说,数据基础已经准备好,下一步要做的是在摘要、Sage 回答、PDF 选区翻译和 Editor Pro 中统一处理 $...$ 或 block formula 的渲染。
5. pf_paper_asset 表的引入
一开始考虑过把 MinerU manifest 放进 pf_paper.metadata。这个做法实现快,但长期会有明显问题:一篇论文可能有几十张图片、多个 JSON、markdown、公式和表格,如果都放进 metadata,论文详情接口会变重,前端不能按需加载,后续也很难按类型查询“某篇论文有哪些图片”或“第 3 页有哪些公式”。
因此当前改为引入 pf_paper_asset,把 MinerU 解析资源提升为系统的一等资源。文件本体仍然保存在 Knowledge Engine 的 data/parsed 目录,资源表保存索引。当前字段包括资源 id、论文 id、来源、资源类型、相对路径、磁盘路径、公开访问 URL、content type、文件大小、页码、bbox、caption、latex、content 和 metadata。
这张表的实际用途有三个。首先,前端详情页不需要在论文主接口里拿到所有图片,只需要按需请求 /assets?asset_type=image。其次,Editor Pro 可以直接从资源表读取图片候选,而不需要每次重新扫磁盘目录。第三,后续公式定位、图片引用、Sage 引用证据和对象存储迁移都有了统一索引层。
当前 pf_paper_asset 由 Python Knowledge Engine 在写入解析结果时自动建表和补列。这是为了快速打通链路。更规范的做法是后续整理成正式 SQL migration,避免长期依赖运行时代码修改 schema。
6. MinerU 资产扫描与登记
解析完成后,系统会扫描当前论文的 mineru_precise 目录。扫描逻辑不是只看 images 文件夹,而是结合 MinerU 的 content_list 信息。对于 content_list 中 type=image 或 type=table 的对象,系统会尝试读取 img_path、image_path、table_img_path、path、src 等字段,并匹配真实文件路径。这样可以把图片文件和 MinerU 提供的 page、bbox、caption 联系起来。
如果 content_list 中没有完整字段,系统仍然会 fallback 扫描目录下的图片、markdown 和 json 文件。图片会登记为 asset_type=image,markdown 登记为 markdown,content_list*.json 登记为 content_list_json,layout.json 登记为 layout_json,model.json 登记为 model_json。公式则由正文 blocks 额外生成 formula asset。
每个 asset 都会生成稳定 id。当前 id 基于相对路径 hash,避免同一篇论文重复回填时不断生成新 id。asset 的 public_url 采用统一形式:
/api/v1/papers/{paper_id}/assets/{asset_id}
这样前端不需要知道真实磁盘路径,也不会暴露 /opt/paperflow-knowledge/data/parsed 这种服务器内部结构。
7. 资源访问接口与 Java 代理
Knowledge Engine 新增了资源列表和资源文件接口。列表接口返回 asset metadata,但过滤掉 storage_path,避免把服务器真实路径暴露给前端。单文件接口会根据 paper_id 和 asset_id 查询资源表,确认文件路径仍位于该论文的 parsed 目录下,然后用 FileResponse 返回文件。
外部访问仍然经过 Java:
GET /api/v1/papers/{paperId}/assets?asset_type=image GET /api/v1/papers/{paperId}/assets/{assetId}
content-service 中的 KnowledgeEngineClient 增加了对应代理方法,PapersController 增加了列表和文件转发接口。单个图片文件通过 Java 返回时保留上游 content type,例如 image/jpeg。这点很重要,因为前端 <img> 标签需要直接加载图片资源,不能依赖登录态和不能拿到错误 MIME。
已在服务器用 2605.30345v1 验证过,图片列表接口可以返回 MinerU 图片资产,单图接口可以返回:
200 image/jpeg 63233
8. Editor Pro 的生成逻辑
Editor Pro 的触发点在全文解析之后,而不是 Curator 审核通过之后立即运行。原因是 Pro 需要依赖 MinerU 产出的全文 blocks 和 asset 列表,尤其是图片、公式和 markdown/json 这些资源。如果 PDF 还没有下载,或者 MinerU 还没有完成解析,Editor Pro 就只能退化成和 Editor Lite 类似的摘要卡片,无法体现“全文级编辑”的价值。
当前代码路径中,Daily Update 审核通过论文后会进入 ingest_approved_paper_pdf。这个函数先下载 PDF,再调用 MinerU precise API,随后执行三个关键步骤:flatten_blocks(raw_data) 负责把 MinerU 输出归一化为统一正文 blocks;build_mineru_assets(paper_id, output_dir, raw_data) 负责扫描 MinerU 解包目录并登记图片、markdown、json 等资源;build_formula_assets(paper_id, output_dir, blocks) 负责从公式 blocks 中提取 formula asset。只有这些输入准备好以后,系统才调用 build_editor_pro_card(paper, blocks, mineru_assets) 生成 Editor Pro。
Editor Pro 的输入不是把整篇论文全文直接丢给模型。这样做会浪费 token,也容易让模型被大量正文噪声淹没。当前采用的是压缩后的多源输入:论文基础信息提供标题、摘要、标签、Editor Lite 已经整理过的贡献点和局限;全文 blocks 中抽取若干较长文本片段,作为论文方法和实验内容的语义样本;MinerU asset 中筛出前若干张图片候选,提供图片 id、文件名、页码、caption、文件大小和访问 URL。这个输入结构的目标是让模型既知道论文讲什么,又能在有限候选图里做选择,而不是重新完成全文阅读。
全文样本的抽取也做了简化规则。系统不会把所有 chunk 都放进 prompt,而是从 blocks 中选择长度足够的文本片段,过滤掉过短、信息量低的内容,并截断到固定长度。这种做法牺牲了一部分完整性,但换来了稳定的模型调用成本和响应时间。对于 Editor Pro 第一版来说,它的任务不是生成完整综述,而是为详情页生成“足够帮助用户判断是否深入阅读”的图文导读,因此这种采样是可接受的。
图片候选的构造更依赖 MinerU asset 表。系统会优先选择 asset_type=image 的资源,并限制候选数量,避免把几十张图全部放进模型输入。每张候选图传给模型的不是图片二进制,而是结构化描述,例如 asset id、文件名、页码、caption、size_bytes 和 public_url。这里的设计有一个明确限制:当前模型并没有直接看图,它依据的是 MinerU 解析出的 caption、图片所在页码、文件信息和论文文本上下文来判断哪张图更适合作为详情页插图。
Prompt 对模型的要求也不是简单“总结论文”。它要求模型完成一个编辑决策:从候选图中选择最适合详情页展示的一张,并解释为什么这张图有助于读者理解论文。规则上会偏好方法框架图、系统结构图、流程图、结果总览图和具有明确 caption 的图片;同时避免选择 logo、无意义裁剪、小尺寸图片、纯公式截图或装饰图。这让 Editor Pro 更像一个论文编辑,而不是普通摘要器。
模型调用走模型路由中的复杂任务 editor_summary。在当前 Sophnet 配置下,这类任务会走 qwen3.7-max,因为它需要做综合判断:既要理解论文摘要和全文样本,又要根据候选图信息做内容选择,还要输出结构化 JSON。相比 DeepSeek Flash 这类简单任务模型,复杂模型更适合做这种多约束编辑任务。
Editor Pro 期待模型返回稳定 JSON,而不是自由文本。当前结构包括 display_mode、hero_title、visual_summary、selected_figure_id、figure_reason、figure_caption、key_takeaways 和 limitations。这里最关键的是 selected_figure_id,因为模型只能返回候选图 id,系统再用这个 id 回连到真实 asset。也就是说,模型不直接决定图片 URL,模型只做选择,后端负责把选择结果映射回数据库中的资源记录。
回连 selected figure 之后,系统会把完整的 selected_figure 写入 pf_paper.metadata.editor_pro。这里保留完整 asset 信息是为了让前端详情页可以直接读取 public_url、caption、page 和 rel_path,不必再额外查一次 asset 列表。与此同时,asset 本体仍然由 /api/v1/papers/{paperId}/assets/{assetId} 提供访问,metadata 中只是保存对该资源的引用和必要展示信息。
Editor Pro 还有 fallback 机制。如果模型不可用、模型没有返回合法 JSON、候选图为空,或者返回的 selected_figure_id 无法匹配真实 asset,系统不会让整篇论文入库失败。它会退化为 fallback card:有图片时选择第一张图片作为插图,没有图片时返回 no_figure 状态;标题和摘要则回退到 Editor Lite 的 popular_headline、popular_summary 或论文摘要。这个设计是为了保证 Daily Update 的主链路稳定,不能因为 Pro 阶段失败就影响论文全文入库。
从数据流上看,Editor Pro 的输出最终只写入论文 metadata,而不是单独建一张 Editor Pro 表。这样做是为了第一版实现轻量,因为 Editor Pro 当前是一篇论文的一组展示信息,不涉及多版本对比、人工审核或历史回滚。后续如果要支持“重新生成”“人工修正”“爆款版/客观版多版本并存”,就应该把 Editor Pro 从 metadata 中拆出来,建立独立的 card/version 表。
当前 Editor Pro 第一版已经形成了完整闭环:MinerU 解析提供全文和资源,asset 表提供图片索引,复杂模型做图文导读决策,后端把选择结果写回 metadata,前端详情页读取 metadata.editor_pro 并展示插图和导读。但它仍然不是最终形态。最大限制是模型目前没有直接读取图片像素,选图更像“基于 caption 和上下文的编辑判断”;如果 MinerU 没有给出 caption,模型只能依赖页码、文件名和文本样本。下一阶段如果接入视觉模型,Editor Pro 就可以真正判断图片内容,例如识别方法框架图、实验结果图、消融表格截图或无意义裁剪图,从而显著提高插图选择质量。
9. 前端详情页展示
论文详情页现在会读取 metadata.editor_pro。如果存在 hero_title、visual_summary、selected_figure、key_takeaways 或 limitations,页面会展示 Editor Pro 区块。
展示内容包括图文标题、全文级摘要、选中插图、图片说明、选图理由、Pro takeaways 和 Pro limitations。图片使用 asset 的 public_url,因此前端不需要额外拼接服务器地址,也不需要知道图片实际保存在哪里。
这使详情页从“论文标题 + 摘要 + PDF 入口”进一步升级为“论文图文导读页”。从产品角度看,Editor Lite 更像 Feed 里的摘要卡,Editor Pro 更像进入详情页后真正帮助用户判断“这篇论文值不值得深入读”的编辑层。
10. 摘要一键翻译
详情页还新增了摘要一键翻译。这个能力独立于 Editor Pro,但与论文导读体验相关。前端摘要区的按钮会调用:
POST /api/v1/papers/{paperId}/translate-summary
Knowledge Engine 会优先翻译 summary,如果没有 summary 则 fallback 到 abstract,再 fallback 到 editor_pro.visual_summary。当前翻译走简单任务 light_summary,也就是使用低成本快速模型,而不是每次都调用复杂模型。接口只返回翻译结果,不写入数据库。
不缓存的原因是当前阶段更关注链路验证。如果直接把每次翻译写入 metadata,用户重复点击会产生状态污染,也需要考虑不同目标语言、不同模型版本和缓存失效。后续可以单独设计 metadata.translations 或独立翻译缓存表。
服务器已通过外部接口验证摘要翻译返回 200,前端也已经有按钮入口。
11. 与 Daily Update 入库链路的关系
MinerU 和 Editor Pro 不是单独的离线功能,而是 Daily Update 入库闭环的后半段。当 Curator 审核通过一篇论文后,系统会下载 PDF,然后调用 MinerU 精析,随后保存 parsed chunks 和 assets,最后生成 Editor Pro。
这个顺序解决了实时性和完整性的矛盾。Daily Update 点击后,可以先用 Editor Lite 给用户快速反馈;全文解析完成后,再补上 Editor Pro 和可访问的 MinerU 资源。对于用户上传论文也是类似逻辑:上传后先有基本记录和解析状态,完整解析完成后再逐步增强阅读页。
因此后续如果做 Agent 可视化,应该把这一段显示成状态链:
PDF downloaded -> MinerU parsed -> chunks indexed -> assets registered -> Editor Pro generated
这样用户能知道论文不是“突然出现一张图”,而是经过了可解释的全文资源处理流程。
12. 当前完成度与限制
当前已经完成全文精析的关键基础设施:MinerU precise API 接入、正文 chunks 入库、公式资产登记、图片/markdown/json 资产登记、资源访问接口、Java 代理、Editor Pro 生成和详情页展示。也就是说,系统已经具备把 MinerU 解析结果作为长期知识资源使用的基础。
仍然需要注意的是,Editor Pro 目前是第一版。它能选图和生成导读,但选图依据还不是视觉模型直接理解图片。公式虽然已经入库为 chunk 和 asset,但前端还没有统一 LaTeX 渲染。pf_paper_asset 目前由 Knowledge Engine 自动建表和补列,后续应该整理成正式 migration。老论文也不会自动拥有完整 assets 和 Editor Pro,需要重新解析或执行回填。
下一步如果继续推进这一块,优先级应该是公式渲染和 Editor Pro 模式增强。公式渲染可以直接利用现有 formula asset;Editor Pro 模式增强可以在同一份全文和资产基础上生成“客观评论版”和“爆款导读版”,并允许前端切换。再往后才是多图候选、人工改图和视觉模型理解图片。
