跑了两周的信号日报,我把降级链改了四次

这套东西每天凌晨跑一次:抓 Reddit 和 HN 的帖子,去重,打分,让模型写成摘要,渲染成日报,邮件发出去。

代码从 2000 行长到 6000 行。长的部分几乎全在一个地方:数据源挂了怎么办。

这篇只讲降级链,因为它是改动最多、也最容易做错的部分。上线第一版到现在,我改了四次。

降级链长什么样

javascript 复制代码
OAuth 官方 API  →  公开 JSON 端点  →  RSS 订阅源

每一层能拿到的东西不一样:

层 分数 评论数 正文 时间精度
OAuth API ✅ ✅ ✅ 精确
公开 JSON ✅ ✅ ✅ 精确
RSS ❌ ❌ 部分 只能到 t=week

第一版我写得很朴素:A 失败就试 B,B 失败就试 C,都失败就跳过。跑了三天就出问题了。

坑一:空结果不是失败

Reddit 有些板块当天确实没有新帖,返回的是空数组 []。

我第一版把"空数组"当成了"接口失败",于是触发降级------去抓 RSS,RSS 也空,再触发一次告警。结果每天凌晨给我发一堆"数据源异常",而实际上一切正常。

修正后的判断:

python 复制代码
# 成功但空板块([])是合法结果,不回退------空板块走 403 回退只会制造噪音。
rdt_items = rdt_sub_posts(sub, sort, tf, limit)
if rdt_items is not None:      # 注意:是 is not None,不是 if rdt_items
    return rdt_items

if rdt_items 会把 [] 判成假,is not None 才是"请求成功且结果为空"。

这一行改动省掉了我每天早上删告警邮件的时间。

坑二:降级之后,下游不知道数据变差了

这是最隐蔽的一个。

RSS 只有标题和链接,没有分数、没有评论数。而我的评分模型里,分数占的权重不低------一条 800 分的帖子和一条 30 分的,排序完全不同。

降级到 RSS 之后,所有帖子的分数都是 0。评分模型不会报错,它只是把所有东西都排到最后,然后那一期的日报质量明显变差,但我不知道为什么。

改法是在数据里写一个标记:

python 复制代码
if items:
    print(f"  [{sub}] Reddit API 不可用,已降级 RSS(无分数/评论)")
    for it in items:
        it["_degraded"] = "rss"      # 下游看到这个字段就走降级评分

下游拿到 _degraded 就不再按分数排,改成按时间 + 关键词命中排。数据质量变差是不可避免的,但"不知道变差了"是可以避免的。

坑三:熔断要按"天"计数,不按"次"

LLM 那边也一样,我给它做了三层后端(本地网关 / 远程网关 / 兜底模型),失败就切下一个。

第一版熔断是按"连续失败次数"计的:失败 3 次就标记 degraded,跳过这层。

问题在于------这是个每天只跑一次的任务。

一次任务里可能有 8 个板块要摘要,网关挂了,8 次调用全失败,3 次就触顶。于是"今天网关抖了一下"被记成了"连续失败 3 次",第二天直接跳过网关,用更贵的兜底模型跑了一整天。

改成按天计:

python 复制代码
def _record_gateway_failure():
    """同一自然日内多次失败只计一次(首次 +1,之后只刷新时间),
    连续失败按"连续失败的天数"累计。"""
    st = _load_gw_status()
    today = health_state.today()
    if st.get("last_failure_date") != today:
        st["failed_days"] = int(st.get("failed_days") or 0) + 1
        st["last_failure_date"] = today
    st["degraded"] = st["failed_days"] >= _gateway_fail_threshold()

这样"一天挂一次"和"一天挂八次"是同一件事,而"连续挂三天"才值得熔断。

定时任务的熔断,单位应该是"运行次数",不是"调用次数"。 这一条我花了两次故障才想明白。

坑四:补跑不能走降级路径

后来加了个功能:某天漏跑了,可以补。

补跑有个要求------必须精确锁定那一天的数据 。而 RSS 的 t=week 只能给你"最近一周",没法精确到某一天。

所以补跑路径里我把降级全关了:

python 复制代码
if HIST_WINDOW:
    # 补跑只认官方 API:RSS 的 t=week 无法精确锁定历史某天
    return _reddit_public_fallback(sub, sort, "week", limit)

宁可失败,不要拿到"大概是那几天"的数据。

因为补跑出来的东西会进历史库,一份日期错的数据比一份缺失的数据更麻烦------你不知道它错了,还会拿它算趋势。

现在的三条原则

跑了两个月,降级链最后收敛成三句话:

1. 空和失败要分开。 返回 None 是失败,返回 [] 是"今天没有"。混在一起,告警就废了。

2. 降级必须留痕。 每一层降级都在数据里写一个字段,下游据此改变策略。不写,下游会以为拿到了完整数据。

3. 越接近"事实"的路径,越不能降级。 抓取可以降(少点字段),补跑不能降(日期错一天,整条时间线都废)。

一句题外话

这套东西写完之后我有个感觉:AI 应用里最难的部分,往往不在模型那一侧。

模型调不通,换个后端就行;提示词不好,改两版就行。真正耗时间的是"数据从哪来、坏了怎么办、坏了之后下游知不知道"。

这部分没有 benchmark,没有论文,也没人写教程。但它决定了你的系统能不能连续跑 60 天不出事。

相关推荐
Solara1 小时前
绿灯连亮 6 天、今天我真写了 4 KB:这盏灯从没看过我写的那一层
人工智能·程序员·ai编程
GlobalInfo1 小时前
机器人线缆用无卤TPU料市场分析报告深度版:产业洞察、技术演进与路径
大数据·人工智能·机器人
甲维斯1 小时前
白嫖Tripo3D ?Kimi 3D Make可以玩起来!
人工智能·unity3d
IT枫斗者枫哥1 小时前
CompletableFuture失败后返回空列表:异常没了,业务含义也变了
java·后端
Gopher_HBo1 小时前
opentelemetrySDK源码解析
后端
甜到心里的蛋糕1 小时前
从 QQ 机器人到 B 站 AI 运营:一套可复现的视频流水线踩坑实录
运维·人工智能·python·自动化·视频制作
TK泰妞1 小时前
节日促销前,TikTok 商品视频应该提前准备什么?
前端·人工智能·节日
代立冬1 小时前
927 个 PR:Apache SeaTunnel 3.0.0 正式发布!AI 时代,数据集成正在迎来新的可能
人工智能·apache
程序员清风1 小时前
让 AI 编写与运行测试:从样例通过到回归验证
人工智能·数据挖掘·回归