对话页面的路由策略动态切换,本质是把”用哪个模型、走哪条链路”的决策从写死的配置里解放出来,变成运行时可改、可灰度、可回滚的规则。我们在企业级 AI 网关项目中用”前端策略快照 + 网关规则引擎 + SSE 事件回传”三层结构落地:前端每次发起会话前向网关拉取最新路由策略,网关按模型字段、请求特征、成本维度匹配规则,命中后通过 SSE 的元数据事件把实际命中的模型回传页面展示。整个过程不刷新页面、不重新打包,策略变更在秒级生效。下面拆开讲实现细节。
一、路由策略动态切换的三层结构
1.1 为什么不能把模型写死在前端
早期版本在页面里维护了一份 modelRoutes 常量,模型供应商一换地址就得改代码重新构建。生产环境踩过坑:灰度模型 A 切到 B 需要前端发版,策略回滚又要二次发版,发布窗口直接拉长到小时级。
把路由决策移到网关后,前端只关心两件事:当前会话”应该”用哪个模型、实际”命中”了哪个模型。前者由策略快照决定,后者由网关回传确认。
| 对比维度 | 前端写死路由 | 网关动态路由 |
|---|---|---|
| 模型变更 | 改代码 + 重新打包发版 | 改规则,秒级生效 |
| 灰度能力 | 弱,需按版本分发 | 支持按用户 / 请求特征分流 |
| 故障降级 | 前端写死,难以自动切换 | 健康检查失败自动跳过 |
| 成本控制 | 无法按内容智能分配 | 按内容特征路由到低价模型 |
1.2 三个角色各管一段
- 前端策略层:对话发起前请求
/api/gateway/route/resolve,把会话参数(模型偏好、会话类型、租户)交给网关,网关返回本次会话应走的模型与路由标识; - 网关规则层:维护一组带优先级的路由规则,支持按请求体 model 字段、Header 特征、内容长度、token 预估匹配目标模型,规则库支持热更新;
- SSE 回传层:流式响应期间,网关在每个数据块的元数据事件里携带
model、routeId、fallbackFrom字段,页面据此渲染”当前模型”标签。
二、前端切换策略的具体实现
2.1 用 resolve 接口拿策略快照
对话页挂载和每次切换模型下拉框时,调用网关的解析接口,把结果写入 Pinia 会话状态。关键在接口设计上让网关有最终解释权——前端传的是”偏好”,网关返回的是”决策”。
// stores/session.ts
import { defineStore } from 'pinia'
export const useSessionStore = defineStore('session', {
state: () => ({
model: '',
routeId: '',
fallbackFrom: '' as string | null,
strategyVersion: 0,
}),
actions: {
async resolveRoute(prefer: string, scene: string) {
const res = await fetch('/api/gateway/route/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prefer, scene }),
})
const data = await res.json()
// 网关返回 model / routeId / strategyVersion
this.model = data.model
this.routeId = data.routeId
this.strategyVersion = data.strategyVersion
return data
},
},
})
2.2 规则热更新的同步机制
网关的策略库不能靠请求时全量拉取,也不能只靠本地缓存。我们的做法是”版本号 + 定时轮询”:
- 网关内存维护
strategyVersion,每次规则变更递增; - 前端长连接建立时携带已持有的版本号;
- 网关发现版本不一致,通过 SSE 推送
event: strategy_update事件及最新策略摘要; - 前端收到后仅更新本地展示用的模型清单,不打断进行中的会话;
- 会话的下一次请求自动走新策略,实现灰度模型的平滑过渡。
三、降级与回滚的兜底逻辑
3.1 健康检查驱动的自动降级
网关对每个候选模型做超时健康探测,连续三次失败标记为不可用,路由时直接跳过。前端配合在消息区域展示”已自动切换备用模型”,避免用户误以为请求失败。
// 网关回传的元数据事件解析
function parseMetaEvent(payload: string) {
const meta = JSON.parse(payload)
return {
model: meta.model,
routeId: meta.routeId,
fallbackFrom: meta.fallbackFrom ?? null,
}
}
3.2 前端可感知的三级回滚
- 规则级:网关配置中心保留上一版本规则,出问题一键回切;
- 路由级:灰度分流开启后,不满足条件的请求自动走稳定链路;
- 模型级:同一路由下配置主备两个模型,主模型异常自动落到备用。
四、落地时避开的三个坑
策略快照与长连接之间的竞态最隐蔽。曾出现用户切换模型后,历史消息还在旧路由上继续流式输出的情况,根因是会话 ID 没有随策略切换而更新。修复方案:路由切换时强制生成新的 conversationId,旧连接只允许把已产生的数据收尾,新消息一律走新会话。
超时配置同样要盯紧。网关策略解析接口必须比模型首包响应更快,否则页面出现”策略还没返回、用户已经开始等回答”的错位。我们把 resolve 的超时压到 800ms,失败时降级为前端本地默认模型。
成本标注也不要忽略。SSE 回传的 model 字段最终会落到埋点表,运营按 routeId 聚合单会话成本,这一步在需求阶段就预留,避免上线后再补数据链路。
常见问题(FAQ)
Q1:切换模型后历史会话还能继续吗?
策略切换会生成新会话 ID,旧会话数据保留可回看,但不再续写。
Q2:策略版本不一致时会不会打断对话?
不会。策略更新只影响下一次请求,进行中的流式输出不受影响。
Q3:前端能拿到规则明细吗?
只能拿到本次命中结果,规则权重与优先级不暴露给前端。