1. 一份AI日报的诞生逻辑
每天早上八点半,我习惯性打开自己搭建的AI日报聚合面板,扫一眼过去24小时里全球AI圈发生了什么。这个习惯坚持了快两年,从最开始手动刷十几个信息源,到后来写脚本自动抓取、去重、分类、摘要,再到如今形成一套相对稳定的日报生产流程——今天这篇,就把这套东西完整拆开讲一遍。
所谓“AI日报”,本质上是一份信息密度极高的行业动态摘要,它要解决的核心问题是:在AI领域每天产生海量论文、产品更新、开源项目、融资消息、政策动向的情况下,如何用最短时间让读者抓住真正重要的那几条。适合谁来参考?三类人:一是想持续跟踪AI行业但没时间逐条刷信息的从业者;二是想搭建自己信息聚合系统的开发者;三是做内容运营、需要快速产出行业简报的团队。
我做的这份日报,每天覆盖大约15到25条精选条目,分五个固定板块:模型与论文、产品与工具、开源与社区、行业与资本、值得一读的深度内容。每条包含一句话摘要、来源链接、以及我自己的简短点评。整份日报从抓取到发布,全流程自动化程度大概在80%左右,剩下20%是人工筛选和点评——这部分恰恰是最有价值的。
下面我从整体设计思路开始,把每个环节的考量、踩过的坑、以及可以直接抄作业的配置都讲清楚。
2. 整体架构设计与信息源选型
2.1 为什么选择“聚合+人工筛选”而不是纯自动
最开始我试过纯自动方案:爬虫抓取→NLP摘要→直接生成日报。跑了大概两周就放弃了。问题出在三个地方:第一,AI领域大量信息是重复报道,同一篇论文被十几个媒体转述,纯自动去重很难判断哪些是同一事件的不同角度;第二,重要性判断需要行业背景知识,一个开源项目star数暴涨和一个大模型发布,哪个更值得放头条,算法很难给出符合人类直觉的排序;第三,摘要质量参差不齐,很多自动摘要把关键数字和结论丢掉了。
所以最终方案是:机器负责“广撒网”和“初筛”,人负责“精挑”和“点评”。具体分工是这样的——
- 抓取层:定时任务从RSS、API、网页抓取原始内容
- 清洗层:去重、去广告、提取正文、统一时间格式
- 初筛层:基于关键词权重和来源可信度打分,筛掉明显不相关的
- 人工层:我每天早上花20到30分钟过一遍候选列表,选出15到25条,写点评
- 发布层:渲染成Markdown或HTML,推送到目标渠道
这个架构的好处是,机器处理了90%的脏活累活,人只做最有价值的判断。而且随着时间推移,我可以不断调整初筛规则,让候选列表越来越精准。
2.2 信息源的分层管理
信息源是整个日报的命脉。我目前维护着大约40个信息源,分成四个层级:
第一层:一手信源(权重最高)
- 各大AI实验室的官方博客和公告页
- 顶级会议(NeurIPS、ICML、ICLR等)的论文列表页
- 主流开源社区的trending页面
- 几个核心预印本平台的每日新论文列表
第二层:高质量媒体(权重中高)
- 专注AI领域的垂直媒体
- 科技大媒体的AI频道
- 几个我信任的行业分析师的个人博客
第三层:社区信号(权重中等,用于发现热点)
- 技术社区的热榜
- 社交平台上AI相关的高互动帖子
- 几个活跃的行业群组讨论
第四层:补充信源(权重低,用于交叉验证)
- 综合科技媒体的AI板块
- 行业报告和调研机构的公开摘要
提示:信息源不是越多越好。我一开始加了上百个源,结果每天候选列表超过500条,根本看不过来。后来砍到40个左右,反而覆盖率更高——因为很多源内容高度重叠,留最权威的那个就够了。
每个源我都配置了三个参数:抓取频率(高频源每小时一次,低频源每天一次)、可信度权重(1到10分,影响初筛排序)、内容类型标签(论文/产品/融资/观点等,用于分类)。这套配置存在一个YAML文件里,改起来很方便。
2.3 去重策略的设计
去重是聚合系统里最容易被低估的环节。AI领域的信息重复率极高,一条重要消息可能在几小时内被几十个源报道。如果不去重,日报会变成同一件事的复读机。
我的去重分三层:
第一层:URL精确去重。最简单,直接比对链接。但只能处理完全相同的链接,对于同一事件的不同报道无能为力。
第二层:标题相似度去重。用编辑距离或余弦相似度计算标题之间的相似程度,超过阈值(我设的是0.75)就判定为同一事件。这一层能处理大部分媒体转述的情况。
第三层:实体+时间窗口去重。提取标题和摘要中的关键实体(模型名、公司名、人名、产品名),如果两个条目在同一个时间窗口(比如6小时)内包含相同的核心实体组合,就判定为同一事件。这一层能处理标题写法差异很大的情况。
三层去重跑下来,500条原始内容通常能压缩到80到120条候选,效果还是很明显的。不过要注意,去重阈值不能设得太激进,否则会把相关但不同的事件误合并。我踩过一次坑:把两个不同团队在同一天发布的不同模型误判为同一事件,结果日报漏掉了一条重要消息。后来把阈值调保守了一些,宁可多留几条人工判断,也不轻易合并。
3. 核心环节的实操细节
3.1 抓取层的实现要点
抓取层我用的是Python,核心依赖就几个:feedparser处理RSS,requests+BeautifulSoup处理网页,httpx做异步请求。没有用重型框架,因为需求不复杂,轻量方案更好维护。
RSS源的处理最直接,feedparser几行代码就能搞定。但要注意两个坑:一是时间格式不统一,不同源的发布时间格式五花八门,需要统一转换成ISO 8601格式再做后续处理;二是部分RSS只给摘要不给全文,需要额外请求原文页面提取正文。
网页抓取要复杂一些。我的原则是:能用API就不用网页抓取,能用RSS就不用网页抓取,实在没有前两者才写针对性的爬虫。因为网页结构随时可能变,维护成本很高。对于必须抓取的页面,我会写一个配置化的解析规则,把CSS选择器存在配置文件里,页面改版时只需要改配置不用改代码。
# 抓取层核心逻辑示意 import feedparser import httpx from datetime import datetime async def fetch_rss(url: str, source_config: dict) -> list: """抓取RSS源并标准化输出""" feed = feedparser.parse(url) items = [] for entry in feed.entries: items.append({ "title": entry.get("title", "").strip(), "link": entry.get("link", ""), "summary": entry.get("summary", ""), "published": parse_time(entry.get("published", "")), "source": source_config["name"], "weight": source_config["weight"], "category": source_config.get("category", "general"), }) return items def parse_time(raw: str) -> str: """统一时间格式,失败则返回当前时间""" formats = [ "%a, %d %b %Y %H:%M:%S %z", "%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%d %H:%M:%S", ] for fmt in formats: try: return datetime.strptime(raw, fmt).isoformat() except (ValueError, TypeError): continue return datetime.now().isoformat()抓取频率的控制也很重要。我用的方案是分级调度:高频源每30分钟抓一次,中频源每2小时,低频源每天一次。所有抓取任务通过一个调度器管理,避免同时发起太多请求。另外一定要设置请求间隔和超时,我一般设3到5秒间隔、10秒超时,既不会给目标站点造成压力,也能保证抓取成功率。
3.2 初筛打分的参数设计
初筛打分是整个系统里最需要调参的环节。我的打分公式大致是这样的:
总分 = 来源权重 × 0.4 + 关键词得分 × 0.35 + 时效性得分 × 0.15 + 互动信号 × 0.1
来源权重就是前面说的1到10分。关键词得分基于一个我维护的关键词表,分三档:核心关键词(如具体模型名、重要技术术语)加3分,重要关键词(如“发布”“开源”“融资”)加2分,一般关键词加1分。时效性得分按发布时间线性衰减,24小时内的内容得分最高。互动信号主要看社区讨论的热度,比如评论数、转发数等。
这套参数不是拍脑袋定的,是我根据大概两个月的实际筛选记录反推出来的。具体做法是:每天记录我最终选了哪些条目、放弃了哪些条目,然后定期回顾,看看被放弃的条目里有没有打分很高但被我忽略的,以及被打分很低但我手动捞回来的。根据这些反馈调整权重。
注意:关键词表需要定期更新。AI领域新词层出不穷,上个月还不存在的模型名这个月可能就成了核心关键词。我一般每周花10分钟过一遍候选列表里的高频新词,把有价值的加进关键词表。
3.3 人工筛选的判断标准
机器筛完之后,剩下的80到120条候选需要人工过一遍。这个过程我总结了几个判断标准,按优先级排列:
第一优先级:是否改变了某个重要的事实。比如某个模型的能力边界被刷新了、某个重要项目开源了、某个关键人物有重大动向。这类信息无论来源权重高低都值得收录。
第二优先级:是否提供了新的视角或数据。有些内容本身不是新闻,但提供了有价值的分析框架或独家数据,比如一份深入的行业调研、一个有意思的实验结果。
第三优先级:是否具有实操参考价值。比如新的工具、新的教程、新的最佳实践,读者看完能直接上手用的。
第四优先级:是否反映了某种趋势。单条信息可能不重要,但多条信息放在一起能看出方向性变化,这类也值得收录并加点评。
反过来,以下几类内容我一般会跳过:纯公关稿、没有实质内容的观点输出、重复报道且没有新增信息、标题党、以及明显是营销内容的条目。
3.4 点评撰写的经验
每条日报条目我都会加一句自己的点评,这是整份日报区别于普通聚合页的核心价值。点评不需要长,一两句话就够,但要做到以下几点:
- 说清楚为什么这条值得关注,而不是复述内容
- 如果有背景信息,补充一句,帮助读者理解上下文
- 如果有实操建议,直接给出来,比如“这个工具我试过,XX场景下比XX好用”
- 保持克制,不确定的事情不要下结论,用“可能”“值得观察”这类措辞
写点评最忌讳的是空洞。比如“这个模型很强”就是废话,“这个模型在XX基准上比前代提升了XX%,但实际使用中XX场景下提升不明显”才是有信息量的点评。我一般会要求自己每条点评至少包含一个具体的信息点。
4. 常见问题与排查技巧
4.1 抓取失败怎么办
抓取失败是家常便饭,原因五花八门。我整理了一个排查顺序,按这个顺序走基本能定位到问题:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 全部源抓取失败 | 网络问题或调度器挂了 | 检查调度器日志、手动请求一个源 | 重启调度器、检查网络配置 |
| 单个源持续失败 | 源站改版或封禁 | 手动访问源站看是否正常 | 更新解析规则、降低抓取频率 |
| 间歇性失败 | 请求超时或限流 | 查看失败时间分布 | 增加超时时间、加请求间隔 |
| 抓取成功但内容为空 | 解析规则失效 | 检查返回的HTML结构 | 更新CSS选择器 |
| 时间格式解析失败 | 源站改了时间格式 | 查看原始时间字符串 | 在parse_time里加新格式 |
我踩过最坑的一次是某个重要源突然开始返回JavaScript渲染的页面,requests抓到的全是空壳。后来加了playwright做兜底,对于检测到内容为空的页面自动用浏览器渲染再抓一次。虽然慢一些,但至少不会漏掉重要信息。
4.2 去重误判怎么处理
去重误判分两种:漏合并(同一事件被当成两条)和过度合并(不同事件被当成一条)。漏合并的问题相对小,大不了日报里出现两条相关条目,读者也能理解。过度合并就严重了,会直接导致信息丢失。
我的处理策略是:宁可漏合并,不可过度合并。具体做法是把去重阈值设得保守一些,同时在人工筛选环节加一道检查——如果发现两条候选内容相关但被判定为不同事件,手动合并;如果发现某条候选明显是另一条的重复,手动删除。
另外我会定期回顾去重日志,看看哪些条目被合并了、哪些没有。如果发现某类事件经常被误合并,就调整对应的规则。比如有一段时间,两个不同团队在同一天发布模型更新,因为标题里都包含“模型更新”这个短语,被误判为同一事件。后来我在实体提取里加了团队名的权重,这个问题就解决了。
4.3 日报质量波动的应对
日报质量不可能每天都一样。有些日子AI圈确实没什么大事,硬凑15条只会稀释质量。我的做法是设定弹性区间:最少10条,最多25条,根据当天实际情况调整。如果某天确实没什么重要消息,就少发几条,但在开头说明“今日信息量较少”。反过来如果某天大事特别多,就多发几条,但每条保持精简。
另一个质量波动来源是我自己的状态。有时候早上时间紧,筛选和点评会比较仓促。后来我养成了一个习惯:前一天晚上先把候选列表过一遍,把明显要选的标出来,第二天早上只需要确认和写点评,效率高很多。
4.4 几个独家避坑技巧
技巧一:给每个源加“健康检查”。我写了一个简单的脚本,每天检查每个源最近一次成功抓取的时间。如果超过预期频率的3倍还没成功,就发提醒。这样能在源出问题的早期就发现,而不是等到日报缺内容才察觉。
技巧二:保留原始内容快照。抓取到的原始内容我会存一份到本地,保留30天。这样做的好处是,如果后来发现某条信息有误或者需要补充,可以回溯原始内容。另外如果某个源的内容后来被删了,我本地还有备份。
技巧三:用“周回顾”代替“日纠结”。不要纠结某一天日报的完美程度,把视角拉到一周。每周花半小时回顾这一周的日报,看看有没有系统性遗漏、有没有重复出现的问题、关键词表需不需要更新。周回顾比日纠结有效得多。
技巧四:点评模板化开头。写点评最耗时的不是思考内容,而是组织语言。我总结了几个常用的点评开头句式,比如“这条值得关注是因为……”“背景是……”“实操层面……”,需要的时候直接套,能省不少时间。当然不能每篇都用同一个句式,要轮换着来。
5. 工具链与自动化配置
5.1 核心工具选型
整套系统用到的工具不多,但每个都是经过比较后选的:
- 调度:
APScheduler,轻量、Python原生、支持cron表达式,够用 - 抓取:
feedparser+httpx+BeautifulSoup,覆盖RSS和静态网页 - 兜底渲染:
playwright,处理JS渲染页面 - 存储:
SQLite,单文件、零配置、够用,数据量大了再考虑迁移 - 去重:
rapidfuzz做标题相似度,自己写的实体提取逻辑 - 输出:
Jinja2模板渲染Markdown
没有用数据库服务器、没有用消息队列、没有用容器编排。不是这些技术不好,而是这个场景下没必要。一个人维护的系统,越简单越好。我见过不少人一上来就上重型架构,结果光维护基础设施就耗掉大半精力,真正核心的筛选和点评反而没时间做。
5.2 自动化流程的配置
整个流程通过一个主脚本串联,用APScheduler定时触发。核心配置如下:
# config.yaml 核心配置示意 schedule: high_freq: "*/30 * * * *" # 每30分钟 mid_freq: "0 */2 * * *" # 每2小时 low_freq: "0 6 * * *" # 每天早上6点 daily_report: "0 7 * * *" # 每天早上7点生成日报 dedup: title_similarity_threshold: 0.75 entity_window_hours: 6 url_exact: true scoring: source_weight: 0.4 keyword_score: 0.35 freshness: 0.15 engagement: 0.1 output: min_items: 10 max_items: 25 format: "markdown" sections: - "模型与论文" - "产品与工具" - "开源与社区" - "行业与资本" - "深度阅读"这个配置文件是我整个系统的核心,所有可调参数都在这里。改参数不用改代码,重启一下调度器就生效。
5.3 输出模板的设计
日报的输出模板我改过很多版。最开始是简单的列表,后来加了板块分类,再后来加了点评,最后形成了现在的结构。每个板块的条目格式是统一的:
### [条目标题] **来源**:来源名称 | **时间**:X小时前 **摘要**:一句话说清楚发生了什么 **点评**:我的简短分析或建议这个格式的好处是信息层次清晰,读者扫一眼就能抓住重点。摘要控制在一句话以内,逼着自己提炼核心信息。点评控制在两句话以内,避免啰嗦。
模板用Jinja2渲染,这样格式调整只需要改模板文件,不用动生成逻辑。我一般每个月会微调一次模板,根据读者反馈优化排版。
6. 内容运营与读者反馈
6.1 发布渠道的选择
日报做出来得有人看。我试过几个渠道,各有优劣:
- 邮件列表:到达率高,适合深度读者,但打开率在下降
- 即时通讯群组:传播快,互动强,但信息容易被淹没
- 内容平台:有推荐流量,但格式限制多
- 个人站点:完全可控,但需要自己引流
我目前的策略是以个人站点为主,邮件列表为辅,即时通讯群组做分发。个人站点是内容的家,所有历史日报都能查到;邮件列表触达核心读者;群组用来做即时讨论和反馈收集。
6.2 读者反馈的收集与利用
读者反馈是改进日报的重要依据。我主要通过三个渠道收集:邮件回复、群组讨论、以及一个简单的反馈表单。反馈内容我一般分三类处理:
内容建议类:比如“希望增加XX板块”“XX类信息太多了”,这类反馈我会认真考虑,但不会立刻改,而是观察一段时间看是不是普遍需求。
纠错类:比如“XX条目的信息有误”“链接打不开”,这类反馈优先级最高,确认后立刻修正并在下期日报里说明。
鼓励类:比如“这个日报帮我省了很多时间”,这类反馈虽然不直接指导改进,但能让我知道哪些做法是对的,值得坚持。
6.3 持续运营的心得
做日报最难的不是技术,是坚持。每天雷打不动地筛选、点评、发布,时间长了很容易倦怠。我总结了几条让自己坚持下去的经验:
第一,降低单日负担。不要把日报做成一个大工程。我的目标是每天总投入不超过1小时,其中机器自动跑的部分占大头,人工部分控制在30分钟以内。负担轻了,才容易坚持。
第二,允许不完美。有些日子就是没什么好内容,那就少发几条。有些日子点评写得不够精彩,那也没关系。日报是长期工程,单日质量波动不重要,重要的是持续输出。
第三,从反馈中获取动力。每次收到读者说“这个日报对我有帮助”,都是继续做下去的动力。我会把这些反馈存下来,倦怠的时候翻一翻。
第四,定期迭代。不要一直用同一套流程。每隔一段时间回顾一下,看看哪里可以优化、哪里可以简化。迭代本身也是一种乐趣。
提示:如果你也想做自己的AI日报,建议从最小可行版本开始——先手动做一周,感受一下工作量和内容偏好,然后再逐步自动化。不要一上来就搭复杂系统,那样很容易在技术细节里迷失,忘了做日报的初衷是提供有价值的信息。