用 AI 搭一条自动化热点监控流水线:RSS 聚合、智能筛选、定时推送一次跑通
每天上班第一件事,是不是先刷一遍各种网站看有什么新东西?我认识一个做内容的朋友,每天早上要开十来个页面:几个资讯站、两个社区、一个项目趋势榜。刷完花半小时,看完能用的没几条,大部分是噪音。而且刷的过程很影响上午的状态,一打开手机就停不下来。
后来我把这件事整个自动化了:一条流水线,早上七点自动跑完,把筛选好的热点直接推到我手机里。今天这篇文章就把这条流水线的完整搭建过程写出来,每一段都有能直接跑的代码。你照着搭,也能拥有一套自己的 AI 热点雷达。
先说架构:三段式流水线
整条流水线分成三段,各干各的活:
- 采集段:从各信息源拉取最新内容(RSS 为主,兼容性好)
- 筛选段:用大模型理解内容,按你的标准打分、分类、去重
- 推送段:把筛选结果推到目标渠道(企业微信、邮件、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,推送量降了一半,但每一条我基本都会点开看。信息筛选做到位的效果,是让你每天省下半小时的浏览时间,把注意力留给真正值得读的那几篇。
最后:一份可保存的清单
- 采集段用 feedparser 拉 RSS,多源容错
- 筛选段先关键词预筛,再送大模型打分
- 推送段按"标题+链接+理由"格式汇总
- cron 定时跑,一天两次足够
- 指纹去重 + 单源容错 + 汇总推送,三个坑必避
- 成本优化:预筛砍调用量,摘要打分控 token
这条流水线搭好之后,我每天早上的信息摄入从"刷半小时网页"变成了"看一条推送"。省下来的时间拿来读那两篇真正值得读的,比什么都值。整套代码加配置加起来不到三百行,全跑在一个最低配的服务器上,成本几乎可以忽略。
你有想自动化又一直没动手的流程吗?评论区聊聊,说不定下一篇文章就是它。觉得有用的话点个收藏,搭的时候照着来。