- 人工智能
- AI 应用
- RAG
- 科研
【免费下载链接】zotero-arxiv-daily
Recommend new arxiv papers of your interest daily according to your Zotero libarary.
本文是一份面向研究者的实战部署指南,围绕开源项目Zotero-arXiv-Daily展开:它通过读取你 Zotero 文献库的上下文,每天自动检索 arXiv(以及 bioRxiv / medRxiv / chemRxiv)新发布论文,用嵌入相似度排序后由大模型生成 TL;DR,最终以邮件形式推送到你的邮箱。读完本文,你将掌握在 GitHub Actions 上零成本部署、用CUSTOM_CONFIG完整自定义配置、理解多源检索与重排序的底层实现,并能把整个流水线迁移到本地运行。
项目是什么
Zotero-arXiv-Daily的核心思路是"以你的 Zotero 文献库为兴趣画像":先读取你已收藏论文的标题与摘要,再从 arXiv 等预印本平台拉取前一天新发布的论文,计算两者之间的语义相似度并排序,把最贴近你近期研究方向的文章推送给你。项目描述原文为:
Track new scientific researches of your interest by just forking (and staring) this repo!😊
它的典型部署形态是GitHub Actions Workflow——只需 fork 仓库并配置少量环境变量,即可实现零成本、无需安装、每日自动投递。对于公开仓库,所有计算都可以在 GitHub Actions runner 的免费配额内完成。
核心特性
- 完全免费:所有计算都在 GitHub Action runner 本地完成(公开仓库配额内),无需购买服务器。
- AI 生成的 TL;DR:为每篇论文生成一句话摘要,方便快速筛选目标论文。
- 作者机构解析:论文中作者的所属机构会被解析并展示在邮件中。
- PDF 与代码链接:邮件中包含论文 PDF 下载链接,以及(如果存在的话)代码实现链接。
- 相关性排序:论文列表按照与你近期研究兴趣的相关度排序。
- 快速部署:fork 仓库后在 GitHub Actions 页面设置环境变量即可。
- LLM API 支持:可接入任意 OpenAI 兼容 API 生成 TL;DR。
- 忽略不想要的文献:支持用 glob 模式列表过滤掉 Zotero 库中不想参与兴趣建模的条目。
- 多论文源支持:内置 arxiv、biorxiv、medrxiv、chemrxiv 四种检索源。
工作原理:从 Zotero 到邮箱的完整流水线
README 的 "How it works" 一节描述了整体机制:项目首先通过 API 读取 Zotero 文献库中的全部论文,以及前一日发布的所有新论文;随后用嵌入模型计算每篇论文摘要的向量表示;每篇候选论文的得分是其与所有 Zotero 文献的加权平均相似度(越新加入文献库的论文权重越高);每篇论文的 TL;DR 由 LLM 基于pymupdf4llm提取的正文文本生成。
结合源码,这条流水线在 executor.py 的Executor.run()中被编排为六个环节:
- 拉取 Zotero 语料库:
fetch_zotero_corpus()(executor.py)通过 pyzotero 读取conferencePaper / journalArticle / preprint三类条目,且要求abstractNote非空,并为每篇文献递归计算其在 Zotero 集合中的路径。 - 过滤语料库:
filter_corpus()(executor.py)先按include_path白名单筛选,再按ignore_path黑名单剔除(忽略规则优先)。 - 检索新论文:按配置的
executor.source依次调用各 Retriever(arxiv / biorxiv / medrxiv / chemrxiv)拉取新论文。 - 重排序:调用 Reranker 对候选论文打分并按分数降序排列,再截取前
max_paper_num篇。 - 生成 TL;DR 与机构:逐篇调用 LLM 生成一句话总结与作者机构列表。
- 渲染并发送邮件:
render_email()生成 HTML,再经 SMTP 发送(utils.py)。
整个流水线的拓扑结构也在 CLAUDE.md 中得到了印证,其插件式设计值得关注:Retriever 通过@register_retriever注册、Reranker 通过@register_reranker注册,均通过统一的工厂函数get_retriever_cls()/get_reranker_cls()按名称动态加载(见 retriever/base.py 与 reranker/base.py)。
快速开始:GitHub Actions 部署
第 1 步:Fork 仓库
打开仓库主页点击右上角 Fork 按钮(顺手 Star 一下也是作者欢迎的😊):
[!IMPORTANT] 请密切关注上游仓库的动态,上游发布更新时及时将你 fork 的分支与上游合并(merge),以便享受新功能并修复已发现的 bug。
第 2 步:设置 GitHub Actions 环境变量(Secrets)
在仓库的Settings → Secrets and variables → Actions页面中,把下表列出的密钥逐个添加为Repository secrets。一旦设置,这些值对包括你本人在内的所有人都不可见,安全性有保障:
| Key | 说明 | 示例 |
|---|---|---|
ZOTERO_ID | 你的 Zotero 账户User ID。注意它不是用户名,而是一串数字。在 Zotero 设置页的 Security 面板中获取,位置见 assets/userid.png。 | 12345678 |
ZOTERO_KEY | 一个具备只读权限的 Zotero API key,同样在 Zotero 设置页 Security 面板创建。 | AB5tZ877P2j7Sm2Mragq041H |
SENDER | 负责发信的 SMTP 服务器邮箱账号。 | abc@qq.com |
SENDER_PASSWORD | 发件账号的密码。注意它不一定是邮箱客户端登录密码,而是SMTP 服务的授权码,请向你的邮箱服务商索取。 | abcdefghijklmn |
RECEIVER | 接收论文列表的邮箱地址。 | abc@outlook.com |
OPENAI_API_KEY | 通过 API 访问 LLM 的密钥(可选用硅基流动等平台提供的开源大模型免费 API)。 | sk-xxx |
OPENAI_API_BASE | 访问 LLM 的 API 地址。 | https://api.siliconflow.cn/v1 |
第 3 步:设置公共变量CUSTOM_CONFIG
除了 Secrets,还需要在Settings → Secrets and variables → Actions → Variables中新增一个公共变量CUSTOM_CONFIG(见 assets/repo_var.png 与 assets/config_var.png),并把下面的 YAML 内容粘贴为它的值:
zotero: user_id: ${oc.env:ZOTERO_ID} api_key: ${oc.env:ZOTERO_KEY} include_path: null # Or e.g. ["2026/survey/**", "2026/reading-group/**"] email: sender: ${oc.env:SENDER} receiver: ${oc.env:RECEIVER} smtp_server: smtp.qq.com smtp_port: 465 sender_password: ${oc.env:SENDER_PASSWORD} llm: api: key: ${oc.env:OPENAI_API_KEY} base_url: ${oc.env:OPENAI_API_BASE} api_mode: chat_completion # Or response to use the Responses API. generation_kwargs: model: gpt-4o-mini source: arxiv: category: ["cs.AI","cs.CV","cs.LG","cs.CL"] include_cross_list: false # Set to true to include arXiv cross-list papers in these categories. executor: debug: ${oc.env:DEBUG,null} source: ['arxiv']这里的${oc.env:XXX,yyy}是 Hydra/OmegaConf 的环境变量插值语法:表示取环境变量XXX的值,若该变量未设置,则使用默认值yyy。所以 Secrets 中的ZOTERO_ID、ZOTERO_KEY、SENDER、RECEIVER、SENDER_PASSWORD、OPENAI_API_KEY、OPENAI_API_BASE会分别被注入到对应配置项;executor.debug则读取DEBUG环境变量,未设置时默认为null。
若希望把 arXiv 跨列表(cross-list)论文也纳入候选,可将source.arxiv.include_cross_list设为true。
第 4 步:手动触发测试
配置完成后即可在Actions页面手动触发工作流进行验证:
[!NOTE]
Test-Workflow是主工作流(Send-emails-daily)的调试版本,它总是检索 5 篇 arxiv 论文,与日期无关;而主工作流每天自动触发,检索昨天发布的新论文。周末和节假日 arXiv 没有新论文,此时主工作流日志中可能出现 "No new papers found"。
触发后检查 Actions 日志以及收件邮箱即可确认结果。默认情况下主工作流每天22:00 UTC运行,可通过修改工作流配置.github/workflows/main.yml调整触发时间。
完整配置参考
README 同时给出了覆盖全部模块的完整配置骨架,其中???表示该值必须填写:
zotero: user_id: ??? # User ID of your Zotero account. api_key: ??? # An Zotero API key with read access. include_path: null # A list of glob patterns marking the Zotero collections that should be included. Example: ["2026/survey/**", "2026/reading-group/**"] source: arxiv: category: null # The categories of target arxiv papers. Find the abbr of your research area from arxiv category taxonomy. Example: ["cs.AI","cs.CV","cs.LG","cs.CL"] include_cross_list: false # Whether to include arXiv cross-list papers in subscribed categories. Example: true biorxiv: category: null # The categories of target biorxiv papers. Find categories from biorxiv site. Example: ["biochemistry","animal behavior and cognition"] medrxiv: category: null # The categories of target medrxiv papers. Find categories from medrxiv site. Example: ["psychiatry and clinical psychology", "neurology"] chemrxiv: include_new_versions: false # Whether to include revised versions (v2, v3, ...) of previously posted chemrxiv preprints in addition to new first postings. chemrxiv has no category filter: all new preprints (a few dozen per day) are retrieved via Crossref and left to the reranker. Example: true email: sender: ??? # The email account of the SMTP server that sends you email. Example: abc@qq.com receiver: ??? # The email account that receives the paper list. Example: abc@outlook.com smtp_server: ??? # The SMTP server that sends the email. Ask your email provider (Gmail, QQ, Outlook, ...) for its SMTP server. Example: smtp.qq.com smtp_port: ??? # The port of SMTP server. Example: 465 sender_password: ??? # The password of the sender account. Note that it's not necessarily the password for logging in the e-mail client, but the authentication code for SMTP service. Ask your email provider for this. Example: abcdefghijklmn llm: api: key: ??? # API Key of your LLM API. Example: sk-xxx base_url: ??? # API URL of your LLM API. Example: https://api.openai.com/v1 api_mode: chat_completion # The LLM API to use. Options: chat_completion or response. generation_kwargs: # Arguments for the selected LLM API. max_tokens: 16384 model: ??? language: English # Preferred language for the TL;DR. Example: English reranker: local: model: jinaai/jina-embeddings-v5-text-nano # The Hugging Face model name of the local embedding model. Example: jinaai/jina-embeddings-v5-text-nano encode_kwargs: # The kwargs for the encode method of the local embedding model. Details see sentence-transformers encode API. task: retrieval prompt_name: document api: key: null # API Key of your embedding model API. Example: sk-xxx base_url: null # API URL of your embedding model API. Example: https://api.openai.com/v1 model: null # The model name of the embedding model. Example: text-embedding-3-large batch_size: null # The batch size for embedding API requests. Adjust to match your provider's limit. Example: 64 executor: debug: false # Whether to use debug mode. Example: true send_empty: false # Whether to send an empty email even if no new papers today. Example: true max_paper_num: 100 # The maximum number of the papers presented in the email. Example: 100 source: ??? # The sources of papers to retrieve. Example: ['arxiv','biorxiv','medrxiv','chemrxiv'] reranker: local # The reranker to use. Example: 'local' or 'api'配置分层机制
配置系统基于Hydra + OmegaConf组合式配置,入口在 main.py 的@hydra.main(config_path="../../config", config_name="default")。default.yaml通过defaults依次组合base与custom两份配置:
- config/base.yaml:完整参数骨架,含所有默认值与注释(例如
email.smtp_server: ???、executor.max_paper_num: 100、executor.reranker: local); - config/custom.yaml:面向 GitHub Actions 的覆盖层,把密钥类参数与
ZOTERO_ID、SENDER等环境变量绑定,并预置smtp.qq.com: 465、model: gpt-4o-mini、arxiv 默认分类["cs.AI","cs.CV","cs.LG","cs.CL"]等开箱即用的取值; - 你在 Actions 页面设置的
CUSTOM_CONFIG变量会被覆盖到该分层配置之上,作为最终生效配置。
值得注意的是,README 示例中本地 reranker 模型名写作jinaai/jina-embeddings-v5-text-nano,而仓库内 config/base.yaml 的默认值实际为jinaai/jina-embeddings-v5-text-nano-retrieval,两者都指向 jina 的轻量级文本嵌入模型,以base.yaml为准即可。
配置与实现深入解析
Zotero 语料过滤:include_path 与 ignore_path
include_path用于把 Zotero 中某些集合纳入兴趣建模(例如只跟踪2026/survey/**、2026/reading-group/**目录下的文献),ignore_path则用于排除不需要的条目(例如archive/**、已读文献)。
其底层实现由normalize_path_patterns()(executor.py)与glob_match()(utils.py)完成:glob_match基于 Pythonglob.translate(..., recursive=True)把 glob 模式编译为正则,支持**递归匹配;而normalize_path_patterns明确规定只接受字符串列表或 null,传单个字符串会直接抛出TypeError(这一行为在 tests/test_executor.py 中有对应测试)。过滤顺序为先 include 后 ignore,ignore 优先级更高(见 executor.py)。
多源论文检索器
arXiv:RSS 订阅 + 全文本三级提取
arxiv_retriever.py 通过https://rss.arxiv.org/atom/{category1+category2+...}拉取 RSS 订阅,并利用条目中的arxiv_announce_type字段区分new(新发布)与cross(跨列表)论文:include_cross_list=false时只保留new类型;debug 模式下仅处理前 10 条。RSS 抓取内置了 5 次重试(每次间隔 5 秒),并会校验 HTTP 状态与 feed 解析状态,避免把错误的 feed 当作有效数据。
真正有技术含量的是全文本提取:convert_to_paper()(arxiv_retriever.py)按 "源码 tar → HTML → PDF" 的顺序尝试获取论文全文:
- 源码 tar:从
https://arxiv.org/src/{paper_id}下载 LaTeX 源码包,由extract_tex_code_from_tar()(utils.py)识别主.tex文件——优先依据.bbl文件匹配,找不到时寻找包含\begin{document}的候选文件,多候选时用BM25 算法按论文标题挑出最相关的文件,并把\input/\include的子文件内联合并; - HTML:通过 trafilatura 从
https://arxiv.org/html/{id}抽取正文; - PDF:下载 PDF 后用
pymupdf4llm.to_markdown()转成 Markdown(utils.py)。
下载与提取都设置了硬性超时:连接超时(10, 60)秒、PDF 提取与 tar 提取均为 180 秒,超时进程会被强制终止(_run_with_hard_timeout,arxiv_retriever.py),保证单个坏论文不会拖垮整个工作流。
bioRxiv / medRxiv:REST API + 最新日期过滤
biorxiv_retriever.py 调用https://api.biorxiv.org/details/{server}/2d拉取近两天数据,先取日期最大(即最新发布日)的那一批论文,再按category列表过滤;请求失败时最多重试 10 次、每次间隔 10 秒。受限于站点的爬虫限制,bioRxiv 论文不提取全文(full_text=None)。而 medrxiv_retriever.py 仅一行:直接继承 bioRxiv Retriever 并把server改为medrxiv,充分体现了插件体系的复用性。
chemRxiv:经由 Crossref 获取
chemrxiv_retriever.py 是一个较新的实现:由于 chemRxiv 已迁移到 Wiley Research Exchange 平台并启用 Cloudflare 防护,旧公开 API 不可用,因此改为通过Crossref REST API(前缀10.26434)获取所有 chemRxiv 预印本。它没有分类过滤(chemRxiv 每天仅几十篇新预印本),按 Crossref 的created时间戳筛出过去lookback_hours(24 小时)内的记录,并通过include_new_versions控制是否包含修订版本(v2、v3…)。docstring 中的这段注释同样说明了选择 Crossref 的缘由:每篇 chemRxiv 预印本发布后几分钟内就会以10.26434前缀注册到 Crossref。
重排序算法:时间衰减加权相似度
README 提到"越新加入文献库的论文权重越高",其精确公式在 reranker/base.py:
- 语料库按
added_date倒序排列; - 时间衰减权重
w_i = 1 / (1 + log10(i + 1))(i从 0 开始),并归一化使权重之和为 1; - 计算候选论文与语料库的相似度矩阵
sim(形状为[候选数, 语料数]); - 加权求和后整体乘以 10:
score = (sim * w).sum(axis=1) * 10,得到每篇候选论文的最终得分,再按得分降序排列。
这样的设计让最近加入的文献对兴趣画像的贡献最大,而数年前的旧文献影响力对数衰减。得分会被展示在邮件中作为相关性评分。
Reranker 有两种实现,通过executor.reranker: local / api切换:
- local(默认):使用
sentence-transformers本地加载嵌入模型(local.py),默认模型为jinaai/jina-embeddings-v5-text-nano系列,encode_kwargs中的task: retrieval、prompt_name: document是发给模型 encode 方法的参数;非 debug 模式下会静默各框架的日志与警告。 - api:通过 OpenAI 兼容的 embeddings 接口批量调用(api.py),按
batch_size(默认 64)分批编码,再对向量做 L2 归一化后计算余弦相似度矩阵。这一模式把嵌入计算从本地 GPU/CPU 挪到云端,适合不想下载模型的用户。
两种实现都继承自BaseReranker,rerank()中"排序语料 → 计算时间衰减权重 → 加权打分"的骨架完全共享,只有get_similarity_score()这一抽象方法不同。
LLM 生成 TL;DR 与机构解析
LLM 相关逻辑集中在 protocol.py 的Paper数据类中:
- TL;DR 生成(protocol.py):把标题、摘要、正文预览拼进 prompt,要求模型用
language指定的语言生成一句话总结;为避免超长 prompt,先用 gpt-4o 分词器把 prompt 截断到4000 tokens;调用失败时回退为直接使用摘要。 - 机构解析(protocol.py):基于全文开头 2000 tokens,让模型输出按作者顺序排列的 Python 机构列表,并要求只保留顶级机构(例如 "Department of Computer Science, TsingHua University" 只取 "TsingHua University")、去重;失败时置为
None。 - API 模式(
_request_llm,protocol.py):api_mode: chat_completion走chat.completions.create;api_mode: response走新版responses.create,且会自动把max_tokens映射为max_output_tokens。两种模式都接受generation_kwargs中的任意参数(如model、max_tokens)。
由于 TL;DR 需要逐篇调用 LLM,Executor.run()中使用了tqdm进度条并逐篇处理;邮件渲染(construct_email.py)会把相关性得分保留一位小数展示,作者超过 5 人时缩写为"前 3 + … + 后 2"的形式,机构最多展示前 5 个。
邮件发送:TLS → SSL → 明文自动降级
send_email()(utils.py)使用 Python 标准库smtplib:先尝试SMTP + starttls(),失败则尝试SMTP_SSL,再失败则退化为明文连接;邮件主题为Daily arXiv {yyyy/mm/dd},发件人显示为 "Github Action"。当send_empty=true且当天没有新论文时,邮件正文会是 "No Papers Today. Take a Rest!";而send_empty=false时则不发送任何邮件(Executor.run()中的短路逻辑,executor.py)。
本地运行
README 与仓库说明(CLAUDE.md)都支持在本地用 uv 直接运行。前提是安装 uv,并设置与 GitHub Actions 部署相同的环境变量:
# 先导出所有环境变量(示例) # export ZOTERO_ID=xxxx # export ZOTERO_KEY=xxxx # export SENDER=xxxx # export SENDER_PASSWORD=xxxx # export RECEIVER=xxxx # export OPENAI_API_KEY=xxxx # export OPENAI_API_BASE=https://api.openai.com/v1 cd zotero-arxiv-daily uv run main.py本仓库的实际入口位于 src/zotero_arxiv_daily/main.py,因此更稳妥的等价命令是uv run src/zotero_arxiv_daily/main.py。入口会先通过dotenv加载根目录.env文件(main.py),再依据executor.debug调整日志级别,最后构造Executor并执行整条流水线。项目要求 Python ≥ 3.13(见 pyproject.toml),核心依赖包括pyzotero、arxiv、sentence-transformers、pymupdf4llm、hydra-core、openai等,uv run会自动根据uv.lock同步环境。若偏好容器化部署,仓库还提供了 Docker 部署说明(见 assets/use_docker.md),支持定时执行、日志持久化与模型缓存。
与上游保持同步
项目处于活跃开发中,建议在 GitHub 上订阅该仓库的Watch → Releases通知,以便在发布新版本时及时获得提醒:
同步时在你 fork 的仓库中把上游 main 分支合并进来即可,具体操作可参考 GitHub 的 "Sync fork" 功能。
已知限制
- 推荐算法比较简单:README 明确说明,当前算法可能无法精确反映你的真实兴趣,欢迎提出更好的算法改进思路。这里的"简单"是指它只用摘要嵌入的加权相似度,没有引入更复杂的协同过滤或学习型排序。
max_paper_num不宜过大:过高的MAX_PAPER_NUM会让执行时间超出 GitHub Actions runner 的限额(公开仓库单次执行 6 小时,私有仓库每月 2000 分钟)。对个人使用而言,公开仓库配额通常足够;如有特殊需求,可以在自己的服务器部署、使用自托管 GitHub Actions runner,或为超出的执行时间付费。
结语
Zotero-arXiv-Daily用一套相当克制的技术栈完成了"个性化论文订阅"这件事:Zotero 做兴趣画像、多源 Retriever 做候选采集、嵌入重排序做相关度打分、LLM 做摘要与机构解析、SMTP 做最终投递,整套流水线在 executor.py 中环环相扣,并借助 Hydra 配置与插件注册机制保持高度可扩展。无论是想要一个零成本的每日 arXiv 推荐服务,还是想研究"文献库驱动的论文推荐"这一模式的工程实现,它都是一份可以直接 fork 运行、也可以细细研读的参考实现。项目以 AGPLv3 协议分发(见 LICENSE)。
- 人工智能
- AI 应用
- RAG
- 科研
【免费下载链接】zotero-arxiv-daily
Recommend new arxiv papers of your interest daily according to your Zotero libarary.
相关推荐
如何用Zotero-arXiv-Daily打造专属论文推荐系统:每天3分钟获取领域前沿研究
如何用Zotero arXiv Daily打造专属论文推荐系统:每天3分钟获取领域前沿研究 Zotero arXiv Daily是一款基于Zotero图书馆的a
人工智能AI 应用RAG科研【亲测免费】 Zotero-arXiv-Daily:每日推荐您感兴趣的 arXiv 论文
Zotero arXiv Daily:每日推荐您感兴趣的 arXiv 论文 项目介绍 Zotero arXiv Daily 是一个开源项目,旨在帮助科研人员跟踪
人工智能AI 应用RAG科研Zotero-arXiv-Daily学术论文自动推荐系统使用指南
Zotero arXiv Daily学术论文自动推荐系统使用指南 项目简介 Zotero arXiv Daily是一款基于GitHub Actions的开源智能
人工智能AI 应用RAG科研
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考