1616 字
8 分钟
- 次浏览
每日简报不能因为一个来源挂了就整份消失
读前导览

daily-open-source-brief 拆成插件以后,我补了失败隔离。GitHub、RSS、网页或投递出错,当天简报也要尽量发出来。

正文
1616 字
阅读
8 分钟
结构
8 节
来源线索
GitLaughs/daily-open-source-brief
daily-open-source-brief:plugins daily-open-source-brief:runner daily-open-source-brief:source-errors

daily-open-source-brief 时,我很快遇到一个比摘要质量更基础的问题:公开信息源并不稳定。

GitHub API 可能限流,RSS 可能返回旧格式,公开网页可能改版,邮件或飞书投递也可能临时不可用。如果这些东西都写在一个顺序脚本里,任何一步异常都有机会让当天整份日报消失。

所以这个项目后来拆成插件,主要是为了把失败隔开,扩展来源反而是后面的事。

公开仓库在这里:

https://github.com/GitLaughs/daily-open-source-brief

一份日报有太多外部依赖#

日报看起来像一个简单成果:每天早上收到一封邮件,里面有 GitHub 项目、RSS 文章、公开网页通知和一点摘要。

但它背后的依赖链很长:

  • GitHub 仓库搜索。
  • GitHub Trending 页面。
  • 多个 RSS 或 Atom feed。
  • 公开网页列表。
  • 本地 SQLite 写入。
  • 个人反馈权重。
  • 摘要模型。
  • HTML 渲染。
  • 邮件或飞书投递。

这些依赖没有一个值得完全信任。它们可能慢、可能空、可能报错,也可能只是今天没有新内容。

如果把日报系统理解成“采集成功才生成”,它会非常脆弱。我后来把目标改成:每天尽量产出一份能交代清楚状态的日报。即使某些来源挂了,也要写明哪里失败了。

插件阶段不是为了好看#

项目现在把流程拆成几个阶段:

provider -> collector -> enricher -> summarizer -> renderer -> sender

我拆这些阶段,不是想让架构图好看一点,而是为了出问题时能直接定位到哪一段。

collector 只负责把外部来源变成统一 item。GitHub、RSS、网页采集之间不该互相依赖。

enricher 负责派生信息,例如反馈打分、截止日期提取、跨来源去重。它可以改变本次排序,但不该破坏原始采集证据。

summarizer 负责生成文本。模型不可用时,系统仍然可以退回确定性模板。

renderer 负责写 HTML 归档和本地记录。只要前面还有可用 item,它就应该尽量产出可读页面。

sender 只是投递通道。投递失败不等于日报内容不存在。

这样系统就不再是一条长链路,而是几段可以分别检查的流程。

失败要进入旁路,而不是中断主路#

采集公开网页和 RSS 时,最常见的失败不一定是程序 bug,也可能是来源本身临时出问题:超时、证书问题、HTML 结构变化、feed 里缺字段。

我不想把这些错误静默吞掉,也不想让它们一路抛到最外层把任务打断,所以把它们记到 source_errors 这类旁路结构里。

这样最后摘要可以同时包含两件事:

今天成功采集到了哪些内容
今天哪些来源没有成功返回

这对个人工具尤其重要。每天打开日报时,我不想猜“今天没有消息”到底是真的没消息,还是系统坏了。把失败来源展示出来,日报才有可信度。

CLI 开关是临时隔离阀#

插件配置解决的是长期启停,CLI flag 解决的是临时绕路。

例如本地 smoke test 时,我可能只想跑 GitHub 样例,不想访问公开网页,也不想真的发邮件。这时命令可以写成:

Terminal window
python -m app.run_daily --sample --skip-web --skip-rss --skip-mail --skip-lark --force-send

这不是偷懒,是为了给系统留一条最小验证路径。

如果一个项目只能在所有外部条件都满足时才能测试,那它很难维护。日报这种批处理工具尤其需要局部运行能力:今天只验证渲染、只验证采集、只验证投递,都应该能做到。

健康状态比成功日志有用#

我更喜欢记录插件健康状态,而不是只在日志里打印“成功了”。

原因是日志是给人临时看的,健康状态是给系统下一轮判断用的。

例如插件管理器运行某个 collector 后,可以记录:

  • 插件名。
  • 阶段。
  • 是否启用。
  • 最近一次状态。
  • 最近一次错误。
  • 采集条目数量。

这样后续 dashboard、CLI plugin status、甚至下一篇日报本身,都能复用这些结构化状态。

这比在日志文件里搜索字符串稳定得多。

投递失败不该抹掉归档#

邮件和飞书是两个很容易被误解的位置。

从用户视角看,没收到消息就像日报失败了。不过从系统视角看,内容生成和投递是两件事。

我希望即使 SMTP 配置缺失、飞书目标不可用,系统也能先把 HTML 归档写出来,把 digest 保存到 SQLite,再记录投递状态。

这样后续修好投递配置时,至少知道当天生成过什么,而不是面对一个空白历史。

这也是为什么 sender 阶段应该尽量靠后。它可以失败,但不该把已经完成的采集、排序、摘要和渲染成果一起带走。

小工具也要先想清楚失败时怎么办#

很多个人自动化项目不是死在功能少,而是死在没有想清楚失败时怎么办。

最常见的写法是:

拉数据 -> 总结 -> 发出去

这个模型在 demo 里很顺,在真实环境里很脆弱。只要任何一个来源抖动,整个系统就会变成“今天没声音”。

更耐用的写法应该是:

尽量拉数据 -> 记录失败来源 -> 尽量生成摘要 -> 写归档 -> 尝试投递 -> 记录投递结果

这套流程不复杂,但更适合每天定时跑。

这次拆插件真正解决了什么#

这次给 daily-open-source-brief 拆插件,最有用的地方不是以后能多接几个来源,而是某个 RSS 或投递通道挂了以后,当天任务还能留下记录。

对每日简报来说,我最在意的是打开之后能不能看明白:哪些抓到了,哪些失败了,失败有没有影响最后生成出来的内容。

偶尔跑成功一次不难,麻烦的是它每天定时跑,出错时也别只留下一片空白。

每日简报不能因为一个来源挂了就整份消失
https://blog.sunmmyapi.xyz/posts/daily-brief-plugin-failure-isolation/
作者
Sun
发布于
2026-05-29
许可协议
CC BY-NC-SA 4.0

继续读

相关内容