打造AI原生RSS阅读器:架构、功能与踩坑全复盘

📅 发布时间:2026/10/19 4:26:01
打造AI原生RSS阅读器:架构、功能与踩坑全复盘
老实说当我第一次看到“AI原生RSS阅读器”这个概念的时候第一反应是这不就是把大模型塞进一个 RSS 工具里吗但真正动手把一个叫Oksskolten的阅读器从零搭出来之后我发现自己之前的理解确实太片面了。RSS 不算新鲜信息过载在今天反而比十年前更严重大模型也不算新鲜人人都能调接口。把这两者放在一起真正难的点不是“接入模型”而是怎么接得顺手、怎么接得不像一个「带 AI 按钮的普通阅读器」而是从底层逻辑上就为模型能力重新设计了一遍工具。这篇文章是我对这个项目的一次完整复盘。我会从为什么做、架构怎么选、核心功能怎么实现、踩过哪些坑、以及最后给想抄作业的同学什么建议一条条讲清楚。它不是那种“跑个 demo 就发推”的项目总结里面有不少我在实际开发中反复调整才想明白的东西。如果你也在做 RSS 相关工具或者想把大模型塞进一个具体产品里这篇应该能让你少走一点弯路。1. 为什么我会做一款AI原生RSS阅读器1.1 信息过载与RSS的尴尬处境我一直是 RSS 的重度用户订阅了大概 200 多个源从技术博客到智库报告从行业新闻到个人 newsletter。按理说 RSS 应该帮我高效阅读但实际上每天打开阅读器面对的是一屏一屏的“原始文章堆”。标题、摘要、全文混杂在一起不同源反复报道同一件事大量水文和广告软文混在中间。我只能靠自己的判断力去筛一天两小时刷完真正有价值的信息不到十分之一。问题出在哪RSS 这套协议本身提供的是“原始订阅能力”它把内容分发给你但并没有帮你做信息整理。传统阅读器做的更多是排版优化、未读管理、多端同步本质上是把凌乱的全文变成更好看的列表。可是信息过载的核心矛盾不是“列表不好看”而是“需要人花时间判断哪些值得读、哪些不需要读”。这种情况下AI 似乎理所当然应该出场。但大部分现有工具的 AI 功能是在阅读列表里加一个“AI 摘要”按钮点一下才调模型生成一段总结。这种用法我试过几十篇文章挨个点按钮体验非常割裂。它更像是在一个旧工具外面贴了一层人工智能并没有改变阅读流程本身。1.2 “AI增强”与“AI原生”到底差在哪我后来想明白一个概念上的区别AI增强和AI原生是两件事。AI增强指的是“原来的功能不变AI 在旁边辅助”AI原生指的是“整个产品的工作流以 AI 为核心重新设计”。我用一个生活化的类比。传统手机拍照加个美颜滤镜那是增强但今天手机拍照默认就是靠多帧合成、计算摄影来成像算法在拍照的每一步都起作用这叫原生。RSS 阅读器也一样——不是“抓完文章用户在看之前可以点一个摘要按钮”而是“抓完文章系统自动完成摘要、分类、去重、聚类用户打开看到的是已经整理好的结果”。这就是我在 Oksskolten 里想实现的东西。实现这种体验意味着系统在抓取和展示之间插入了一个“AI 处理层”。每一篇文章进来之后先被模型阅读和理解生成摘要、提取关键词、判断类型、输出结构化信息然后才进入用户的阅读队列。用户面对的不再是 200 个源丢过来的原始内容而是一个已经被过滤、去重、归类过的信息流。这个流程里模型不是辅助你阅读的工具而是阅读管线上不可分割的一部分。1.3 项目目标与功能边界当然“AI 原生”也不能无限膨胀。刚开始我把需求想得很大想做全文自动重写、想做个性化推荐、想做智能生成周刊、想做社区分享。后来冷静下来划定了最小可用范围。最终我确定的核心功能只有四个智能摘要、自动聚类、划词问答、质量过滤。这四个功能覆盖了“减少阅读量、看清事件全貌、快速获取解释、过滤低质内容”这四个最痛的场景。我不做全文重写因为会改变作者原意伦理和法律风险都高我也不做社交推荐因为那跟 RSS 的“用户自主订阅”精神是冲突的。这个边界很重要。很多个人项目死掉不是因为做得太少而是因为野心太大最后核心功能都没打磨好。AI 原生产品尤其容易掉进“什么都能做但什么都不精”的坑。2. 核心架构与技术选型2.1 总体数据流解析Oksskolten 的数据流是我画的最多的一张图。整条链路可以概括成一条单向管道订阅源 → 抓取调度器 → 正文提取器 → 内容净化器 → AI 处理引擎 → 存储与索引 → 阅读界面这个顺序看着简单但每一步都有学问。抓取调度器负责按周期获取源内容它不关心文章内容是什么只负责拿到原始数据。紧接着正文提取器把 HTML 或 RSS 摘要转成干净的全文。这一步筛掉导航菜单、页脚、广告脚本。内容净化器做的是清理去掉无意义字符、修正乱码、统一编码格式。然后才轮到 AI 处理引擎。这里我做了一个经验总结AI 不要直接吃原始网页抓下来的文本否则模型会把导航文字、版权信息、无关推荐一起当成正文去总结效果非常差。必须经过前面的净化步骤喂给模型的才是高质量正文。处理完的文章会带着摘要、标签、分类等元信息进入存储层同时生成向量索引。用户在界面看到的内容已经是这条流水线的成品不是原始半成品。2.2 文章解析与正文提取方案正文提取是整个项目里最容易被低估的部分。RSS 源有两种常见情况一种是源已经提供了全文内容那直接取content字段就行另一种是只提供摘要这时候必须去抓取原始页面再从 HTML 里提取正文。针对第二种情况我一开始想过用比较重的方式比如训练一个专门模型来识别正文区域。后来发现成本太高而且训练数据难以覆盖各种网页结构。实际采用的方案是“文本密度打分法”——解析 HTML 成 DOM 树遍历所有块级元素根据文本长度、链接密度、标点符号比例来打分。一个段落如果文字很多、链接很少很可能就是正文反过来如果一段内容满屏都是a标签那基本可以判定是导航或者文章列表。这个方案看起来很土但实测下来稳定度很高。配合几个兜底规则比如优先选择article标签内部的内容实在找不到就取整页去掉nav、footer、script、style之后的文本能覆盖绝大多数站点。真正需要为每个站单独写解析规则的不到 5%。2.3 AI能力如何接入本地优先还是调用云端大模型这是整个项目里我纠结最多的一个决策。云端大模型的摘要质量确实更高表达更自然理解能力更强但需要把用户订阅的文章原文发送到远端服务这里牵扯隐私、费用、网络稳定性三个问题。本地小模型则完全相反离线可用、隐私可控、免费但质量上限确实低一些。Oksskolten 最终采用的是“本地优先 云端可选”的双轨策略。默认情况下摘要和关键词生成跑在本地模型上划词问答因为需要更强的推理能力默认走云端通用模型接口但如果用户愿意也可以手动切到本地模型代价是要等更久。从产品角度讲这种设计让用户自己掌握隐私和质量的平衡点而不是替用户做决定。很多开源项目把这做成二选一但我认为应该允许“摘要用本地、问答用云端”这种混合模式。实际运行的体验是本地模型生成的摘要虽然偶尔语句生硬但信息密度足够云端模型更适合回答“这篇文章的核心论点是什么”这类开放问题。2.4 存储与索引设计存储层我用的是嵌入式数据库好处是单文件、零配置、备份方便非常适合个人自托管场景。表结构上分成几个核心部分订阅源表、文章表、AI 元信息表、阅读状态表、向量索引表。文章表保存标题、原文、净化后正文、抓取时间、原文链接等基础信息。AI 元信息表保存摘要、关键词、内容类型、质量评分。向量索引表负责给文章标题和摘要分别生成向量这是做聚类和语义检索的基础。我踩过的一个坑是向量索引在文章量超过一定数量之后查询速度会肉眼可见地下降。之后我做了两个优化一是只对标题和摘要做向量不对全文做向量因为全文向量太占空间二是给向量索引表单独做了分区按月份划分查询时只搜索最近 90 天的索引。这样在几万篇文章的规模下检索依然能保持在毫秒级响应。3. 关键功能背后的实现思路3.1 智能摘要为什么不能直接截取正文在实现摘要之前我犯过一个直觉错误把文章前 N 个字符截出来或者把每个段落第一句拼起来以为这就是摘要。这种抽取式摘要虽然速度快但很难抓住文章的核心脉络。比如一篇技术教程开头通常在讲背景和安装最精华的用法和坑点在中后段直接截前 500 字读者根本看不到重点。后来我改用生成式摘要但这里有个新问题大模型输入有 token 限制长文章必须分段处理或截断。我的做法是先把正文压缩到模型可接受的长度通过“提取每段的核心句”做一个粗筛把最关键的 20% 文本保留下来再把这些内容连同标题一起喂给模型生成摘要。这个两步法的好处是兼顾精度和效率。模型读到的不是随机截断的开头而是经过统计筛选后代表全文重点的内容。生成的摘要控制在 3 到 5 句话第一句交代事件第二句给核心信息后面补充关键细节。实测下来用户判断“这篇要不要点进去看”所需的信息量这个长度刚好。3.2 自动聚类与话题分组同一个新闻事件第二天必然出现在至少五个订阅源里标题措辞不同角度不同但本质上是同一件事。传统阅读器把这几篇分散开逼着用户反复读到重复内容。我希望能做到“同一事件只出现一次点进去能看到不同来源的报道”。实现思路是这样的每篇文章进来后用标题和摘要分别生成向量。定时任务每隔一段时间跑一次聚类两篇文章的向量余弦相似度超过阈值就判定为同一事件。聚类算法我没有选特别复杂的而是用并查集把相似文章连接成组每组选一个“代表文章”展示在主列表里其他文章收进折叠区。阈值的选择很关键。设太高同一事件的文章无法聚合设太低会把两个无关话题强行合到一起。我在测试集上调试过最终取的是 0.82主要用来判断标题加摘要整体语义相似度而不是只看标题。实际体验中同一事件聚合的准确率能在八成以上偶尔误判我会让用户手动“拆开”这个分组并把修正结果记录到系统里帮助后续优化。3.3 划词问答与对话式检索划词问答是 Oksskolten 里最“炫技”但最实用功能。用户阅读文章时可以选中一段不理解的内容直接问“这句是什么意思”系统会结合上下文给出解释。这个能力本质上是 RAG检索增强生成但实现上有一些细节值得说。首先要把文章切分成语义完整的切片不是按固定字符数硬切那样会把一句话从中间切断。我的做法是按段落标记切分段落过长时再按句号断句保证每个切片是完整的语义单元。提问时把选中片段以及它前后各一个切片作为上下文拼进提示词让模型解释选中的内容。配合全文检索这个功能还能做更大范围扩展用户可以不选中文本直接问一个和整篇文章相关的问题系统会检索全文切片找到最相关的几块内容再生成回答。这里我一定会附上引用来源的位置让用户能点回去核对原文避免无脑信任模型输出。3.4 内容质量过滤与去重质量过滤我使用了规则和模型结合的方式。规则层负责识别明显的广告、推广软文、或只有几百字没营养的内容模型层负责给文章打上内容类型标签例如“资讯”“教程”“观点”“讨论”“转载”并给出一个 0 到 1 的质量分。用户可以根据自己的偏好设置过滤器比如“教程类和质量分高于 0.6 的保留资讯类只看高分源前 20 条”。这个设置直接作用在阅读列表生成环节让每个人看到的信息流都不一样。去重和聚类稍有不同。聚类解决的是“同一事件的多个报道”去重解决的是“完全一样的文章在不同源重复发布”。去重我用的是内容哈希加向量相似度双重判断完全一致的内容直接丢弃相似度超过 0.95 且来源不同的保留发布时间更早、来源权重更高的那篇。这件事必须放在 AI 处理之前做否则模型会对重复内容白白消耗算力。4. 从零到可用的完整构建过程4.1 工程骨架与模块划分Oksskolten 的工程结构按照数据流的阶段划分清楚避免后期改不动。核心模块包括fetcher抓取、parser正文解析、cleaner内容净化、engineAI 处理、storage存储访问、api接口层、web前端界面。模块之间通过简单的事件机制通信抓取完成触发解析完成事件解析完成触发 AI 处理事件。好处是每个模块可以独立开发和测试坏处是如果抓取量大事件链可能变得难以追溯。我做了统一的日志记录每条文章都有处理轨迹出了问题可以直接看到卡在哪个阶段。构建顺序上我不建议先把 UI 做得漂亮。我的经验是先把数据管道跑通能抓到一个源、能提取正文、能调用模型生成摘要、能存进数据库这时候核心价值已经成立UI 只是外皮。我在前期花了一周把管道打通UI 反而是在后面两天内快速搭出来的。4.2 订阅管理与抓取调度的实现细节抓取调度是很容易被低估的模块。如果只是写一个定时任务每半小时把所有源扫一遍实际跑起来会遇到大量问题有的源更新慢频繁抓取浪费资源有的源更新快半小时内可能有新内容错过了有的源对抓取频率敏感容易被临时封禁。我最后实现了“按源的更新频率动态调整抓取间隔”的逻辑。系统记录每个源的更新时间历史根据平均更新间隔计算下一个抓取时间点。如果一个源已经连续 7 天没有任何更新抓取频率自动降低一旦发现新文章立刻恢复高频状态。另外抓取请求要带上合理的 User-Agent遵守目标的robots规则对返回 304 的源使用条件请求跳过下载全文。这些细节不做好整个阅读器就会被各种抓取异常搞崩。我的开发机上曾有一次因为循环抓取同一个坏源把日志文件刷到几个 GB排查到半夜才发现是重试逻辑没加次数上限。4.3 大模型在阅读流程中的接入示例下面给出一段简化后的接入逻辑展示模型调用是怎么跟文章处理流程结合的。这段代码不依赖特定模型厂牌抽象成接口后可以灵活替换后端实现。class SummaryGenerator: def __init__(self, client): self.client client def summarize(self, title, cleaned_content): prompt f 你是一个信息助理。请阅读以下文章内容输出一段中文摘要。 要求 1. 不超过 5 句话 2. 第一句概括核心事件 3. 后续句子补充关键细节、数据、结论 4. 只输出摘要本身不要输出任何额外解释。 标题{title} 正文片段 {cleaned_content[:4000]} result self.client.chat(prompt) return self._clean_output(result) def _clean_output(self, text): # 去掉模型偶尔多出来的引号、前缀等 return text.strip().strip(“”).strip()实际生产里我会把这段摘要生成函数放进处理管线中每篇净化后的文章都会并行调用处理完再入库。调用大模型时一个重要经验是给模型的提示词指令部分固定文章内容部分动态拼接格式越统一模型输出越稳定。如果想进一步压缩成本还可以给摘要增加定时批处理每 10 篇文章打包一次性请求但对单篇摘要质量会有轻微影响我最终没有采用。4.4 前端与交互设计考量前端阅读体验上我坚持三个原则默认视图要为“信息整理后”做优化、键盘操作要快到能脱离鼠标、AI 的痕迹不能喧宾夺主。主界面有三种视图。时间线视图是按时间排序的折叠卡片每个话题组只显示代表文章标题和摘要展开才看到组内其他源。专题视图是按自动聚类结果组织的适合看“这两天发生了什么”。搜索视图是问答入口可以直接自然语言提问例如“最近关于某技术的讨论”系统会返回相关文章和一段汇总。AI 处理结果在界面上是以“标签”和“摘要块”呈现的我刻意弱化“这是 AI 生成的”这一视觉信号。因为对用户来说他们只需要看到“这个信息被整理过”而不是时刻提醒他们在和一个模型互动。任何产品如果在界面上到处标注 AI 徽章反而会让人觉得是堆功能、秀技术。5. 实测中踩过的坑与排查方案5.1 正文提取不准导致AI瞎总结第一个让我头疼的问题是当正文提取器错判时模型基于错误输入输出的摘要会非常荒谬。有一次我在测试某科技媒体的一篇文章摘要居然总结成了“本文介绍了网站底部导航栏的各个链接”——因为正文提取器把整个侧边栏和页脚当成正文了。排查过程让我意识到正文提取的准确率直接决定了 AI 功能质量的上限。针对这个问题我加强了两道防线。第一道提取正文时加入“链接密度阈值”一个区块如果链接占比超过 30%直接判定为导航类内容不参与评分。第二道模型调用前加入“正文质量自检”——如果净化后的正文长度小于 300 字或者正文中大量出现 “首页”“关于我们”“联系”这类导航词就把这篇文章标记为“解析可疑”不执行 AI 摘要等待人工检查或下次抓取时再重试。这个坑给测试方法带来的教训是不能只拿几个热门站点调试正文提取器要拿至少 50 个不同类型的站点做回归测试尤其是那些结构奇怪的个人博客和新闻聚合站。5.2 重复文章与滚动更新的处理重复文章比我想象得要复杂。最开始我只用标题去重结果发现同样一篇报道有的源标题写成“某公司发布新品”有的写成“新品正式发布这几点值得关注”标题看似完全不同但内容几乎一样。后来改成标题向量相似度加正文哈希双重判断才把重复率降下来。还有一个更难处理的情况原文作者更新了文章。同一篇文章 URL 不变但标题改了一个词正文里加了一段更新说明。这时候直接当新文章入库用户会看到两条几乎一样的完全去重又会漏掉作者的更新内容。我的方案是检测到 URL 已存在但内容哈希变化就做“版本更新”保留原文更新正文、摘要和抓取时间并在阅读列表中标记“有更新”。这样既不产生重复也不会漏更新。5.3 请求超时、限流与失败重试抓取线上的源网络问题永远比机器问题多。有的站点响应特别慢有的站点会主动限流有的站点偶尔返回 500。我一开始遇到超时就直接标记失败结果很多正常源被误杀。后来重写了重试策略连接超时 10 秒读取超时 15 秒超时后先等待 30 秒重试一次如果仍失败把重试间隔指数递增最多重试 3 次。重试之间加入随机抖动避免多个源同时被限流后的“惊群”重试。对于返回状态码我做了一个白名单与黑名单的组合判断404、410 表示永久失效直接停用订阅源并通知用户500、502、503 是临时故障按限流策略重试401、403 则可能是需要登录或封禁停止高频抓取等待人工确认。这套规则上线之后抓取成功率从最初不到 85% 提到 97% 以上。5.4 本地模型硬件占用过高怎么办本地模型是这台机器上的资源大户。最初我在一台没有独立显卡的笔记本上跑每次生成摘要 CPU 直接拉满风扇狂转浏览器都跟着卡。后来做了几个调整这一节和踩坑的朋友们分享一下。首先是把模型选型改成量化版本精度有小幅下降但对摘要任务影响极小内存占用能降 60% 左右。其次是限制并发AI 处理的所有请求进入一个队列同一时间最多跑 1 个生成任务避免多个模型实例同时加载。还有个办法是缓存相同文章重复处理时直接读取上次的结果不再调用模型聚类任务和摘要任务分开跑摘要优先级更高聚类可以等系统空闲时再做。有人会问“本地模型摘要质量不如云端怎么办”。我的答案是可以分层固定源里的重要源可以用云端模型其余源用本地模型用户手动标记为收藏的源再触发一次高质量摘要。这种分层策略是我测试下来兼顾成本和效果的最好方式。6. 给想抄作业的同学几点实打实建议6.1 功能优先级排序如果现在让你从零开始做同样的项目我建议按这个顺序来优先级功能模块原因P0RSS 抓取与正文提取没有稳定数据来源一切 AI 功能都是空谈P0存储与重复过滤数据管道的入库环节不干净后期很痛苦P1摘要生成最核心的 AI 价值点快速验证产品方向P1话题聚类解决“同一事件多篇报道”的高频痛点P2划词问答工程量和 AI 成本都高适合做差异化亮点P2质量过滤需要积累一定数据量后效果才明显不适合初期做很多同学一上来就想做问答、做智能推荐实际体验是AI 摘要都没做稳定问答更是错漏百出。先把一个点做到 90 分再扩展是这类项目最稳的路线。6.2 数据隐私与自托管边界这个项目从一开始就定位为“本地优先、自托管”。用户的订阅数据、阅读记录、文章全文都保存在自己的存储里默认不发送到任何远程服务。只有用户主动开启“云端模型增强”后文章片段才会发送给模型服务商处理而且发送的是净化后的正文片段不是原始 HTML。自托管工具的边界问题在于“用户要为运维买单”。如果你要做成开源项目就得把部署流程做得足够简单一键启动配置文件清晰日志可读。我后来把整个项目封装成一个容器镜像用户只需要准备好订阅源列表所有依赖自动拉取。这样既保留了自托管的数据所有权又不至于把用户挡在“会 Docker”这个门槛之外。6.3 后续扩展空间这个项目后续的扩展方向很多我会优先考虑三个第一个是移动端适配现在很多人的阅读场景在通勤路上第二个是“智能日报”每天定时把昨天最重要的几件事生成一份简报推给用户第三个是更多源的适配特别是不再支持 RSS 的平台可以通过 HTML 扫描来弥补订阅源缺失的问题。但我个人不会做的一件事是引入社交媒体式的“推荐流”。RSS 的价值就在于用户自己掌握订阅权如果最终还是变成算法投喂那项目就失去灵魂了。AI 在这个工具里的角色应该是“帮用户更好、更快地处理自己选择的内容”而不是“替用户决定看什么”。这大概是整个项目里我最坚持的一个原则也是我觉得“AI 原生”这个词最值得敬畏的地方——原生不是什么都替用户做了而是在用户划定的范围内把该做的事做到极致。