可靠 AI Agent 背后那些看不见的架构决策

没人在 Demo 里展示的那些部分
每一个 AI Agent 的 Demo 演示的都是同一件事:一个 prompt 进去,一个回答出来,观众鼓掌。但没人展示的,是底下那层基础设施 — 正是它,让这个 Demo 变成了一个每天能扛住上万请求的真实产品。
过去一年我一直在做这层东西。不是 Agent 的逻辑 — 而是它下面那一层:路由、故障转移、缓存、限速、计费、密钥管理。这些东西做不出漂亮的幻灯片,但决定了你的系统能不能活过上线后的第一个月。
这篇文章就是关于这些决策的。不是理论,不是抽象概念。是当你要对真实用户的 AI 调用负责时,不得不做出的那些实际架构选择。
决策 1:把每一个 Provider 都当作不可靠的
每个 AI 团队在生产环境里学到的第一课:Provider 会挂。而且不是那种戏剧性的全面宕机 — 而是微妙的、局部的性能退化,远比宕机更难处理。
某个模型开始对 12% 的请求返回 500。某个模型的延迟从 800ms 飙升到 6 秒。限速在没有任何预警的情况下收紧了,原因是同一层级的另一个客户在疯狂打 API。
初级做法是选一个 Provider 然后祈祷。生产级做法是把系统架构成:任何单一 Provider 的故障都不能让你的应用挂掉。
实际上长什么样
你需要一个 Provider 链 — 一个系统自动评估的有序故障转移列表:
听起来很简单,直到你面对这些边界情况:
-
什么算"失败"? 500 很明显。但 200 响应加空 body 呢?花了 12 秒的请求呢?模型返回了合法 JSON 但完全忽略了一半 system prompt 的情况呢?你需要仔细定义你的失败分类体系,因为所有没被故障转移逻辑捕获的情况,都会变成无声的生产 bug。
-
跨重试怎么处理状态? 如果失败的调用是 Agent 链的第二步,重试是否应该包含第一步已经积累的上下文?如果你在缓存对话历史,重试拿到的是同一个缓存条目还是全新的?这些问题听起来很吹毛求疵,直到你在调试一个生产问题时发现,重试的请求产生了不同的结果,因为上下文是过期的。
-
怎么防止级联故障? 如果 Provider A 退化了,你把所有请求转移到 Provider B,可能会打爆 Provider B 的限速。现在两个 Provider 都在挂。你需要的是背压或熔断机制,不只是一个备用列表。
核心洞察:故障转移不只是一个重试循环。它是一棵决策树,包含超时预算、失败分类和负载分布感知。
决策 2:在 Prompt 级别做缓存,而不是响应级别
传统缓存 — 存储响应,对相同请求直接返回 — 对 LLM 调用不太适用。同一个 prompt 可以合理地产生不同的响应(temperature > 0 的意义就在于此),而且大多数 Agent prompt 都包含动态上下文,使得精确匹配式缓存几乎没用。
更有用的缓存层是在 prompt 前缀级别做的。
Prompt 缓存到底怎么工作
主流 Provider 现在大多支持某种形式的 prompt 缓存:在多次请求中保持不变的长 system prompt 或 few-shot 示例,会被服务端缓存。第一次带某个前缀的请求付全价。后续带相同前缀的请求能拿到大幅折扣 — 有时输入 token 成本能降低 50-90%。
但想利用好它并不简单:
架构层面的决策:你需要让 prompt 的结构保证可缓存部分是稳定和确定性的。这意味着:
-
分离静态和动态内容。 你的 system prompt、工具定义和 few-shot 示例应该放在一个稳定的前缀中。用户特定的上下文放在最后。
-
哈希并跟踪缓存命中率。 如果命中率下降了,说明你的 prompt 构建流程有什么变了。也许 system prompt 里混进了一个时间戳。也许某个开发者调换了工具定义的顺序。你需要检测到这一点,因为缓存和未缓存之间的成本差异在规模化时非常显著。
-
注意不同 Provider 的缓存语义差异。 Anthropic 的 prompt 缓存和 OpenAI 的工作方式不同。TTL 不同,最小前缀长度也不同。如果你的系统会跨 Provider 路由(如果你做了决策 1,那就应该会),你的缓存策略需要考虑这些差异。
在规模化场景下,prompt 缓存可以让 LLM 成本降低 40-60%。但前提是你的架构从一开始就支持它。事后再加通常意味着你得把系统里的每一个 prompt 都重构一遍。
决策 3:在项目级别做请求隔离
这个决策在开发阶段感觉像是过度设计,但上线第一周就会变成刚需。
当你有多个 AI 功能 — 比如一个聊天机器人、一个内部摘要器、一个内容审核管道 — 全部跑在同一套基础设施上时,你需要在它们之间做硬隔离。不只是为了组织管理,更是为了安全。
隔离到底意味着什么
-
每个项目独立 API 密钥。 如果聊天机器人的 API 密钥泄露了,你可以只轮换它而不用碰摘要器。如果审核管道出了失控的成本问题,你可以直接干掉它的密钥而不影响聊天机器人。这不是可选项。多个功能共享密钥是安全和运维的噩梦。
-
每个项目独立限速。 你的聊天机器人可能需要每分钟 100 个请求。你的摘要器可能只需要 10 个。如果它们共享一个限速池,流量高峰时聊天机器人可能会饿死摘要器。按项目限速让你可以根据实际需求分配容量。
-
每个项目独立成本追踪。"我们的 AI 一共花了多少钱?"是错误的问题。"我们的 AI 每个功能花多少、每个客户层级花多少、每个使用场景花多少?"才是正确的。如果你回答不了这个问题,你就没法做出关于定价、优化或砍功能的理性决策。
-
每个项目独立 Provider 配置。 你的聊天机器人可能需要 GPT-4o 加 Claude 备用。你的摘要器可能用 GPT-4o-mini 就够了。你的审核管道可能需要一个擅长内容分类的特定模型。每个项目都应该有自己的 Provider 链而不影响其他项目。
架构模式:把每一个 AI 功能都当作一个独立租户,即使它们在同一套代码库里。额外开销很小,但运维收益巨大。
决策 4:按量计费,不要只是计数
每个 AI 系统都需要计费或至少是成本追踪。最简单的方式是数请求。生产级做法是按实际消耗计量。
为什么请求计数不够用
两个都是"一个请求"。收一样的钱要么是在让利,要么是在多收小用户的钱。两者都不可持续。
基于 token 的计量更精确,但引入了新的复杂性:
-
不同模型定价不同。 GPT-4o 每 token 的价格大约是 GPT-4o-mini 的 15 倍。如果你的用户可以选模型,你的计费系统需要追踪每个请求实际用了哪个模型。
-
缓存和非缓存 token 定价不同。 如果你的系统使用了 prompt 缓存(决策 2),缓存的输入 token 成本低很多。你的计费系统需要区分缓存和非缓存用量,否则你会多收那些受益于缓存的用户的钱。
-
故障转移会改变成本。 如果一个请求在 Provider A 失败了然后在 Provider B 成功了,按谁的定价算?如果 Provider B 更贵呢?失败尝试消耗的 token 要不要收费?这些边界情况需要明确的决策,不能事后再想。
计量架构需要是事件驱动的。每次请求完成都会发出一个使用事件,包含:模型、Provider、输入 token、输出 token、缓存 token、延迟、成功/失败。一个下游的计费系统把这些事件汇总成可计费单元。这种解耦至关重要,因为计费逻辑一定会变 — 定价层级、批量折扣、免费额度 — 你不希望这些逻辑和你的请求路由纠缠在一起。
决策 5:在应用层面加密密钥
大多数团队把 API 密钥存在环境变量里就完事了。对于单人开发、单 Provider 的场景,这行得通。但当出现以下情况时就不行了:
- 多个团队成员需要访问不同的密钥
- 密钥需要轮换但不能重新部署
- 你在存储客户自带的 API 密钥(Bring-Your-Own-Key 场景)
- 你需要密钥使用的审计日志
Vault 模式
生产级做法是应用级加密加专门的密钥管理器:
-
密钥在静态状态下加密,使用由云 KMS(AWS KMS、GCP KMS 等)管理的主密钥。应用永远不会在数据库里存储明文密钥。
-
密钥仅在请求时解密,只在 API 调用期间存在于内存中。它们永远不会被记录到日志、序列化到磁盘、或出现在错误报告里。
-
密钥轮换是一个数据操作,不是一次部署。 你更新数据库里的加密密钥,下一个请求就会使用新密钥。不需要重新部署。
-
访问权限是有范围的。 一个项目的 API 密钥只能被针对该项目认证过的请求访问。跨项目的密钥访问应该在架构层面就不可能,而不只是靠政策禁止。
这确实增加了复杂度,但替代方案 — 一个 API 密钥在日志或错误信息里被暴露的生产事故 — 后果要严重得多。
决策 6:让异步成为一等公民
传统的请求-响应循环对 AI 工作负载来说行不通:
- LLM 调用根据模型和输入大小需要 1-15 秒
- Agent 链会把这个时间乘以步骤数
- 用户即使在底层工作很慢时也期望有响应性体验
一个常见的诱惑是把所有东西都做成同步,然后在上面加 streaming。对简单聊天机器人这行得通。对更复杂的场景就会崩掉。
事件驱动方案
与其让前端等整个 AI 管道跑完:
这个模式带来连锁的好处:
-
超时变得可控。 前端不需要在 15 秒里一直保持一个 HTTP 连接。后端想花多久就花多久。
-
重试对用户不可见。 如果 Provider 挂了,系统在备用 Provider 上重试,这一切发生在后台。用户只是多等了一点,不是看到一个错误。
-
后端逻辑可以在生成和交付之间运行。 验证输出、用数据库数据补充、跑内容审核、过滤 PII — 全部在用户看到结果之前完成。
-
多个消费者可以订阅。 同一个 AI 完成事件可以触发 UI 更新、数据库写入、分析打点、Slack 通知,前端完全不需要知道这些的存在。
代价是复杂度。你需要消息队列或事件总线、幂等性保证、以及对乱序事件的仔细处理。但对于生产级 AI 系统,这种复杂度在上线第一个月就能回本。
决策 7:观测请求,而不只是指标
Dashboard 告诉你发生了什么。请求日志告诉你为什么。
当你凌晨 2 点在调试生产问题时,这个区别变得异常明显。你的 dashboard 说"p99 延迟飙到了 8 秒"。好的。但是哪些请求?哪个模型?Agent 链的哪一步?是 Provider 问题还是 prompt 问题?故障转移触发了吗?缓存有帮到忙吗?
生产级可观测性需要什么
-
每请求时间线。 不只是"这个请求花了 3.2 秒",而是一个分解:200ms 用于 prompt 构建,2.8 秒用于 LLM 调用(包括 1.1 秒的失败尝试和 1.7 秒的成功重试),200ms 用于响应处理。
-
请求和响应体日志(可关闭)。当 Agent 产出一个错误的答案时,你需要看到实际发出的完整 prompt 和返回的完整响应。光靠 token 数量和延迟无法解释行为层面的 bug。
-
跨 Agent 步骤的关联。 如果一个 Agent 每个用户请求要发四次 LLM 调用,你需要一种方式把这四次调用归为一组并作为一个整体来检查。没有这个,调试 Agent 就像调试一个没有 trace ID 的分布式系统 — 可以做到,但很痛苦。
-
可过滤性。 按模型、按 Provider、按时间范围、按延迟阈值、按错误类型、按 workflow 名称、按自定义元数据。当你每小时有几千个请求时,能快速缩小到你关心的那些特定请求,就是 5 分钟修好和调查 2 天之间的区别。
一个常见的错误是先做指标 dashboard,后加请求级可观测性。应该反过来。先从每请求的可见性开始。聚合指标可以很容易地从单个请求数据推导出来,反过来就不行了。
决策 8:从第一天就为模型迁移做设计
这是大多数团队会跳过、然后深感后悔的决策。
LLM 领域每隔几个月就会发生变化。新模型发布,旧模型被弃用,定价调整,性能特征演变。如果你的应用和某个特定模型或 Provider 紧耦合,每次模型更换都会变成一个跨团队项目。
你可能还会喜欢: 2026 年值得关注的顶级 LLM 框架
可迁移架构长什么样
-
你的应用代码引用逻辑模型名称,而不是 Provider 特定的标识符。 不是
gpt-4o-2024-08-06,而是一个 workflow 名称,比如classify或summarize。从 workflow 到模型的映射关系放在配置里,不在代码里。 -
Provider 特定的 prompt 适配在路由层处理。 不同模型有不同的 system prompt 格式、工具调用约定和响应结构。这些转换应该发生在 AI 基础设施层,而不是分散在应用代码各处。
-
模型更换是配置变更,不是代码变更。 想把摘要器从 GPT-4o 换成 Claude 3.5 Sonnet?更新 workflow 配置就行。不需要代码审查,不需要部署。新模型立即生效。
-
模型间的 A/B 测试是运维操作,不是开发工作。 把 10% 的流量导向新模型,比较质量和成本,然后逐步切换。这应该是一个开关,不是一个 feature 分支。
前期投入很小:在应用代码和 LLM Provider 之间加一层间接层。长期价值是巨大的。当 GPT-5 发布,你想评估它的时候,迁移时间应该以分钟计,不是以 sprint 计。
底下的规律
如果你把这八个决策放在一起看,一个规律浮现了:生产级 AI 的硬问题不是 AI 问题,而是基础设施问题。
故障转移是负载均衡问题。缓存是系统问题。隔离是多租户问题。计费是事件驱动架构问题。密钥管理是安全问题。异步执行是分布式系统问题。可观测性是 DevOps 问题。迁移是抽象层问题。
过去二十年,我们已经为数据库、API 和微服务解决了所有这些问题。AI 行业正在重新解决它们,但答案看起来惊人地相似。
那些早早意识到这一点的团队 — 意识到生产级 AI 本质上是"中间夹了一个模型调用的生产级基础设施" — 构建出了能规模化的系统。那些把每个 AI 问题都当作全新的 AI 特有挑战来处理的团队,最终在重新发明轮子,并且烧掉了大量工程时间。
这些最终指向哪里
在 ModelRiver,我们把这些架构决策构建成了一个位于你的应用和 LLM Provider 之间的基础设施层。故障转移链、prompt 缓存、项目隔离、按 token 计费、加密密钥管理、异步优先执行、每请求可观测性、以及零停机模型迁移 — 全部在路由层处理,让你可以专注于真正让你的产品与众不同的 Agent 逻辑。
如果这些决策引起了你的共鸣,我们的文档详细介绍了具体实现。或者直接把你的 base_url 指向 https://api.modelriver.com/v1,发出你的第一个请求 — 光是那条请求日志,就能告诉你比现有方案多得多的关于你 LLM 调用的信息。
