拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI资讯日报系统搭建实战:信源分级、去重排序与自动摘要

AI资讯日报系统搭建实战:信源分级、去重排序与自动摘要

1. 当“日报”变成一种技术活:AI资讯聚合的真实门槛

每天早上七点,我的手机闹钟还没响,Slack 里的机器人已经推了三条链接过来。点开一看,两条是三天前的旧闻,一条是某公司融资通稿的二次转载。这种体验相信不少做 AI 方向的朋友都遇到过——明明订阅了十几个信源,RSS 阅读器里堆了几百条未读,真正有价值的信息却像大海捞针。

“2026-09-21 AI最新资讯日报”这个标题看起来简单,好像就是把当天的 AI 新闻汇总一下。但真正动手做过资讯聚合的人都知道,这里面涉及的技术栈和判断逻辑远比想象中复杂。它本质上是一个多源异构数据的采集、去重、排序、摘要与分发系统,涉及爬虫调度、文本相似度计算、时效性加权、信源可信度评估、自动摘要生成等多个环节。任何一个环节偷懒,最终产出的日报就会变成“垃圾进、垃圾出”的典型样本。

这篇文章面向的是那些想自己搭建一套 AI 资讯日报系统的开发者、内容运营者,或者单纯想搞清楚“为什么我看到的日报质量参差不齐”的读者。我会从信源管理、去重策略、排序算法、摘要生成、分发机制这几个维度,把我在实际搭建和维护过程中踩过的坑、总结的经验完整地分享出来。文章里提到的所有方案都是我在真实环境中跑过的,代码片段可以直接参考,参数设置也有具体的计算依据。

先说一个反直觉的结论:资讯日报的质量瓶颈从来不在采集环节,而在去重和排序。采集是最容易解决的,写个爬虫或者接几个 API 就能拿到数据。但当你面对 500 条原始条目时,如何从中挑出真正值得看的 15 条,这才是区分“能用”和“好用”的分水岭。接下来的内容会围绕这个核心矛盾展开。

2. 信源分级与采集调度:别把官方博客和营销号放在同一个池子里

2.1 信源可信度分级的具体标准

我见过太多人做资讯聚合时,把所有信源一视同仁地扔进一个列表里轮询。结果就是 OpenAI 的官方公告和某个不知名搬运号的标题党文章在同一个权重下竞争,最后排序出来的结果自然惨不忍睹。正确的做法是建立信源分级体系,我通常把它分为四个等级:

等级信源类型代表示例采集频率权重系数
S 级官方公告与一手论文各大 AI 实验室博客、arXiv 新论文每 2 小时1.0
A 级权威科技媒体主流科技媒体的 AI 频道每 4 小时0.8
B 级行业分析师与知名博主有长期跟踪记录的独立分析师每 6 小时0.6
C 级聚合平台与社区热帖技术社区的热门讨论每 12 小时0.4

这个权重系数不是拍脑袋定的。S 级信源的内容一旦发布,基本可以认为是“事实源”,不需要交叉验证。A 级媒体偶尔会有解读偏差,但事实准确度仍然很高。B 级和 C 级的内容需要至少两个独立信源交叉验证后才会被采纳。我在实际运行中发现,如果把 C 级信源的权重调到 0.5 以上,日报里就会出现大量重复的、缺乏信息增量的内容。

采集频率的设置也有讲究。S 级信源每 2 小时抓一次,是因为官方公告的时效性极强,晚几个小时可能就被别人抢先了。而 C 级信源每 12 小时抓一次,是因为社区热帖的发酵需要时间,抓太勤反而会拿到大量噪音。这里有个经验值:采集频率应该和信源的内容产出速率成正比,而不是和你的焦虑程度成正比。

2.2 采集调度的工程实现细节

采集调度看起来简单,实际上有几个容易忽略的坑。第一个是请求去重。同一个 URL 可能在多个聚合平台上出现,如果你不做 URL 规范化处理,同一个页面会被抓取多次。我的做法是在入库前对 URL 做标准化:去掉 utm 参数、统一 http 和 https、去掉末尾斜杠。这个简单的处理能减少大约 30% 的重复采集。

第二个坑是增量采集的边界判断。很多信源没有提供“最后更新时间”字段,你只能通过对比内容哈希来判断是否有更新。我的方案是维护一个content_hash表,每次采集后计算正文的 SimHash 值,如果和上次相同就跳过。SimHash 的好处是它对微小改动不敏感,比如页面上的广告位轮换不会触发误判。

第三个坑是失败重试的策略。网络请求失败是常态,但无脑重试会浪费资源。我的策略是:首次失败后等待 30 秒重试,第二次失败后等待 5 分钟,第三次失败后标记该信源为“临时不可用”,30 分钟后再试。如果连续 3 次标记为不可用,就发告警通知我手动检查。这套策略运行了半年多,误报率很低。

import hashlib from urllib.parse import urlparse, urlunparse, parse_qs, urlencode def normalize_url(url): parsed = urlparse(url) # 去掉 utm 等追踪参数 query = parse_qs(parsed.query) filtered = {k: v for k, v in query.items() if not k.startswith('utm_')} new_query = urlencode(filtered, doseq=True) # 统一协议和末尾斜杠 normalized = parsed._replace( scheme='https', query=new_query, path=parsed.path.rstrip('/') ) return urlunparse(normalized) def simhash(text): # 简化版 SimHash,实际使用建议用 64 位 tokens = text.lower().split() v = [0] * 64 for token in tokens: h = int(hashlib.md5(token.encode()).hexdigest(), 16) for i in range(64): v[i] += 1 if (h >> i) & 1 else -1 fingerprint = 0 for i in range(64): if v[i] > 0: fingerprint |= (1 << i) return fingerprint

这段代码是我实际在用的简化版本。normalize_url处理 URL 规范化,simhash生成内容指纹。注意 SimHash 的位数选择:64 位在大多数场景下够用,如果你处理的文本量特别大,可以考虑 128 位。但位数越多,计算开销越大,需要根据实际情况权衡。

3. 去重不是简单的字符串匹配:从精确去重到语义去重的三层过滤

3.1 第一层:URL 与标题的精确匹配

去重的第一道防线是最简单的:URL 完全相同、标题完全相同的内容直接丢弃。这一步能过滤掉大约 40% 的重复条目,因为很多聚合平台就是直接转载原始链接。但这里有个细节需要注意:标题的精确匹配要考虑标点符号和空格的差异。比如“OpenAI 发布 GPT-5”和“OpenAI发布GPT-5”应该被视为同一个标题。我的做法是在比较前先做一次标准化:去掉所有空格、统一全角半角标点、转小写。

这一步的实现非常简单,用一个set存储已见过的 URL 和标准化标题即可。但要注意内存占用,如果你每天处理上万条数据,这个 set 会越来越大。我的方案是只保留最近 7 天的记录,更早的自动清理。因为资讯日报的时效性通常不超过一周,超过一周的重复内容即使漏掉也无所谓。

3.2 第二层:基于 SimHash 的近似去重

精确匹配只能处理完全相同的条目,但现实中更多的是“改了个标题、换了个来源”的近似重复。比如同一篇论文,arXiv 上的标题是“Attention Is All You Need”,某科技媒体的标题是“谷歌发布颠覆性论文:Attention Is All You Need”,某社区帖子的标题是“Attention is all you need 论文解读”。这三条内容的核心信息是一样的,但字符串完全不同。

SimHash 就是解决这个问题的。它的原理是把文本映射成一个 64 位的指纹,内容越相似,指纹的汉明距离越小。我通常把汉明距离小于等于 3 视为重复。这个阈值是怎么来的?根据我的实测数据,距离为 3 时,误判率大约在 2% 左右,漏判率在 5% 左右。如果你对准确性要求更高,可以把阈值降到 2,但会漏掉一些改写幅度较大的重复内容。

这里有个经验:SimHash 对短文本的效果不如长文本。如果标题只有十几个字,SimHash 的区分度会下降。所以我的做法是:标题用 SimHash 做粗筛,正文用 SimHash 做精筛。只有标题和正文都判定为重复时,才真正丢弃。这样可以把误判率控制在 1% 以下。

3.3 第三层:基于语义嵌入的深度去重

前两层过滤之后,剩下的条目通常还有 20% 到 30% 的语义重复。比如“某公司获得 10 亿美元融资”和“某公司完成 10 亿美元 B 轮融资”,这两条在 SimHash 看来可能距离较远,但语义上完全是同一件事。这时候就需要用到语义嵌入模型。

我的方案是用一个轻量级的句子嵌入模型(比如 all-MiniLM-L6-v2)把每条内容编码成 384 维的向量,然后计算余弦相似度。相似度大于 0.85 的视为语义重复。这个阈值同样来自实测:0.85 时,误判率约 3%,漏判率约 8%。如果你追求更高的召回率,可以降到 0.8,但会引入更多误判。

语义去重的计算开销比前两层大得多,所以我的策略是只对前两层过滤后剩下的条目做语义去重。这样每天需要计算嵌入的条目从 500 条降到 150 条左右,计算时间从几分钟降到几十秒。这个优化在实际运行中非常关键,否则日报的生成延迟会让人无法接受。

注意:语义去重模型的选择要考虑你的硬件条件。如果你在本地跑,all-MiniLM-L6-v2 是个不错的平衡点;如果有 GPU 资源,可以用更大的模型获得更好的效果。但不要盲目追求大模型,去重任务的精度提升边际递减很快。

4. 排序算法的设计:时效性、信源权重与内容质量的三角平衡

4.1 时效性衰减函数的参数计算

资讯日报的核心价值在于“新”,所以时效性必须是排序的第一权重。但“新”的定义需要量化。我的做法是设计一个时效性衰减函数,把发布时间映射到 0 到 1 之间的分数。常用的衰减函数有指数衰减和线性衰减两种,我选择指数衰减,因为它更符合信息价值的实际衰减规律。

具体公式是:score_time = exp(-λ * hours_since_published)。这里的 λ 是衰减系数,需要根据你的日报定位来调整。如果日报是给需要快速跟进行业动态的人看的,λ 应该大一些,比如 0.1,这样 24 小时前的信息分数会降到 0.09 左右。如果日报是给需要深度了解的人看的,λ 可以小一些,比如 0.05,24 小时前的信息还有 0.3 的分数。

我实测下来,λ = 0.08 是个比较平衡的值。这意味着:1 小时前的信息分数约 0.92,6 小时前约 0.62,12 小时前约 0.38,24 小时前约 0.15。这个衰减曲线在大多数场景下都能给出合理的排序结果。当然,你可以根据自己的需求调整,但建议不要偏离这个范围太远。

4.2 信源权重与内容质量的加权方式

时效性之外,信源权重和内容质量是另外两个关键因子。信源权重前面已经定义了,直接查表即可。内容质量的评估则复杂一些,我通常从三个维度来打分:

  • 信息密度:正文中有效信息的比例。我的做法是计算正文长度与“停用词+模板句”长度的比值。比值越高,信息密度越大。
  • 原创性:内容是否包含一手信息。如果正文中引用了大量外部链接,原创性分数会降低。
  • 完整性:内容是否包含完整的背景、事件、影响分析。这个维度比较主观,我的做法是用一个简单的规则引擎来判断:如果正文包含“背景”“影响”“分析”等关键词,且段落数大于 3,就给较高的完整性分数。

最终的排序分数是三个因子的加权和:final_score = 0.5 * score_time + 0.3 * score_source + 0.2 * score_quality。这个权重分配是我经过多次调整后确定的。时效性占 50% 是因为日报的本质是“新闻”,信源权重占 30% 是因为权威性很重要,内容质量占 20% 是因为在时效性和权威性相近的情况下,质量高的内容应该优先。

4.3 多样性约束:避免日报变成单一话题的复读机

排序算法还有一个容易被忽略的问题:话题多样性。如果你不加约束,排序结果很可能被同一个话题霸占。比如某天某公司发布了新模型,前 10 条可能全是关于这个模型的报道。这样的日报读起来非常枯燥,而且信息增量很低。

我的解决方案是引入话题多样性约束。具体做法是:先用聚类算法(比如 DBSCAN)把候选条目按语义相似度聚成若干簇,然后每个簇最多取 2 条进入最终日报。如果某个簇的条目特别多,就只取分数最高的 2 条。这样既能保证重要话题不被遗漏,又能避免单一话题过度重复。

聚类的时候有个参数需要调整:DBSCAN 的 eps 参数。我通常设为 0.3(基于余弦距离),这意味着相似度大于 0.7 的条目会被聚到同一簇。这个阈值比去重时的 0.85 要宽松,因为去重是“完全相同才丢弃”,而聚类是“相似就归为一类”。两者的目的不同,阈值自然也不同。

5. 自动摘要的工程化落地:从抽取式到生成式的取舍

5.1 抽取式摘要的快速实现与局限

自动摘要是资讯日报的另一个核心环节。没有摘要,读者只能看到标题,无法快速判断内容是否值得点开。摘要的质量直接影响日报的可用性。我的方案是分两步走:先用抽取式方法生成候选摘要,再用生成式模型做润色。

抽取式摘要的实现相对简单,常用的算法有 TextRank 和 LexRank。我选择 TextRank,因为它的效果稳定,而且实现起来不复杂。核心思路是把正文拆成句子,构建句子之间的相似度图,然后用 PageRank 算法计算每个句子的重要性,最后选出排名最高的几个句子组成摘要。

import numpy as np from sklearn.metrics.pairwise import cosine_similarity def textrank_summarize(sentences, top_n=3): # 假设 sentences 是已经分好句的列表 # 用 TF-IDF 或嵌入向量计算相似度矩阵 # 这里用简化的词袋模型示意 from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer() tfidf_matrix = vectorizer.fit_transform(sentences) sim_matrix = cosine_similarity(tfidf_matrix) # PageRank 迭代 n = len(sentences) scores = np.ones(n) / n damping = 0.85 for _ in range(50): new_scores = np.ones(n) * (1 - damping) / n for i in range(n): for j in range(n): if sim_matrix[j][i] > 0: new_scores[i] += damping * scores[j] * sim_matrix[j][i] / sim_matrix[j].sum() scores = new_scores # 选出 top_n 个句子,按原文顺序排列 ranked = np.argsort(scores)[::-1][:top_n] ranked = sorted(ranked) return [sentences[i] for i in ranked]

这段代码是 TextRank 的简化实现。实际使用中,我建议用sumy或pytextrank这样的成熟库,它们处理了更多边界情况。抽取式摘要的优点是速度快、不会产生事实错误,缺点是句子之间的连贯性差,读起来像拼凑的。

5.2 生成式摘要的提示词设计与事实性校验

抽取式摘要的局限促使我引入生成式模型做润色。但生成式模型有个致命问题:幻觉。它可能会编造原文中不存在的信息。在资讯日报这种场景下,幻觉是不可接受的。所以我的做法是:生成式模型只负责“改写”,不负责“新增”。

具体的提示词设计是这样的:

请将以下句子改写成一段连贯的摘要,要求:1. 不添加原文中没有的信息;2. 保持所有数字和专有名词不变;3. 长度控制在 100 字以内。原文:{抽取的句子}

这个提示词的关键约束是“不添加原文中没有的信息”和“保持数字与专有名词不变”。我在实际使用中发现,如果不加这两个约束,模型很容易“自作主张”地补充背景信息,导致摘要与原文不符。

生成之后,我还会做一次事实性校验:把生成的摘要和原文做一次 NLI(自然语言推理)检查,如果摘要中的某个陈述在原文中找不到支持,就标记为可疑,回退到抽取式摘要。这个校验步骤增加了一些计算开销,但能把幻觉率从 5% 降到 1% 以下,非常值得。

5.3 摘要长度的动态调整策略

摘要的长度不是固定的。我的策略是根据内容的重要性和复杂度动态调整:

  • S 级信源的重要公告:摘要长度 150 到 200 字,确保关键信息完整。
  • A 级信源的常规报道:摘要长度 80 到 120 字。
  • B 级和 C 级信源:摘要长度 50 到 80 字,只保留核心事实。

这个策略的依据是:重要内容值得多花篇幅,次要内容简洁即可。如果所有摘要都一样长,日报的节奏感会很差,读者也很难快速定位重点。

6. 分发与呈现:日报的最终形态决定用户体验

6.1 日报的排版逻辑与信息层级

日报的排版不是简单的列表堆砌。我的排版逻辑是:按话题分组,按重要性排序,每条包含标题、来源、摘要、链接四个要素。话题分组用前面提到的聚类结果,每个话题下面按 final_score 排序。

信息层级的设计也很关键。我会用不同的视觉权重来区分内容的重要性:S 级信源的内容加粗标题,A 级信源正常显示,B 级和 C 级信源用较小的字号。这样读者扫一眼就能知道哪些是重点。摘要的长度也遵循同样的逻辑:重要的内容摘要长,次要的内容摘要短。

还有一个细节:链接的呈现方式。我坚持在每条内容后面附上原始链接,而不是只给一个聚合页面的链接。这样做的好处是读者可以直接跳转到源头,避免中间环节的信息损耗。虽然这样会让日报看起来更长,但用户体验更好。

6.2 推送时机的选择与反馈闭环

推送时机对日报的打开率影响很大。我测试过几个时间点:早上 7 点、早上 9 点、中午 12 点、下午 6 点。数据表明,早上 7 点到 8 点之间的打开率最高,因为很多人在通勤或刚到办公室时会浏览行业动态。中午 12 点次之,下午 6 点最差。

但推送时机不是一成不变的。我会根据用户的反馈做动态调整。具体做法是:在日报末尾加一个简单的反馈入口(比如“这条日报对你有帮助吗?”),收集用户的点击和反馈数据,然后用这些数据优化推送时间和内容选择。这个反馈闭环运行了三个月后,日报的打开率提升了大约 40%。

反馈数据的另一个用途是信源权重的动态调整。如果某个信源的内容经常被用户点击,说明它的价值高,权重可以适当上调。反之则下调。这个调整不需要太频繁,每周一次就够了。调整的幅度也不要太大,每次 ±0.05 即可,避免权重剧烈波动。

6.3 日报的存档与检索功能

日报发出去之后,还需要考虑存档和检索。我的做法是把每天的日报存成 Markdown 文件,按日期命名,放在一个 Git 仓库里。这样做的好处是:版本可控、方便检索、可以随时回溯。Git 的 commit 历史还能记录每天的日报变化,如果某天出了问题,可以快速定位。

检索功能我用的是一个简单的全文搜索工具(比如ripgrep),支持按关键词、日期、信源等维度过滤。这个功能对我自己特别有用,因为有时候需要查“某个月某家公司发布了什么”,直接搜一下就能找到,比翻聊天记录快得多。

7. 运行半年后的一些真实体会

这套系统从搭建到稳定运行花了大约两个月,之后又持续调优了半年。踩过的坑不少,这里分享几个印象深刻的。

第一个坑是过度依赖单一信源。早期我把某个聚合平台的权重设得很高,结果有一天那个平台改版,采集规则失效,日报直接断更了。从那以后,我给每个信源都设置了备用方案,S 级信源至少有 2 个独立来源,A 级信源至少有 1 个备用。

第二个坑是摘要的幻觉问题。有一次生成式模型把“某公司融资 10 亿美元”改成了“某公司融资 10 亿人民币”,虽然只是单位变了,但性质完全不同。幸好我在发布前做了人工抽查,及时发现了。从那以后,我加了事实性校验环节,并且对数字和单位做了强制保留的约束。

第三个坑是排序算法的过拟合。有一段时间我为了让日报“看起来更好”,不断调整权重参数,结果日报变得越来越“精致”,但信息量反而下降了。后来我意识到,排序算法的目标是“让重要信息排在前面”,而不是“让日报看起来漂亮”。回归本质之后,我把权重参数固定下来,不再频繁调整,日报的质量反而稳定了。

最后一个体会是:资讯日报的价值不在于“全”,而在于“准”。每天发生的 AI 新闻可能有几百条,但真正值得关注的可能只有十几条。与其把所有内容都塞进日报,不如花精力把最重要的那几条选出来、讲清楚。这个思路的转变,是我做这套系统最大的收获。

返回列表