PaperRepo 是一个本地运行的 AI 科研论文管理与精读 Web 平台。它用于导入 PDF 论文、维护论文元数据、管理标签与研究集合,并通过兼容 OpenAI Chat Completions 格式的 API 生成摘要和进行论文问答。
前端使用原生 HTML/CSS/JavaScript,后端使用 Python 标准库提供静态文件、SQLite 数据 API 和 AI 代理,不需要额外安装 npm 或 Python 依赖。
在项目根目录运行:
python3 server.py 8000然后在浏览器访问:
http://localhost:8000
如果 8000 端口被占用,可以换一个端口:
python3 server.py 8010PaperRepo 当前使用两类本地存储:
- 论文元数据:保存到项目根目录的 SQLite 数据库
paperly.sqlite3。 - PDF 原文和抽取文本:保存到浏览器 IndexedDB。
- API 配置、笔记、对话历史、主题、侧边栏宽度等偏好:保存到浏览器
localStorage。
这意味着:换浏览器、清理浏览器站点数据,可能会丢失 PDF 原文、笔记或对话记录;但论文标题、作者、摘要、标签、状态、收藏和集合关系会保存在 SQLite 中。
可以通过两种方式导入 PDF:
- 点击左侧导航栏的“导入论文”。
- 将 PDF 文件拖拽到页面任意位置。
导入后,系统会先创建论文卡片,并尝试抽取 PDF 首页文本。如果已配置 AI API,会调用模型识别标题、作者、年份、摘要和标签;如果首页无法抽取出足够文字,系统会把 PDF 首页渲染成图片,尝试交给支持视觉输入的模型识别扫描版或图片型 PDF。AI 摘要会尽量统一生成中文。如果没有配置 API,则会使用有限的本地规则和默认占位信息。
支持批量导入多个 PDF。
全部论文页以单列卡片展示论文。每张卡片包含:
- 标题
- 作者和年份
- 收藏星标
- AI 摘要
- 标签
- 阅读状态
- 所属研究集合管理按钮
- 删除和开始阅读按钮
点击卡片空白区域或“开始阅读”会进入阅读页。标题、作者、摘要、标签、星标、AI 摘要等区域不会触发跳转,方便复制文本或操作按钮。
顶部搜索框会检索以下字段:
- 标题
- 作者
- AI 摘要
- 标签
命中的关键词会在卡片中以浅蓝色高亮显示。搜索框不会只匹配标题或标签。
快捷键:
Ctrl/Cmd + K:聚焦搜索框。Ctrl/Cmd + U:打开导入 PDF 文件选择器。
每篇论文可以有多个标签。
- 在论文卡片的标签区域点击
+,可以原地输入新标签。 - 按
Enter或输入框失焦会提交。 - 标签右侧的
x可以删除该标签。 - 左侧导航栏“常用标签”会根据当前库中所有论文的标签频率,显示前 3 个高频标签。
论文有三种阅读状态:
- 待读:橙色。
- 阅读中:蓝色。
- 已读:绿色。
打开一篇待读论文时,会自动变为“阅读中”,并记录最近阅读时间。阅读页右上角的绿色对勾按钮可以在“阅读中”和“已读”之间切换。
左侧“最近阅读”只显示真正打开阅读过的论文,不包含刚导入但未阅读的论文。
每篇论文卡片和阅读页右上角都有星标按钮。点击后可以收藏或取消收藏。收藏图标使用金色星形图标。
左侧导航栏“收藏”会筛选出所有已收藏论文。
研究集合用于按主题组织论文。一篇论文可以加入多个集合。
点击左侧导航栏的“研究集合”会进入集合页面。侧边栏只显示最近查看过的集合。
集合页面中每个卡片会展示:
- 集合名
- 论文数量
- 待读数量
- 已读数量
- 高频标签
集合页面第一个虚线 + 卡片用于新建集合。点击后输入集合名称:
Enter或失焦创建。Esc取消。
在集合页面点击集合卡片上的集合名,可以原地重命名。
- 点击页面其他位置会保存。
Enter保存。Esc取消。未分类是系统默认集合,不能重命名。- 如果目标名称已存在,会提示“集合已存在”。
重命名会同步更新论文所属集合、最近查看集合和当前筛选状态。
每个非 未分类 集合卡片右下角有垃圾桶按钮。点击后会弹出确认框。
删除集合只删除集合本身,不删除其中论文。如果论文删除该集合后不属于任何其他集合,会自动回到 未分类。
论文卡片底部有集合图标按钮。点击后会弹出多选列表,可以将论文加入一个或多个集合。
未分类 不会出现在多选列表中。如果取消所有集合,论文会自动回到 未分类。
阅读页分为左右两栏:
- 左侧:PDF 原文或占位论文预览。
- 右侧:论文概览 / AI 讨论。
左右两栏之间的分隔条可以拖拽,调整阅读区和 AI 区域的宽度。布局比例会保存到浏览器本地。
阅读页顶部提供:
- 返回论文库
- 标题
- 已读/阅读中切换按钮
- 收藏按钮
阅读页会尽量占满一个屏幕高度,左右面板内部滚动,底部的保存笔记按钮和发送按钮会保持可见。
“论文概览”页签展示:
- AI 摘要
- 作者
- 年份
- 标签
- 我的笔记
笔记保存在浏览器 localStorage,按论文 ID 区分。点击“保存笔记”后写入本地浏览器。
“AI 讨论”页签可以围绕当前论文进行问答。系统会优先使用已抽取的论文文本;如果没有正文,则基于标题和摘要回答,并在提示词中要求模型说明依据。
对话历史保存在浏览器 localStorage,按论文 ID 区分。
点击左侧底部“设置与 API”打开配置窗口。需要填写:
- API Base URL,例如
https://api.openai.com/v1 - API Key
- 模型名称,例如
gpt-4o-mini
点击“测试连接”可以验证配置是否可用。点击“保存配置”后,配置会保存到浏览器 localStorage。
后端通过同源 /proxy/* 转发请求,用于避免浏览器跨域和 Authorization 预检问题。实际模型接口需要兼容 OpenAI Chat Completions 格式。
顶部搜索栏右侧有主题切换按钮,可以在浅色和深色模式之间切换。主题偏好会保存到浏览器 localStorage。
如果没有手动选择主题,页面会尝试跟随系统的 prefers-color-scheme 设置。
侧边栏支持:
- 点击顶部按钮收起或展开。
- 拖拽右侧边缘调整宽度。
- 收起后只显示图标。
侧边栏宽度和收起状态会保存到浏览器 localStorage。
server.py 提供以下接口:
GET /api/papers:读取所有论文元数据。PUT /api/papers:覆盖保存论文元数据。GET /api/collections:读取集合列表。PUT /api/collections:覆盖保存集合列表。POST /proxy/*:代理 AI API 请求。
SQLite 中目前有两张表:
papers(id INTEGER PRIMARY KEY, data TEXT, updated_at INTEGER)collections(name TEXT PRIMARY KEY, updated_at INTEGER)
论文对象以 JSON 字符串保存到 papers.data。这让前端字段演进更灵活,但不是最终生产化设计。
旧版本曾将论文元数据保存在浏览器 localStorage.papers。当前版本启动时,如果后端 SQLite 没有论文且浏览器中存在旧数据,会尝试迁移到后端数据库。
如果后端不可用,前端会临时回退到浏览器本地数据,并提示“后端数据库不可用,已临时使用浏览器本地数据”。
因为 AI 请求需要通过 /proxy/* 转发,论文元数据也通过 /api/papers 和 /api/collections 保存到 SQLite。直接打开 HTML 文件不会有这些后端接口。
PDF 原文目前保存在浏览器 IndexedDB,而不是 SQLite。换浏览器或清理站点数据会影响 PDF 原文和抽取文本。
请检查:
- 是否已配置 API Base URL、API Key 和模型名。
- 是否点击过“测试连接”并成功。
- 是否用
python3 server.py启动,而不是普通静态文件服务器。 - 当前 API 是否兼容 OpenAI Chat Completions 格式。
不会。删除集合只会移除集合关系。没有其他集合的论文会回到 未分类。
可以。论文和集合是多对多关系。
修改前端脚本后,可以运行:
node --check app.js修改后端后,可以运行:
python3 -m py_compile server.py检查 diff 是否有多余空白:
git diff --check当前版本适合个人本地使用,还不是生产级多用户系统。主要限制包括:
- PDF 原文和抽取文本仍在浏览器 IndexedDB,不在后端对象存储。
- API Key 保存在浏览器本地,不适合多人共享部署。
- SQLite 中论文数据以 JSON 存储,便于原型迭代,但不利于复杂查询。
- AI 问答还不是完整 RAG,没有稳定的引用定位和向量检索。
- 尚未实现用户鉴权、权限隔离和同步机制。
后续可以考虑:
- 将 PDF 原文迁移到后端文件存储或对象存储。
- 建立论文、作者、标签、集合的规范化关系表。
- 增加用户表与登录鉴权,并按
user_id隔离数据。 - 加入正文切块、向量检索和带引用的 RAG。
- 对接 arXiv、Semantic Scholar、Zotero 等外部元数据源。