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.0A 级权威科技媒体主流科技媒体的 AI 频道每 4 小时0.8B 级行业分析师与知名博主有长期跟踪记录的独立分析师每 6 小时0.6C 级聚合平台与社区热帖技术社区的热门讨论每 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, doseqTrue) # 统一协议和末尾斜杠 normalized parsed._replace( schemehttps, querynew_query, pathparsed.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.0524 小时前的信息还有 0.3 的分数。我实测下来λ 0.08 是个比较平衡的值。这意味着1 小时前的信息分数约 0.926 小时前约 0.6212 小时前约 0.3824 小时前约 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_n3): # 假设 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 新闻可能有几百条但真正值得关注的可能只有十几条。与其把所有内容都塞进日报不如花精力把最重要的那几条选出来、讲清楚。这个思路的转变是我做这套系统最大的收获。
返回列表