Skip to content

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

知枢Paper-Agent · 个性化科研工作台

输入一个研究主题 → 收获论文调研、阅读以及综述撰写的全周期信息

Python FastAPI Vue uv


🆕 3.0 版本更新

Paper-Agent 3.0 是相对旧版@GreatZack项目的一次全新重写。它保留了旧版「检索 → 阅读 → 分析 → 写作」的相关思路,但在Agent化实现上做了全面升级:

  • 前端改为 Vue 3 + TypeScript + Vite,交互更现代、响应更快;
  • 包管理统一使用 uv,一条命令即可完成 Python 依赖安装;
  • 论文来源 扩展到 arXiv、OpenAlex、Semantic Scholar 三源检索;
  • 会话持久化改用 SQLite + 文件系统,浏览器刷新后历史线程不丢失;
  • 实时进度基于 SSE 推送到工作台,从检索到写作每一步都可观察。

👀 界面概览

imageimage
输入研究主题,实时追踪检索论文、精读论文、生成综述等各环节进度(综述内部的分析、大纲、逐节写作以同一张卡片的实时状态文字呈现)

image

在浏览器中可视化配置模型 Provider 与 Agent 档位,一键测试连通性


🎯 为什么是 Paper-Agent?

做学术调研时,你一定经历过这些:

场景 传统方式 Paper-Agent
初步了解陌生研究方向 手动搜索多个来源,逐个打开论文判断相关度,耗时费力 从 arXiv / OpenAlex / Semantic Scholar 自动检索,按主题和约束去重、筛选、整理
论文阅读与资料积累 读完一篇记一篇笔记,资料散落在各处,容易丢失和重复 先读摘要判断相关性,再按条件下载全文、解析、切分,逐块精读并把全文与笔记留在本地
撰写领域综述 边读边写,反复调整结构,常常写到一半推倒重来 先从论文整体分析、再做全局综合,据此生成结构化大纲,然后逐节按证据写作
管理长流程任务 每跑一步都担心进度、状态和重启后丢失,不敢中途停下 会话、运行状态、阶段产物和实时进度都在工作台可见;刷新后历史与实时流可接回,进程中断的任务会被标记为「已中断」,可基于工作区已保存的产物继续下一轮调研
控制模型成本 全程用一个模型档位,不清楚每个环节花了多少 token 精读、追问、综述等子 Agent 的 token 用量会累加到对应工具卡片;主对话按轮次上报,多轮对话下看到的是最近一轮的用量

Paper-Agent 是一个完整的 AI 研究助理——它找得到论文、读得懂全文、理得清脉络、写得出综述。


✨ 核心特性

特性 一句话说明
🔍 多来源论文检索 内置 arXiv、OpenAlex、Semantic Scholar 连接器,统一为 PaperDocument,按年份、来源、数量和排除词筛选并去重;按研究主题用概念组检索,用户给出一篇已知论文的完整标题时由助手自动改走标题检索,不必拆成布尔关键词。多源结果用 RRF(排名融合)排序——每篇论文按它在各源里给出的名次累计得分,被多个源同时命中、且名次靠前的排在前面
📖 从摘要到全文的渐进式阅读 先读摘要判断相关性,满足条件的论文走下载 → 用 Nougat 把每一页写成 Markdown → 插图仍按页面截图并配上图注 → 按块装箱 → 逐块精读并汇总成报告。用户只要 Markdown、不要精读报告时,可以单独把全文转成 Markdown 并写入本地缓存。需要再精读一遍时,只清掉旧报告、复用已经切好的正文片段重新阅读,不必从工作区删文再检索。全文下载或转换失败时自动降级为「基于摘要的精读」,并把失败原因告知用户
🧩 本机论文长期记忆 一篇论文完成全文分片和精读后,报告和分片留在本机;论文身份、别名和各会话引用记在会话库同一个 SQLite 里。之后任意会话再检索到同一篇(按 DOI / arXiv / 标题认),会自动召回已有报告和分片,不必重新精读;只有摘要的降级报告不进入这份记忆。工作区删除只退出当前会话;清除本地全文或手工删掉缓存目录时,卡片保留,已精读改回可精读
🧠 上下文预算与自动压缩 对话历史不再按固定轮数截断,而是按「模型窗口 × 0.8」的 token 预算管理:超预算先免费归档旧工具结果(附 get_history 取回工具),仍不够再把最老的整轮经一次 LLM 调用压成要点摘要(带缓存、不落库);问答子 Agent 同样改为「全文目录 + read_sections 按需取原文」,长论文中间内容不再被截断弄丢
🔬 分层研究分析 综述先按字数把论文分成几批做子主题分析,再做全局综合。每篇论文的卡片留在综述状态里,后面各步只把有字数上限的摘录发给模型,形成研究现状、共识、争议、空白、时间演化与展望等结构化内容
✍️ 证据约束下的综述写作 先生成大纲与证据映射,再逐节写作;证据不足时检索补充,写完后审查并限次修改。完成后可在对话卡片里打开全文,并下载 Markdown
📡 实时会话工作台 SSE 实时推送检索、阅读、分析、大纲与逐节写作进度,SQLite + 文件系统持久化,刷新后历史可恢复;长对话打开时先显示最近几轮,避免一次画出全部历史
🎛️ 可视化模型配置 在浏览器中管理 Provider 协议、API 地址、密钥与各 Agent 档位,一键测试连通性,保存即生效

🔧 架构

flowchart TB
    subgraph Main["主对话 Agent (researchAgent)"]
        direction TB
        U[用户输入] --> LLM[LLM 工具循环]
        LLM --> T1[search_papers]
        LLM --> T2[evaluate_papers]
        LLM --> T3[deep_read_paper]
        LLM --> T4[ask_paper]
        LLM --> T5[generate_review]
        LLM --> T6[expand_by_citations]
        LLM --> T7[list_papers]
        LLM --> T8[get_paper_details]
        LLM --> T9[remove_papers]
        LLM --> T10[download_paper]
        LLM --> T11[get_history]
        LLM --> T12[convert_paper_to_markdown]
    end
    
    subgraph SubAgents["子 Agent(agent-as-tool)"]
        direction TB
        DR[DeepReadAgent\n全文精读 map-reduce]
        PQ[PaperQAAgent\n目录 + read_sections 按需取原文追问]
        RV[ReviewPipeline\nStateGraph 编排综述流程]
    end
    
    T3 --> DR
    T4 --> PQ
    T5 --> RV
    
    subgraph Sources["外部数据源"]
        direction LR
        AX[arXiv]
        OA[OpenAlex]
        SS[Semantic Scholar]
    end
    
    T1 --> Sources
    T6 --> OA
    T6 --> SS
Loading

主对话共注册 12 个工具(上图全部列出)。convert_paper_to_markdown 把论文全文转成 Markdown 并写入本地缓存,不生成精读报告。引文扩展(expand_by_citations)由 OpenAlex 与 Semantic Scholar 承担:arXiv 的接口本身不提供引文数据,所以它不参与回答,但 arXiv 来源的论文只要带编号(arXiv 编号或 DOI)一样可以扩展。

编排范式:什么用流程图、什么用ReAct循环

项目里有两种编排方式,分工是明确的:

流程长什么样 用什么 为什么
阶段能提前枚举出来(综述:分析 → 大纲 → 逐节写作 → 摘要 → 参考文献 → 终稿) LangGraph StateGraph 控制流一眼看得全,而且阶段之间天然就是"这一步做完了"的边界,正好用来存检查点
需要模型每轮自由决定下一步(主对话:想检索就检索、想精读就精读、够了就直接回答) ReAct 循环 循环里要插三件框架不让你顺手插的事:每轮开头的取消检查、推理模型 thinking 块的协议回传、同一轮多个工具的并发控制

所以"主对话"和"综述"看起来风格不一样,不是没统一,是两件事的形状本来就不一样。

综述流水线(StateGraph)

flowchart TB
    S([收到综述请求]) --> P["准备<br/>收集工作区论文、整理分析输入"]
    P --> A1["子主题分析"]
    A1 --> A2["全局综合分析"]
    A2 --> O["组装分析报告 + 生成写作大纲"]
    O --> W["写一个小节"]
    W -.->|"还有小节没写"| W
    W -->|"小节都写完了"| AB["生成摘要"]
    AB --> C["整理参考文献、把正文里的编号换成序号、拼出终稿"]
    C --> F["保存产物 + 推综述卡片"]
    F --> E([完成])
Loading

步骤级断点续跑:每做完一个阶段,图的状态会写进会话目录下的 checkpoints.db(随会话一起删除)。所以综述跑到一半进程挂了、或者用户中途点了停止,可以从最后一个做完的阶段接着写——已经写好的小节不用重写,只有崩溃时正在写的那一节要重做。也可以在失败或已停止的卡片上点"继续"。

两个实现细节:

  • 小节内部是普通循环,不另存检查点。 写一节时先看证据够不够,不够就补资料(最多 5 次),写完再审查,不通过最多改 2 轮。这些中间状态没有恢复价值:外层已经是写完一节才记一笔,续跑只会从这一节开头重来。分析、大纲、写作是这条流水线里的函数,不再各算一个子 Agent。
  • 检查点库的大小随综述规模增长:每完成一节就落一次快照,快照里含当时已写的全部小节。一份 12 节、单篇论文的综述实测约 2 MB。

说明:这是流程级的恢复,粒度是"一节一个检查点",只有综述流水线支持。主对话的恢复是另一套:消息落库 + 刷新页面接回实时流,粒度是整轮。


📦 技术栈

层级 技术选型
运行时 Python 3.12+、uv、Uvicorn
后端 API FastAPI、REST、Server-Sent Events(SSE)
编排模式 主对话:Orchestrator-workers(主 Agent + 子 Agent 以 tool 形式注册,多轮ReAct工具循环);综述流水线:LangGraph StateGraph
执行持久化 LangGraph SQLite checkpointer;检查点库在会话目录内(checkpoints.db,随会话一起删除)。只覆盖综述流水线,粒度是"一节一个检查点"
Agent 主对话 Agent(researchAgent)+ 精读 / 追问 / 综述三个子 Agent
LLM 适配 OpenAI 兼容协议、Anthropic Messages 协议
论文来源 arXiv、OpenAlex、Semantic Scholar
全文处理 Nougat 按页把 PDF 写成 Markdown;插图仍用 PyMuPDF 截图并配图注;分片按这些块装箱,不从一块中间切开
会话存储 SQLite + 本地 JSON/Markdown 文件
前端 Vue 3、TypeScript、Vite、Vue Router、Lucide

📂 项目目录

Paper-Agent/
├── main.py                         # FastAPI 本地启动入口
├── pyproject.toml                  # Python 项目元数据与依赖
├── package.json                    # 根目录前端快捷命令
├── config/
│   ├── model.json                  # 本地模型配置,不提交
│   ├── model.example.json          # 模型配置示例
│   └── system.yaml                 # 系统默认参数
├── front/                          # Vue 3 + TypeScript 前端
│   ├── src/api/                    # 会话与设置 API 客户端
│   ├── src/components/             # 工作台、状态和会话组件
│   ├── src/views/                  # 会话工作台、系统配置页
│   └── vite.config.ts              # 开发服务器、代理和端口配置
├── src/
│   ├── agents/                     # 智能体,按职责再分成四个包
│   │   ├── common/                 # 共用骨架:基类、工具登记、提示词、技能文档
│   │   ├── research/               # 主对话
│   │   ├── reading/                # 摘要相关性、全文精读、追问
│   │   └── review/                 # 综述流水线
│   ├── api/                        # FastAPI 应用与路由
│   ├── runtime/                    # 一次运行的取消、进度上报和共用的网络资源
│   ├── llm/                        # Provider 适配、配置解析和统一响应
│   ├── models/                     # 会话、阅读与工作区领域模型
│   ├── paper_retrieval/            # 论文模型、编号规则、检索服务和来源连接器
│   ├── repositories/               # SQLite、JSON 与阶段产物持久化
│   ├── services/                   # 会话、运行、设置、论文长期记忆和工作流服务
│   └── utils/                      # 日志、编号等小工具;全文处理在 fulltext/
│       └── fulltext/               # PDF 转写、分块、缓存和插图
├── data/                           # 本地会话库、论文缓存与会话数据
├── logs/                           # 运行日志
└── test/                           # unittest 测试与联调辅助代码

🚀 快速开始

1. 安装项目依赖

在项目根目录执行:

uv venv --python 3.12
uv sync
npm run front:install

如果你已经有可用的 Python 3.12 虚拟环境,也可以直接执行 uv sync 和 npm run front:install。

全文阅读还要一个单独的 Nougat 环境(不要装进上面这个主环境)。双击 start.bat 时会自动装上依赖,并下载大约 1GB 的模型权重。已经有的会跳过。 想手动准备的话,在项目根目录执行:

uv venv --python 3.12 tools/nougat_trial/.venv
uv pip install --python tools/nougat_trial/.venv/Scripts/python.exe --index-url https://mirrors.aliyun.com/pypi/simple/ nougat-ocr==0.1.17 transformers==4.38.2 albumentations==1.4.24 pypdfium2==4.30.0 requests
uv pip install --python tools/nougat_trial/.venv/Scripts/python.exe --index-url https://mirrors.aliyun.com/pypi/simple/ "https://mirrors.aliyun.com/pytorch-wheels/cu128/torch-2.11.0%2Bcu128-cp312-cp312-win_amd64.whl" "https://mirrors.aliyun.com/pytorch-wheels/cu128/torchvision-0.26.0%2Bcu128-cp312-cp312-win_amd64.whl"

最后一条里的 PyTorch 大约 2.6GB。不要改成 uv sync --project tools/nougat_trial,那个目录里的 CUDA 地址不是 uv 能解析的软件源。

2. 创建本地模型配置

推荐通过 Web 工作台的「系统配置」页面修改和测试配置。也可以手动拷贝 config/model.example.json 为 config/model.json,至少确认:

  1. providers 中存在一个可用 Provider,并填写 api_base;
  2. api_key 或 api_key_env 能提供有效密钥;
  3. agents.default_agent 已配置;

3. 启动后端

uv run python main.py

后端默认监听 127.0.0.1:8000,开发模式自动重载。API 文档:http://127.0.0.1:8000/docs

4. 启动前端

npm run front:dev

打开 http://127.0.0.1:5173/,先进入「系统配置」测试模型,再进入会话工作台创建研究任务。

前端默认只监听本机,并代理 /api、/webui 请求到 127.0.0.1:8000。如需局域网其他设备访问:

npm run front:dev:network

5. 只用一个端口运行(不需要 Node)

上面第 4 步的双服务模式是给开发用的。如果只是想把整个应用跑起来自己用,可以先构建一次前端, 之后由后端直接托管界面,不需要 Node,也不需要 5173 端口:

npm run front:build
uv run python main.py

注意 main.py 是带 reload=True 启动的(开发时改代码自动重启,会额外拉一个监视进程)。 想要真正的单进程运行,用分发用的那个启动器,它显式关掉了 reload:

uv run python scripts/launch.py

然后访问 http://127.0.0.1:8000/ 即可(不是 5173)。后端会挂载 front/dist 并接管前端路由, /api 与页面同源,因此不需要任何代理配置。

npm run front:build 会先清空 front/dist 再写入。启动器还会核对产物里的构建记录和当前源码:对不上就先删掉旧产物再构建;构建失败则拒绝启动,避免继续打开旧页面。首页响应带 Cache-Control: no-cache,浏览器不会一直拿着旧首页去加载旧脚本。

注意:front/dist 只在构建后存在。缺了它后端会打一条 warning 并跳过界面挂载, 此时只有 /docs 和 /api/* 可用。发给同事的压缩包里只有这一次的构建结果,没有前端源码,同事看到的界面就是打包当时的页面。要更新界面,在有源码的机器上重新执行打包。

想把这个形态做成「解压双击即用」的分发包,用内置的打包与启动脚本:

uv run python scripts/package.py    # 生成 Paper-Agent-<日期>.zip

包内自带 start.bat 和 uv.exe,解压后双击即可,不用装 Python 或 Node。如果目录里没有 tools/uv.exe、电脑上也没装 uv,start.bat 会先把它下载到 tools/uv.exe。 面向使用者的说明见 使用说明.md。


🔧 模型配置

系统支持配置多个 Provider。内置档位只有 default_agent 一个;主对话另有可选档位 research_agent(未配置时回退 default_agent)。精读、追问和综述不单独配档位,直接复用主对话这次用的模型。

  • 配置主文件:config/model.json(含密钥,不入库),示例见 config/model.example.json,系统参数见 config/system.yaml
Agent 运行时实际使用的档位 主要职责
researchAgent(主对话) research_agent,未配置时回退 default_agent 多轮对话、工具调用与任务分派
deepReadAgent / paperQaAgent 复用主对话的档位快照 全文精读、基于全文的追问
综述流水线 复用主对话的档位快照 分析论文、生成大纲、逐节写作并审查修改
ReadAgent 复用主对话的档位快照 阅读摘要,判断相关性并整理笔记

default_agent 是唯一必需的档位,缺失时配置无法工作。

说明:配了 research_agent 时,精读、追问、综述和摘要相关性评价跑的都是这一档的模型。ReadAgent 的 AgentSpec 里虽然写了 llm_profile,对话里并不按它另装一份。综述里的分析、大纲、写作也不再单独声明档位。

Provider 后端

支持 backend 类型:openai、openai_compat、anthropic、anthropic_compat、opencode_go。示例:

{
  "providers": {
    "my_provider": {
      "backend": "openai_compat",
      "api_key_env": "OPENAI_API_KEY",
      "api_base": "https://api.openai.com/v1",
      "extra_headers": {},
      "extra_body": {}
    }
  }
}

opencode_go 对应 OpenCode 的 Go 订阅端点(https://opencode.ai/zen/go/v1,OpenAI 兼容), 它强制要求每个请求都带上 x-opencode-session 请求头,这个头由代码自动补齐、不需要手工配置; 可用的模型名以 GET https://opencode.ai/zen/go/v1/models 返回的清单为准。

api_key 与 api_key_env 二选一即可。使用兼容网关时通常需要同时填写 backend、api_base 和模型名称。

系统参数

config/system.yaml 存放系统级默认值和阅读参数:defaults.llm 是所有 Agent 档位未声明字段时的兜底生成参数,paper_retrieval 是检索数据源密钥,read 是阅读与下载参数(缓存目录、连接/下载超时、最大文件大小、PDF 解析器)。

其中 defaults.llm.context_window_tokens 默认按 1M token 计(DeepSeek 等长窗口模型的量级),主 Agent 用它给对话历史算预算:窗口 × 0.8 = 历史可占用上限,超出会自动「归档旧工具结果 → 整轮 LLM 压缩」。想快速验证压缩效果,可临时把它调小(如 8000)。

正文是怎么变成片段的

PDF 正文由本机的 Nougat 按页写成 Markdown。它读的是整页,公式会写成 LaTeX。写进 paper.md 之前,把 Nougat 的 \[...\] 换成 $$、\(...\) 换成 $...$,这样后面的分块仍能把公式认成一整块。

paper.md 和 chunk.json 仍从同一份文字来。精读、追问、综述写作读到的仍是每段里的文字,不另存一份块文件。

一块仍是这几种:标题、段落、独立公式、夹在句子里的公式、表格、图。公式和表格以 Nougat 写出来的样子为准,不再截图交给精读模型重排。

插图不交给 Nougat。仍用原来的办法在页面上找图、配图注、截到 assets/,再把 ![图注](assets/...) 接到对应页的末尾。精读时的插图解读也不变。

Nougat 装在 tools/nougat_trial 这个单独环境里。start.bat 启动时如果还没有这份环境,会从阿里云装上 PyTorch,并把大约 1GB 的模型权重下到 tools/nougat_trial/weights(只留在本机,不提交)。主程序用子进程调用。环境或权重不在时,这次转换直接失败,不再退回按字形抽正文。

分片顺着这些块的阅读顺序装箱,片段之间不重叠:

  • 装到 1200 字就换下一段,不把一块从中间劈开。
  • 新的一级标题到来、而且当前段里已经有正文时,从标题另起一段。字母小节(如 A.)不算一级标题。
  • 单块超过 4000 字才单独成段。大表仍按行切开,每一段补上表头。

这两个数字写在 PageChunker 里,不通过配置文件调整。一整块不到 4000 字时可以比 1200 字长,所以不能把 1200 理解成每段的硬上限。精读读某一段时,会附带上一段末尾、下一段开头各一两句作为衔接,切片文件本身仍不重叠。

插图清单来自「图」这种块。网页全文没有这些块,才退回正文里的图片链接。

转换器和分片各自有版本号(现在是 9 和 10)。旧的 paper.md / chunk.json 对不上就重做。没有现成的块时,分片按空行从 Markdown 认块。


👤 作者

The-never-lemon

The-never-lemon
@The-never-lemon


🤝 参与贡献

欢迎提交 Issue 和 Pull Request。建议贡献前先完成:

  1. 在 test/ 中补充或更新对应行为的测试;
  2. 运行 uv run python -m unittest discover -s test -v;
  3. 运行 npm run front:build,确保前端类型检查和构建通过;
  4. 在 PR 描述中说明改动范围、配置影响和复现步骤。

项目地址:https://github.com/The-never-lemon/PaperAgentMain


让论文检索更快,让研究脉络更清楚。

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages