
1. 当一份日报只剩下日期这个标题到底在说什么看到AI 日报 — 2026-09-21这个标题第一反应可能是这不就是个日期吗正文空的关键词空的摘要也空的。但恰恰是这种三空状态反而暴露了一个非常典型的真实场景——日报类内容的自动化生产与归档。我在过去几年里帮不少团队搭过内部信息聚合系统也维护过几个面向特定圈子的资讯简报。这类XX日报 日期的命名方式几乎成了自动化流水线的标准产物每天定时抓取、筛选、排版、归档文件名或标题就是主题 日期。所以这个标题背后真正值得聊的不是AI日报这四个字有多新鲜而是一套能稳定跑起来、不翻车、可回溯的日报生成机制。它解决的核心问题很具体信息源太多、人工筛选太慢、格式不统一、历史版本找不到。适合谁来参考三类人最有用——一是想给自己或小团队做每日信息简报的技术同学二是需要定期输出行业动态、但又不想每天手动复制粘贴的运营或研究岗三是已经在跑自动化脚本、但经常遇到今天抓空了格式乱了旧文件被覆盖了这些破事的人。我先把结论摆前面日报系统的难点从来不在抓取而在筛选规则的设计和归档结构的稳定性。抓取是最容易的部分随便一个请求加解析就能拿到一堆内容真正让人头疼的是怎么让每天产出的东西质量稳定、怎么让三个月后还能翻出某一天的内容、怎么在源站改版时不至于整个流程崩掉。下面我按实际搭建的顺序把这套东西拆开讲。2. 日报系统的骨架从触发到落盘的完整链路2.1 为什么定时触发要用错峰 重试而不是整点执行很多人第一版都会写个整点定时任务比如每天 08:00 跑。实测下来问题很明显如果多个任务都挤在整点资源争抢、接口限流、甚至脚本互相阻塞都会出现。我的做法是把触发时间错开到非整点比如 07:43 或 08:17并且加一层重试。重试不是简单粗暴地循环三次而是要有退避策略。我一般用指数退避第一次失败等 30 秒第二次等 2 分钟第三次等 10 分钟。这样做的理由是很多抓取失败是临时性的对方服务抖动、网络瞬断立刻重试大概率还是失败等一会儿反而能成。import time import random def fetch_with_retry(task, max_retry3): delays [30, 120, 600] for attempt in range(max_retry): try: return task() except Exception as e: if attempt max_retry - 1: raise # 加一点随机抖动避免多个任务同时重试 jitter random.uniform(0, 10) time.sleep(delays[attempt] jitter)提示重试次数不要设太多。超过三次还失败基本说明是源站结构变了或者被封了继续重试只是浪费时间应该直接告警。2.2 抓取层为什么我坚持先存原始数据再解析新手最容易犯的错是抓到页面直接解析、解析完就丢掉原始内容。我踩过这个坑某次源站改版解析规则全废想回头重新解析历史数据结果发现原始 HTML 根本没存只能干瞪眼。正确做法是两段式第一段只负责把原始响应HTML、JSON、RSS原封不动落盘按日期分目录存第二段再从原始数据里解析出结构化字段。这样即使解析逻辑改了也能对历史数据重新跑一遍不用重新抓。目录结构我一般这样设计data/ raw/ 2026-09-21/ source_a.html source_b.json parsed/ 2026-09-21/ items.json reports/ 2026-09-21.md这个结构的好处是按日期天然分区找某一天的数据直接进对应目录不用在几万条记录里翻。而且 raw 和 parsed 分离清理时也能分别处理——raw 占空间大可以只保留 30 天parsed 体积小长期留着做趋势分析。2.3 解析层字段设计决定了后面所有环节的天花板解析出来的每条内容我建议至少包含这几个字段缺一个后面都会难受字段类型说明为什么必须有idstring内容唯一标识去重、回溯、更新都靠它titlestring标题展示和检索的核心urlstring原文链接溯源、核实sourcestring来源标识按来源筛选和统计published_atdatetime发布时间排序、时效过滤fetched_atdatetime抓取时间排查延迟问题summarystring摘要日报正文素材tagslist标签分类和聚合scorefloat相关度评分排序和筛选其中id 的生成方式很关键。我一般用source 原始url的哈希或者源站自带的唯一标识。千万别用标题做 id因为标题可能重复、可能微调会导致去重失效。2.4 落盘与归档为什么文件名要用 ISO 日期而不是9月21日标题里那个2026-09-21就是标准 ISO 日期格式这不是随便写的。用 ISO 格式YYYY-MM-DD的好处是字符串排序等于时间排序文件列表天然按时间排列脚本处理时不用额外解析。如果用9月21日这种排序会乱跨年跨月更是灾难。归档时我还会额外维护一个索引文件记录每天日报的元信息条数、来源分布、生成时间、是否有异常。这个索引在排查某天日报为什么是空的时特别有用一眼就能看出是抓取失败还是筛选规则把内容全过滤了。3. 筛选规则日报质量的分水岭3.1 为什么全量罗列是最差的选择我见过不少日报就是把当天抓到的所有内容按时间倒序排一遍几十条堆上去。这种日报没人看因为信息密度太低读者要自己从噪音里找信号。日报的价值在于替读者做减法而不是把找到的都塞给他。筛选的核心逻辑是先做硬性过滤再做软性排序最后做数量控制。硬性过滤是去重、去广告、去明显不相关的软性排序是按相关度、时效、来源权重打分数量控制是最终只保留 Top N比如 10 到 15 条。3.2 相关度评分一个能落地的简单算法评分不用搞得太复杂我常用的是一套加权公式实测效果够用score w1 * keyword_match w2 * source_weight w3 * freshness w4 * engagementkeyword_match标题和摘要里命中关键词的数量归一化到 0-1source_weight来源权重优质源给 1.0普通源给 0.5这个需要人工维护一个源清单freshness时效分越新越高可以用1 / (1 小时差/24)这种衰减函数engagement如果有互动数据阅读、点赞、评论可以纳入没有就设 0权重我一般设 w10.4, w20.3, w30.2, w40.1。这个配比不是拍脑袋是因为关键词匹配最能反映是否相关来源权重反映是否可信时效反映是否新鲜三者重要性依次递减。注意关键词匹配不要用简单的字符串包含中文要做分词英文要注意词形还原。否则AI会匹配到said里的ai闹笑话。3.3 去重为什么标题完全相同远远不够去重是日报系统里最容易被低估的环节。同一件事不同源站的标题可能完全不同比如某模型发布新版本和XX 公司推出升级版模型字符串比对根本发现不了。我的做法是三层去重精确去重url 或 id 完全相同直接去掉近似去重标题做归一化去标点、转小写、去停用词后算相似度超过阈值比如 0.85视为重复语义去重对摘要做向量化算余弦相似度超过阈值视为同一事件前两层能解决 90% 的问题第三层成本高只在内容量大、重复严重时才上。相似度计算可以用简单的编辑距离或 Jaccard 相似度不一定非要上模型。3.4 数量控制与保底机制最终输出条数我一般控制在 10-15 条。太少显得单薄太多读者看不完。但这里有个坑如果某天优质内容不足硬凑数量会拉低质量如果某天内容爆炸砍到 15 条又可能漏掉重要的。我的处理是设一个保底 弹性机制保底 8 条上限 20 条。如果筛选后不足 8 条就放宽阈值补足如果超过 20 条就提高阈值压缩。同时给每条打上必读和可选标记必读的永远保留可选的按分数取舍。4. 日报正文的生成从数据到可读文本4.1 模板设计为什么结构要固定但内容要灵活日报正文我坚持用固定模板因为固定结构让读者形成阅读习惯知道去哪找什么。但固定不等于死板模板里要留出灵活空间。我常用的结构是这样的# AI 日报 — 2026-09-21 ## 今日要闻 3-5 条最重要的每条 2-3 句话 ## 行业动态 按主题分组的若干条 ## 值得一读 深度内容、长文、报告 ## 数据速览 如果有可量化的指标今日要闻是编辑重点需要人工或规则挑出最重要的几条行业动态可以按标签自动分组值得一读放那些不适合快速浏览但价值高的内容。4.2 摘要生成抽取式还是生成式摘要这块我的建议是优先用抽取式谨慎用生成式。抽取式就是从原文里挑出关键句优点是绝对不会编造、不会失真生成式虽然读起来更顺但有幻觉风险日报这种要求准确性的场景一条编造的信息就可能毁掉整个日报的可信度。抽取式的实现可以用 TextRank 或简单的关键词句打分把原文分句给每句按包含关键词数量 位置权重首句权重高 长度适中打分取最高分的 1-2 句。这个方案简单、可控、不会出错。如果确实要用生成式做润色一定要加一道事实校验生成的内容里出现的数字、名称、时间必须能在原文里找到对应找不到就退回抽取式结果。4.3 排版细节那些让日报看起来专业的小事排版这事看着小但直接影响阅读体验。我总结几个实测有效的细节每条内容之间留空行不要挤在一起标题加粗但不要整段加粗链接用文字锚点不要直接贴长 url来源标注统一格式比如统一放在末尾的括号里日期格式全文统一要么全用 2026-09-21要么全用 9月21日别混还有一个容易忽略的移动端适配。很多人只在电脑上看日报但实际很多读者是在手机上看。所以段落别太长表格别太宽代码块要能横向滚动。5. 稳定性与可维护性让系统能跑一年的关键5.1 源站改版是必然事件怎么把影响降到最低我做过的日报系统没有一个能逃过源站改版的。区别只在于有的改版后半小时就恢复了有的改版后一周都没发现日报已经空了。关键在监控。我一般加三层检查抓取量监控每天抓到的条数如果比过去 7 天均值低 50% 以上告警解析成功率监控解析出的有效字段比例如果低于 80%告警内容质量监控随机抽几条人工看或者用规则检查比如标题是否为空、url 是否有效告警不用搞得太复杂发个消息到群里或者邮件就行。重点是要有人看到否则监控等于没有。5.2 配置与代码分离改规则不该动代码筛选阈值、来源权重、关键词列表这些东西一定要放在配置文件里不要硬编码在代码里。因为这些东西会频繁调整每次调都改代码、重新部署效率太低。我一般用一个 YAML 或 JSON 配置文件sources: - name: source_a weight: 1.0 enabled: true - name: source_b weight: 0.5 enabled: true filter: min_score: 0.3 max_items: 20 min_items: 8 keywords: - 大模型 - 多模态 - 推理这样调整规则时只改配置代码不用动风险也小。5.3 历史数据的价值别把旧日报当垃圾很多人日报生成完就不管了旧文件堆在那占空间。其实历史日报是很有价值的可以做趋势分析某个话题的热度变化、可以做回溯检索三个月前那条重要消息是哪天发的、可以做质量对比现在的筛选规则比半年前好吗。所以我在归档时会额外做两件事一是把每天的 parsed 数据存成结构化格式JSON方便后续分析二是维护一个全局索引记录每天的关键词分布和条数做趋势时直接读索引就行。6. 我踩过的几个真实坑6.1 时区问题为什么今天的日报里混进了昨天的内容这个坑我踩过两次。第一次是服务器时区和源站时区不一致导致今天的边界判断错了第二次是发布时间和抓取时间混用排序时把旧内容排到了前面。解决办法是统一用 UTC 存储展示时再转本地时区。所有时间字段都存 UTC比较和排序也用 UTC只在最终生成日报时转成读者所在时区。这样不管服务器在哪、读者在哪逻辑都不会乱。6.2 编码问题中文乱码的几种成因中文乱码看着简单成因其实挺多响应头没声明编码、HTML meta 里的编码和实际不符、解析时用了错误的编码。我的经验是不要相信任何声明直接检测。用 chardet 之类的库检测实际编码或者干脆先按 UTF-8 解解出来有乱码再试 GBK。还有一个隐蔽的坑有些源站返回的内容里混了多种编码整体检测会失败。这种情况只能按段落分别处理或者直接放弃这个源。6.3 空日报最尴尬的故障某天早上打开日报发现是空的——这种故障最尴尬因为读者会以为系统挂了。空日报的成因通常有三个抓取全失败、筛选阈值太高把内容全过滤了、解析规则失效导致字段全空。我的处理是加一个空日报保护如果最终条数为 0不要直接输出空日报而是输出一条说明告诉读者今日无符合条件的内容同时触发告警。这样至少读者知道系统还在跑只是今天确实没内容。7. 关于这套系统我个人的几点体会搭日报系统这事技术难度其实不高难的是持续维护的耐心。我见过太多人第一版做得漂漂亮亮跑了两周就荒废了原因无非是源站改版没及时修、筛选规则没持续调、内容质量下滑没人管。我的建议是先把最小可用版本跑起来别一上来就追求完美。第一版只要能抓、能筛、能输出就行哪怕只有三个源、只有十条内容。跑起来之后根据实际阅读体验慢慢调比一开始憋大招要靠谱得多。另外日报的编辑感很重要。纯自动生成的日报读起来总差点意思。我现在会在自动生成的基础上每天花五分钟人工过一遍调整一下顺序、补一句点评、删掉明显不合适的。这五分钟的投入能让日报的可读性提升一大截。最后分享一个小技巧给日报加一个昨日回顾或本周精选的入口。读者看日报是碎片化的容易漏掉重要内容。加一个回顾入口让错过的人能补上日报的长期价值会高很多。这个功能实现起来也简单就是把历史 parsed 数据按时间范围聚合一下成本很低但很实用。