1. 从一份日报标题说起:AI行业简报到底在解决什么问题
每天早上打开电脑,我的第一件事不是看邮件,而是花十五分钟扫一遍当天的AI行业动态。这个习惯坚持了快两年,中间换过无数种方式——从最早的收藏夹里存二十个网站挨个点开,到后来用RSS订阅器聚合,再到现在自己搭了一套半自动的简报生成流程。说实话,效率提升不是一点半点。今天这篇内容,就是围绕“每日AI行业简报”这个项目,把我踩过的坑、用过的工具、以及最终跑通的那套方案完整拆开讲一遍。
你可能会问,不就是收集新闻吗,有什么好讲的。但真正做过信息聚合的人都知道,难点从来不在“收集”,而在于筛选、去重、分类、摘要、分发这五个环节的自动化衔接。尤其是AI这个领域,信息密度极高,每天光arXiv上新增的论文就上百篇,再加上各大厂商的博客更新、开源社区的release、社交平台上的讨论,如果没有一套系统化的处理流程,你很容易陷入“收藏了等于看了”的自我欺骗。
这套简报系统的核心目标很明确:在每天早上八点之前,把过去二十四小时内AI领域最值得关注的十到十五条信息,按照模型发布、工具更新、行业动态、技术论文四个维度整理好,附上简短的摘要和原文链接,推送到我自己的终端上。整个流程从抓取到推送全自动完成,我只需要在早上花十分钟浏览一遍,就能对当天的行业脉络有个清晰的把握。
适合谁来参考这套方案?如果你是AI从业者、技术博主、投资分析师,或者单纯是对AI行业保持高度敏感的学习者,这套东西都能帮你省下大量重复劳动的时间。不需要你有很深的编程功底,但至少要能看懂Python代码,知道怎么在命令行里跑脚本。下面我会从整体设计思路开始,一步步拆到每个模块的具体实现。
2. 整体架构设计与技术选型思路
2.1 为什么选择Python作为主力语言
这个项目的核心是数据处理和自动化调度,Python在这两个场景下的生态优势太明显了。抓取用requests和feedparser,解析用BeautifulSoup和lxml,去重和摘要用scikit-learn配合大模型API,调度用schedule或者系统自带的cron,整个链路下来几乎不需要引入什么重型框架。我试过用Node.js重写一版,虽然异步处理确实舒服,但在文本处理和机器学习相关的库支持上,Python还是更顺手。
另外一个考虑是可维护性。这套脚本我断断续续改了快一年,中间加过新源、换过摘要模型、调整过推送格式,Python的代码可读性让我每次回来改的时候不至于一脸懵。如果你打算长期维护一个类似的项目,语言的选择上建议优先考虑自己最熟悉的那个,而不是盲目追求性能。
2.2 数据源的筛选与分层策略
信息源的质量直接决定了简报的质量。我一开始贪多,订阅了五十多个源,结果每天抓回来三百多条内容,光去重和筛选就耗掉大量时间,最后生成的简报长得像一篇论文。后来痛定思痛,把源砍到十五个左右,分成三个层级:
- 核心层:官方博客和公告页,比如OpenAI、Anthropic、Google DeepMind的新闻页,以及arXiv上cs.AI、cs.CL、cs.LG三个分类的最新论文列表。这些是必须覆盖的,每天必查。
- 扩展层:技术社区的热门讨论,比如Hacker News上AI相关的首页帖子、Reddit的r/MachineLearning板块的精华帖。这些内容质量参差不齐,但偶尔能挖到宝藏。
- 补充层:行业媒体的深度报道,选两到三家靠谱的即可,用来补充官方渠道不会提及的商业动态。
注意:不要试图覆盖所有信息源。信息过载的代价不是“看得更多”,而是“什么都看不进去”。十五个源是我实测下来既能保证覆盖面、又不会让简报臃肿的平衡点。
2.3 去重与摘要的技术方案对比
去重这块我走过弯路。最早用的是简单的标题字符串匹配,结果同一件事不同媒体的标题措辞不一样,根本去不掉。后来换成基于URL的去重,效果好了一些,但遇到同一篇论文被多个平台转载的情况还是无能为力。
最终的方案是标题向量化+余弦相似度。具体做法是用sentence-transformers把每条内容的标题转成向量,然后计算两两之间的余弦相似度,超过0.85的判定为重复,只保留来源优先级最高的那条。这个阈值是我反复调整后定下来的,太低会误杀不同事件的相似标题,太高又去不干净。
摘要生成这块,我试过三种方案:一是直接用TextRank算法抽取原文关键句,速度快但摘要读起来很生硬;二是调用大模型API生成摘要,质量高但每天十几条内容下来成本不低;三是混合方案,先用TextRank抽取出候选句,再把候选句喂给大模型做润色和压缩。实测下来第三种方案在质量和成本之间取得了最好的平衡,每条内容的摘要生成成本控制在几厘钱。
3. 核心模块拆解与关键实现细节
3.1 信息抓取模块的容错设计
抓取看起来简单,但实际跑起来问题不少。最常见的是某个源突然改版,导致解析规则失效,整个脚本报错退出。我的处理方式是在每个抓取函数外面包一层异常捕获,单个源失败不影响其他源的抓取,同时把失败信息记录到日志里,第二天早上看简报的时候顺便扫一眼日志,发现哪个源连续失败就手动去检查一下。
代码结构大概长这样:
import requests from bs4 import BeautifulSoup import logging logging.basicConfig(filename='fetch_errors.log', level=logging.WARNING) def fetch_source(name, url, parser_func): try: resp = requests.get(url, timeout=15, headers={'User-Agent': 'Mozilla/5.0'}) resp.raise_for_status() items = parser_func(resp.text) return items except Exception as e: logging.warning(f"Source {name} failed: {e}") return []超时设置我给的15秒,实测大部分源都能在5秒内返回,15秒是留给偶尔网络波动的情况。User-Agent一定要带,不然很多站点会直接拒绝请求。另外建议在请求之间加个time.sleep(1),避免请求频率过高被限流。
3.2 内容清洗与结构化存储
抓回来的原始内容格式五花八门,有的是HTML片段,有的是纯文本,还有的带着一堆导航栏和广告。清洗的目标是把每条内容统一成包含title、url、source、publish_time、raw_content五个字段的结构化数据。
HTML标签的去除我用的是BeautifulSoup的get_text()方法,配合正则表达式去掉多余的空格和换行。发布时间这块比较麻烦,不同源的格式不一样,有的给的是ISO格式,有的给的是“2 hours ago”这种相对时间。我的处理方式是优先解析标准格式,解析不了的统一标记为抓取时间,后续在排序时再做二次处理。
存储用的是SQLite,轻量、无需额外配置、Python原生支持。表结构很简单:
CREATE TABLE items ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, url TEXT UNIQUE, source TEXT, publish_time TEXT, raw_content TEXT, summary TEXT, category TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP );url字段加了唯一索引,这样即使同一条内容被抓取多次,数据库层面也能自动去重。category字段是后续分类模块写入的,初始为空。
3.3 基于向量相似度的去重实现
去重模块的核心逻辑是:把当天新抓取的内容和数据库中最近三天内的内容做比对,如果标题相似度超过阈值,就跳过这条新内容。这里有个细节需要注意——只和最近三天的数据比对,而不是全量比对。因为超过三天的内容即使标题相似,也大概率是不同的事件,全量比对既慢又容易误判。
from sentence_transformers import SentenceTransformer from sklearn.metrics.pairwise import cosine_similarity import numpy as np model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') def is_duplicate(new_title, existing_titles, threshold=0.85): if not existing_titles: return False new_vec = model.encode([new_title]) existing_vecs = model.encode(existing_titles) sims = cosine_similarity(new_vec, existing_vecs)[0] return np.max(sims) > threshold模型选的是多语言版本,因为我的信息源里有中文内容。如果你只处理英文内容,可以用更小的英文模型,速度会快不少。阈值0.85这个数字不是拍脑袋定的,我拿过去一周的数据做过测试,0.80的时候会误杀大约5%的不同事件,0.90的时候又会漏掉一些明显的重复,0.85是比较稳妥的中间值。
3.4 分类与摘要的自动化流水线
分类我用的是一套基于关键词加规则的方案,没有上机器学习模型。原因很简单——AI领域的分类边界相对清晰,模型发布、工具更新、行业动态、技术论文这四类的特征词区分度很高,规则方案的可解释性更强,出错了也容易调试。
规则大概是这样:
- 标题或摘要中出现
release、launch、announce、model、checkpoint等词,归入“模型发布” - 出现
update、version、library、framework、tool等词,归入“工具更新” - 出现
funding、acquisition、partnership、regulation等词,归入“行业动态” - 来源是arXiv或者标题包含
paper、arxiv,归入“技术论文”
摘要生成走的是前面提到的混合方案。先用TextRank从raw_content里抽出三到五个候选句,然后拼成一段prompt发给大模型,要求它用两句话概括核心内容。prompt我调了好几版,最终稳定下来的是这个:
以下是一篇AI领域文章的关键句,请用两句话概括其核心内容,第一句说明做了什么,第二句说明为什么值得关注。不要使用“本文”“该研究”等指代词,直接陈述。 关键句: {extracted_sentences}实操心得:摘要生成的质量很大程度上取决于输入的关键句质量。如果TextRank抽出来的句子本身就不包含核心信息,大模型再强也概括不出好东西。我的做法是在抽取阶段多留一些候选句(五到八句),让大模型自己判断哪些重要。
4. 调度、推送与日常运维实操
4.1 定时任务的配置与避坑
调度这块我用的是系统自带的cron,没有引入额外的调度框架。原因是我只需要每天固定时间跑一次,cron足够用,而且稳定性经过了时间检验。配置很简单,在crontab -e里加一行:
0 6 * * * /usr/bin/python3 /home/user/ai_daily/main.py >> /home/user/ai_daily/cron.log 2>&1这行配置的意思是每天早上六点执行main.py,标准输出和错误输出都追加到cron.log里。这里有个坑我踩过——cron执行时的环境变量和你在终端里手动执行时不一样,尤其是PATH。如果你的脚本里调用了其他命令(比如git或者某个虚拟环境里的Python),一定要用绝对路径,或者在脚本开头手动设置PATH。
另一个坑是时区问题。如果你的服务器时区和你所在时区不一致,cron的执行时间会和你预期的不一样。我建议在脚本里统一用UTC时间做逻辑判断,只在最终推送时转换成你所在时区的时间显示。
4.2 推送渠道的选择与格式优化
推送渠道我试过三种:邮件、Telegram Bot、以及企业微信机器人。最终留下来的是Telegram Bot,原因是它的Markdown格式支持好、推送及时、而且手机端阅读体验不错。企业微信机器人也不错,但格式限制比较多,表格和代码块支持得不好。
推送内容的格式我调整过很多版,核心原则是信息密度高、扫读友好。每条内容占一行,格式是:
[分类] 标题 | 一句话摘要 | 链接分类用方括号标注,标题加粗,摘要控制在五十字以内,链接用Markdown的超链接语法。整个简报按分类分组,每组之间空一行。实测下来,这种格式在手机屏幕上扫读效率最高,一眼就能看到当天有哪些值得关注的内容。
4.3 日志监控与失败重试机制
任何自动化系统都需要一套日志和重试机制,不然出了问题你都不知道。我的做法是在每个关键步骤(抓取、去重、分类、摘要、推送)完成后都写一条日志,记录处理了多少条内容、耗时多少、有没有异常。日志文件按天切割,保留最近三十天的记录。
重试机制主要针对抓取和推送两个环节。抓取失败会自动重试两次,间隔分别是30秒和60秒。推送失败会重试三次,如果三次都失败,就把简报内容存到本地文件,同时发一条告警消息到我的备用渠道。这套机制跑了大半年,真正触发告警的次数屈指可数,但每次触发都帮我及时发现了问题。
提示:日志里不要记录完整的文章内容,只记录标题和URL就够了。一是节省空间,二是避免敏感信息泄露。如果你的简报涉及付费内容,这一点尤其重要。
5. 常见问题排查与实战避坑指南
5.1 抓取失败的高频原因与排查路径
抓取失败的原因五花八门,我整理了一张速查表,遇到问题的时候按顺序排查:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 返回403 | 被反爬拦截 | 检查User-Agent | 换成真实浏览器UA |
| 返回空列表 | 页面结构改版 | 手动打开页面看HTML | 更新解析规则 |
| 超时 | 网络问题或源站慢 | 用curl测试响应时间 | 增加超时时间或跳过 |
| 编码乱码 | 字符集识别错误 | 检查response.encoding | 手动指定编码 |
| 内容重复 | 源站分页问题 | 检查URL规律 | 加URL去重逻辑 |
最常见的是403和页面改版。403一般换个User-Agent就能解决,页面改版就需要手动去更新解析规则了。我的建议是每周花十分钟检查一下日志,看看有没有哪个源开始频繁报错,早发现早处理,不要等到简报内容明显变少了才去查。
5.2 摘要质量不稳定的调优经验
摘要质量不稳定是这套系统里最让人头疼的问题。同样的prompt,有时候生成的摘要精准到位,有时候又啰嗦得不行。我总结下来,影响因素主要有三个:
第一是输入关键句的质量。如果TextRank抽出来的句子本身就是些边角料信息,大模型再厉害也概括不出核心。解决办法是在抽取阶段增加候选句数量,同时给TextRank的权重加上位置因子——文章开头和结尾的句子权重更高。
第二是prompt的稳定性。我试过在prompt里加各种约束条件,比如“不要超过五十字”“不要使用被动语态”,结果发现约束越多,模型的表现反而越不稳定。最终我砍掉了大部分约束,只保留最核心的两条要求,效果反而更好。
第三是模型的选择。不同模型对同一段输入的表现差异很大。我的经验是,摘要任务上,中等规模的模型往往比超大模型更稳定,因为超大模型容易“过度发挥”,加入原文没有的信息。
5.3 推送延迟与消息丢失的处理
推送延迟一般不是推送渠道的问题,而是前面的处理环节耗时太长。我遇到过几次早上八点还没收到简报的情况,查下来都是因为某个源的抓取卡住了,导致整个流程被拖慢。解决办法是给每个抓取任务设置独立的超时时间,超时就直接跳过,不要让它拖累整体进度。
消息丢失的情况比较少见,但一旦发生就很麻烦。我的应对策略是推送前先落盘——在调用推送接口之前,先把简报内容写入本地文件,推送成功后再删除。这样即使推送失败,你也能从本地文件里找到当天的简报内容,手动补发一下就行。
5.4 系统长期运行的成本控制
这套系统跑一年下来,主要成本在两个方面:服务器和API调用。服务器我用的是最基础的云主机配置,一个月几十块钱,跑这套脚本绰绰有余。API调用是大头,如果每天处理十五条内容,每条摘要生成消耗大约五百个token,一个月下来也就几十万token,按现在的价格算不到十块钱。
如果你想进一步压缩成本,有两个方向:一是把摘要生成从大模型换成小模型,质量会下降一些但成本能降一个数量级;二是把去重和分类的环节做得更精准,减少需要生成摘要的内容数量。我的做法是在去重阶段稍微激进一点,宁可多去掉一些边缘内容,也不让简报变得臃肿。
6. 关于这套系统的一些个人体会
这套简报系统从最初的一个简单脚本,到现在跑得比较稳定,中间经历了大概七八次比较大的重构。最大的感受是,自动化系统的价值不在于技术有多复杂,而在于它能不能稳定地帮你省下时间。我见过很多人花大量精力去追求“完美”的架构,结果系统跑了两周就因为维护成本太高而废弃了。
我的建议是,如果你打算做类似的东西,先从最简单的版本开始——哪怕就是一个Python脚本,每天定时跑一次,把抓到的内容存到本地文件里。跑上一周,看看哪些环节最耗时间,哪些信息源最有价值,然后再逐步优化。不要一上来就想着做全自动、多源、智能摘要,那样很容易在半路就放弃了。
另外一点体会是,信息源的质量比数量重要得多。我现在只保留十五个源,但每个源都是我反复验证过、确实能稳定产出高质量内容的。与其订阅五十个源然后每天被信息淹没,不如精选十个源然后认真读完。
最后分享一个我最近在尝试的扩展方向:把这套系统从“每日简报”扩展成“实时监控”。具体做法是给几个核心源加上更频繁的抓取频率,一旦发现有重大发布(比如新模型发布或者重要论文上线),就立即推送一条简短的通知,而不是等到第二天早上。这个功能还在调试中,等跑稳定了再单独写一篇来分享。