学习 / 实践

AI Radar 工程拆解:从 RSS 到网站,中间发生了什么?

AI 应用 · 工程案例

沿着本站真实代码,追踪采集窗口、每日配额、去重、自动检查和部署,并用证据定位“采集成功却没有更新”。

进阶 · 12 分钟 · 更新于 2026-09-22

浏览统计暂不可用

本文目录 7
跳到正文

“任务是绿色的,但网站没有新资讯。”这句话其实混合了五个不同的问题:请求是否成功、是否有符合条件的候选、是否写入文件、是否进入主干、线上是否部署了那个提交。

这篇文章沿着本站代码把它们拆开。阅读后,你应该能拿着一次采集报告找到内容停在哪一步,也能理解为什么“每两小时运行”不等于“每两小时必须新增十条”。

证据范围

本文由 AI 辅助整理,依据博客仓库提交 558e0a9 的实现。描述的是可检查的代码行为,不是一次新的线上事故复盘,也没有测量生产延迟或筛选准确率。文中的配额算例是说明性输入。可从 固定版本源码 对照阅读。

1. 一条资讯经过哪些边界?

RSS 来源
  → 请求与解析
  → 主题 / 技术机制筛选
  → 窗口、重复、总配额、来源配额
  → 可公开的数据 + 关联报道
  → 内容检查 + 三语言构建
  → Git 提交与推送
  → 部署系统
  → 读者访问的页面

前三步决定“值不值得收”。后半段决定“能不能稳定送达”。两者需要不同的日志,不能用一个成功图标替代。

边界 本站入口 需要核对的证据
来源与规则 config/news-sources.json 来源启用状态、域名白名单、每日限额
原始响应转候选 scripts/news/lib.mjsparseFeed 有效发布时间、标题、原文 URL、摘录
研究相关性 scripts/news/relevance.mjs 主题证据、技术机制、拒绝原因
本轮选择 selectItems outside-windowduplicatedaily-limit 等原因
发布文件 scripts/news/collect.mjs data/news/items.json 的实际差异
检查与推送 .github/workflows/ai-news.yml 构建结果、提交 SHA、推送结果

表中路径均属于上述固定提交。页面不会在读者打开 Radar 时重新抓取 RSS 或调用模型;它读取已生成的数据。这让来源故障不直接拖慢页面请求。

2. 三种时间,解决三个问题

代码同时保存 publishedAtcollectedAt,报告另有 runAt。前者是来源声称的发布时间,后者是本站收录时间;补采旧文章时,它们天然不同。

当前配置有两层时间限制:解析器最多接受过去 14 天的条目;一次任务默认只选择过去 48 小时的候选。手动任务可调整 window_hours,但不能超过解析范围。扩大窗口也不能让 RSS 返回它已经移除的旧记录。

每天十条的“天”按 UTC 收录日计算,而不是来源发布日期,也不是北京时间午夜。在中国标准时间中,UTC 换日对应上午 08:00。因此,07:50 和 08:10 两次运行可能落入不同配额日。

当前调度是 35 */2 * * *,即 UTC 偶数小时的第 35 分钟。它是计划触发时间,不能作为准点完成的保证。以上均可核对 工作流选择函数

3. 十条是上限,六条也不是保底

设今天已收录 7 条,其中 AIHOT 5 条。即使下一轮拿到了很多合格候选,全站也只剩 3 个名额,AIHOT 只剩 1 个名额。其他来源默认各自最多 2 条/日;配置可以覆盖来源上限。

候选按研究优先级、来源优先级、发布时间、ID 依次排序,再逐条判断。AIHOT 的来源优先级不能越过研究筛选,也不会绕过已用配额。高优先级来源没有新内容时,不会用不相关内容凑数。

下面是独立可运行的配额算例,只演示剩余容量,不替代真实选择算法:

const remaining = (limit, used) => Math.max(0, limit - used);
const siteSlots = remaining(10, 7);
const aihotSlots = remaining(6, 5);
const aihotCanAdd = Math.min(siteSlots, aihotSlots);
console.log({ siteSlots, aihotSlots, aihotCanAdd });
// { siteSlots: 3, aihotSlots: 1, aihotCanAdd: 1 }

这里有一个值得注意的设计取舍:当前是先到先占的跨运行配额,不是当天结束后再挑最佳十条。早间入库的普通候选不会因为晚间出现更好的文章而自动被替换。每天严格控量与全天最优选择是两个不同目标;后者需要候选池和延迟发布策略,目前未实现。

4. 为什么全文源有帮助,但仍会误筛?

简短标题可能只写“新版本上线”,真正有价值的信息藏在正文中的“沙箱隔离、测试套件、上下文卸载”。AIHOT 全文源让这些技术证据进入临时字段 relevanceText,供规则判断。

公开写入前,publicNewsItem 移除该字段;页面保存的来源摘录最多 180 个字符。读全文用于判断,不意味着全文转载。 当前自动工作流显式关闭模型摘要,所以来源摘录也不能标成 AI 总结。

现有筛选包含主题与机制组合,例如上下文工程搭配预算/压缩,机器人搭配轨迹/数据对齐。商业宣传、纯排名等还会经过排除规则。它比单个关键词更具体,但本质仍是规则,不理解全部语义:一句技术文章中的商业背景也可能触发排除,一段营销文案也可能刻意堆满术语。

因此,规则通过只意味着“满足本站收录条件”,不意味着“报道属实”或“方法有效”。改进筛选应先收集误收、漏收样本,再调整 相关性规则,而不是盲目降低阈值。

5. 去重与聚类,不是一回事

采集选择阶段用规范化 URL 和去掉标点、大小写后的标题阻止重复入库。展示阶段的事件合并还支持相同 URL,以及 72 小时内的相同标题或已有内容哈希。详见 Radar 数据适配器

这并不是向量语义聚类。中文报道与英文官方公告即便描述同一件事,只要链接、标题和哈希不同,就可能仍是两个事件。代码“支持内容哈希”也不代表当前 RSS 解析器为每条记录都生成了它。

同组报道会优先选择原始或官方来源,但来源排序不等于事实核验。verification 需要核验者、时间和证据,不能用“官方”或“多个站点转述”自动制造已核实状态。

6. “成功但没更新”的排查顺序

先打开该次 Actions 的 news-run-report 附件,再按下面顺序缩小范围:

观察 下一步 不能直接得出的结论
来源 failed 看 HTTP、超时、解析失败,检查上一阶段安装是否成功 所有来源都失效
来源成功,inWindow = 0 对比最新发布时间与报告窗口 定时任务没有运行
selected = 0 rejectednotSelected 的原因 RSS 没有内容
selected > 0,构建失败 看内容检查/三语言构建日志 已经发布
构建通过,推送失败 核对远端提交与并发更新,重新集成后检查 数据已经在主干
推送成功,页面仍旧 比较部署绑定的提交 SHA、生产域名和构建数据 一定是浏览器缓存

流程只在至少一个来源成功时保持采集阶段成功;部分来源失败可以和整个任务绿色同时出现。发布步骤还可能因为“没有文件差异”正常退出。这两种情况都需要读报告,而不是只看任务颜色。

当前工作流的并发锁只约束同组采集任务,不会锁住人工推送。若检查期间主干前进,最后的推送可能被拒绝;这个版本没有自动重算并重试推送。定位时要保留最后成功的边界,不要通过强制推送跳过问题。

7. 从这个案例带走什么?

把任何内容流水线写成“输入 → 选择 → 存储 → 发布”,然后为每个箭头定义可检查的产物。这里分别是来源状态、候选决策、文件差异、提交 SHA 与生产部署。

对本站而言,下一步最有价值的工程工作是建立一小份人工标注的筛选样本集,持续检查误收与漏收;它属于评估工作,不能用今天有几条新闻来代替。想看目前实际产物,可以回到 AI Radar,也可以查看 站点项目

写给未来的自己合上文章前,留下一点自己的理解。

你的私人学习便签,只存在当前浏览器,不会上传或公开。清理浏览器数据会丢失,请导出留存。三种语言共用这篇文章的便签。

继续探索

继续探索

  1. 项目 / 001

    Hohoo’s AI Lab

    公开学习、研究资讯与实践记录共用的个人知识站点。

    已上线
  2. 实战教程

    Java 8 调用大语言模型:从第一次请求到多轮对话

    用三个真实运行的 Java Demo,理解模型请求、JSON 解析、读取超时与对话历史。

    15 分钟已发布

接下来,走哪条路?

依据文章关联与同主题已发布内容整理,不使用随机推荐。

全部文章