用 AI 搭一条自动化热点监控流水线:RSS 聚合、智能筛选、定时推送一次跑通

用 AI 搭一条自动化热点监控流水线:RSS 聚合、智能筛选、定时推送一次跑通

每天上班第一件事,是不是先刷一遍各种网站看有什么新东西?我认识一个做内容的朋友,每天早上要开十来个页面:几个资讯站、两个社区、一个项目趋势榜。刷完花半小时,看完能用的没几条,大部分是噪音。而且刷的过程很影响上午的状态,一打开手机就停不下来。

后来我把这件事整个自动化了:一条流水线,早上七点自动跑完,把筛选好的热点直接推到我手机里。今天这篇文章就把这条流水线的完整搭建过程写出来,每一段都有能直接跑的代码。你照着搭,也能拥有一套自己的 AI 热点雷达。

先说架构:三段式流水线

整条流水线分成三段,各干各的活:

  1. 采集段:从各信息源拉取最新内容(RSS 为主,兼容性好)
  2. 筛选段:用大模型理解内容,按你的标准打分、分类、去重
  3. 推送段:把筛选结果推到目标渠道(企业微信、邮件、Telegram 都行)

三段之间用队列串起来,任何一段挂了不影响其他段。这个设计最大的好处是:换数据源、换模型、换推送渠道,都是改一段的事,不用动整条链。

第一段:RSS 采集

RSS 到现在依然是最稳定的信息获取协议,没有反爬烦恼,格式统一。Python 生态里有现成的库,几行代码就能拉取。

python 复制代码
import feedparser

SOURCES = [
    "https://news.ycombinator.com/rss",
    "https://github.com/trending.atom",
    "https://www.infoq.cn/feed",
]

def fetch_all():
    items = []
    for url in SOURCES:
        feed = feedparser.parse(url)
        for entry in feed.entries[:20]:
            items.append({
                "title": entry.get("title", ""),
                "link": entry.get("link", ""),
                "summary": (entry.get("summary") or "")[:800],
                "published": entry.get("published", ""),
            })
    return items

几个注意点:

  • feedparser 对绝大多数 RSS/Atom 源都兼容,不用关心格式差异
  • 每个源取前 20 条就够,后面做增量去重
  • summary 字段有的源是 HTML,先截断再做清洗,别把标签带进筛选

第二段:AI 筛选

采集是体力活,筛选才是这条流水线的灵魂。直接全量推送给你会被信息淹没,必须让 AI 先替你读一遍、排个优先级。

筛选的核心逻辑是:给大模型一个评分标准,让它对每条内容打分并给出理由。标准可以非常具体,比如"与 AI 工具、自动化、效率相关的内容加 20 分;标题含具体数字加 10 分;纯广告不加分"。

python 复制代码
from openai import OpenAI

client = OpenAI()

def score_item(item):
    prompt = f"""你是热点筛选助手。根据以下标准给这条内容打分(0-100):
1. 与 AI 工具/自动化/效率 相关:+30
2. 标题包含具体数字或结果:+20
3. 有实操案例或代码:+20
4. 发布日期在 3 天内:+15
5. 明显广告或标题党:-40

内容标题:{item['title']}
内容摘要:{item['summary'][:500]}

只输出 JSON:{{"score": 分数, "reason": "一句话理由"}}"""

    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    result = json.loads(resp.choices[0].message.content)
    item["score"] = result["score"]
    item["reason"] = result["reason"]
    return item

这条流水线跑起来之后,我最大的体感是:筛出来的东西真的比我手动翻的准。因为标准是明确的、一致的,不会像人一样今天状态好就多看两眼,状态差就漏掉重点。

调用大模型有成本,所以要做两层优化:第一,只在标题和摘要上打分,不全文分析,token 消耗很小;第二,加一个关键词预筛,命中关键词的才送进模型,完全无关的直接丢掉,省掉绝大部分调用。

python 复制代码
KEYWORDS = ["AI", "自动化", "效率", "agent", "工作流"]

def cheap_prefilter(item):
    text = (item["title"] + item["summary"]).lower()
    return any(k.lower() in text for k in KEYWORDS)

第三段:推送

筛选完之后,把高分内容推送到你常用渠道。我用的是企业微信机器人,因为手机上提醒最直接;你换成邮件、Telegram 都行,接口大同小异。

python 复制代码
import requests

WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"

def push_digest(top_items):
    if not top_items:
        return
    text = "📮 今日热点速递\n\n"
    for i, it in enumerate(top_items[:8], 1):
        text += f"{i}. {it['title']}\n   {it['link']}\n   理由:{it['reason']}\n\n"
    requests.post(WEBHOOK, json={"msgtype": "text", "text": {"content": text}})

推送格式按"标题 + 链接 + 筛选理由"组织,这样你在手机上扫一眼就能决定点不点开,不用再进一遍原文页。

调度:cron 定时跑

流水线写好了,剩下就是让它每天自动跑。最简单的方式是系统自带的 cron:

bash 复制代码
# 每天早上 7 点跑一次,日志落盘
0 7 * * * cd /path/to/project && /usr/bin/python3 pipeline.py >> logs/run.log 2>&1

如果你要的是更精细的调度(比如每两小时跑一次、错峰跑不同数据源),用 APScheduler 在 Python 进程里做也行:

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler

scheduler = BlockingScheduler()
scheduler.add_job(run_pipeline, "cron", hour=7, minute=0)
scheduler.add_job(run_pipeline, "cron", hour=13, minute=0)
scheduler.start()

我实际跑下来,一天两次比较合理:早上一次给上班前看,下午一次补当天新增。太频繁没意义,热点信息没有那么多。

数据源管理:别把所有源写死在代码里

刚开始我图省事,把数据源直接写死在列表里。后来想加一个源,得改代码再部署,麻烦。改成配置文件管理之后舒服多了:

yaml 复制代码
sources:
  - name: "黑客新闻"
    url: "https://news.ycombinator.com/rss"
    category: "技术"
    weight: 1.0
  - name: "GitHub 趋势"
    url: "https://github.com/trending.atom"
    category: "开源"
    weight: 1.2
  - name: "掘金热门"
    url: "https://juejin.cn/rss"
    category: "开发者社区"
    weight: 1.0
python 复制代码
import yaml

with open("sources.yaml") as f:
    SOURCES = yaml.safe_load(f)["sources"]

def fetch_all():
    items = []
    for src in SOURCES:
        try:
            feed = feedparser.parse(src["url"])
            for entry in feed.entries[:20]:
                items.append({
                    "title": entry.get("title", ""),
                    "link": entry.get("link", ""),
                    "summary": (entry.get("summary") or "")[:800],
                    "source": src["name"],
                    "category": src["category"],
                    "weight": src["weight"],
                })
        except Exception as e:
            print(f"[采集失败] {src['name']}: {e}")
            continue
    return items

这样加源只改配置,权重还可以参与后续打分(比如 GitHub 趋势的内容乘 1.2 权重),非常灵活。

增量与去重:SQLite 做记忆

前面提到用指纹去重,但要真正做好增量更新,需要把"已经处理过的内容"持久化下来。我用 SQLite 当记忆库,轻量又可靠:

python 复制代码
import sqlite3

DB_PATH = "pipeline.db"

def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS seen (
            fingerprint TEXT PRIMARY KEY,
            title TEXT,
            processed_at TEXT DEFAULT CURRENT_TIMESTAMP
        )
    """)
    conn.close()

def is_seen(fp):
    conn = sqlite3.connect(DB_PATH)
    row = conn.execute("SELECT 1 FROM seen WHERE fingerprint=?", (fp,)).fetchone()
    conn.close()
    return row is not None

def mark_seen(fp, title):
    conn = sqlite3.connect(DB_PATH)
    conn.execute("INSERT OR IGNORE INTO seen (fingerprint, title) VALUES (?, ?)", (fp, title))
    conn.commit()
    conn.close()

主循环里:先算指纹,is_seen 直接跳过,处理完 mark_seen。这样断点续跑、重复执行都不会产生重复推送。

完整主循环

把三段串起来,主循环长这样:

python 复制代码
def run_pipeline():
    init_db()
    raw = fetch_all()
    fresh = []
    for item in raw:
        fp = fingerprint(item["title"])
        if is_seen(fp):
            continue
        mark_seen(fp, item["title"])
        if cheap_prefilter(item):
            fresh.append(item)

    scored = [score_item(it) for it in fresh]
    scored.sort(key=lambda x: x["score"], reverse=True)
    top = [it for it in scored if it["score"] >= 70]

    if top:
        push_digest(top)
        print(f"[完成] 采集 {len(raw)} 条,筛选 {len(scored)} 条,推送 {len(top)} 条")
    else:
        print("[完成] 今日无高分内容")

跑一次的流程一目了然:采集 → 去重 → 预筛 → 打分 → 排序 → 推送。哪一段出问题,日志里看得清清楚楚。整条链路里最值得花时间调的就是评分标准,它决定了你每天看到的到底是金子还是沙子。

踩过的坑(真实经历)

坑一:去重没做好,同一条新闻推了三次。 不同源会转载同一条内容,标题还不完全一样。解决办法:归一化标题(去空格、转小写、去掉"报道""快讯"这类词),再用哈希做指纹去重,已经见过的直接跳过。

python 复制代码
import hashlib, re

def fingerprint(title):
    norm = re.sub(r"[报道快讯独家|,。!?\s]", "", title.lower())
    return hashlib.md5(norm.encode()).hexdigest()

坑二:RSS 源偶尔抽风。 有的源临时返回 500,或者网络抖动超时。流水线里每个源都要包 try/except,单独失败不能影响整体。

坑三:推送频率失控。 刚搭好的时候,我设置的是每来一条高分内容就推一条,结果一天推了四十多条,手机直接炸了。改成"攒够一批,定时汇总推送"之后才消停。高频消息只会变成噪音,坚持下来的人都是在和它做斗争。

坑四:模型调用费。 全量内容都送进模型,一天几百次调用,成本蹭蹭涨。加上关键词预筛之后,调用量砍掉八成,效果基本没变。

推送扩展:邮件版

企业微信适合手机即时提醒,但如果你想要更正式的存档,或者分享给团队,邮件版更合适。Python 标准库 smtplib 就能发,不用额外依赖:

python 复制代码
import smtplib
from email.mime.text import MIMEText

SMTP_HOST = "smtp.qq.com"
SMTP_PORT = 465
SMTP_USER = "your@qq.com"
SMTP_PASS = "授权码"

def push_email(top_items):
    body = "\n\n".join(
        f"{i}. {it['title']}\n   {it['link']}\n   理由:{it['reason']}"
        for i, it in enumerate(top_items[:8], 1)
    )
    msg = MIMEText(body, "plain", "utf-8")
    msg["Subject"] = "今日热点速递"
    msg["From"] = SMTP_USER
    msg["To"] = "your@qq.com"

    with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT) as s:
        s.login(SMTP_USER, SMTP_PASS)
        s.send_message(msg)

注意 QQ 邮箱这类服务需要开启 SMTP 并申请授权码,不能直接用登录密码。邮件版和机器人版可以同时开:机器人即时看,邮件留档查。

稳定性:重试与监控

定时任务跑久了总会遇到各种意外:网络抖动、模型服务临时不可用、源站改版。不加防护的话,某天流水线悄悄挂了,你连续几天收不到推送,等你发现已经漏了一周信息。

我的做法是三层防护:

第一层,单源失败不阻断。每个数据源独立 try/except,失败打日志继续下一个,一次最多损失一个源。

第二层,关键调用加重试。模型打分这种核心步骤,遇到限流或 5xx 错误,退避重试三次:

python 复制代码
import time

def with_retry(fn, retries=3, base_delay=2):
    for attempt in range(retries):
        try:
            return fn()
        except Exception as e:
            if attempt == retries - 1:
                raise
            time.sleep(base_delay * (attempt + 1))

第三层,运行日志留痕。每次跑完写一行日志:时间、采集数、筛选数、推送数。哪天没推送了,翻日志一看就知道是采集挂了还是筛选挂了,不用瞎猜。

成本:一天到底花多少钱

很多人担心调用大模型会很贵,实际跑下来完全可以接受。我的经验数据是这样:预筛过后每天送进模型的条目大概几十条,每条只分析标题加摘要,token 消耗很小。用便宜的小模型(比如 gpt-4o-mini 级别),一天的调用成本大概相当于一瓶水,一个月下来也就是几杯奶茶的钱。

真正要注意的其实在调用量失控。如果哪天真有热点爆发,一条源瞬间涌进来几百条内容,预筛又全命中,那一天的调用量会暴涨。我的做法是给每轮打分设置上限:超过上限就只打前 N 条,剩下的明天再说。宁可少看几条,也别让账单失控。

这套架构的扩展性很好,几个方向供参考:

  • 多语言源:加英文 RSS 源,prompt 里要求返回中文摘要,等于免费给你配了个翻译
  • 自定义评分规则:做电商就加权商品类关键词,做科技就加权大模型关键词,标准完全可定制
  • 结果入库:把每天的筛选结果存 SQLite,攒一个月就能看出你的信息圈在往哪个方向漂
  • 接 LLM 代理:模型调用走统一网关,换模型不用改业务代码

跑起来是什么样

给你看一条真实的推送长什么样,感受一下效果:

arduino 复制代码
📮 今日热点速递

1. 某开源项目发布新一代自动化框架
   链接:https://github.com/...
   理由:AI 自动化相关 +30,标题含具体版本号 +10,发布时间 2 天内 +15 → 85 分

2. 一篇 AI 效率工具深度测评
   链接:https://...
   理由:与 AI 效率强相关 +30,有实操案例 +20,时效 +15 → 80 分

3. 某公司宣布开源内部工作流引擎
   链接:https://...
   理由:自动化方向 +30,发布 1 天内 +15 → 65 分(低于 70 分阈值,未推送)

注意看第三条:65 分没推。打分在这里真正起作用了------相关的内容很多,但不是每条都值得打断你,只有过线的高质量信息才会出现。我跑了一个月之后,把阈值从 60 调到 70,推送量降了一半,但每一条我基本都会点开看。信息筛选做到位的效果,是让你每天省下半小时的浏览时间,把注意力留给真正值得读的那几篇。

最后:一份可保存的清单

  1. 采集段用 feedparser 拉 RSS,多源容错
  2. 筛选段先关键词预筛,再送大模型打分
  3. 推送段按"标题+链接+理由"格式汇总
  4. cron 定时跑,一天两次足够
  5. 指纹去重 + 单源容错 + 汇总推送,三个坑必避
  6. 成本优化:预筛砍调用量,摘要打分控 token

这条流水线搭好之后,我每天早上的信息摄入从"刷半小时网页"变成了"看一条推送"。省下来的时间拿来读那两篇真正值得读的,比什么都值。整套代码加配置加起来不到三百行,全跑在一个最低配的服务器上,成本几乎可以忽略。

你有想自动化又一直没动手的流程吗?评论区聊聊,说不定下一篇文章就是它。觉得有用的话点个收藏,搭的时候照着来。

相关推荐
土星云SaturnCloud1 小时前
智慧温室精准环控:土星云边缘计算设备实现温光水肥本地闭环调节
服务器·人工智能·ai·边缘计算
孙启超1 小时前
【AI应用开发】LangChain 中 Chain 和 Agent 核心区别?
java·人工智能·langchain·llm·rag·ai应用开发·agent loop
别动我齐刘海1 小时前
Day6 unitree_G1人形机器人GMR—— MotionInput
c语言·c++·人工智能·学习·机器学习·机器人·github
火山引擎开发者社区1 小时前
基于 TLS 的 Coding Agent 全链路可观测体系:打破执行黑盒,实现全流程透明复盘
人工智能
ACP广源盛139246256731 小时前
DeepSeek‑V4‑Flash 公测@ACP#昇腾 950 国产算力组合落地,国产 PCIe 交换芯片 IX9104 有哪些硬件机会
大数据·数据库·人工智能·分布式·单片机·嵌入式硬件·microsoft
leoZ2311 小时前
实战复盘:用 Claude Code 从零搭一个 GitHub PR 统计工具
java·人工智能·python·深度学习·自然语言处理·github·llama
无忧智库1 小时前
数字化转型:打造智慧城市大脑,引领未来城市发展(PPT)
人工智能·智慧城市
ltqvibe2 小时前
数据中台为什么走不通——本体语义给出的替代路径
人工智能·原型模式·本体语义平台
yuezhilangniao2 小时前
AI工具全家桶:从开发到运维,从数据库到产品经理-含魔塔社区简介
运维·数据库·人工智能