从 Demo 到 Production:AI 开发中被 90% 开发者忽视的“失败路径”设计

0 阅读3分钟

封面7.png

01. 幸存者偏差:Demo 里的“假成功”

很多人在做 AI 功能时,最专注的事情通常只有一件:“怎么让它跑出想要的结果?”

当模型第一次返回完美答复时,那种成就感很容易让人产生一种幻觉:这个功能已经做完了。但真实情况是,Demo 关注的是“上限”(模型能多聪明),而产品关注的是“下限”(系统能多稳健)。

真正的 AI 工程,不是只在成功时成立,它必须在失败的时候,依然保持清醒且不失控。


02. 为什么 AI 链路天生“易碎”?

传统 Web 开发的接口通常是确定的(要么通,要么断)。但 AI 链路叠加了多重不确定性:

  1. 远程调用风险:大模型 API 通常比普通接口更慢、更易超时。
  2. 内容合规拦截:模型可能会因为触发安全过滤而返回空值或报错。
  3. 结构化解析风险:模型返回了 JSON,但可能少了个括号,或字段类型突变。
  4. 模型幻觉:输出内容完全脱离预期。

在 AI 工程里,失败不是“万一”,而是“必然”。


03. 防御性编程:try-except 只是起点

如果你的代码里只有成功路径,那么网络抖动一下,你的整个应用就会直接“摔在地上”。

❌ 试玩版(极脆)

def get_ai_summary(text):
    # 如果接口超时或 API Key 出问题,程序直接 crash
    response = client.chat.completions.create(model="gpt-4", messages=[...])
    return response.choices[0].message.content

✅ 工程版(具备韧性)

def get_ai_summary_safe(text):
    try:
        # 1. 显式设置超时,防止主线程被拖死
        response = client.chat.completions.create(
            model="gpt-4o", 
            messages=[...],
            timeout=15.0 
        )
        return response.choices[0].message.content
    except openai.APITimeoutError:
        print("请求超时,进入降级逻辑")
        return "摘要生成中,请稍后再试..."
    except Exception as e:
        # 2. 捕获异常,确保系统不崩
        logging.error(f"AI 调用异常: {e}")
        return None 

04. 进阶策略:重试、超时与降级

一个成熟的 AI 功能,应该具备以下三种工程直觉:

1. 重试意识(Retry)

网络瞬时不稳定是常态。对于非永久性错误(如 502/503 或网络抖动),自动重试 1-2 次通常能解决 80% 的偶发问题。 建议:使用指数退避算法(Exponential Backoff),不要紧接着立即重试。

2. 超时意识(Timeout)

AI 推理很慢,但程序不能无限等待。你必须明确:这个任务最多等多久? 超过 20 秒后是继续转圈还是告诉用户“换个短点的试试”?

3. 降级思维(Fallback)

如果 AI 彻底挂了,系统该怎么办?

  • 文案改写场景:返回原句(不改总比崩了好)。
  • 关键词提取场景:返回空数组。
  • 智能客服场景:转接人工或返回静态 FAQ。

05. 解析失败:消费阶段的“隐形杀手”

很多人记住了“调用会失败”,却忘了“解析也会失败”。

即便模型返回了 200 OK,如果它给的 JSON 缺了一个字段,你的前端 data.item.name 依然会报 undefined。因此,解析逻辑必须也是失败路径的一部分。

直觉:模型吐出了内容,不代表流程安全了;只有校验通过了,流程才算真正闭环。


结语:让失败也“可预测”

一个成熟的 AI 工程师,在写下第一行 client.chat 之前,脑子里想的应该是:如果它没回我怎么办?如果它回乱码了怎么办?

AI 工程化的本质,是把模型的不确定性,通过程序侧的防御性设计,转化为用户侧的确定性体验。


讨论区:在你的 AI 项目中,哪种异常情况最让你头疼?你是如何做 Fallback 的?欢迎评论区交流!


本文首发于掘金,记录前端开发者向 AI 工程转型的稳定性实践。