AI日报信息聚合系统实战:从采集去重到摘要归档的完整方案
1. 为什么我要做一份“AI 日报”式的信息聚合项目信息过载这件事做技术的人体会最深。我每天要刷十几个信息源从模型发布、工具更新到行业动态光是筛选和去重就能吃掉一个多小时。更麻烦的是很多内容当天看完了过两周想回头查却发现根本记不住在哪看到的。所以我从两年前开始给自己搭了一套“AI 日报”式的信息聚合流程每天固定时间产出一份结构化的摘要标题就按日期走比如“AI 日报 · 2026-09-14周一”这种格式。这套东西的核心目标很明确把分散在各处的 AI 相关信息在每天固定时间点收敛成一份可检索、可回溯、可分享的日报。它解决的不是“获取信息”的问题而是“信息沉淀和二次利用”的问题。适合谁来参考如果你是个体开发者、技术团队负责人、或者单纯想系统跟踪 AI 领域动态但不想被信息流牵着走的人这套思路都能直接拿去改。我踩过的最大一个坑是一开始只做“抓取”不做“归档”。结果每天生成一堆 Markdown 文件散落在不同目录一个月后想找某条模型更新记录翻了半小时没找到。后来我把整个流程重新设计了一遍从数据源管理、去重策略、摘要生成到归档索引每个环节都做了取舍。下面我把这套东西完整拆开讲包括为什么这么设计、具体怎么落地、以及那些只有实际跑起来才会遇到的问题。2. 整体架构设计与核心思路拆解2.1 日报系统的本质是什么很多人一听“日报”就觉得是个简单的定时脚本抓几个 RSS 源然后拼起来就完事了。我一开始也这么想结果做出来的东西自己都不想看。问题出在哪儿日报的本质不是信息搬运而是信息降噪和结构化重组。你想想一个正常的 AI 信息源一天可能产出几十条内容其中真正值得关注的可能就三五条。如果只是把原始标题堆在一起读者还是要自己判断哪条重要、哪条重复、哪条跟自己相关。这跟直接刷信息流没有本质区别。所以日报系统的核心价值在于三个动作筛选、归类、标注。筛选是去掉噪音归类是建立上下文标注是给出判断依据。我最终确定的架构是四层数据采集层、清洗去重层、摘要生成层、归档索引层。每一层都有明确的输入输出边界层与层之间通过标准化的数据结构传递。这样做的好处是任何一层出问题都不会影响其他层而且后续想替换某个环节比如换一个摘要模型成本很低。2.2 为什么选择“日期驱动”而不是“事件驱动”标题里带日期比如“AI 日报 · 2026-09-14周一”这个设计看起来不起眼但背后有明确的考量。我试过两种模式一种是事件驱动有重要新闻才出日报另一种是日期驱动每天固定出一份哪怕内容少也出。事件驱动的问题在于“重要”这个判断很难自动化。你设的阈值高了可能一周都不出一份阈值低了又变成天天出但质量参差不齐。而且事件驱动会导致归档混乱因为文件名没有规律检索起来很麻烦。日期驱动就不一样了每天一份文件名就是日期天然可排序、可检索、可对比。哪怕某天内容很少你也可以在日报里写一句“今日无重大更新”这本身就是有价值的信息——它告诉你那天确实没什么事发生。另外日期驱动还有一个隐性好处它强迫你建立稳定的工作节奏。我每天早上九点跑一次生成脚本十点前过一遍人工审核这个习惯坚持了两年现在已经成为我日常的一部分。事件驱动很容易变成“想起来才做”最后就不了了之了。2.3 数据源的选择与分层策略数据源这块我折腾过很多轮最后稳定下来的配置是分三层的。第一层是核心源大概五到八个这些是必须每天覆盖的比如主流模型厂商的官方博客、几个头部技术社区的热榜。第二层是补充源大概十五到二十个这些是轮询覆盖的不一定每天都有内容但有了就值得收录。第三层是长尾源数量不限通过关键词订阅的方式接入只有命中特定关键词才会进入日报。为什么要分层因为不同层级的源处理策略完全不一样。核心源需要全量抓取、逐条审核补充源可以做自动摘要、人工抽检长尾源基本就是关键词匹配后直接归档不进入日报正文。如果不分层要么工作量爆炸要么漏掉重要信息。这里有个实操心得数据源不是越多越好。我一开始接了五十多个源结果每天光去重就累得半死而且很多源内容高度重叠。后来我做了个统计发现前八个源覆盖了大概百分之七十的有效信息。所以现在我的策略是先把核心源做深做透再考虑扩展。2.4 去重策略为什么简单的标题匹配不够用去重是日报系统里最容易被低估的环节。我最早用的是标题精确匹配后来发现根本不够。同一个事件不同来源的标题可能完全不一样比如“某模型发布新版本”和“某模型迎来重大更新”字面上看是两个东西实际上是一回事。我现在的去重策略是三层过滤。第一层是URL 去重这个最简单直接比对链接。第二层是标题相似度用编辑距离或者简单的分词重合度来判断阈值设在零点七左右。第三层是内容指纹对正文做 SimHash比对海明距离。三层下来重复率能压到百分之五以下。但这里有个坑过度去重比不去重更危险。我有一次把阈值调得太激进结果把两条相关但不相同的内容合并了导致日报里少了一条重要信息。后来我加了一个“疑似重复”的中间状态不直接删除而是标记出来让人工判断。这个改动虽然增加了一点工作量但避免了信息丢失。3. 核心细节解析与实操要点3.1 采集环节RSS 与 API 的取舍采集环节我主要用两种方式RSS 和 API。RSS 的优点是通用、稳定、不需要维护认证缺点是更新延迟可能比较大而且有些源不提供全文。API 的优点是实时、结构化、可以拿到更多元数据缺点是需要处理认证、限流、以及不同 API 的格式差异。我的选择是优先用 RSSAPI 作为补充。原因很简单RSS 的维护成本低得多。一个 RSS 源配好之后基本不用管除非对方改了地址。API 就不一样了token 会过期、限流策略会变、返回格式可能升级维护起来很烦。只有那些 RSS 覆盖不到或者延迟太大的源我才会去接 API。具体配置上我用的是一个 YAML 文件管理所有源每个源包含名称、类型、地址、层级、更新频率这几个字段。这样增删改源只需要改配置文件不用动代码。下面是一个示例结构sources: - name: example-blog type: rss url: https://example.com/feed.xml tier: core interval: daily - name: example-api type: api endpoint: https://api.example.com/v1/posts tier: supplement interval: hourly auth: token注意RSS 地址一定要定期检查。我遇到过好几次源地址悄悄变了但没通知的情况结果连续几天日报里少了那个源的内容直到我手动核对才发现。3.2 清洗环节正文提取的三种方案对比拿到原始内容之后下一步是提取正文。这块我试过三种方案各有优劣。第一种是基于规则的提取比如用 Readability 算法或者自己写 CSS 选择器。优点是快、可控缺点是每个站点都要单独适配维护成本高。第二种是基于模板的提取针对固定几个源写专门的解析逻辑。优点是准确率极高缺点是扩展性差。第三种是基于模型的提取用一个轻量级的文本分类模型来判断哪些段落是正文。优点是通用性好缺点是需要标注数据而且偶尔会出错。我现在的做法是混合使用核心源用模板提取保证准确率补充源用规则提取够用就行长尾源直接用模型提取反正不进入日报正文准确率要求不高。这样在准确率和维护成本之间找到了一个平衡点。这里有个细节值得说正文提取之后一定要做长度过滤。有些源会把导航栏、页脚、相关推荐都抓进来导致正文里混入大量噪音。我的做法是设置一个最小长度阈值比如少于两百字符的段落直接丢弃同时用简单的启发式规则去掉包含“点击查看”“”这类短语的段落。3.3 摘要生成为什么我不直接用大模型摘要生成这块很多人第一反应是直接调大模型 API 让它总结。我一开始也这么干后来发现两个问题。第一是成本每天几十条内容每条都调一次大模型一个月下来费用不低。第二是一致性大模型的输出风格不稳定有时候总结得很精炼有时候又啰嗦得不行导致日报读起来很割裂。我现在的策略是分级处理。核心源的内容用大模型生成摘要因为这部分质量要求高值得花成本。补充源用抽取式摘要就是从原文里挑出最重要的两三个句子拼起来虽然读起来不如生成式流畅但胜在稳定、免费、不会胡编。长尾源基本不做摘要只保留标题和链接。抽取式摘要我用的是一种基于 TextRank 的变体核心思路是把文本拆成句子计算句子之间的相似度然后选出重要性最高的几个句子。这个算法不复杂网上有很多开源实现调参主要就是控制摘要长度和句子数量。我的经验是摘要长度控制在原文的百分之十五到二十之间比较合适太短了信息不够太长了又失去摘要的意义。3.4 归档索引文件命名与检索设计归档这块我走过弯路。最早我是按“年-月”建文件夹文件名用时间戳。结果检索的时候特别麻烦因为时间戳不直观而且跨月检索要翻好几个文件夹。后来我改成按日期命名平铺存放文件名就是“AI-日报-2026-09-14.md”这种格式。这样用任何文件管理器都能直接按名称排序找某一天的日报非常快。但光靠文件名还不够因为你想找的是“某条具体信息”而不是“某一天的日报”。所以我额外维护了一个索引文件用 JSON Lines 格式每行一条记录包含日期、标题、来源、关键词、摘要这几个字段。这样我可以用 grep 或者简单的脚本快速检索。比如我想找所有关于“模型压缩”的内容直接搜索引文件就行不用去翻每天的日报正文。# 检索示例查找所有包含“模型压缩”的日报条目 grep 模型压缩 index.jsonl | jq -r .date | .title这个索引文件是自动生成的每次日报生成完之后追加写入。时间长了之后这个索引本身就变成了一个有价值的知识库。我现在经常用它来做趋势分析比如统计某个关键词在过去三个月出现的频率变化。4. 实操过程与核心环节实现4.1 环境准备与依赖安装整套系统我跑在一台常开的迷你主机上系统是 Ubuntu配置不高但足够用。核心依赖就几个Python 环境、几个处理 RSS 和 HTML 的库、以及一个用于调模型的 SDK。下面是我实际用的依赖清单和安装命令。# 创建虚拟环境 python3 -m venv ai-daily-env source ai-daily-env/bin/activate # 安装核心依赖 pip install feedparser beautifulsoup4 lxml requests pip install textrank4zh jieba pip install openai # 用于调用摘要模型 pip install pyyaml python-dateutil这里解释一下每个依赖的用途。feedparser负责解析 RSSbeautifulsoup4和lxml负责 HTML 解析requests处理 API 调用textrank4zh和jieba做中文抽取式摘要openai是摘要模型的 SDKpyyaml读配置文件python-dateutil处理日期计算。这些都是很成熟的库安装过程基本不会出问题。提示如果你的源里有大量英文内容建议额外装一个英文的摘要库或者直接用模型处理。中文的 TextRank 实现对英文支持一般。4.2 配置文件编写与源管理配置文件是整个系统的入口我把它放在项目根目录下的config.yaml。除了前面提到的源列表还包括一些全局参数比如摘要长度、去重阈值、输出目录等。下面是一个完整的配置示例。global: output_dir: ./daily index_file: ./index.jsonl summary_ratio: 0.18 dedup_threshold: 0.72 min_paragraph_length: 200 sources: - name: core-blog-a type: rss url: https://example-a.com/feed tier: core - name: core-blog-b type: rss url: https://example-b.com/rss tier: core - name: supplement-c type: api endpoint: https://api.example-c.com/posts tier: supplement auth_env: EXAMPLE_C_TOKEN几个关键参数说明一下。summary_ratio控制摘要长度占原文的比例我设的是零点一八也就是百分之十八左右。dedup_threshold是标题相似度的去重阈值零点七二是我实测下来比较平衡的值。min_paragraph_length是正文段落的最小长度低于这个值的段落会被丢弃。auth_env表示认证信息从环境变量读取避免把密钥写进配置文件。4.3 采集脚本的核心逻辑采集脚本是整个流程的第一步它的任务是把所有源的内容抓下来统一成标准格式。核心逻辑不复杂但有几个细节需要注意。下面是我实际用的采集函数骨架。import feedparser import requests import yaml from datetime import datetime def load_config(pathconfig.yaml): with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def fetch_rss(source): feed feedparser.parse(source[url]) items [] for entry in feed.entries: items.append({ source: source[name], tier: source[tier], title: entry.get(title, ), link: entry.get(link, ), published: entry.get(published, ), content: entry.get(summary, ), }) return items def fetch_api(source): token os.environ.get(source.get(auth_env, )) headers {Authorization: fBearer {token}} if token else {} resp requests.get(source[endpoint], headersheaders, timeout15) resp.raise_for_status() data resp.json() items [] for post in data.get(items, []): items.append({ source: source[name], tier: source[tier], title: post.get(title, ), link: post.get(url, ), published: post.get(date, ), content: post.get(body, ), }) return items这里有几个实操要点。第一一定要设超时。我有一次没设超时某个源响应特别慢整个采集流程卡了十几分钟。第二异常要捕获但不能吞掉。我的做法是记录错误日志但继续处理下一个源不让单个源的失败影响整体。第三时间字段要统一格式。不同源返回的时间格式五花八门我统一转成 ISO 格式方便后续排序和过滤。4.4 去重与清洗的实现细节采集完之后下一步是去重和清洗。这块的逻辑比采集复杂一些因为要处理各种边界情况。下面是我用的去重函数。import hashlib from difflib import SequenceMatcher def title_similarity(a, b): return SequenceMatcher(None, a, b).ratio() def content_fingerprint(text): # 简化的 SimHash 实现 words jieba.lcut(text) bits [0] * 64 for word in words: h int(hashlib.md5(word.encode()).hexdigest(), 16) for i in range(64): bits[i] 1 if (h i) 1 else -1 fingerprint 0 for i in range(64): if bits[i] 0: fingerprint | (1 i) return fingerprint def hamming_distance(a, b): return bin(a ^ b).count(1) def deduplicate(items, threshold0.72): unique [] for item in items: is_dup False for existing in unique: if item[link] existing[link]: is_dup True break if title_similarity(item[title], existing[title]) threshold: is_dup True break fp_a content_fingerprint(item[content]) fp_b content_fingerprint(existing[content]) if hamming_distance(fp_a, fp_b) 3: is_dup True break if not is_dup: unique.append(item) return unique这段代码里有几个值得说的点。SequenceMatcher是 Python 标准库里的不需要额外安装对于标题这种短文本来说够用了。SimHash 的实现我做了简化实际生产环境建议用成熟的库因为自己写的版本在长文本上可能不够稳定。海明距离的阈值我设的是三意思是两个指纹相差三个比特以内就认为是重复。这个值可以根据实际情况调整值越小越严格。清洗部分主要是正文提取和长度过滤。正文提取我用的是 BeautifulSoup 配合一组启发式规则核心思路是找包含最多文本的div或article标签。长度过滤就是前面说的丢弃少于两百字符的段落。4.5 摘要生成与日报组装摘要生成是最后一步也是决定日报质量的关键环节。我的策略是分级处理核心源用模型生成补充源用抽取式。下面是摘要生成的代码骨架。from textrank4zh import TextRank4Sentence def extractive_summary(text, ratio0.18): tr4s TextRank4Sentence() tr4s.analyze(texttext, lowerTrue, sourceall_filters) sentences tr4s.get_key_sentences(num5) total_len len(text) target_len int(total_len * ratio) summary for s in sentences: if len(summary) len(s.sentence) target_len: break summary s.sentence return summary.strip() def generate_daily_report(items, date_str): lines [f# AI 日报 · {date_str}, ] core_items [i for i in items if i[tier] core] supp_items [i for i in items if i[tier] supplement] lines.append(## 核心动态) for item in core_items: summary call_model_summary(item[content]) lines.append(f### {item[title]}) lines.append(f来源{item[source]} | 链接{item[link]}) lines.append(summary) lines.append() lines.append(## 补充信息) for item in supp_items: summary extractive_summary(item[content]) lines.append(f- **{item[title]}**{summary}) return \n.join(lines)这里有个细节日报的标题格式要固定。我用的是“AI 日报 · 日期星期”这样文件名和内容标题一致检索的时候不会混淆。另外核心动态和补充信息分开呈现读者可以快速扫一眼核心部分有时间再看补充部分。4.6 定时任务与自动化整套流程跑通之后最后一步是自动化。我用的是系统自带的定时任务工具每天早上九点触发一次。脚本会依次执行采集、去重、摘要、组装、归档这几个步骤最后把生成的日报文件放到指定目录同时追加索引记录。# 编辑定时任务 crontab -e # 添加以下行每天早上九点执行 0 9 * * * cd /path/to/ai-daily /path/to/ai-daily-env/bin/python main.py /var/log/ai-daily.log 21日志这块建议单独配置方便排查问题。我遇到过几次定时任务没跑成功的情况都是通过看日志发现的。常见原因包括网络问题、源地址变更、依赖库升级导致的兼容性问题等。5. 常见问题与排查技巧实录5.1 采集失败源地址变更与反爬采集失败是最常见的问题表现是某个源连续几天没有内容。原因通常有两个源地址变了或者对方加了反爬机制。源地址变更比较好处理手动更新配置文件就行。反爬就麻烦一些可能需要加请求头、控制频率、或者改用其他方式获取。我的经验是不要跟反爬硬刚。如果一个源开始反爬说明对方不希望你频繁抓取。这时候要么降低频率要么找替代源。我一般会先检查是不是自己请求太频繁了如果是就调整间隔如果调整后还是不行就直接放弃这个源找别的替代。注意请求频率建议控制在每个源每分钟不超过一次。我见过有人设成每秒一次结果 IP 被封了得不偿失。5.2 摘要质量差模型选择与提示词优化摘要质量差的表现是总结得不准、漏掉关键信息、或者风格不一致。这块我踩过不少坑。最早我用的是一个通用模型效果一般。后来换了一个专门优化过摘要任务的模型效果好很多。再后来我发现提示词的设计比模型选择更重要。我的提示词模板是这样的先给模型一个角色设定然后明确要求总结长度和重点最后给一两个示例。这样出来的摘要风格稳定信息密度也够。另外对于技术类内容我会在提示词里强调“保留技术术语和版本号”避免模型把关键信息简化掉。5.3 去重误判阈值调整与人工复核去重误判有两种漏判和过判。漏判就是重复内容没被去掉日报里出现两条一样的信息。过判就是不同内容被误判为重复导致信息丢失。漏判影响阅读体验过判影响信息完整性后者更严重。我的做法是宁可漏判不可过判。阈值设得稍微宽松一点同时加一个“疑似重复”的标记让人工来判断。具体操作是相似度在阈值附近的内容不直接删除而是标记出来在日报里用特殊符号标注提醒读者这两条可能相关。这样既避免了信息丢失又给了读者判断的依据。5.4 归档检索慢索引优化与分词处理归档检索慢的问题在数据量大了之后会越来越明显。我一开始用 grep 直接搜日报文件几百个文件的时候还行上千个之后就明显卡了。后来改成搜索引文件速度快了很多。但索引文件本身也会变大所以我又加了一个按月份分片的策略每个月一个索引文件检索的时候先定位月份再搜。分词处理也是个优化点。中文检索如果不分词搜“模型压缩”可能搜不到“模型 压缩”这种写法。我的做法是在写入索引的时候对标题和摘要做一次分词把分词结果也存进去。检索的时候对查询词也做分词然后匹配分词结果。这样召回率会高很多。5.5 常见问题速查表下面这张表是我整理的高频问题和对应处理方式方便快速定位。问题现象可能原因排查方式处理建议某源连续无内容地址变更或反爬手动访问源地址更新配置或更换源摘要明显偏离原文模型或提示词问题检查提示词和模型输出调整提示词或换模型日报出现重复条目去重阈值过高检查相似度计算降低阈值或加人工复核检索结果不全分词或索引问题检查索引文件内容优化分词或重建索引定时任务未执行环境或权限问题查看日志文件检查路径和权限配置5.6 几个只有实际跑起来才会知道的坑第一个坑是时区问题。不同源返回的时间可能是不同时区的如果不统一处理排序会乱。我的做法是全部转成 UTC 存储展示的时候再转成本地时区。第二个坑是编码问题。有些源返回的内容编码不是 UTF-8直接解析会乱码。我的做法是在请求的时候显式指定编码或者用chardet自动检测。第三个坑是空内容处理。有些源会返回空标题或者空正文如果不做判断日报里会出现空白条目。我的做法是在清洗阶段就过滤掉这些标题和正文都为空的内容直接丢弃。第四个坑是模型调用失败。网络波动或者限流都可能导致模型调用失败如果不做重试摘要就会缺失。我的做法是加一个简单的重试机制失败后等几秒再试最多试三次。6. 后续可以怎么扩展这套系统这套东西跑稳定之后我陆续加了一些扩展功能。一个是趋势分析基于索引文件统计关键词的出现频率生成周报和月报。另一个是个性化过滤根据我自己的关注领域给内容打分高分内容排在前面。还有一个是多端同步把日报推送到我的笔记软件和阅读器里方便随时查看。如果你也想搭一套类似的系统我的建议是先从最小可用版本开始。不要一上来就追求大而全先把采集和归档跑通再逐步加去重、摘要、索引这些功能。每加一个功能都要确保它真的解决了你的问题而不是为了技术而技术。我见过太多人把系统搭得很复杂结果自己都不用那就失去意义了。最后分享一个我个人的使用习惯我每天生成日报之后会花十分钟快速过一遍把特别重要的条目手动标记出来。这些标记会进入一个单独的“重点”文件月底回顾的时候特别有用。这个动作看起来简单但坚持下来你对整个领域的脉络会清晰很多。