基于AI与静态生成的智能RSS聚合器FeedMe部署与定制指南
1. 项目概述
如果你和我一样,每天被淹没在几十个博客、新闻网站和科技媒体的信息洪流里,却总感觉抓不住重点,那 FeedMe 这个项目可能就是为你量身打造的。它不是一个传统的 RSS 阅读器,而是一个用 AI 帮你“咀嚼”信息的智能聚合器。简单来说,它定时抓取你订阅的所有 RSS 源,然后调用大语言模型(LLM)为每篇文章生成一段精炼的摘要,最后以一个清爽、静态的网页形式呈现给你。你不再需要逐篇点开长文,扫一眼 AI 提炼的核心观点,就能决定哪篇值得深度阅读,效率提升不止一个量级。
这个项目的核心魅力在于它的“轻量”与“自主”。它没有后端服务器,不存储你的任何数据,完全基于静态站点生成。你可以零成本地把它部署在 GitHub Pages 上,也可以一键用 Docker 在本地或自己的服务器上跑起来,所有配置——从 RSS 源列表到 AI 模型的选择——都完全掌握在你手里。对于开发者、内容创作者或任何希望高效获取信息又注重隐私和可控性的用户来说,这是一个非常优雅的解决方案。接下来,我将带你从零开始,深入理解 FeedMe 的每一个环节,并分享我在部署和定制过程中踩过的坑和总结的技巧。
2. 核心架构与设计思路拆解
2.1 为什么选择“静态生成 + 定时更新”模式?
FeedMe 的架构非常清晰:一个用 React + Vite 构建的前端应用,搭配一组在构建时(或定时任务中)运行的 Node.js 脚本。这种设计摒弃了传统的动态数据库查询,选择了预渲染静态 JSON 数据文件。
这么做的核心优势有三点:
- 极致性能与零成本托管:生成的是一堆纯粹的 HTML、JS、CSS 和 JSON 文件,可以托管在任何静态网站服务上,如 GitHub Pages、Vercel、Cloudflare Pages。这些服务通常免费且具备全球 CDN,访问速度极快,完全没有服务器维护成本和流量压力。
- 数据隐私与安全:你的 RSS 源列表和生成的摘要数据,在构建完成后就固化在静态文件里。没有远程数据库,没有用户登录系统,从根本上杜绝了数据泄露或被第三方分析的风险。数据的所有权和控制权百分百归你。
- 惊人的简单与可靠:整个系统没有复杂的运行时状态。网站一旦部署,其内容就是确定的,不会因为后端服务抖动而出现加载错误。更新内容只需重新运行一遍构建流程,生成新的静态文件并覆盖旧的即可,流程简单,易于理解和调试。
当然,这种架构的“代价”是内容无法实时更新。但 RSS 阅读本身并非聊天工具,不需要秒级同步。通过 GitHub Actions 或 Cron 设置每3小时或每天更新一次,完全能满足绝大多数人的信息消费节奏,在“实时性”和“系统复杂度”之间取得了完美平衡。
2.2 AI 摘要生成:从“获取”到“理解”的关键一跃
传统的 RSS 阅读器只是信息的“搬运工”,而 FeedMe 试图成为信息的“提炼者”。这是它最核心的价值所在。
其工作流程可以拆解为:
- 抓取与解析:脚本使用
rss-parser库,根据你的配置,并发抓取所有 RSS 源,并将 XML 格式的源数据解析为结构化的 JavaScript 对象。 - 内容预处理:并非所有 RSS 条目都适合或需要生成摘要。脚本会进行过滤,例如只处理近期(如7天内)的文章,并截取文章内容的前 N 个字符(避免超出模型上下文限制)。
- 调用 LLM API:将预处理后的文章标题和内容,结合一个精心设计的提示词(Prompt),发送至配置的 LLM API(如 OpenAI、SiliconFlow、Ollama 本地模型等)。
- 结果后处理与存储:接收模型返回的摘要文本,进行必要的清洗(如去除多余标记),然后与文章的元数据(标题、链接、发布时间等)一起,序列化为 JSON 文件,保存在
public/data目录下。
这里的关键在于提示词工程。FeedMe 默认的提示词要求模型用中文输出,并遵循“背景-核心观点-总结”的结构。在实际使用中,这个提示词是你优化摘要质量的主要杠杆。例如,如果你主要订阅技术博客,可以调整提示词,要求模型额外关注“技术方案对比”或“代码示例要点”。
实操心得:选择 LLM 服务时,除了考虑成本和速度,更要关注其 API 的稳定性和上下文长度。一些性价比高的国内服务可能偶尔会有波动。建议在
.env配置中准备好备用 API 的LLM_API_BASE,万一主服务出问题,可以快速切换。
3. 详细部署指南与避坑实践
FeedMe 提供了多种部署方式,适应不同用户的需求。我将以最常用的GitHub Pages 部署和最具控制感的Docker 本地部署为例,详细展开步骤和注意事项。
3.1 GitHub Pages 部署:零成本的自动化流水线
这是最推荐给大多数用户的方案,完全自动化,无需管理服务器。
步骤一: Fork 与基础准备
- 访问 FeedMe 的 GitHub 仓库,点击右上角的
Fork按钮,将其复制到你自己的账号下。 - 进入你 Fork 后的仓库,点击
Settings->Pages。在Build and deployment部分,Source选择GitHub Actions。这意味着 GitHub 将使用仓库里的工作流文件来构建和部署页面。
步骤二:配置核心机密(Secrets)—— 最容易出错的一步这是让 AI 摘要功能运转起来的关键。你需要准备一个 LLM API 的密钥。
- 获取 API Key:以使用硅基流动(SiliconFlow)为例,注册登录后,在控制台可以创建 API Key。其他服务如 OpenAI、DeepSeek 等流程类似。
- 配置 Secrets:在你 Fork 的仓库页面,点击
Settings->Secrets and variables->Actions。点击New repository secret来添加:LLM_API_KEY: 填入你获取到的 API 密钥字符串。LLM_API_BASE: 填入 API 的基础 URL。例如硅基流动是https://api.siliconflow.cn/v1,OpenAI 是https://api.openai.com/v1。务必注意末尾不要有斜杠。LLM_NAME: 填入模型名称。例如THUDM/GLM-4-9B-0414或gpt-3.5-turbo。需要确保该模型在你使用的 API 服务中可用。
踩坑记录:我曾将
LLM_API_BASE错误地配置为https://api.siliconflow.cn/v1/(多了斜杠),导致 API 调用 URL 拼接错误,工作流一直失败。调试这类问题,一定要去 Actions 日志里查看具体的请求 URL 是什么。
步骤三:触发首次构建与验证
- 点击仓库顶部的
Actions标签页,你应该能看到一个名为 “Update data and deploy” 的工作流。点击它,然后选择Run workflow->Run workflow,手动触发一次构建。 - 等待约 2-5 分钟,构建完成后,再次进入
Settings->Pages,你会看到一个绿色的提示和你的站点 URL(格式为https://[你的用户名].github.io/[仓库名]/)。 - 访问这个 URL,如果能看到页面但摘要区域显示“加载失败”或空白,请打开浏览器开发者工具(F12)的
Console和Network标签页,检查是否有 JavaScript 错误或data/feeds.json文件加载失败。这通常意味着上一步的 Secrets 配置有误,或者构建过程中 AI 摘要生成失败了。
步骤四:理解自动化工作流部署成功后,一切就自动化了。项目根目录下的.github/workflows/update-deploy.yml文件定义了这个流水线:
- 触发时机:每3小时一次(cron 表达式)、每次向
main分支推送代码、手动触发。 - 执行步骤:
- 检出代码。
- 安装 Node.js 和 pnpm。
- 安装项目依赖。
- 关键步骤:运行
pnpm update-feeds。这个脚本会读取你的 RSS 配置,调用 LLM API 生成摘要,并将结果写入public/data。 - 运行
pnpm build,使用 Vite 构建静态网站到dist目录。 - 将
dist目录的内容部署到 GitHub Pages。 - 额外步骤:同时将
dist目录的内容推送到一个名为deploy的分支。这个分支是为 Vercel 或阿里云 ESA 等平台准备的,它们可以监控这个分支的更新并自动部署。
你可以通过修改这个 YAML 文件中的cron: '0 */3 * * *'来调整更新频率。例如,'0 0 * * *'表示每天 UTC 时间 0 点运行一次。
3.2 Docker 本地部署:完全掌控的私有化方案
如果你希望数据完全留在本地,或者想在 NAS、家庭服务器上运行,Docker 方案是最佳选择。
步骤一:环境准备与配置
- 确保你的机器上安装了 Docker 和 Docker Compose。
- 克隆项目代码:
git clone https://github.com/Seanium/feedme.git && cd feedme。 - 复制环境变量文件并编辑:
cp .env.example .env。用文本编辑器打开.env文件,填入你的 LLM API 信息,格式与 GitHub Secrets 一致。
步骤二:深入docker-compose.yml启动前,理解一下docker-compose.yml文件做了什么:
services: app: build: . ports: - "3000:3000" volumes: - ./public/data:/app/public/data environment: - NODE_ENV=production env_file: - .env command: sh -c "node server.js"build: .:基于当前目录的Dockerfile构建镜像。Dockerfile定义了从安装依赖到构建产物的完整流程。volumes: 将本地的./public/data目录挂载到容器的/app/public/data。这是关键,它使得容器内生成的feeds.json数据文件能持久化保存在你的主机上,即使容器重建也不会丢失。env_file: 指定使用我们刚才编辑的.env文件来注入环境变量。command: 容器启动后运行一个简单的 Node.js 静态文件服务器 (server.js)。
步骤三:启动与访问在项目根目录下运行:
docker-compose up --build--build参数会强制重新构建镜像。首次运行需要下载基础镜像和安装依赖,时间稍长。看到输出中出现Server is running on http://localhost:3000后,即可在浏览器中访问。
步骤四:配置自动更新(Cron inside Docker)FeedMe 的 Docker 方案巧妙地在容器内使用了cron来定时更新数据。
- 核心文件是
src/config/crontab-docker,其内容类似:# 每3小时更新一次 0 */3 * * * /usr/local/bin/node /app/scripts/update-feeds.js >> /var/log/cron.log 2>&1 && cd /app && pnpm build >> /var/log/cron.log 2>&1 - 在
Dockerfile中,这个 crontab 文件被复制到容器内,并通过RUN crontab /app/src/config/crontab-docker加载。 - 容器启动时,会同时启动
cron服务(通过supervisord管理)。
这意味着,容器启动后,它会每3小时自动执行一次数据抓取、摘要生成和网站重建。由于数据卷 (public/data) 是挂载的,新生成的数据会立即生效。前端服务器 (server.js) 服务的是dist目录,而pnpm build会更新这个目录,所以网站内容也就自动更新了。
重要提示:如果你想修改更新频率,不能直接修改容器内的文件。正确做法是修改本地的
src/config/crontab-docker文件,然后重新构建并启动容器:docker-compose down && docker-compose up --build。
4. 个性化定制与高级配置
4.1 定制你的信息源:编辑 RSS 配置
FeedMe 的灵魂在于你喂给它什么信息。配置文件位于src/config/rss-config.js。打开它,你会看到一个feeds数组。
const feeds = [ { name: '科技爱好者周刊', url: 'https://github.com/ruanyf/weekly/commits/gh-pages.atom', category: '技术' }, { name: '阮一峰的网络日志', url: 'http://www.ruanyifeng.com/blog/atom.xml', category: '技术' }, // ... 更多源 ];- 添加源:找到你喜欢的博客或网站的 RSS 链接(通常在网站底部或侧边栏有 RSS 图标),按照格式添加一个新对象即可。
category字段用于前端分类筛选,可以自定义,如“生活”、“财经”、“设计”。 - 调整抓取数量:同文件中的
maxItemsPerFeed变量控制每个 RSS 源最多获取多少篇文章。默认是 5,可以根据需要调大,但注意这会增加 API 调用量和页面加载数据量。 - 处理特殊 RSS 源:有些网站的 RSS 输出不标准,可能导致解析失败。如果遇到,可以尝试在
scripts/update-feeds.js中为rssParser.parseURL添加自定义的解析选项,或者寻找该网站提供的替代 RSS 地址。
4.2 调整 AI 摘要行为:修改提示词与模型
如果你对默认生成的摘要风格不满意,或者想切换生成语言(如改为英文摘要),需要修改scripts/update-feeds.js文件。
找到generateSummary函数内部构造prompt的代码段。默认的提示词是:
const prompt = `请用中文为以下文章生成一段摘要,要求概括文章背景、核心观点和结论,语言精炼:\n\n标题:${title}\n\n正文:${content}`;你可以将其改为:
- 英文摘要:
Please generate a concise summary in English for the following article, covering the background, key points, and conclusion:\n\nTitle: ${title}\n\nContent: ${content} - 更技术导向:
请用中文为以下技术文章生成摘要,重点提炼其解决的问题、采用的技术方案和最终效果:\n\n标题:${title}\n\n正文:${content} - 更简短的要点:
请用中文列出以下文章的3个核心要点:\n\n标题:${title}\n\n正文:${content}
切换模型:直接在.env文件或 GitHub Secrets 中修改LLM_NAME即可。例如,从THUDM/GLM-4-9B-0414切换到gpt-3.5-turbo。不同模型的能力、速度和成本差异很大,需要根据你的需求进行权衡。对于摘要任务,中等规模的模型(如 7B-13B 参数)通常性价比很高。
4.3 前端样式与功能微调
FeedMe 使用 React + shadcn/ui + Tailwind CSS 构建,修改界面非常方便。
- 修改主题:主色调等在
tailwind.config.js中定义。如果你想换一种蓝色调,可以修改primary相关的颜色值。 - 调整布局:主要页面组件在
src/pages目录下。例如,想改变文章卡片的样式,可以修改src/components下的相关组件。 - 添加新功能:比如想增加一个“标记为已读”的功能,由于是静态页面,这个状态无法持久化。但可以利用浏览器的
localStorage在客户端实现。你需要在src目录下创建新的组件和 Hook 来管理这个状态。
开发建议:在本地进行修改前,先运行
pnpm dev启动开发服务器,可以实时看到修改效果。确认无误后,再提交代码并触发部署。
5. 常见问题排查与优化技巧
在实际部署和使用中,你可能会遇到一些问题。这里我总结了一份排查清单。
5.1 摘要生成失败或为空
这是最常见的问题,症状是网站上文章摘要部分显示“生成中…”或空白。
排查步骤:
- 检查 API 密钥与配置:确认
LLM_API_KEY、LLM_API_BASE、LLM_NAME三个变量填写正确,没有多余空格,且 API 密钥有足够的余额或调用权限。 - 查看构建日志:
- GitHub Pages:去仓库的
Actions标签页,点击最近一次运行的工作流,查看Run update-feeds步骤的详细日志。搜索Error或Failed关键词。通常日志会直接打印出 API 返回的错误信息,如Incorrect API key provided或Model not found。 - Docker 部署:查看容器的日志
docker-compose logs app,或者查看 Cron 的日志文件(如果配置了输出到文件)。
- GitHub Pages:去仓库的
- 检查网络连通性:如果你部署在服务器上,确保服务器能访问你配置的
LLM_API_BASE。可以进入容器内部执行curl命令测试。 - 内容过长被截断:有些 RSS 源输出全文且很长。
update-feeds.js脚本中有const contentSnippet = item.contentSnippet || item.content || '';和后续的截取逻辑。如果文章太长,被截断后可能丢失关键信息,导致模型无法生成有效摘要。可以考虑调整截取长度,但要注意模型本身的上下文限制。
5.2 网站能打开,但数据(JSON)加载失败
症状是页面框架正常,但一直显示加载中或列表为空。
- 检查
public/data目录:在构建产物中,确认feeds.json文件是否存在且内容有效。对于 GitHub Pages,你可以直接访问https://[你的用户名].github.io/[仓库名]/data/feeds.json看是否能下载到一个 JSON 文件。 - 检查 JSON 格式:如果 JSON 文件存在但格式错误(比如因为 API 返回异常信息被写了进去),前端就无法解析。手动打开 JSON 文件,检查其是否为合法的 JSON 结构。
- 浏览器控制台报错:打开浏览器开发者工具,查看
Console是否有类似Failed to load resource或SyntaxError: Unexpected token ... in JSON的错误。这能精确定位问题。
5.3 Docker 容器 Cron 任务不执行
容器运行了,但数据从不更新。
- 检查 Cron 日志:首先确认
docker-compose.yml中是否将 Cron 日志挂载出来,或者crontab-docker中是否配置了输出到文件(如>> /var/log/cron.log)。查看这个日志文件是第一步。 - 检查容器内进程:执行
docker-compose exec app ps aux | grep cron,确认cron进程在运行。 - 检查环境变量:Cron 任务执行的环境与交互式 Shell 环境不同,可能读取不到
.env文件中的变量。在update-feeds.js脚本开头,最好显式地打印一下process.env.LLM_API_KEY是否存在,或者确保在crontab-docker的命令中通过环境变量传递。FeedMe 目前的 Dockerfile 通过supervisord启动 Cron,环境变量继承自容器,通常是可行的。 - 手动测试命令:进入容器内部
docker-compose exec app sh,然后手动执行一次 crontab 中的命令,看是否能成功运行。
5.4 性能优化与成本控制
- 控制 RSS 源数量和抓取频率:这是影响 API 调用成本和构建时间的主要因素。
maxItemsPerFeed不要设置过高,5-10 条通常足够。更新频率cron设置为每6小时或每天一次,对新闻类源也完全够用。 - 选择性价比高的 LLM 服务:对于摘要任务,不一定需要最顶尖的模型。许多提供
ChatGLM3、Qwen或Llama 3系列模型的国内平台,成本远低于 GPT-4,效果对于摘要来说绰绰有余。 - 利用缓存:目前每次构建都会重新抓取所有源并生成所有摘要。一个高级优化思路是,在
scripts/update-feeds.js中实现一个简单的缓存机制:记录每篇文章的唯一标识(如链接),如果之前已经生成过摘要且文章未更新,则直接使用缓存的结果,避免重复调用昂贵的 LLM API。这需要修改脚本,将历史摘要存储起来。
我个人在本地 Docker 部署后,将更新频率调整为每天凌晨4点一次,订阅了约15个技术博客源,每个源抓取5条。每天早上一杯咖啡的时间,就能通过 FeedMe 生成的摘要快速浏览过去24小时的技术动态,效率极高。整个系统安静地运行在树莓派上,数据完全私有,这种掌控感和流畅的体验,是使用任何云端订阅服务都无法比拟的。如果你也厌倦了信息过载,不妨亲手搭建一个属于自己的智能信息过滤器。
