《Spring AI 2.0实战》第23招 做到真正能上线
## 01、痛点 凌晨两点,运维群突然炸了。AI客服在订单页里信誓旦旦地告诉用户:您的退货政策是30天内可退,运费由平台承担。问题是,这个政策半年前就改成7天了,而且用户所在地区有特殊规则。为什么AI能一本正经地胡说?因为向量库超时,RAG检索返回空,模型没有拿到知识,又不敢说自己不知道,于是用训练数据里的旧记忆编了一段。 我是老李,过去一年我见过不少团队把演示环境里的AI能力直接推上生产。演示很漂亮,上线很狼狈。撞上的问题基本不是模型不够聪明,而是时间超了、限流了、模型挂了、向量库挂了、工具调用失败了、流式输出断了、成本爆了、厂商要切了。每一个都足够让用户怀疑人生。 最典型的场景:用户问“我的订单到哪了”,Tool去查物流,网络抖动超时。理想情况是系统说“暂时查不到,请稍后再试”。实际情况往往是模型没有拿到Tool返回,就自己补了一个单号,还补得有模有样。这不是技术故障,这是业务事故。 为什么现在要讲这个?因为大模型的接入门槛似乎被拉低了,很多团队以为只要把接口调通、把提示写清楚,就能上生产。但他们忽略了,生产化意味着系统不仅“回答得出来”,还要在回答不出来的时候,依然可控、可预期、可追溯。 ## 02、是什么 生产级AI系统的可靠性,本质不是在追求智能上限,而是在给智能下限兜底。我把它定义为:系统不仅“回答得出来”,还要应对Timeout、429、模型故障、Vector Store故障、Tool失败、流式断连、成本失控和Provider切换。 要做到这一点,靠的不是某一个银弹,而是一组基础能力。这组能力包括: Timeout:每一个外部依赖调用都必须有截止时间。模型生成、向量检索、工具调用、MCP能力发现,都要设置单独的超时,不能无限等待。 Retry:重试要分场景。对幂等的查询可以重试,对已经产生副作用的调用要谨慎。重试必须带指数退避和最大次数,否则会放大故障。 Fallback:主路径失败后,系统要有一条可用的备选路径。备选不一定要同等智能,但必须明确可用。 Rate Limit:对模型、工具、协议的调用要做频率和并发限制,既保护下游,也保护自己的钱包。 Circuit Breaker:连续失败达到阈值后,快速熔断,让流量不再打到已经不可用的依赖上,给下游恢复时间。 Cache:对相似请求做缓存。缓存不是为了让AI更聪明,而是为了降本和提速,尤其是高频知识问题。 Model Router:根据任务难度、成本、延迟、健康状态,把请求分发到不同模型。一个模型挂了,路由要能切到另一个。 Graceful Degradation:最后一道防线,当所有主路径都不可用时,系统能降级到更弱但可用的能力,或者干脆明确拒绝。 这八项能力加在一起,才构成生产化AI的底座。生产化不是让系统更聪明,而是让系统在极端情况下仍然可预期。 ## 03、为什么 有人会问,传统后端不也一直做稳定性吗?为什么AI服务这么特殊? 第一个原因是依赖面变多。传统服务可能只依赖数据库和缓存,而一个生产AI链路往往依赖模型、向量库、外部工具、MCP协议,以及它们之间的网络。假设每个依赖的可用性是99%,四个依赖串行下来只有96.06%,这还没算限流、超时和模型输出的概率波动。 第二个原因是模型本身是概率系统。同样一个问题,可能上一次回答正确,下一次重试却跑偏。超时重试尤其危险,因为两次生成结果可能不一致,甚至产生冲突。这意味着很多传统上靠重试解决的问题,在AI场景下不能无脑用。 第三个原因是静默错误比显式失败更可怕。传统接口报500,大家看得见。AI可能在检索失败后继续生成,把猜测当成事实。这种错误没有异常日志,没有返回码,却会造成最严重的信任问题。所以生产AI必须优先解决不可预测的失败,而不是只关注成功率。 第四个原因是成本失控。模型调用按token计费,工具调用可能有次数配额。如果没有Rate Limit和预算控制,一次故障可能引发重试风暴,几分钟烧掉一个月的预算。429错误本身也常常是因为你在厂商侧的配额被刷爆。 第五个原因是Provider切换不可避免。无论你选哪家模型厂商,都会遇到限流、故障、服务降级。如果没有Model Router和配置化切换,每次切换都要改代码、发版本,这在生产环境是不可接受的。 ## 04、架构 在生产AI的架构里,我建议把系统分成四层:接入层、编排层、依赖层、可观测层。 接入层负责解析用户请求、管理会话和输出格式。编排层是核心,负责调用模型、RAG、Tool、MCP,并执行超时、重试、熔断、降级等策略。依赖层是外部服务和协议,可观测层则贯穿所有调用,记录Trace、日志、指标和账单。 关键是,不要把这些兜底逻辑散落在业务代码里。业务代码只声明意图,具体调用哪个模型、超时多少、失败后降级到哪,应该由编排层统一治理。否则每个业务开发都按自己的理解写一套,最后平台化改造会非常痛苦。 针对四类依赖,要提前设计统一的Failure Mode表: Model的失败模式包括超时、429、内容不合规、输出截断、空响应。RAG的失败模式包括向量库超时、索引缺失、相似度低、返回空、格式错误。Tool的失败模式包括调用超时、返回结构错误、业务拒绝、参数不合法。MCP的失败模式包括协议握手失败、能力发现超时、工具描述变更、权限异常。 每一种失败模式都要有对应的超时策略、重试策略、熔断阈值和降级动作。不要等到线上出问题再拍脑袋。 可观测层同样重要。每一个Model调用、RAG检索、Tool调用、MCP握手都要带上trace_id,并且把耗时、状态、重试次数、降级原因记录下来。没有这些记录,故障复盘就靠猜。 架构原则很简单:主路径可以失败,降级路径必须可解释,所有失败都可以追溯。 ## 05、实战 下面说几个我落地过的具体场景。 场景一,主模型超时降级到备用模型。 我们把主模型的超时设成800毫秒,连续3次超时触发熔断。熔断后Model Router把流量切到备用模型。备用模型能力弱一些,但处理简单咨询够了。同时保留10%的流量继续探测主模型,主模型恢复后再切回来。注意,这里没有做无限重试,因为重试会放大延迟,还会增加成本。 场景二,RAG不可用时禁止编造知识答案。 当向量库超时或相似度低于阈值时,系统必须触发降级模板:我暂时无法查询该政策,请稍后再试或转人工。这里最关键的不是模板写得有多好,而是禁止模型自由发挥。很多事故不是检索坏了,而是检索坏了之后模型还在生成,而且生成得特别自信。 场景三,Tool超时返回可解释失败并保留Trace。 物流查询Tool的调用超时设成800毫秒。超时后,编排层返回一个结构化错误:{ code: TOOL_TIMEOUT, message: 物流查询暂时不可用 },并把trace_id放进上下文。前端展示友好提示,用户不会看到异常堆栈。后端保留完整失败记录,后续可以看是网络问题还是下游故障。 场景四,MCP协议故障降级。 MCP握手或能力发现超时,编排层会禁用该MCP提供的工具,降级为本地只读能力。如果权限校验异常,则直接终止调用链,不允许执行未知工具。 另外,流式断连也不能忽视。SSE流式输出到一半断开,客户端要能够重连,或者服务端要做幂等缓存。对于已经生成的部分内容,如果用户重复请求,可以直接从缓存恢复,避免重复扣费。 成本失控方面,我们给每个用户每天设了token预算,给每个场景设了QPS上限。预算用完就返回明确提示,而不是继续扣费。每个月对账单做一次归因,找出哪些场景是成本黑洞。 这些实战方案都不复杂,关键在于提前设计,而不是等事故发生了再补救。 ## 06、洞见 这里有一个问题值得讨论:AI服务是不是越自动越好? 我的答案是否定的。越自动,越需要人为设定边界。全自动意味着故障也被全自动放大。生成式AI的产出带有不确定性,如果让它在没有边界的情况下自动调用工具、自动回答知识问题,一旦某个环节出错,错误会被包装成非常像正确答案的内容。 生产AI最重要的能力之一,是在智能能力不可用时仍然可预测地失败。这句话听起来有点反直觉,但经历过几次事故就会明白。用户不怕系统说“我现在做不了”,用户怕的是系统说得头头是道,结果全是错的。 失败分三种:可预测失败、不可预测失败、静默错误。可预测失败可以降级,不可预测失败可以熔断,静默错误最难处理。模型编造、工具错误被吞、检索失败后继续生成,都是静默错误。生产AI的稳定性工作,很大程度上是在消灭静默错误。 所以,可解释的“我不能”比强撑的“也许对”更有价值。系统的可信度不是建立在永远正确上,而是建立在错误时也能被识别和处理上。 自动化的边界也应该重新划分:把确定性部分自动化,比如路由、限流、熔断、缓存;把不确定性部分留给可观测和人工复核,比如内容合规、知识准确性。团队的重心要从模型调优,逐步转向失败工程。 ## 07、行动 如果你正在把AI能力推上生产,可以按下面的顺序落地。 第一步,为Model、RAG、Tool、MCP四类依赖分别设计Failure Mode和降级方案。不要笼统地说“要做稳定”,要具体到每一类依赖每一种失败模式。 对于Model:主模型超时,触发熔断并路由到备用模型;遇到429,使用指数退避;内容不合规,拒绝输出并记录;输出截断,提示用户重新生成或缩短上下文。 对于RAG:向量库超时,返回明确不可查提示;相似度太低,告知用户未找到依据;索引缺失,禁止模型自由发挥;返回空结果,走兜底话术。 对于Tool:调用超时,返回可解释失败;返回结构错误,丢弃并重试一次;业务拒绝,原样透传原因;参数不合法,修正参数或拒绝调用。 对于MCP:协议握手失败,禁用该MCP能力;能力发现超时,使用本地缓存的能力列表;工具描述变更,先比对版本再执行;权限异常,终止调用链。 第二步,先做Timeout和Fallback。这两项门槛最低,收益最大。每个依赖都要有超时,每条主路径都要有备选。 第三步,再做Retry和Circuit Breaker。重试只对幂等操作,熔断阈值可以从3次连续失败开始。 第四步,最后做Cache、Model Router和预算控制。这三项需要更多数据支撑,但做好了能明显降本提效。 第五步,每周复盘失败案例,维护一份Failure Mode清单。每出现一种新故障,就补进清单,并落实到代码和配置里。 ## 08、小结 生产化AI不是让回答更聪明,而是让失败更体面。真正拉开差距的,不是模型在正常情况下的表现,而是模型、知识库、工具、协议任何一个环节不可用时,系统是否还能给出确定、可解释、可追溯的结果。 当你把超时、重试、降级、限流、熔断、缓存、路由和优雅降级都做扎实,用户不会觉得系统变笨了。相反,他们会更信任这个系统,因为它知道什么时候该说“我能”,什么时候该说“我不能”。 ## 09、链接与概念说明 这里整理几个文中提到的关键概念: Timeout:调用方为一次外部请求设置的最长等待时间,超过即按失败处理。 Retry:失败后的重试机制,通常配合指数退避和最大次数使用,适合幂等操作。 Fallback:主路径不可用时的备选方案,可以是备用模型、缓存结果或明确拒绝。 Rate Limit:对单位时间内调用次数的限制,保护下游服务和控制成本。 Circuit Breaker:熔断器,当连续失败达到阈值后快速失败,避免雪崩。 Cache:对相同或相似请求的结果进行缓存,降低延迟和成本。 Model Router:根据场景、成本、延迟、健康状态把请求分发到不同模型。 Graceful Degradation:优雅降级,在部分依赖不可用时提供更弱但可用的服务。 这些概念大多来自后端稳定性和分布式系统的经典实践。如果想进一步了解,可以搜索 resilience engineering、circuit breaker pattern、graceful degradation 等关键词。它们不是新概念,但在生成式AI生产化里,被赋予了新的必要性。