🚀

AI 驱动的 RSS 智能摘要阅读器:每天自动消化 50+ 篇文章

手把手教你用 Python + RSSHub + Ollama 搭建一个 AI 摘要阅读器,告别信息过载。

👁️ - 次阅读

项目背景

每天刷几十个公众号、看十几篇技术博客,时间全耗在"浏览"上了。

真正的阅读是理解,不是翻页。

我试过 Feedly、Inoreader,它们能聚合内容,但不会帮你提炼重点。后来我搭了一个 AI RSS 摘要器,每天早上推送 10 条精华摘要,30 秒搞定一天的信息摄入。

今天把这个方案分享出来。

技术选型

组件选择理由
语言Python 3.10+生态丰富
RSS 解析feedparser经典稳定
AI 模型Ollama + Qwen2.5本地运行,免费
定时任务cron系统自带
推送方式钉钉 Webhook配置简单

实现步骤

第一步:安装依赖

pip install feedparser requests

确保 Ollama 已安装并拉取模型:

ollama pull qwen2.5:7b

第二步:编写核心脚本

创建 ai_rss_summarizer.py

#!/usr/bin/env python3
"""AI RSS 智能摘要阅读器"""

import json
import os
import time
import hashlib
from datetime import datetime
from pathlib import Path

import feedparser
import requests

# ============ 配置区 ============

# RSS 源列表(替换为你关注的源)
RSS_FEEDS = [
    {"name": "Hacker News", "url": "https://hnrss.org/frontpage"},
    {"name": "InfoQ 中文站", "url": "https://www.infoq.cn/feed"},
    {"name": "阮一峰周刊", "url": "https://www.ruanyifeng.com/blog/atom.xml"},
    {"name": "V2EX", "url": "https://www.v2ex.com/index.xml"},
]

# 摘要数量(每个源取最新 N 条)
ARTICLES_PER_FEED = 3

# 摘要提示词模板
SUMMARY_PROMPT = """你是一个技术资讯编辑。请对以下文章进行摘要。

要求:
1. 用一句话概括核心内容
2. 列出 2-3 个关键要点
3. 标注难度等级(入门/进阶/高阶)

文章标题:{title}
作者:{author}
发布时间:{pub_date}
正文摘要:{content}

只输出 JSON 格式,不要其他文字:
{{"summary": "...", "key_points": ["...", "..."], "difficulty": "..."}}
"""

# 钉钉 Webhook(留空则只打印到终端)
DINGTALK_WEBHOOK = os.getenv("DINGTALK_WEBHOOK", "")

# 缓存文件路径
CACHE_FILE = Path.home() / ".ai_rss_cache.json"


def load_cache() -> dict:
    """加载已处理的文章 ID 缓存"""
    if CACHE_FILE.exists():
        return json.loads(CACHE_FILE.read_text())
    return {}


def save_cache(cache: dict):
    """保存缓存"""
    # 只保留最近 1000 条记录,防止无限增长
    items = list(cache.items())[-1000:]
    CACHE_FILE.write_text(json.dumps(dict(items)))


def fetch_articles(feed: dict) -> list:
    """从 RSS 源获取文章列表"""
    entries = []
    try:
        d = feedparser.parse(feed["url"])
        for entry in d.entries[:ARTICLES_PER_FEED]:
            entries.append({
                "feed_name": feed["name"],
                "title": entry.get("title", "无标题"),
                "link": entry.get("link", ""),
                "author": entry.get("author", "未知"),
                "pub_date": entry.get("published", "未知"),
                "content": (entry.get("summary", "") or "")[:500],
                "id": hashlib.md5(entry.get("link", "").encode()).hexdigest(),
            })
    except Exception as e:
        print(f"[错误] 获取 {feed['name']} 失败: {e}")
    return entries


def summarize_with_ollama(article: dict) -> dict:
    """调用 Ollama 生成摘要"""
    prompt = SUMMARY_PROMPT.format(
        title=article["title"],
        author=article["author"],
        pub_date=article["pub_date"],
        content=article["content"],
    )
    
    try:
        resp = requests.post(
            "http://localhost:11434/api/generate",
            json={
                "model": "qwen2.5:7b",
                "prompt": prompt,
                "stream": False,
                "options": {
                    "temperature": 0.3,
                    "num_predict": 300,
                },
            },
            timeout=30,
        )
        result = resp.json()
        raw_output = result.get("response", "")
        
        # 尝试从返回中提取 JSON
        start = raw_output.find("{")
        end = raw_output.rfind("}") + 1
        if start >= 0 and end > start:
            return json.loads(raw_output[start:end])
        
        # 降级:直接返回文本摘要
        return {
            "summary": raw_output[:200],
            "key_points": [],
            "difficulty": "未识别",
        }
    except Exception as e:
        print(f"[警告] Ollama 请求失败: {e}")
        return {
            "summary": article["title"],
            "key_points": [article["content"][:100]],
            "difficulty": "未知",
        }


def send_dingtalk(notifications: list):
    """发送钉钉通知"""
    if not DINGTALK_WEBHOOK:
        return
    
    text = "📰 AI RSS 每日精选\n\n"
    for i, item in enumerate(notifications, 1):
        text += f"{i}. **{item['feed_name']}**: {item['title']}\n"
        text += f"   {item['summary']}\n\n"
    
    payload = {
        "msgtype": "markdown",
        "markdown": {
            "title": "AI RSS 每日精选",
            "text": text,
        },
    }
    
    requests.post(DINGTALK_WEBHOOK, json=payload, timeout=10)


def main():
    cache = load_cache()
    notifications = []
    new_count = 0
    
    for feed in RSS_FEEDS:
        articles = fetch_articles(feed)
        for article in articles:
            # 跳过已处理的文章
            if article["id"] in cache:
                continue
            
            cache[article["id"]] = datetime.now().isoformat()
            
            # 生成摘要
            summary = summarize_with_ollama(article)
            
            notification = {
                "feed_name": article["feed_name"],
                "title": article["title"],
                "link": article["link"],
                "summary": summary["summary"],
                "key_points": summary.get("key_points", []),
                "difficulty": summary.get("difficulty", "未知"),
            }
            notifications.append(notification)
            new_count += 1
    
    save_cache(cache)
    
    if notifications:
        print(f"\n✅ 共发现 {new_count} 条新内容\n")
        for item in notifications:
            print(f"[{item['feed_name']}] {item['title']}")
            print(f"  摘要: {item['summary']}")
            print(f"  难度: {item['difficulty']}")
            print()
        
        send_dingtalk(notifications)
        print("📤 钉钉通知已发送")
    else:
        print("📭 没有新内容")


if __name__ == "__main__":
    main()

第三步:设置定时执行

crontab -e

添加以下内容(每天早上 9 点执行):

0 9 * * * /usr/bin/python3 ~/ai_rss_summarizer.py >> ~/rss_summary.log 2>&1

第四步:测试运行

python3 ai_rss_summarizer.py

预期输出类似:

✅ 共发现 12 条新内容

[Hacker News] Show HN: I built a real-time collaborative code editor
  摘要: 作者展示了一个基于 CRDT 算法的实时协作代码编辑器,支持多人同时编辑同一文件...
  难度: 进阶

[阮一峰周刊] 2026年第28期:AI Agent 框架盘点
  摘要: 本期周刊聚焦 2026 年主流的 AI Agent 开发框架,包括 LangChain、AutoGen 等...
  难度: 入门

运行效果

跑起来之后,每天早上 9 点你会收到一份精简版资讯。

每条内容包含:一句话摘要、关键要点、难度标签。

不需要点开链接就能判断哪些值得深入阅读。

我实际跑了两周,每天大约处理 30-50 条新内容,真正需要点开看的不到 10 条。

优化方向

加个 Web 界面。 用 Streamlit 写个简单的页面,可以手动刷新、收藏、搜索历史摘要。

接入更多数据源。 RSSHub 能覆盖几乎所有网站,把 RSSHub 的地址加到 RSS_FEEDS 里就行。

模型切换。 如果 Qwen2.5 响应太慢,可以换成 llama3.2:1b 或者调用 API 版的 GPT-4o-mini。

去重增强。 当前用 URL MD5 做去重,如果遇到短链会被误判。可以加一个语义相似度检查,用 sentence-transformers 算余弦距离。

你平时都关注哪些 RSS 源?加到我的列表里,明天就能看到效果。

这篇文章对你有帮助吗?