论文图表驱动的逻辑结构与要点总结工具。
从一篇论文的 PDF 出发:提取每张图(图像 + caption + 子图 panel)与正文引用句 → 用多模态 LLM 解读每个子图、归纳图表逻辑结构 → 与摘要/引言做对比、定位正文里的差异与遗漏 → 综合图表信息和正文总结关键要点 → 把结果写成一条 Zotero 笔记。
灵感来自 zotero-figure(PDF 图表提取思路)与一个本地 fig_explain 产物包(manifest schema + 自包含 HTML 模板),但本项目是独立的 Python 脚本工具,不依赖 Zotero 插件运行环境。
PDF (Zotero 附件)
│
├─ pdf_extract.py ── 每图图像 + caption + 子图分割 + 正文 Fig 引用句 + 摘要/引言
│
├─ llm_explain.py ── 阶段1:每图 panel 中文注释 + 图核心结论 + 逻辑角色 (vision)
│ 阶段2:图表逻辑结构 vs 摘要/引言 → 差异/遗漏 → 正文定位 → 要点总结
│
└─ note_writer.py ── 自包含 HTML 笔记 (图+panel表+结构+差异表+要点) → 写入 Zotero
pip install -r requirements.txt需要:Python 3.10+,PyMuPDF、Pillow、requests。
- 启动 Zotero(本地 HTTP 服务
127.0.0.1:23119随之运行)。 - 在 Zotero 里点开目标文献所在的分类。
- 运行:
python run.py # 列出当前分类的文献,输入序号选择
python run.py <itemKey> # 直接按 Zotero item key 处理- 首次运行会交互输入 OpenAI 兼容
base_url/api_key/ 模型名 / Zotero storage 目录,存到~/.figexplain/config.json(之后每次运行可回车保留或覆盖)。模型需支持 vision(如gpt-4o、qwen-vl-max)。 - 跑完后,Zotero 当前分类下会出现一条顶层笔记(标题
图表解读:<原文献名>,标签fig-explain/fig-explain-<key>),同时生成一份本地 HTML 副本figexplain_<key>.html。
工具主要作为网页应用使用。直接访问 http://127.0.0.1:8788/ 显示「无法访问」或空白,不是代码 bug,而是服务没有在运行——该地址由本地 API 进程提供,必须先把服务起起来。
最省事的方式:双击桌面入口 启动 figexplain 网页.bat(位于桌面,不在本仓库内)。它会:
- 检测
127.0.0.1:8788是否已有服务在跑;有则直接打开浏览器,不重复启动。 - 没有则启动 API,轮询
/health真正就绪后再自动打开浏览器。 - 关闭该窗口(或 Ctrl+C)即停止自己启动的服务,避免残留进程。
项目内也提供了等价脚本,可手动运行:
python start_web.py # 默认 127.0.0.1:8788,就绪后自动开浏览器网页界面只保留三块:模型配置、PDF 拖拽/点击上传、Zotero 导入;任务状态与结果三者共用同一个列表与轮询逻辑。拖入 PDF 后立即自动上传并轮询结果,完成后给出结果 HTML 链接;Zotero 文献点击「导入解析」后走同一套任务状态/结果展示。网页端 PDF 单文件上限 100MB。
页面顶部「模型配置」表单覆盖:
- 视觉模型(逐图解读)
explain_base_url/explain_model/explain_api_key - 综合模型(结构分析)
synthesize_base_url/synthesize_model/synthesize_api_key - 默认/兜底(openai_*) 上面两组留空时回退到此
保存即写入 ~/.figexplain/config.json。API Key 不会回显到浏览器:加载时只显示「已保存/未保存」,提交时空着的 Key 字段表示保留原值、不修改。也可在网页里直接改 Zotero storage 目录。
「Zotero 导入」区在页面加载时自动探测连接状态,并列出 Zotero 当前选中分类下的 journalArticle 文献:
- 必须先在 Zotero 里点开目标分类(Zotero 本地 API 没有「读取当前选中条目」端点,只能读「当前选中分类」再列其条目)。
- Zotero 需运行且本地 HTTP 服务可用(默认
127.0.0.1:23119)。未运行时区域显示「未连接」并提示启动 Zotero 后点「刷新当前分类」。 - 点「刷新当前分类」重新读取当前分类文献;每行「导入解析」即对该文献发起解读任务,复用与 PDF 上传相同的任务状态轮询与结果展示,完成后给出可打开的结果 HTML 链接(
/result/<job_id>)。 - 若自动列表不可用,可用底部的「手动输入 item key」兜底(如
4IX5ZWDT)。
同一 item_key 若已有任务在进行中(queued/running),再次提交会被服务端去重、复用同一任务,不会重复消耗 LLM。
底层接口(仍可用,供脚本/CLI 调用):
GET /config 返回非敏感配置 + key 是否已保存(不回显明文)
POST /config 保存配置(空 key 字段保留原值)
POST /upload 原始 PDF 请求体,X-Filename 传文件名
POST /explain {"item_key": "4IX5ZWDT"} 启动 Zotero 文献解读(同 item_key 进行中去重)
GET /jobs/<job_id> 查询任务状态(不含整段 HTML / 本机路径)
GET /result/<job_id> 查看结果 HTML(Zotero 与 PDF 任务通用)
GET /zotero/status 只读:Zotero 是否运行 + 当前分类名/id(未运行返回 503 + 中文错误)
GET /zotero/articles 只读:当前分类下文献 key/title/authors/date(不含 API key/敏感路径)
GET /health 健康检查
默认只监听本机 127.0.0.1;若需让 Docker 或局域网客户端访问,可设置 FIGEXPLAIN_HOST=0.0.0.0 后再启动。PowerShell 写法:$env:FIGEXPLAIN_HOST="0.0.0.0"; python start_web.py。
- 本地 HTTP API 不能创建"挂到父文献下的子笔记"(
saveItems忽略parentItem),也没有"读取当前选中条目"的端点。因此笔记以顶层 note写入当前分类,内容里嵌原文献标题/作者/DOI/key 做关联。网页端「Zotero 导入」同样受此限制:点击导入后,结果会写成顶层笔记 + 一份figexplain-out/figexplain_<key>.html本地副本,但无法自动挂到对应文献下,需在 Zotero 里手动拖入。 - 网页端 Zotero 列表只读「当前选中分类」,所以导入前请在 Zotero 中先点开目标分类;切换分类后点「刷新当前分类」重新读取。
DELETE/PATCH/PUT一律返回 501,不可用。
run.py # 入口
figexplain/
config.py # ~/.figexplain/config.json,交互式配置
zotero_local.py # Zotero 本地 HTTP API 客户端
pdf_extract.py # 图表 / caption / panel / refs / 摘要 / 引言 提取
llm_explain.py # 多模态 LLM 解释(每图 + 综合)
note_writer.py # 笔记 HTML 组装 + 写回
requirements.txt
- API key 只存在本机
~/.figexplain/config.json,不进仓库(见.gitignore)。 - 不向任何第三方服务发送数据,除你配置的 LLM 端点(图图像 + 文本)和本机 Zotero 外。
- 笔记本地副本
figexplain_*.html已加入.gitignore。
- 服务默认只监听
127.0.0.1(本机)。不要把FIGEXPLAIN_HOST设为0.0.0.0并暴露到公网——那样任何能访问该端口的人都能上传文件、读取你的配置与解读结果,且 API Key 仅以明文存于本机配置文件。 - 桌面入口与
start_web.py仅在本机使用;关闭启动窗口即停止服务。 - 上传的 PDF 仅作为任务输入,结果生成后服务端会自动删除上传副本(结果 HTML 落在
figexplain-out/)。
MIT