1. 当"日报"变成一种自动化基础设施
做内容的人都有一个共同的痛点:信息过载。每天醒来,光是扫一遍行业动态、技术更新、产品发布、社区讨论,就得花掉一两个小时,而且大部分信息是噪音。我身边不少同行都有过类似经历——收藏夹里躺着几百篇"稍后读",结果再也没打开过。真正有价值的信息,往往是在某个群里被人随手转发时才被注意到,这种被动获取的方式既不系统,也不可靠。
"AI 日报"这个项目,本质上要解决的就是这个问题:把每天散落在各处的信息,经过筛选、归类、摘要,最终以一份结构化的日报形式呈现出来。它不是一个简单的爬虫脚本,而是一套从信息采集、去重、打分、摘要生成到最终排版输出的完整流水线。适合谁来参考?如果你正在做内容聚合类产品、想搭建个人知识管理系统、或者单纯想给自己做一个每天自动推送的行业简报,这套思路都能直接复用。
我前后迭代过三版类似的系统,第一版用纯脚本硬编码,第二版引入了消息队列做异步处理,第三版才把摘要生成这一环交给大模型来做。每一版踩的坑都不一样,但核心逻辑始终没变:采集要广、过滤要狠、摘要要准、输出要稳。下面我把这套系统的设计思路、关键实现和实际运行中遇到的问题,完整地拆开讲一遍。
2. 信息源的分层设计与采集策略
2.1 为什么不能把所有源一视同仁
刚开始做的时候,我犯了一个典型错误:把所有信息源放在同一个优先级队列里,统一抓取、统一处理。结果就是,某些更新频率极高的源(比如某些社区的热帖列表)每天产生上千条数据,把真正重要的官方公告、技术博客更新淹没掉了。后来我意识到,信息源必须分层。
我的做法是把源分成三层:
- 核心层:更新频率低但价值极高,比如官方文档的更新日志、重要项目的 Release Notes、行业标准的修订公告。这类源每天可能只有几条更新,但每一条都值得完整阅读。
- 常规层:技术博客、行业媒体的日常更新、社区精选内容。这类源数量多、更新稳定,需要做摘要和筛选。
- 补充层:社交媒体讨论、论坛热帖、问答社区的高赞回答。这类源噪音最大,但偶尔能发现一些非正式渠道流出的有价值信息。
分层之后,采集策略也随之不同。核心层用全量抓取,不做任何过滤;常规层做关键词匹配和热度排序;补充层则只保留互动量超过阈值的条目。
2.2 采集频率与反爬的平衡
采集频率的设置是个技术活。设得太密,容易被目标站点限流甚至封禁;设得太疏,日报的时效性就没了。我实测下来比较稳妥的方案是:
| 源类型 | 采集间隔 | 单次请求上限 | 失败重试策略 |
|---|---|---|---|
| 核心层 | 每 2 小时 | 50 条 | 立即重试 1 次,间隔 30 秒 |
| 常规层 | 每 1 小时 | 100 条 | 延迟 5 分钟重试,最多 2 次 |
| 补充层 | 每 30 分钟 | 200 条 | 延迟 15 分钟重试,最多 1 次 |
这里有个经验:不要用固定的 User-Agent。我早期图省事,所有请求都用同一个 UA,结果第三天就被某个站点识别出来了。后来改成从一个 UA 池里随机选取,并且配合请求间隔的随机抖动(比如设定 1 秒间隔,实际在 0.7 到 1.5 秒之间随机),稳定性明显提升。
另外,对于返回 JSON 的接口,尽量直接调接口而不是解析 HTML 页面。HTML 结构一变,解析规则就得重写,而接口相对稳定得多。如果只能用 HTML 解析,建议用 CSS 选择器而不是 XPath,前者对页面微调的容忍度更高。
2.3 去重的三个层次
去重这件事,看起来简单,做起来坑很多。我把它分成三个层次来处理:
第一层是 URL 去重。这是最基础的,把 URL 规范化之后(去掉追踪参数、统一协议头、处理末尾斜杠)做哈希比对。但光靠这个不够,因为同一篇文章可能通过不同 URL 访问到。
第二层是标题相似度去重。用编辑距离或者 SimHash 做近似匹配,阈值设在 0.85 左右比较合适。太低会误杀,太高会漏掉。我试过用 0.9,结果同一事件的不同报道全被当成重复的删掉了,日报内容变得很单薄。
第三层是内容指纹去重。对正文做分词后取关键词集合,用 Jaccard 相似度判断。这一层主要处理那些标题不同但内容高度雷同的情况,比如通稿。
注意:去重阈值不要设得太激进。宁可多保留几条相似内容,也不要误删掉角度不同的报道。我吃过这个亏,有次把一篇深度分析和一篇快讯当成重复的合并了,结果日报里只剩一个干巴巴的结论。
3. 从原始信息到可读摘要的加工链路
3.1 打分模型:什么内容值得进日报
采集回来的原始数据,可能有一大半是不值得放进日报的。怎么判断一条内容的价值?我设计了一个多因子打分模型,每个因子权重不同,最终得分超过阈值的才进入摘要环节。
打分因子包括:
- 来源权重:核心层 1.0,常规层 0.7,补充层 0.4
- 时效性:发布时间在 6 小时内的得满分,超过 24 小时的得分衰减到 0.3
- 互动热度:评论数、点赞数、转发数的加权和,做归一化处理
- 关键词命中:命中预设关键词列表的加分,命中多个的累加
- 内容长度:太短(少于 100 字)的降权,太长的也不加分,适中最好
这套打分模型跑下来,每天从几千条原始数据里能筛出 50 到 80 条候选内容,再经过人工规则微调,最终进入日报的大概 20 到 30 条。这个量级刚好,既不会让读者觉得信息量不足,也不会造成阅读负担。
3.2 摘要生成:大模型用在哪一步最合适
摘要生成是整个链路里最考验效果的一环。我试过三种方案:
方案一:抽取式摘要。用 TextRank 或者类似的算法,从原文里挑出最重要的几个句子拼起来。优点是快、不依赖外部服务,缺点是读起来很生硬,句子之间的衔接不自然。
方案二:模板式摘要。针对不同类型的源,预设不同的模板,把关键字段填进去。比如技术博客用"标题 + 核心观点 + 关键数据"的模板。优点是可控性强,缺点是灵活性差,遇到模板覆盖不到的内容就抓瞎。
方案三:生成式摘要。把原文交给大模型,让它用一两句话概括核心内容。优点是读起来自然、信息密度高,缺点是有时候会"幻觉",编造原文里没有的信息。
我最终采用的是方案三为主、方案一为辅的混合策略。具体来说,先用大模型生成摘要,然后用抽取式摘要做交叉验证——如果生成的内容里出现了原文中不存在的关键实体(比如人名、产品名、数字),就标记为可疑,降级使用抽取式摘要的结果。
调用大模型的时候,Prompt 的设计很关键。我用的模板大致是这样的:
prompt_template = """ 请用不超过 80 个字概括以下内容的核心信息。 要求: 1. 保留关键数据和技术名词 2. 不要添加原文中没有的信息 3. 如果原文是英文,用中文概括 4. 直接输出概括结果,不要加任何前缀 原文: {content} """这里有几个细节值得说。第一,字数限制要明确,不然模型可能生成一大段。第二,明确禁止添加原文没有的信息,这能显著降低幻觉率。第三,要求直接输出结果,避免模型加上"这段内容主要讲了……"之类的废话前缀。
3.3 分类与标签:让日报有结构
摘要生成之后,还需要给每条内容打上分类标签。我的分类体系是两级结构:
- 一级分类:技术更新、产品发布、行业动态、观点讨论、工具推荐
- 二级分类:在一级分类下再细分,比如技术更新下面分前端、后端、数据库、运维等
分类的实现方式,早期我用的是关键词规则匹配,后来发现准确率不够,就改成了用大模型做零样本分类。把分类体系和每个类别的描述一起放进 Prompt,让模型判断每条内容属于哪个类别。实测准确率能到 85% 以上,剩下的 15% 通过人工规则兜底。
标签体系还有一个作用:控制日报的版面平衡。如果某天的技术更新特别多,产品发布特别少,日报读起来就会偏科。我会在最终排版前做一个类别配额的检查,确保每个大类至少有 2 条内容,最多不超过 8 条。
4. 日报的排版输出与分发机制
4.1 排版模板的设计取舍
日报最终是要给人看的,排版直接决定了阅读体验。我试过纯文本、Markdown、HTML 邮件三种格式,最后选择了 Markdown 为主、HTML 邮件为辅的方案。
Markdown 的好处是通用性强,可以很方便地转换成其他格式,而且在不同平台上渲染效果一致。我的日报模板大致是这样的结构:
# AI 日报(日期) ## 今日要闻 (3-5 条最重要的内容,每条包含标题、摘要、来源链接) ## 技术更新 (按二级分类分组,每组下列出相关条目) ## 产品动态 (新产品发布、版本更新、功能变更) ## 观点与讨论 (值得关注的行业观点、技术讨论) ## 工具推荐 (新发现的实用工具或资源)每条内容的格式统一为:加粗标题+ 摘要 + 来源标注。来源标注我坚持保留,一方面是对原作者的尊重,另一方面也方便读者追溯原文。
4.2 分发渠道的选择
日报做出来,得送到读者手里。我目前用的是三个渠道并行:
- 邮件订阅:最正式的分发方式,适合深度读者。用定时任务在每天早上 8 点准时发送。
- 即时通讯群组:适合快速触达,把日报的核心内容精简成几条消息推送。
- 静态页面:生成一个每日更新的网页,方便存档和检索。
这三个渠道的内容不是简单复制的。邮件版最完整,群组版只推送今日要闻,网页版则保留全部历史记录并支持搜索。这样做的好处是,不同场景下的读者都能找到适合自己的消费方式。
4.3 定时任务的可靠性保障
整个流水线是靠定时任务驱动的,所以任务的可靠性至关重要。我踩过的坑包括:任务执行超时导致后续任务堆积、某个环节失败导致整条链路中断、服务器重启后任务丢失等。
现在的做法是:
- 每个环节独立成任务,环节之间通过消息队列传递数据,避免单点阻塞。
- 每个任务都有超时设置,超时后自动重试,重试次数用完后发送告警。
- 任务状态持久化到数据库,服务器重启后能从上次中断的地方继续。
- 每天早上日报发送前,有一个"健康检查"任务,确认所有环节的数据都正常,否则发送告警而不是发送一份残缺的日报。
提示:定时任务的时间设置要留足缓冲。我一开始把采集和摘要生成安排在同一个时间窗口,结果采集偶尔延迟,摘要生成就拿不到完整数据。后来改成采集提前 2 小时开始,摘要生成在发送前 1 小时执行,就再没出现过数据不全的问题。
5. 运行三个月后暴露的真实问题
5.1 摘要质量的波动与人工兜底
系统跑了一个月之后,我发现摘要质量并不稳定。有些天的摘要读起来很顺,有些天则明显感觉"不对劲"——要么是信息遗漏,要么是表述别扭。排查下来,原因主要有两个:
一是原文质量参差不齐。有些源的文章本身逻辑就混乱,模型概括出来的东西自然也好不到哪去。二是模型输出的随机性。同样的输入,不同时间调用可能得到不同的输出,偶尔会碰到质量较差的版本。
我的应对策略是引入人工抽检机制。每天日报发送前,我会随机抽取 5 条摘要快速过一遍,如果发现明显问题,就手动修正或者替换成抽取式摘要的结果。这个动作只花几分钟,但能显著提升日报的整体质量。另外,我建了一个"问题摘要"的记录表,把每次人工修正的案例记下来,定期回顾,看看有没有规律性的问题可以优化。
5.2 信息源的失效与动态维护
信息源不是一成不变的。有些站点改版了,原来的解析规则失效;有些接口关闭了,需要找替代方案;还有些源的内容质量下降,不值得继续采集。
我现在的做法是给每个源加一个健康度指标,综合考量采集成功率、内容更新频率、摘要质量评分。健康度低于阈值的源会被自动标记,进入观察名单。连续一周健康度不达标的,就暂时停用,等找到替代方案或者确认恢复后再启用。
这套机制帮我省了不少事。以前是等到日报内容明显变少才发现某个源挂了,现在系统会提前告警,我有充足的时间处理。
5.3 成本控制:大模型调用不是免费的
生成式摘要虽然效果好,但调用大模型是有成本的。日报每天处理 50 到 80 条候选内容,每条都要调用一次模型,一个月下来费用不算低。
我做了几件事来控制成本:
- 缓存机制:对相同或高度相似的内容,直接复用之前的摘要结果,不重复调用。
- 分级调用:核心层的内容用高质量模型,常规层和补充层用成本更低的模型。
- 批量处理:把多条内容合并成一个请求发给模型,减少请求次数。不过要注意,批量太大可能影响单条摘要的质量,我实测下来一次处理 5 到 8 条比较合适。
这些优化做下来,成本降低了大概 60%,而日报质量几乎没有下降。
6. 如果你也想搭一套,从哪开始
6.1 最小可行版本的功能边界
如果你看完上面的内容想自己动手,我的建议是先做一个最小可行版本,不要一上来就追求大而全。最小版本只需要包含三个功能:
- 从 3 到 5 个核心源采集内容
- 做基础的去重和关键词过滤
- 生成一份简单的 Markdown 日报
这个版本一两天就能搭起来,跑通之后再逐步加入打分模型、摘要生成、分类标签这些进阶功能。我见过不少人一开始就设计了一套复杂的架构,结果卡在某个环节迟迟跑不起来,最后不了了之。
6.2 技术选型的务实建议
技术栈方面,我的建议是用你最熟悉的语言和框架,不要为了追求"先进"而选自己不熟的工具。这套系统的核心逻辑并不复杂,用 Python 的 requests + BeautifulSoup 能跑,用 Node.js 的 axios + cheerio 也能跑,甚至用 Go 写也完全没问题。关键是快速跑通,而不是炫技。
数据库方面,早期用 SQLite 就够了,数据量大了再换 PostgreSQL 或 MySQL。消息队列如果不想引入 Redis 或 RabbitMQ,用数据库表模拟一个简单的队列也能凑合。等系统稳定了,再考虑替换成更专业的组件。
6.3 持续迭代的节奏把控
最后说一点关于迭代节奏的体会。这套系统我前后改了三个月,但并不是每天都在改。我的节奏是:每周固定花半天时间做优化,其余时间让它自动跑。优化的时候,优先解决那些"每天都会遇到"的问题,比如某个源经常采集失败、某类摘要质量不稳定。那些"偶尔出现"的问题,先记下来,攒到一起再处理。
这样做的好处是,系统在持续变好,但不会占用我太多精力。毕竟日报是给人看的,内容质量才是核心,系统本身只是工具。工具够用就行,不必追求完美。
我在实际运行中发现,最有价值的改进往往不是技术层面的,而是对"什么内容值得推荐"这个判断的不断校准。这个判断力,只能靠日复一日的观察和调整来积累,没有捷径。