1. 项目概述:当企业级 Agent 遇上模型“掉线”,你靠什么不翻车?
“企业 Agent 的模型路由与容灾,远不止配置一个备用模型”——这句话我第一次在某金融行业客户现场听到时,正盯着他们刚上线三天就连续触发 7 次“大模型不可用”告警的监控看板。他们确实配了备用模型:主用是某国产千亿参数大模型,备用是同厂商的轻量版模型,配置文件里只改了 model_name 和 endpoint,连重试次数都设成了默认的 2 次。结果呢?一次上游模型服务集群因 GPU 显存泄漏导致整体响应延迟飙升至 12 秒以上,Agent 却还在傻等、重试、超时、报错,最终把用户对话卡死在“正在思考…”状态长达 47 秒。这不是故障,这是设计缺陷。
所谓“企业级 Agent”,核心不在它能多聪明,而在于它能不能在真实生产环境里稳如老狗。真实场景里,模型服务不是教科书里的 API 文档,它是活的:版本会灰度、节点会抖动、token 限流会突袭、上下文长度会动态调整、甚至某天凌晨运维同学误操作删了某个模型的路由别名……这些都不是“异常”,而是日常。所以,“模型路由与容灾”根本不是个配置项问题,它是一套覆盖请求生命周期的决策系统——从请求发起前的预判、发起时的策略选择、执行中的实时观测、失败后的分级降级,到恢复后的平滑回切。它要解决的,是“当模型不可靠时,Agent 如何依然可靠”。
这个内容适合三类人:一是正在落地 Agent 产品的技术负责人,你需要向业务方解释“为什么我们不能只换一个模型就上线”;二是负责 Agent 架构设计的后端/算法工程师,你需要知道路由层该埋哪些钩子、采集哪些指标、定义哪些策略;三是参与 SLO 设计或稳定性保障的同学,你会在这里看到模型服务如何真正纳入可观测性体系。它不讲大模型原理,不堆 prompt 工程技巧,只聚焦一件事:让 Agent 在模型频繁波动的现实世界里,依然交付稳定、可预期、有兜底的用户体验。下面所有内容,都来自我在过去 18 个月里,为 5 家不同行业客户(含金融、政务、制造、零售、教育)落地 Agent 系统时踩过的坑、写的代码、调的参数、画的脑图。
2. 内容整体设计与思路拆解:为什么“配个备用模型”只是幻觉?
2.1 企业级 Agent 的稳定性边界在哪里?
先说结论:企业级 Agent 的稳定性,永远由它最脆弱的那个依赖决定,而模型服务,恰恰是当前最不可控的依赖之一。这不是危言耸听,而是基于真实 SLA 数据的判断。我们统计过 2023 年 Q3 到 2024 年 Q1 间,接入的 12 个不同来源模型服务(含自研、云厂商、开源微调)的可用性数据:
| 模型类型 | 平均月度可用率 | P95 延迟(秒) | 主要故障模式 |
|---|---|---|---|
| 自研小模型(CPU) | 99.992% | 0.8 | 偶发 OOM、冷启动延迟 |
| 云厂商中型模型 | 99.93% | 2.1 | 限流抖动、版本灰度失败、GPU 资源争抢 |
| 开源微调大模型 | 99.78% | 4.7 | 显存泄漏、长上下文崩溃、推理超时 |
| 第三方 API 模型 | 99.61% | 3.9 | 网络抖动、认证失效、配额耗尽 |
注意看:即便是表现最好的自研小模型,其 P95 延迟也高达 0.8 秒,而一个典型 Agent 对话链路(含意图识别、工具调用、结果整合)的端到端 P95 目标通常是 1.5 秒以内。这意味着,模型调用本身已吃掉超过一半的延迟预算。更关键的是,99.93% 的可用率,对应每月约 52 分钟不可用时间——这还不包括那些“可用但极慢”、“可用但返回乱码”、“可用但 token 截断严重”的灰色故障。这些,传统“HTTP 200 就算成功”的健康检查根本发现不了。
所以,单纯配置一个备用模型,本质是假设:主备模型故障是完全独立事件,且备用模型总能在主模型失败的同一时刻提供同等质量的服务。现实是:云厂商的两个模型实例可能共享同一物理机;开源模型的两个部署可能共用同一个推理框架版本;甚至你的“备用”模型,可能只是主模型的一个低配版,在复杂 query 下准确率直接掉 30%。这已经不是容灾,这是把鸡蛋放进另一个篮子,还指望篮子不会被同一阵风吹倒。
2.2 真正的容灾架构:三层防御体系
我们最终在所有客户项目中落地的,是一个名为“Model Mesh”的三层防御体系。它不追求“零故障”,而是追求“故障可感知、可分级、可收敛”。这个体系的核心思想是:将模型服务抽象为一个可编程的、带状态的、可观测的“网络节点”,而非一个静态的 endpoint。
第一层:路由层(Routing Layer)—— 决策中枢
这是整个体系的大脑。它不直接调用模型,而是接收所有 Agent 的模型请求,根据预设策略、实时指标、历史画像,动态选择最优模型实例。它的输入不是简单的“model_name”,而是包含intent(意图类型)、context_length(当前上下文长度)、urgency(业务紧急度)、fallback_level(允许降级等级)等元信息的 rich request。输出也不是单一 endpoint,而是一个包含target_model、timeout_ms、max_tokens、fallback_strategy的完整执行计划。第二层:观测层(Observability Layer)—— 感知神经
这是体系的眼睛和耳朵。它在每个模型服务的入口和出口埋点,采集远超 HTTP 状态码的指标:request_start_time、first_token_latency、e2e_latency、output_truncated_ratio、hallucination_score(通过轻量校验模型计算)、token_usage_rate。这些指标不是丢给 Prometheus 就完事,而是实时聚合成每个模型实例的“健康画像”,每 30 秒更新一次。一个健康的模型,必须同时满足:P95 延迟 < 2.5s、截断率 < 5%、幻觉分 < 0.3、token 使用率在合理区间(避免因 prompt 过长导致隐式限流)。第三层:执行层(Execution Layer)—— 执行肌肉
这是体系的手和脚。它严格按路由层下发的执行计划工作,但绝不盲目执行。它内置了“熔断-降级-重试”三位一体的执行引擎:- 熔断(Circuit Breaker):当某模型实例的错误率在 60 秒内超过 50%,或 P95 延迟超过阈值 2 倍,自动熔断 5 分钟,期间所有请求直接跳过该实例;
- 降级(Degradation):当请求携带
fallback_level=2,且主模型熔断或超时,自动切换至预设的“语义降级模型”(如仅做关键词提取+规则匹配),而非硬切至另一个大模型; - 重试(Retry):重试不是简单地再发一次。它会根据首次失败原因智能调整:若为 timeout,则缩短下次 timeout;若为 rate_limit,则指数退避并尝试切换至同组其他实例;若为 malformed_input,则清洗 input 后重试。
这三层不是线性调用,而是形成闭环:执行层的每一次调用结果,都会反哺观测层更新健康画像;观测层的画像变化,会触发路由层重新评估策略;路由层的新策略,又指导执行层下一步动作。这才是一个活的、自适应的容灾系统。
2.3 为什么必须放弃“主备”思维,拥抱“多模态路由”?
很多团队卡在第一步:他们认为“路由”就是 A 不行切 B,B 不行切 C。这种线性 fallback 思维,在 Agent 场景下是灾难性的。原因有三:
语义鸿沟无法跨越:一个用于生成营销文案的模型,和一个用于解析合同条款的模型,其训练数据、微调目标、输出格式完全不同。强行把“合同解析失败”的请求 fallback 给“文案生成模型”,得到的不是答案,是胡言乱语。我们的一个政务客户曾因此把“请帮我查社保缴纳记录”的请求,fallback 给了政务问答模型,结果返回了一段关于“社保政策宣传稿写作要点”的长篇大论。
成本与效果严重失衡:大模型调用成本是按 token 计费的。一个简单的“今天天气如何”查询,如果 fallback 到一个千亿参数模型,其成本可能是专用轻量模型的 8 倍,而效果提升几乎为零。我们在某零售客户项目中测算过:在 30% 的简单查询场景下,强制 fallback 至大模型,使单次对话平均成本上升 220%,但用户满意度反而下降 15%(因为响应变慢、回答过于冗长)。
故障传播风险放大:当所有流量在主模型故障时瞬间涌向备用模型,极易造成备用模型雪崩。我们曾在一个制造客户项目中目睹:主模型因显存泄漏响应变慢,大量请求超时后 fallback 至备用模型,导致备用模型的并发连接数在 8 秒内从 200 暴涨至 1800,最终触发其自身的熔断机制,全链路瘫痪。
因此,“多模态路由”的核心,是按需匹配,而非被动 fallback。它要求我们对模型能力进行细粒度标注和分类:
- 按任务类型:
qa(问答)、summarize(摘要)、extract(抽取)、rewrite(改写)、classify(分类)、generate(生成); - 按输入特征:
short_text(<100 字)、long_context(>2000 字)、structured_data(JSON/表格)、unstructured_data(PDF/图片 OCR 结果); - 按输出要求:
strict_format(需 JSON Schema 校验)、free_text(自由文本)、step_by_step(分步推理); - 按业务 SLA:
realtime(<1s)、near_realtime(<3s)、batch(<30s)。
路由层拿到一个请求,首先解析其intent和context_length,然后在模型能力矩阵中查找所有满足task=qa AND input=short_text AND output=free_text AND sla=near_realtime的候选模型,再根据它们当前的健康画像(延迟、准确率、成本)进行加权排序,选出 Top 1。这才是真正的“智能路由”。
3. 核心细节解析与实操要点:从概念到代码的关键跃迁
3.1 模型能力画像:如何给每个模型实例打上精准“标签”?
“健康画像”不是一句空话,它需要一套可落地、可量化、可自动化的打标体系。我们不依赖人工标注,而是通过三个维度的自动化探针来构建:
维度一:基础可用性探针(Liveness Probe)
这是最底层的“心跳”。我们部署了一个独立的probe-service,每 15 秒向每个模型 endpoint 发送一个标准化的探测请求:
curl -X POST "https://model-api.example.com/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "model": "probe-model-v1", "messages": [{"role": "user", "content": "probe:health"}], "temperature": 0, "max_tokens": 10 }'关键点在于:
- 探测请求的
model参数必须是模型自身支持的合法值,不能是占位符; messages内容固定为probe:health,确保模型无需复杂推理,只测试基础通路;temperature=0强制确定性输出,避免因随机性导致结果校验失败;max_tokens=10严格限制输出长度,防止长输出拖慢探测周期。
探针服务会记录:http_status、total_time_ms、first_token_time_ms、response_body。只有当http_status==200且total_time_ms < 3000且response_body包含"probe:ok"字符串时,才判定为“存活”。否则,标记为liveness=unhealthy。
维度二:语义准确性探针(Readiness Probe)
这是进阶的“能力验证”。它测试模型是否真的“能干活”,而不仅仅是“在线”。我们准备了一个小型的、覆盖核心能力的黄金测试集(Golden Test Set),包含 50 个精心构造的样本,例如:
- 输入:“北京今天最高气温是多少?” → 期望输出:包含数字和“摄氏度”的短句;
- 输入:“请将以下 JSON 提取为表格:{...}” → 期望输出:符合 Markdown 表格格式的字符串;
- 输入:“总结这段文字:[一段 200 字新闻]” → 期望输出:长度在 50-80 字之间的摘要。
探针服务每天凌晨 2 点,对每个模型实例运行一轮全量测试,计算accuracy@50(50 个样本中正确回答的数量)。当accuracy@50 < 45(即错误率 >10%)时,标记为readiness=degraded。这个阈值不是拍脑袋定的,而是基于我们对 12 个模型的历史测试数据统计:当错误率超过 10%,用户投诉率会呈指数级上升。
维度三:业务适配性探针(Business Fit Probe)
这是最贴近业务的“价值验证”。它不关心模型多通用,只关心它在你的具体场景下多好用。我们为每个业务线(如“客服对话”、“合同审核”、“营销文案”)定义了专属的业务测试集(Business Test Set),包含 200 个真实脱敏的线上 case。例如,客服对话测试集会包含:“用户说‘我的订单没收到’,Agent 应该调用哪个工具?”、“用户发送一张模糊的快递单照片,如何引导其重拍?”。
这个测试集不每天跑,而是每次模型版本升级、或路由策略重大调整后手动触发。它的输出不是简单的“通过/失败”,而是一个business_fit_score,范围 0-100,计算公式为:business_fit_score = (correct_tool_calls * 0.4) + (accurate_summaries * 0.3) + (user_satisfaction_rating * 0.3)
其中user_satisfaction_rating来自 A/B 测试中,真实用户对 Agent 回答的 1-5 分评分均值。
这三个探针的结果,共同构成模型实例的“三维健康画像”。它不是一个布尔值,而是一个结构化对象:
{ "instance_id": "model-prod-03", "liveness": {"status": "healthy", "last_check": "2024-05-20T08:15:22Z"}, "readiness": {"accuracy": 48, "last_check": "2024-05-20T02:00:00Z"}, "business_fit": {"score": 86.2, "last_check": "2024-05-18T14:30:00Z"}, "latency_p95_ms": 1850, "truncation_ratio": 0.023, "hallucination_score": 0.12 }路由层正是基于这个丰富、动态、多维的画像,来做决策。没有这个画像,一切路由都是空中楼阁。
3.2 路由策略引擎:如何让决策既智能又可控?
有了画像,下一步是决策。我们摒弃了复杂的规则引擎(如 Drools),而是采用一种轻量、可编程、易调试的“策略函数”(Strategy Function)模式。每个路由策略,就是一个 Python 函数,接收request_context和candidate_models作为输入,返回排序后的模型列表。
一个典型的qa_short_text策略函数如下:
def route_qa_short_text(request_context, candidate_models): """ 路由策略:短文本问答 优先级:1. 业务适配分 > 85 2. P95 延迟 < 1500ms 3. 成本最低 """ # Step 1: 过滤掉不满足基本要求的模型 filtered = [] for model in candidate_models: if model.health.business_fit.score < 85: continue # 业务适配分太低,直接淘汰 if model.health.latency_p95_ms > 1500: continue # 延迟太高,影响用户体验 filtered.append(model) # Step 2: 按综合得分排序 scored = [] for model in filtered: # 业务适配分权重最高 score = model.health.business_fit.score * 0.5 # 延迟越低,加分越多(归一化到 0-1) latency_score = max(0, 1 - (model.health.latency_p95_ms / 1500)) * 0.3 # 成本越低,加分越多(假设 cost_per_1k_token 是已知属性) cost_score = max(0, 1 - (model.cost_per_1k_token / 10)) * 0.2 scored.append((model, score + latency_score + cost_score)) # Step 3: 返回按得分降序排列的模型列表 return [m for m, s in sorted(scored, key=lambda x: x[1], reverse=True)]这个函数的优势在于:
- 可读性强:逻辑清晰,任何工程师都能看懂、能修改;
- 可测试性强:可以针对
route_qa_short_text编写单元测试,用 mock 的candidate_models验证排序逻辑; - 可灰度性强:新策略上线时,可以只对 1% 的流量启用,对比新旧策略的
success_rate和avg_latency; - 可追溯性强:每次路由决策,都会记录
strategy_used和reasoning_trace,方便事后复盘。
我们为不同场景预置了 7 个核心策略函数,并允许业务方通过 YAML 配置文件,为不同intent动态绑定策略:
routing_policies: qa: short_text: "route_qa_short_text" long_context: "route_qa_long_context" structured_data: "route_qa_structured" summarize: default: "route_summarize_default" extract: json: "route_extract_json" table: "route_extract_table"提示:策略函数的编写,最常犯的错误是过度优化。比如,有人想加入“用户历史偏好”作为因子。这在技术上可行,但会极大增加策略复杂度和调试难度。我们的经验是:前 80% 的稳定性收益,来自于对基础健康指标(延迟、准确率、可用性)的精准把控;后 20% 的极致体验,才需要引入更复杂的上下文因子。先把地基打牢,再盖楼。
3.3 执行层的“熔断-降级-重试”三位一体引擎详解
执行层是策略落地的最后一公里,也是最容易出问题的地方。我们见过太多项目,路由策略写得天花乱坠,但执行层还是一个requests.post()加个try-except。这完全无法应对真实世界的复杂性。
熔断器(Circuit Breaker)的设计要点:
我们没有使用现成的库(如tenacity),而是自己实现了一个基于滑动窗口的熔断器。关键参数不是固定的,而是根据模型类型动态调整:
- 错误率阈值(failure_threshold):对自研小模型设为 30%(容忍度高),对云厂商大模型设为 15%(更敏感);
- 窗口大小(window_size):统一为 60 秒,但采样精度不同:小模型每 5 秒采样一次,大模型每 2 秒采样一次;
- 熔断时长(sleep_duration):不是固定值,而是
base_sleep * (2 ^ failure_count),即失败越多次,熔断时间越长,避免反复试探。
熔断器的状态存储在 Redis 中,key 为circuit_breaker:{model_instance_id},value 是一个 JSON:
{ "state": "OPEN", // OPEN/CLOSED/HALF_OPEN "failure_count": 3, "last_failure_time": "2024-05-20T08:15:22Z", "open_until": "2024-05-20T08:20:22Z" }降级(Degradation)的层级设计:
我们定义了 4 级降级,不是简单的“模型 A -> 模型 B”,而是能力降级:
- Level 0(无降级):使用原始策略选定的模型;
- Level 1(轻量模型):切换至同任务类型的专用轻量模型(如
qa-light-v2),牺牲部分泛化能力,换取速度和成本; - Level 2(规则引擎):放弃大模型,转而调用基于关键词、正则、模板的规则引擎,回答确定性高的问题(如“营业时间”、“联系方式”);
- Level 3(兜底回复):返回预设的友好提示,如“我正在全力思考这个问题,请稍候”,并异步触发人工客服介入。
降级的触发条件,由路由层在下发执行计划时指定。例如,一个urgency=low的后台分析请求,其fallback_level可能是 2;而一个urgency=high的客服对话请求,其fallback_level必须是 0 或 1,绝不能降到规则引擎。
重试(Retry)的智能逻辑:
重试不是“再试一次”,而是“换种方式再试”。我们为不同错误类型定义了不同的重试策略:
- Timeout 错误:缩短
timeout_ms(减半),并尝试切换至同组内latency_p95_ms最低的其他实例; - Rate Limit 错误:等待
retry-after头部指定的时间,若无此头,则按2^retry_count秒指数退避; - Malformed Input 错误:调用
input_sanitizer模块,对messages进行清洗(如移除不可见字符、截断超长 content、标准化 JSON 格式),然后重试; - Internal Server Error:不重试,直接进入降级流程。
这个三位一体的引擎,其核心价值在于:它把原本分散在各处的、杂乱的错误处理逻辑,收束到一个统一、可配置、可监控的模块中。每一次熔断、每一次降级、每一次重试,都会产生一条结构化日志,包含event_type、model_id、request_id、duration_ms、reason。这些日志,是后续优化路由策略、调整健康阈值、甚至推动模型供应商改进服务的最有力证据。
4. 实操过程与核心环节实现:从零搭建 Model Mesh 的完整路径
4.1 环境准备与核心组件选型
搭建 Model Mesh 不需要推倒重来,它应该是一个可插拔的中间件层,无缝集成到现有 Agent 架构中。我们推荐的最小可行架构(MVP)包含 4 个核心组件,全部基于成熟、开源、社区活跃的技术栈:
| 组件名称 | 推荐选型 | 选型理由 | 部署方式 |
|---|---|---|---|
| API 网关 | Kong Gateway (OSS) | 成熟的插件生态,原生支持 JWT、限流、负载均衡;其pre-function插件可完美嵌入路由策略逻辑 | Kubernetes |
| 观测数据存储 | TimescaleDB (PostgreSQL) | 专为时间序列优化,完美支持高频指标写入与复杂聚合查询;与 PostgreSQL 生态无缝兼容 | Kubernetes |
| 策略执行引擎 | Python + FastAPI 微服务 | 开发效率高,易于编写和调试策略函数;FastAPI 的异步特性完美匹配模型调用的 IO 密集型特点 | Kubernetes |
| 配置中心 | HashiCorp Consul | 提供服务发现、健康检查、KV 存储;其watch机制可实时推送路由策略变更,无需重启服务 | Kubernetes |
注意:我们刻意避开了某些“热门但重”的方案。例如,不推荐用 Istio 做路由,因为其配置复杂度远超需求,且对模型服务的细粒度指标采集支持不足;不推荐用 Prometheus + Alertmanager 做健康检查,因为它擅长“告警”,不擅长“决策”,无法直接驱动路由行为。选型的核心原则是:够用、稳定、易维护、团队熟悉。
4.2 核心环节一:构建模型注册与发现中心
所有模型服务,必须先“注册”到 Mesh 中,才能被路由。注册不是一次性动作,而是一个持续的过程。我们设计了一个model-registry服务,它通过两种方式发现模型:
方式一:主动注册(Recommended)
每个模型服务在启动时,向model-registry发送一个POST /v1/models/register请求,携带其元数据:
{ "model_id": "finance-qa-v3", "endpoint": "https://finance-qa-api.example.com/v1/chat/completions", "type": "llm", "task": ["qa"], "input_features": ["short_text", "structured_data"], "output_requirements": ["strict_format"], "sla": {"p95_latency_ms": 2500, "availability": 0.999}, "cost_per_1k_token": 0.012, "tags": ["finance", "production"] }model-registry会将其持久化到 Consul KV 中,并启动对该 endpoint 的 Liveness Probe。
方式二:Kubernetes Service 发现(For K8s 用户)
如果模型服务部署在 Kubernetes 中,model-registry会监听Service和EndpointSlice的变更事件。当检测到带有model-type: llm和model-task: qa标签的 Service 时,自动为其生成注册信息。这种方式适合快速接入大量内部模型。
注册完成后,model-registry会为每个模型生成一个唯一的model_instance_id(如finance-qa-v3-prod-01),并将其健康画像的存储路径(TimescaleDB 表名)也一并注册。这样,观测层和路由层就能精准定位到每个实例的数据。
4.3 核心环节二:部署观测层与健康画像生成
观测层是 Model Mesh 的“眼睛”,其部署质量直接决定了整个系统的可靠性。我们采用“边车(Sidecar)+ 中心化采集”的混合模式:
- 边车探针(Sidecar Probe):为每个模型服务 Pod 注入一个轻量
probe-sidecar容器。它只做一件事:每 15 秒,向主容器的/health端点(或模型 API endpoint)发送探测请求,并将原始响应(含时间戳、状态码、body)通过 Unix Socket 发送给主容器内的metrics-exporter。 - 中心化采集器(Central Collector):一个独立的
collector-service,它定期(每 30 秒)从 TimescaleDB 中拉取所有模型实例的原始探针数据,进行聚合计算,生成健康画像,并写回 TimescaleDB 的model_health表中。
model_health表的 schema 设计至关重要,它必须支持高效的多维查询:
CREATE TABLE model_health ( time TIMESTAMPTZ NOT NULL, model_instance_id TEXT NOT NULL, liveness_status TEXT, -- 'healthy', 'unhealthy' readiness_accuracy INTEGER, business_fit_score NUMERIC(4,2), latency_p95_ms NUMERIC(10,2), truncation_ratio NUMERIC(5,4), hallucination_score NUMERIC(4,2), -- 添加 TimescaleDB 的分区键 TIME_BUCKET('1 hour', time) ); SELECT create_hypertable('model_health', 'time');这个设计的好处是:我们可以轻松写出这样的查询来获取某个模型的健康趋势:
SELECT time_bucket('5 minutes', time) AS five_min, AVG(latency_p95_ms) AS avg_latency, AVG(truncation_ratio) AS avg_truncation FROM model_health WHERE model_instance_id = 'finance-qa-v3-prod-01' AND time > now() - INTERVAL '1 hour' GROUP BY five_min ORDER BY five_min;4.4 核心环节三:实现路由层与策略分发
路由层是 Model Mesh 的“大脑”,其实现分为两部分:策略计算和策略分发。
策略计算(Strategy Computation)
这是一个独立的router-service,它从 Consul 中读取当前生效的路由策略配置(YAML),并加载所有预定义的策略函数(Python 文件)。当收到一个 Agent 的请求时,它执行以下步骤:
- 解析请求 header 中的
X-Intent、X-Context-Length、X-Urgency等元信息; - 根据
X-Intent查找对应的策略函数; - 从
model-registry获取所有满足task和input_features的候选模型; - 从
model_health表中查询这些候选模型的最新健康画像; - 调用策略函数,传入
request_context和candidate_models,得到排序后的模型列表; - 生成一个
execution_planJSON,包含target_model_id、timeout_ms、max_tokens、fallback_level等字段。
策略分发(Strategy Distribution)router-service不直接调用模型,而是将execution_plan通过 Kafka Topicmodel-execution-queue发布出去。所有 Agent 服务(或专门的executor-service)都订阅这个 Topic。当它们收到一个execution_plan时,就知道该调用哪个模型、用什么参数、超时多久、降级到哪一级。
这种“计算与执行分离”的架构,带来了巨大的好处:
- 弹性伸缩:
router-service是纯 CPU 密集型,可以水平扩展以应对高并发路由请求;executor-service是 IO 密集型,可以独立扩展以应对高并发模型调用; - 故障隔离:
router-service挂了,Agent 可以 fallback 到本地缓存的默认策略;executor-service挂了,Kafka 会重试,保证消息不丢失; - 灰度发布:新策略只需更新
router-service的配置和代码,无需重启任何 Agent 服务。
4.5 核心环节四:集成执行层与全链路追踪
执行层是 Model Mesh 的“手脚”,它必须与 Agent 的业务逻辑深度集成。我们提供了一个标准的 Python SDKmodelmesh-sdk,Agent 开发者只需几行代码即可接入:
from modelmesh_sdk import ModelMeshClient client = ModelMeshClient( router_url="https://router.example.com", kafka_bootstrap_servers="kafka:9092" ) # 构造 rich request rich_request = { "messages": [{"role": "user", "content": "我的订单号是123456,查下物流"}], "intent": "qa", "context_length": 42, "urgency": "high", "fallback_level": 1 } # 发起调用(SDK 内部完成:路由、执行、重试、降级) response = client.chat_completions(rich_request) if response.status == "success": print(response.content) elif response.status == "degraded": print(f"已降级至 {response.fallback_level}, 内容: {response.content}") else: print(f"调用失败: {response.error_message}")这个 SDK 的核心价值在于:它把所有复杂的容灾逻辑封装在了chat_completions这一个方法里。Agent 开发者完全不需要关心背后是哪个模型、超时多久、失败后怎么降级。他们只需要关注自己的业务逻辑。
为了确保全链路可观测,modelmesh-sdk会自动注入 OpenTelemetry Trace ID,并在每个关键节点(路由开始、执行开始、重试、降级、结束)打点。这些 trace 会被收集到 Jaeger 中,形成一条完整的调用链:
[Agent] --(trace_id: abc123)--> [Router-Service] --> [Executor-Service] --> [Model-API] ↑ (记录 execution_plan)当出现一个慢请求时,运维人员可以在 Jaeger 中直接搜索trace_id,看到:
- 路由花了多少毫秒?
- 执行计划中的
timeout_ms是多少? - 是否发生了重试?重试了几次?每次重试的
timeout_ms是多少? - 是否触发了降级?降级到了哪一级?
- 最终调用的
model_instance_id是什么?
这种级别的可观测性,是排查复杂故障的基石。没有它,所谓的“容灾”就是黑盒,出了问题只能靠猜。