做混合大模型部署这件事,说起来其实是被业务逼出来的。我们内部有好几个业务线同时要上AI能力,有的要求数据绝对不能出内网,有的对成本极度敏感,还有的偏偏就认云端大模型那口“理解力”。单独押注任何一边都不能服众:全走云端API,财务和合规过不了;全本地化部署,效果和迭代速度又跟不上。最后只能走一条“混合”的路——把本地私有化模型(Qwen、Llama这类的开源权重模型)和云端大模型API揉进一套架构里,用路由层决定“谁来回答这个问题”,再用高可用机制保证“不管谁挂了,业务都不停摆”。
这套架构里最核心的,就是多Agent协作模型和路由层、高可用机制怎么配合。写这篇文章,就是把我们在这套混合架构里踩过的坑、验证过的方案,以及在路由策略、并发配置、故障转移上的具体参数和实现思路整理出来,给正在做类似架构选型的朋友一个参考。
1. 混合部署架构的整体设计思路
1.1 为什么非得混合部署
在讲架构之前,先把“为什么混合”这件事说清楚。这不是技术炫技,而是成本和现实的折中。
第一是成本曲线的问题。云端大模型API按Token计费,业务量小的时候看不出来,一旦进入生产环境、日均请求上万次,费用增长非常快。我大概估过一笔账:假设每天有50万次Prompt请求,平均每次输入输出加起来2000 Token,按云端API常见价格每百万Token几十元来算,一天的成本就要上千块,一个月下来是一笔不小的开销。而本地部署的模型,成本主要是一次性购置GPU服务器和电费、运维成本,用量越大,摊薄越明显。
第二是数据合规和隐私的要求。金融、医疗、政务这类业务,明文数据根本不允许出内网,这个没得商量。但问题在于,本地模型在复杂推理、长文本理解和指令遵循上的能力确实跟头部云端模型有一定差距。所以现实的选择是:普通内容生成、信息抽取、意图分类这类任务,本地模型已经够用,放本地;遇到复杂推理、角色扮演、长程任务规划等高质量要求的,再走云端API。
第三是稳定性的考虑。只依赖云端API,一旦供应商出故障或限流,业务全军覆没;只依赖本地模型,模型效果迭代又慢。混合部署天然带了一层互备属性,云端挂了本地顶上,本地算力不足时云端分担,这就是高可用的雏形。
1.2 架构分层:接入层、路由层、Agent层与模型层
我们的整体架构分成四层,每一层的职责相对独立:
- 接入层:统一接收来自业务方(Web、小程序、IM机器人、内部系统)的请求,完成鉴权、参数校验和并发控制。
- 路由层:这是整个架构的“交通警察”,负责决定每个请求去哪类模型(本地还是云端)、哪个具体模型,同时承载负载均衡、重试、熔断、降级策略。
- Agent层:面向具体任务的执行单元,多Agent各自负责任务拆解、工具调用、结果汇总,Agent本身并不直接绑定模型,而是通过路由层获取模型响应。
- 模型层:包括本地部署的开源模型服务(如vLLM、TGI、ollama等容器化推理服务)和云端大模型API(通过统一的HTTP/SSE接口封装)。
这套分层最大的好处是故障隔离和灵活演进。模型层升级一个模型,路由层只需要改一下权重配置,Agent层和接入层完全无感知。Agent层增加一个新的Agent,只需要复用现有路由能力,不需要重新写模型调用逻辑。路由层单独抽出来做高可用策略,也不会影响上层业务和底层模型供应商的切换。
有一点值得提:路由层和高可用机制我们一开始是分开设计的两套组件,后来发现很多故障处理逻辑必须放在路由决策里才能生效。比如某个模型延迟飙升,如果路由层不知道这个信息,还在继续给它分配流量,那高可用组件只能不停做失败后的重试,效率和效果都会差很多。最后我们把两者合并成了同一个“自适应路由网关”,一个组件同时负责请求分发和故障转移。
2. 多Agent协同架构的路由与并发控制
2.1 Agent协作模式:流水线、分层委派与事件驱动
多Agent架构听起来高大上,说白了就是让不同角色的AI“员工”配合做一件事。我们在实践中用到了三种主流协作模式:
- 流水线模式:Agent A的输出作为Agent B的输入,像一个生产流水线一样依次处理。适合内容生成、数据处理管线等固定流程。优点是链路清晰,容易排查问题;缺点是整体耗时等于所有Agent耗时之和,延迟高。
- 分层委派模式:一个主Agent负责理解用户意图,把任务拆解成子任务后分发给不同的专用Agent,最后回收结果进行汇总。适合“一个用户请求涉及多个子任务”的场景,比如做行业研究时,需要数据采集Agent、数据分析Agent、报告撰写Agent协同。
- 事件驱动模式:Agent之间通过消息队列解耦,每个Agent订阅自己感兴趣的事件,处理完再发布新事件。这个模式灵活性强,适合复杂的异步场景,但调试起来相对困难。
我们在大部分业务场景里用的是“分层委派为主、流水线辅助”的混合模式。主Agent负责任务拆解和结果汇总,保证用户能够感知到整个任务的进展;专业Agent各司其职,完成具体的检索、计算、生成工作。
2.2 路由调度机制:任务分发与上下文传递
Agent协同架构里面很容易忽略的一个问题,是“Agent之间的数据怎么传”。很多初期的方案直接把上一个Agent的输出文本整个传给下一个Agent,很快就会发现上下文越来越长、Token开销爆炸、还会出现“串语境”的幻觉问题。
我们的做法是把Agent间的通信数据分成两类:
- 元数据(任务ID、状态、路由信息、调用链追踪ID)放Redis或消息队列的Header里
- 真正的业务数据(检索结果、结构化数据、中间结论)放对象存储或数据库,只在消息体里传引用ID
这样做的直接收益是:Agent之间传递的数据量小了,响应速度更快;整条链路可追踪,任务卡在哪个Agent下一目了然。我们也在每个Agent的输入输出做了统一的Schema约束,字段命名和数据类型保持一致,避免“一个Agent输出JSON,另一个Agent还要写一堆解析逻辑”的情况。
路由调度上,除了“哪个Agent处理什么任务”这种业务路由,还需要考虑“同一个Agent有多个实例时,任务发给谁”。我们用的是带权轮询加最少连接数的混合策略:正常情况下按权重分发,当某个Agent实例的积压任务数超过阈值,自动减少分配,并把这个实例的状态同步给路由表。
2.3 并发配置与关键参数
多Agent跑起来之后,第一个遇到的实际问题就是并发配置。这里涉及两层并发:Agent层的并发(多少个任务同时在跑)和模型层的并发(多少个模型推理请求同时在进行)。两层都得配好,否则要么Agent闲着等模型,要么模型被打满导致超时。
Agent层并发数可以按这个公式来估算:
理论并发数 = 模型层最大吞吐(每秒请求数) × 平均单任务调用模型次数 × (1 + 缓冲系数)
举个例子:假设本地模型服务(vLLM部署)单实例最大吞吐是20 QPS,每个Agent任务平均会调用模型3次,缓冲系数留30%,那么Agent层的理论并发数就是 20 × 3 × 1.3 ≈ 78。实际操作中我们会对这个数值做压测校准,一般先按计算值的80%设置,再逐步调大,观察延迟和错误率的变化。
超时配置是另一个重灾区。我们的经验是设置三重超时:
- 单次模型调用超时:本地模型通常30秒,云端API按模型不同可放宽到60秒
- Agent单任务总超时(包含多次模型调用):本地任务90秒,涉及云端API的任务180秒
- 用户侧可以接受的响应超时:一般控制在10秒内返回“任务已接收”,长任务走异步通知
表格里是我们生产环境的常用参考值:
| 参数 | 本地模型 | 云端模型API | 备注 |
|---|---|---|---|
| 单次调用超时 | 30s | 60s | 超过直接熔断 |
| 最大重试次数 | 2 | 3 | 重试必须退避 |
| Agent任务总超时 | 90s | 180s | 包含多次调用 |
| 模型服务最大并发 | 16~32 | 按供应商配额 | 需要压测实测 |
| 请求队列容量 | 500 | 500 | 超出返回限流错误 |
还有一个容易被忽略的参数是“单用户并发限制”。如果某个用户同时开了多个对话,每个对话又触发多个Agent任务,很快就能把一个Agent实例池打满,影响其他用户。我们在接入层对每个用户做了并发数限制,默认2个并发,重要客户可以调高。
3. 本地与云端模型的路由策略实现
3.1 路由策略:成本优先、质量优先与延迟优先
混合架构的核心,就是“路由”。但路由不是简单地随机分发或者按固定比例分发,而是要能够针对不同场景自动选择最合适的模型。我们实现了三档路由策略,通过配置动态切换:
- 成本优先策略:尽可能把请求调度到本地部署的开源模型,只有本地模型不适合(例如缺少某种工具调用能力)或本地服务不可用的时候,才走云端API。适用于内部知识问答、信息抽取、文本分类等任务。
- 质量优先策略:凡是复杂推理、长文档理解、创意写作类任务,优先走能力更强的云端模型;本地模型作为兜底。适用于对输出质量要求高的场景。
- 延迟优先策略:根据实时延迟指标,把请求调度到当前响应最快的模型服务。本地模型负载低时优先本地,云端API延迟低时切到云端。适用于对响应时间敏感的客服场景。
这三种策略对应的就是路由表里一套加权因子。我们可以简单配置每个模型服务在不同策略下的权重值,路由层每次选择一个模型时按权重做随机选择。
| 模型服务 | 成本优先权重 | 质量优先权重 | 延迟优先权重 |
|---|---|---|---|
| 本地Qwen-72B | 80 | 20 | 50 |
| 本地混合专家模型 | 60 | 40 | 40 |
| 云端通用模型API | 15 | 80 | 40 |
| 云端超大模型API | 5 | 60 | 10 |
权重配置不是一劳永逸的,需要根据实际运行数据动态调整。我们的做法是:每15分钟统计一次各模型服务的平均延迟、成功率、Token成本,如果某个模型的综合评分明显下降,自动调低它的权重,并触发告警通知管理员确认。
3.2 规则路由与语义路由:谁来做决策
路由决策的实现方式,直接决定了这套系统能聪明到什么程度。
第一层是规则路由。通过正则、关键词、业务线ID、用户等级等维度做快速筛查,准确率很高、几乎没有额外延迟。比如:用户请求中包含“转人工”或投诉类关键词,直接路由到专门训练的客服模型;请求来自于VIP客户,则全部走质量优先策略。规则路由的缺点是泛化能力差,遇到没见过的说法就会失手。
第二层是语义路由。用Embedding模型把用户请求向量化,再通过一个轻量级分类器(或者直接算相似度)判断当前请求属于哪个任务类型,进而决定走哪个模型。语义路由能应对用户花样百出的表达方式。我们的实现里用了一个小巧的意图识别模型(大概1.5B参数),在单独部署的GPU实例上跑,单次分类的额外延迟控制在50毫秒以内。核心代码如下:
import numpy as np from sentence_transformers import SentenceTransformer from sklearn.metrics.pairwise import cosine_similarity # 预定义的请求类别与对应的模型通道 request_types = { "simple_fact": {"embedding": None, "channel": "local_qwen", "strategy": "cost"}, "complex_reason": {"embedding": None, "channel": "cloud_gpt", "strategy": "quality"}, "code_generation": {"embedding": None, "channel": "cloud_coder", "strategy": "quality"}, "summarization": {"embedding": None, "channel": "local_qwen", "strategy": "balance"}, } encoder = SentenceTransformer("/data/models/bge-m3") def build_type_vectors(): """初始化每个类别的示例文本向量(示例文本用各类别常见query填充)""" samples = { "simple_fact": ["今天天气怎么样", "北京是哪个省的", "李白的诗有哪些"], "complex_reason": ["分析一下新能源汽车行业竞争格局", "写一份市场策略分析报告"], "code_generation": ["写一个Python函数计算斐波那契数列", "帮我实现一个登录接口"], "summarization": ["总结这篇文章的主要内容", "把下面这段对话压缩成摘要"], } for req_type, texts in samples.items(): vectors = encoder.encode(texts) request_types[req_type]["embedding"] = np.mean(vectors, axis=0) def route_request(user_query: str) -> str: query_vec = encoder.encode([user_query]) best_type = None best_score = -1.0 for req_type, info in request_types.items(): if info["embedding"] is None: continue score = cosine_similarity(query_vec, info["embedding"].reshape(1, -1))[0][0] if score > best_score: best_score = score best_type = req_type # 兜底策略:相似度太低,走默认通道 if best_score < 0.4: return request_types["simple_fact"]["channel"] return request_types[best_type]["channel"]这段代码的思路很朴素,但在生产环境里非常稳。要注意的是,分类样本需要持续迭代,我们在每次路由后保留了“实际使用哪个模型”的日志,定期人工抽检,把抽检后确认应该调整分类的样本回填到样本集里重新计算向量中心点。
3.3 指标反馈与动态权重调整
如果路由策略只会静态配置,那高可用就无从谈起。我们的做法是让“数据说话”。
路由网关会周期性采集以下指标:
- 每个模型服务的平均响应时间(P50、P95)
- 请求成功率、错误类型分布
- 平均每请求Token消耗和折算成本
- 队列积压数量
- 用户侧反馈评分(点赞/点踩、人工质检结果)
这些指标汇总后,输入到一个简单的打分函数里,得到一个“服务健康分”。健康分 = 成功率 × 权重系数 - 延迟惩罚 - 成本惩罚。然后根据健康分动态调整各模型在路由表中的实时权重。
举例说明:本地模型服务因为GPU故障导致成功率从99%降到70%,健康分急剧下降。路由网关自动把这个模型的可分配权重下调(成本优先策略下可能从80降到20),同时增加云端API的权重。整个过程不需要人工介入,大概在故障发生后一到两轮健康检查(约10~20秒)内完成切换。等本地服务恢复稳定、健康分回升,再逐步调高权重。
这个机制的关键点是“调节速度”要适中。太激进会导致流量在模型之间频繁抖动,影响用户体验和日志分析;太保守则起不到故障转移的作用。我们的经验是:权重每次最大调整幅度不超过15个百分点,并且强制设置5分钟的最小切换间隔。
4. 高可用方案:健康检查、熔断、降级与切换
4.1 面向大模型服务的健康检查设计
传统微服务的健康检查一般是进程级检查(进程活着就算健康),但大模型服务完全不同。进程活着、显存还有余量、但是推理队列积压了一百个请求,这个服务实际上已经“不健康”了。
我们的健康检查分了三个级别:
- L1接口可用性检查:每10秒探测一次模型的HTTP服务端口,确认服务进程存活。
- L2推理延迟检查:每30秒发送一个极短的Prompt(比如“你好”),记录响应时间,超过阈值标记为亚健康。
- L3业务正确性检查:每5分钟从测试用例集里抽取一条真实业务请求,走完整推理链路,验证输出质量。
这里一定要提“探测流量要短”。有次我们用了太长的探测Prompt,结果健康检查请求本身就排队排了十几秒,导致系统误判为模型故障,把所有请求切到了云端,白白增加了一笔成本。后来把探测Prompt改成了1到2个Token的最小请求,延迟数据才恢复正常参考意义。
4.2 超时重试与指数退避
高可用机制里最容易想到、也最容易写错的就是重试。很多初期的实现是“请求失败就立即重试”,结果模型服务已经过载了,重试请求又涌进去,直接把它打挂。
重试必须配合退避算法。我们用的是指数退避加随机抖动(jitter):
等待时间 = 基础间隔(1秒) × 2^重试次数 + 随机数(0~500毫秒)第一次重试等待约1.5秒,第二次约3秒,第三次约5.5秒,最多重试3次。同时要区分错误类型:超时类错误可以重试;限流类错误(HTTP 429)等待后重试;参数类错误(HTTP 400)不重试,直接返回调用方。
一个容易踩的坑是“重试放大效应”。一个主Agent拆分10个子任务,每个子任务失败都会重试3次,主Agent发现子任务失败又整体重试,极端情况下原始请求被放大了几十倍。我们在路由层加了一个“全链路重试预算”机制:一条请求链路最多允许重试12次,超过预算直接走降级策略,不再重试。
4.3 熔断降级与多活切换:借鉴数据库高可用思路
做AI应用架构的人,往往没意识到一个事实:数据库高可用那套成熟方案(主从切换、故障转移、读写分离),在模型服务层同样适用,只是很少有人这么用。
本地模型和云端API天然就是一对“主备”。我们把本地模型优先级设为默认主,云端API设置为热备。正常情况下流量按路由策略走;当熔断器检测到本地模型连续失败率超过30%(滑动窗口为30秒),熔断器打开,流量自动切换到云端API。这个切换时间大概在15秒以内。
系统设计里我们特意保留了一个手动切换开关,可以在管理后台一键切换“主备”。为什么要手动开关?因为自动熔断不能覆盖所有场景——例如云端API本身出问题,自动路由也有可能把它选为主,反而把不稳定扩大了。手动开关的作用是,在自动路由无法处理“双活同时故障”的时候,运维人员可以强制执行人工策略,将流量切换到一个尚未出问题的备用供应商模型,或者启动“只读模式”(只返回缓存结果)。
降级方案我们准备了三档:
- 第一档降级:本地模型故障,切换到云端API,功能不降。
- 第二档降级:云端API也受限,把非核心的Agent任务挂到延迟队列,优先保障核心链路(比如客服对话)的服务质量。
- 第三档降级:所有模型都不可用时,返回预设的兜底回复和排队凭证,并通知用户“稍后消息统一送达”。
这个降级体系在架构上参考了MySQL MGR和PostgreSQL高可用部署中的“多数派和仲裁”思想——故障发生时宁可牺牲部分功能,也不能让整个系统崩溃。多Agent链路也一样,子任务失败时,主Agent可以选择性放弃“增值Agent”的输出(比如推荐理由生成),只返回最核心的结果。
4.4 上下文一致性与跨模型切换时的数据保障
跨模型切换还有一个隐蔽问题:如果用户在对话中,前几轮用的是云端模型,中间因为故障切换到本地模型,本地模型没有前文记忆,会导致体验断崖式下降。
我们的方案是把“用户会话上下文”从模型中剥离出来,单独存储在Redis里。每次路由到一个模型时,从Redis加载最近的对话历史(按Token长度截断,例如保留最近12000个Token),作为Prompt的一部分传给模型。同时,切换模型后会减少上下文对模型的依赖。比如把较长的检索结果先压缩成摘要,再发送给模型,这样即使用不同模型接收,也能基于同样的摘要信息给出连贯的回答。
长期记忆(用户画像、历史偏好)则单独存到PostgreSQL里,按用户ID组织,Agent在任务开始时自主决定是否加载。这样的隔离设计,让模型切换对终端用户的影响降到最低——至少不会出现“聊了半小时AI突然失忆”的情况。
5. 实战中的常见问题与排查技巧
5.1 Agent并发打满、请求堆积
现象:监控面板上任务积压数持续走高,用户响应越来越慢,但模型层吞吐并没有达到上限。
排查思路:先看Agent层和模型层的队列长度。很多时候问题是Agent层的并发配置太大,大量任务同时去请求模型,模型端排队严重;反过来也有可能Agent层配置太小,模型资源闲置,任务堆积在Agent等待队列里。我们内部有一个“两段式压测”法——先单独压测模型服务,确定模型真实吞吐上限,再压测Agent层,以模型吞吐上限为基准反推Agent并发度,避免两层互相拖累。
5.2 本地模型显存溢出与推理卡顿
现象:本地推理服务偶发返回内存不足错误,或者单个请求的响应时间突然从2秒变成30秒。
原因通常是两类:一类是模型服务的最大并发设置超过了GPU显存能承载的并发请求数;另一类是输入序列长度没有做限制,个别用户的超长Prompt把显存打爆。
解决措施:
- 根据GPU显存精确计算最大并发:单并发峰值显存占用 = 模型权重大小 + KV Cache大小 + 激活值。例如A100 80G部署72B模型(BF16权重约144G)肯定放不下,但量化到4-bit后约40G,留出约20G作为KV Cache,大概可以支撑8~16并发。
- 在模型服务端和接入层都设置输入长度上限。我们的默认配置是单次请求输入最多8000 Token,超长内容先走“长文本切分摘要”管线。
- 推理框架开启连续批处理和前缀缓存(比如vLLM的自动前缀缓存),能显著缓解长Prompt请求下的吞吐下降问题。
5.3 云端API限流与配额管理
现象:某段时间突然出现大量HTTP 429错误,但本地模型一切正常。
云端API限流分两层:每分钟请求数限制(RPM)和每分钟Token数限制(TPM)。我们的经验是,路由网关不仅要维护“每个模型服务的健康状态”,还要维护“当前时间窗口内的已用配额”,在请求路由前先估算“这个请求要消耗多少Token”“是否会撞上TPM上限”,如果会,宁可多等200毫秒切换到其他模型,也比发了请求被429然后浪费一次重试更划算。
配额估算可以用一个简单模型:预估消耗Token = 输入Token数 + 配置的最大输出Token数。把它乘以预估权重,再加到当前窗口用量上。这个机制上线后,我们云端API的429错误率从每个月十几次降到了个位数。
5.4 路由误判与质量劣化
现象:明明写的是技术问题,路由到了低成本模型,回答质量明显下降;或者所有请求都集中到了云端API,本地模型闲置,成本飙升。
排查方法:
- 查看路由日志中“语义分类的相似度得分”和“规则匹配命中的关键词”。低分且误判的样本,回填到样本集做增量训练。
- 如果发现本地模型闲置,检查权重动态调整功能是否误触发了。我们遇到过一次因为健康检查探测Prompt“业务正确性”连续失败导致本地模型权重降为零,后来才发现是测试用例集里混进了英文题目,而本地模型是中文优化版,英文回答质量总不达标,被判定为不健康。把测试用例改成中英分离之后,问题解决。
5.5 上下文丢失与串话问题
现象:用户在Agent对话里提到了前面聊过的内容,Agent毫无记忆;或者一个用户的问题被另一个用户的历史干扰了。
排查重点:
- 确认Redis里的会话上下文Key是不是用了正确的会话ID。有段时间我们因为前端小程序切换页面时重新生成了会话ID,每个请求都是新会话,Agent当然什么都不记得。
- 检查上下文是否被多个Agent并行写后互相覆盖。我们最终在Redis里用“每个Agent只写自己负责的上下文分区”,主Agent统一拉取汇总,避免并发写冲突。
- 跨模型切换时,检查是否真正加载了历史上下文。这部分我们加了切换前后各打一条日志,记录“当前模型”“历史Token数”“加载的上下文长度”,追查起来非常方便。
写在最后的一点经验
这套混合大模型架构从设计到上线,我最大的一点体会是:路由和高可用不是两个独立的问题,它们本质上是同一件事的两面。路由层能不能感知故障、能不能依据实时数据调整流量分配,直接决定了高可用能做到多好。而高可用机制反过来也在影响路由决策的边界——没有熔断保护的路由策略,调度得再精准也只会在故障来临时全盘崩溃。
另外想强调一个容易被忽视的运维习惯:给每一个路由决策、每一次模型切换、每一次降级动作都打上结构化日志,把原因字段(规则命中、健康分变化、重试预算耗尽)写清楚。这套日志在正常时期几乎没人看,但真正出故障的时候,它就是你能快速恢复业务的唯一线索。
如果你们也在做类似的混合部署架构,我建议先把“路由策略”和“降级预案”想清楚再动手写代码。这两件事看起来是“锦上添花”,实际上才是整套架构真正稳定运行的地基。