1. 先想清楚:存量培训系统的 AI 升级,难在哪儿
我去年接手了一个运营多年的培训管理系统,功能挺全:课程中心、题库、在线考试、证书管理、学习记录,还有一套老旧的权限体系。业务方提的需求也直接:给学员加一个 AI 助教,让它可以回答课纲相关问题;给讲师加一个自动批改主观题的能力;再把课程内容做摘要,方便学员快速了解一门课值不值得学。
听上去都是常规的 AI 应用场景,但真正动工以后我才发现,难的不是写提示词,而是怎么把 AI 能力接进这套跑了好几年的老系统里。项目最终定下的方案叫“统一 AI 能力网关与适配层设计”,核心思路是把大模型这类外部能力统一收口,做成一个标准化接入层,老系统各业务模块只需要对接一个内部接口。这篇就把整个方案的设计过程、落地细节和踩坑点完整捋一遍。
1.1 存量系统的真实状态
先说说这套培训系统的现状,这是后面所有设计的前提。系统本质上是一个典型的单体 Web 应用,后端以 Java Spring 技术栈为主,但也混着几个历史遗留模块,有的用 Python 写的,有的干脆是早期 PHP 改过来的。数据库比较统一,MySQL 加 Redis,数据量不算大,但表结构非常深,光学员学习记录相关的表就二十多张。
代码层面最大的问题是模块边界模糊。课程、考试、证书这几个核心域各自有独立数据表,但业务逻辑经常交叉调用,比如学员完成某个课程自动触发考试资格,考试通过又自动发证书。这类流程散落在不同服务里,硬耦合非常严重。如果在这个基础上直接每块业务各拉一条大模型 API 通道,代码会更乱,而且以后大模型供应商一换,改动面会非常吓人。
还有团队情况。维护这套系统的团队也就五六个人,一半时间还在做业务支持,没有专职的 AI 工程师。这种资源背景下,不可能搞一个复杂的 AI 中台,更不可能让每个业务模块各自研究模型适配。所以方案的定位非常明确:考核系统不能小修小补,但也不能推倒重来,必须在现有架构基础上,用最小侵入的成本让所有业务模块都能用得上一套统一的 AI 能力。
1.2 业务模块对 AI 的需求盘点
在设计统一接入方案之前,我把培训系统里各个核心模块对 AI 的潜在需求先盘了一遍。这一步很重要,因为它决定了你要给业务方提供的不是“调用大模型的能力”,而是一组具体、稳定的业务能力。
课程中心最直接的需求是课程摘要和智能问答。一门课程录完以后,视频、讲义、PPT 散落在不同目录里,学员想快速了解课程大纲,或者想针对某个知识点提问,传统检索基本帮不上忙。这里需要的是基于课程上下文的问答能力,核心是 RAG,先对课程内容做切片处理,向量化后存库,用户提问时先检索再让大模型生成答案。
考试模块则需要两个能力:一个是智能出题,给定知识点范围或课程材料,自动生成选择题、判断题甚至简答题;另一个是主观题批改,这个对培训系统来说非常刚需,尤其是开放式的案例分析题,讲师人工批改工作量巨大。批改的关键不在于大模型能不能判断对错,而在于评分逻辑要稳定,同一份答案在不同时间提交,得分浮动不能太大。
学习管理模块还有学习路径推荐、学员学习行为总结这些偏个性化的需求。后面我在设计统一网关时发现,这些需求虽然场景不一样,但底层能力模型高度重合:上下文理解、内容生成、结构化输出、多轮对话。这就给“统一收口”提供了很好的前提。如果各业务的需求差异极大,比如一个是图像识别、一个是语音合成,那网关设计会复杂得多。
1.3 直接在业务代码里调大模型 API 的坑
我见过很多团队做 AI 升级时,第一反应是在业务代码里直接引入某个大模型厂商的 SDK,调几个接口,能跑就行。小项目可以这么干,但放到存量培训系统里,几乎每一条都会变成后期的雷。
第一个坑是供应商锁定。假设你在课程中心接了大模型 A 的接口,写了上千行业务代码,做了各种 prompt 工程。过了半年,公司采购部门谈下了大模型 B 的折扣价,或者大模型 A 下线了一个版本导致回答质量下降,你要换供应商,就得把每处调用代码全部改一遍,光是测试回归就能拖垮整个迭代节奏。
第二个坑是密钥和成本失控。如果每个模块各自配一个 API Key,费用中心看到的是一堆互相独立的消耗条目,根本无法统计某个业务功能到底花了多少钱。更麻烦的是权限不好控制,前端如果直接持有一个带余额的 Key,一旦泄露,别人刷你的接口,一夜之间账单爆掉。
第三个坑是异构接口问题。市面上主流大模型厂商的接口设计大同小异,但细节差异很多:有的用 SSE 流式返回,有的只支持非流式;有的把 string 类型参数叫prompt,有的叫messages;有的支持 function calling,有的不支持。给每一个供应商各写一套接入逻辑,后期维护工作量翻倍。
第四个坑是可靠性和审计。大模型接口不稳定,经常超时、限流、返回异常,业务代码里如果没有统一的兜底和重试逻辑,用户看到的就是功能时好时坏。而且培训系统里有员工信息、学习记录,这些数据在外面过了一圈,如果没有统一的审计日志,合规检查根本过不了关。
统一 AI 能力网关的价值,就是把上面这些问题在架构层面一次性解决。对外,网关对接各家大模型供应商;对内,网关把能力标准化,业务模块只认内部接口。
2. 统一 AI 能力网关与适配层的整体思路
当我们确定要做统一收口后,第一个问题就是:这层东西到底放在哪里,承担什么职责,长什么样。我最初理解得很简单,以为就是一个 API 转发服务,把业务请求转发给大模型再返回结果。但实际设计后发现,它至少要承担四个层面的职责:接入、适配、治理、安全。
网上很多文章喜欢用“API 网关”这个词来概括,但我在实际落地中觉得,还是要把“适配层”单独拎出来讲清楚,因为它是整个架构里最核心、也最容易被人忽视的部分。从培训系统团队的角度看,业务侧想要的不是“某个大模型厂商的 API”,而是一个稳定的、语义明确的“培训系统 AI 能力接口”。适配层要做的就是把你和外部模型之间的合约关系固定下来。
2.1 整体分层:从业务调用到模型供应商
我画的架构分四层:最上层是业务接入层,也就是培训系统里的课程中心、考试中心、学习管理模块;第二层是统一的 AI API 面,负责定义业务方能看到的能力契约;第三层是网关核心层,处理路由、限流、鉴权、日志;第四层是适配层,将不同外部模型供应商的接口差异封装起来,向上提供标准化的调用方式。
业务接入层不用多讲,各模块按自己的节奏改造。重点是第二层 API 面,这一层决定了业务方是舒服还是痛苦。我给培训系统定义了四个核心能力接口:智能问答、文本生成(摘要/出题)、结构化抽取(用于主观题评分)、对话管理(多轮会话上下文)。这四类接口的参数尽量统一,比如都有sceneCode、sessionId、temperature、maxTokens,这样业务侧的学习成本非常低。
网关核心层我直接使用了开源 API 网关来承载,没有自己重复造轮子。这一层负责统一入口、API Key 管理、调用频率限制、成本配额、审计日志。这样设计的好处是,已经有成熟的流量治理能力可以复用,团队把精力集中在业务能力抽象上,不用去研究网关底层的线程模型和高可用方案。
适配层是最重要的一层。它位于网关核心层之下,外部模型供应商之上。每一个模型供应商对应一个适配器,适配器负责三件事:协议转换,比如把内部统一请求转成供应商要求的格式;鉴权对接,把统一的接入凭证映射到供应商的 Key;响应归一化,把不同供应商返回的格式处理成内部统一结构。对外屏蔽差异,对内统一标准。
2.2 为什么必须有适配层,而不是直接封装一个 SDK
有人会问,我做个网关,然后每个业务模块自己引入 SDK,不也能工作吗,为什么要多一层适配器?这里我吃过大亏,最早我们确实这么干过。当时有一个讲师端的智能批改功能,直接在服务里引入了一家大模型厂商的 Java SDK,功能倒是很快上线了,结果不到两个月,那家厂商调整了接口签名,SDK 里面的一个核心方法直接 deprecated,我们紧急排期升级,前后花了一周多。
适配层解决的是“供应商易变”的问题。SDK 封装的是厂商的接口,而适配层封装的是“能力”。两者的稳定性完全不同。厂商接口三天两头改,但培训系统里的“智能问答”“文本摘要”“结构化评分”这些能力,短期内不会有太大变化。适配层的职责就是在这两个生命周期之间做缓冲。
另外,适配层让多供应商切换成为一件低成本的事。比如培训系统内网部署了一个私有化模型,适合处理敏感数据;公有云大模型能力强,适合通用问答。有了适配层,这两个模型可以在路由层做策略调度,而不需要业务方感知。同理,如果某个模型供应商因为政策或版本原因没法用了,可以只改适配器和路由配置,业务代码一行不动。
再补充一个实操细节:适配器里建议实现一个ping或者modelCheck方法,定时检测各个供应商的连通性和响应延时,把状态上报给路由层。这样路由层可以优先把流量导向健康的模型,而不是等到用户报障才发现某个供应商已经挂了很久。
2.3 网关层到底管什么:别把网关做成纯转发
很多团队做 AI 网关容易走两个极端:一是把网关做成一个纯转发器,除了代理什么都没干;二是把网关做得过重,把业务流程逻辑也塞进去,结果网关变成了一个大泥球。我的建议是,网关核心层只做流量治理,不做业务理解。
在培训系统这个场景里,网关核心层主要管四件事。第一是 API 统一入口和文档管理,所有 AI 相关请求都走固定的域名路径,比如/ai/v1/chat,不用一个个模块各找各的接口。第二是鉴权和配额,每个业务模块配独立的 AppKey,网关侧设置每模块每日调用量上限,超过就拒绝,防止单个模块的异常流量把整个 AI 能力拖垮。
第三是限流和熔断。培训系统正常使用的高峰期是工作日的午休和下班后,这两个时段调用量可能是平时三四倍,如果没有限流,后端模型供应商的额度很容易被打满。我设定的是单模块 QPS 上限 20,超过的请求直接排队或走缓存,避免雪崩。第四件事是审计日志。每一条 AI 调用记录会落库,包含调用时间、业务模块、用户标识、使用的模型、token 消耗、返回状态码、错误信息。这事看起来简单,真做起来非常有价值,后面排查问题全靠它。
网关层不要碰业务规则,比如不要判断“这门课程属于哪个讲师”,不要往请求体里塞业务字段。一旦网关开始理解业务,它就会和身后的系统绑死,失去通用性。保持网关的“笨”,它才能活得久。
3. 落地实现:从接口定义到路由策略
设计思路确定之后,真正动手实现时有很多细节需要抠。这一章我把我们团队实际写代码、调接口的过程梳理出来,重点是给出可以直接参考的接口定义、路由实现方式和降级策略。培训系统的流量不算大,技术选型不需要很复杂,但思路必须完整。
3.1 先定统一接口:内部契约是一切的基础
统一接口是整个方案的“宪法”。接口定得不好,后面所有适配器都跟着乱。我参考了几个主流大模型公网 API 的格式,结合培训系统的实际场景,定了一套偏中性的接口规范。核心原则是:接口语义贴近业务能力,而不是贴近某个厂商的协议。
我们内部定义的智能问答接口大致长这样:
POST /ai/v1/chat { "sceneCode": "course_qa", "sessionId": "ce57f1a2-1c13-4d3e-8f32-7c4bc2eb7a21", "messages": [ {"role": "system", "content": "你是培训助教,只回答与课程相关的内容"}, {"role": "user", "content": "这门课讲的是微服务架构吗"} ], "temperature": 0.3, "maxTokens": 800, "stream": true }返回统一采用这种格式:
{ "code": 0, "message": "success", "data": { "sessionId": "ce57f1a2-1c13-4d3e-8f32-7c4bc2eb7a21", "content": "是的,这门课主要讲微服务架构的设计方法……", "model": "providerA-gpt-4o-mini", "usage": {"promptTokens": 321, "completionTokens": 78} } }有几个设计细节值得说。sceneCode是业务场景编码,网关根据它做路由。sessionId由业务方传入,网关层不做会话状态管理,只透传。这样设计的原因是培训系统里有的场景需要多轮对话,有的场景是一次性问答,如果把会话管理放到网关层,那网关就要维护每用户每场景的状态,复杂度会直线上升。
temperature和maxTokens虽然是非必填字段,但我强烈建议所有内部接口都保留,并且给默认值。因为不同模型对这两个参数的支持不一样,统一透传后适配层可以根据模型能力做丢弃或精度的转换。比如老版本的大模型不支持temperature参数,适配器可以在转换时悄悄移除,而不会报错。
流式和非流式我建议都支持。培训系统里的课程摘要,通常等个几秒没问题;但 AI 助教这类交互式场景,必须流式返回,否则用户等明显卡顿。统一接口通过stream字段区分,内部统一走 SSE 协议。
3.2 路由策略:让请求找到最合适的模型
路由是整个网关里最能体现技术含量的部分。同一个业务请求,到底该打到哪一家模型的哪一个版本,不能写死在配置里,而是要有清晰的策略。
我在路由层实现了一个简单的能力矩阵。每个模型供应商在接入时,会登记自身的元信息:模型名称、支持的最大上下文窗口、最大输出 token、是否支持流式、是否支持 function calling、单价、可用性权重。比如:
| 模型标识 | 上下文窗口 | 流式 | Function Calling | 单价(相对) | 可用性 |
|---|---|---|---|---|---|
| providerA-fast | 16K | 是 | 是 | 低 | 高 |
| providerB-logic | 32K | 是 | 否 | 中 | 中 |
| providerC-private | 8K | 是 | 否 | 内部成本 | 高 |
路由规则的优先级是这样的:先按场景编码查表,如果场景显式指定了模型,就直接走指定模型,不做干预。这是为了兼容一些对模型版本有强要求的业务,比如某个课程的内容只适配了特定模型的 prompt。如果没有指定,就按业务要求的能力类型筛选,比如需要长上下文的走 32K 窗口的模型,需要稳定的结构化输出走支持 function calling 的模型。
如果筛选后还剩多个模型,再按成本和可用性加权打分。我初始化分数时成本占 60%,可用性占 40%,后期会根据线上指标动态调整。比如某模型最近响应成功率降到 95% 以下,路由会自动降低它的权重,减少流量进入。
这种设计不是一上来就要做到多智能,但至少要保证“可配置、可观测、可调整”。我见过有人把路由逻辑做成一个巨大的 if-else,每来一个新模型就加一个分支,这种代码三个月后没人敢动。路由必须数据驱动,元信息表的可维护性比代码逻辑本身更重要。
3.3 上下文管理:多轮对话的粘连问题
培训系统的 AI 助教不可避免地要做多轮对话。学员问一句,助教回答一句;学员再追问细节,助教要记得之前聊过什么。这个问题如果不提前设计,会在后期让适配层非常难受。
我的处理方式是:会话上下文的存储放在业务侧,网关只做透传。业务侧每次调用时把sessionId传进来,网关层从 Redis 里读取该会话最近若干条消息,拼到请求里;拿到模型返回后,再把新消息追加回缓存,并设置过期时间,默认两个小时。为什么网关要管这个?因为如果不做,业务侧每轮都要自己拼历史,很容易把多条历史消息全部传进来,导致 token 消耗暴涨。
token 估算是个实用技能。每条 message 的 token 数不是精确计算的,我通常按中文一个字约等于 1 到 1.5 个 token、英文一个单词约等于 1.3 个 token 来粗算。在拼接历史消息前,先累加当前请求的 token 估算值,如果超过模型上下文窗口的 70%,就把最早的历史消息截断,只保留最近的对话片段。
这里有一个值得注意的细节:不同模型的上下文窗口计算方式不一样,有的会把 system prompt 算进去,有的不算。适配器做 token 截断时,最好根据模型登记的最大上下文窗口参数动态计算,不要写死在适配器代码里。我见过有人把 8K 窗口写死在代码里,后来接了个 128K 窗口的模型,白白浪费了能力,却还自认为做了“合理的截断”。
还建议做一层简单的缓存。对于相同sceneCode加相同问题、且系统提示词一致的请求,可以直接返回历史结果,减少重复的大模型调用。培训系统里学员问的问题重合度很高,这段时间实测缓存命中率大约在 15% 到 20%,虽然不是特别高,但胜在完全不影响体验。
3.4 降级、熔断与重试:别让大模型拖垮业务
大模型接口的稳定性远不如普通的数据库查询,这是我在一期项目里最深刻的体会。很多培训系统的业务链路是:用户从前端发起请求,后端调用大模型,然后等结果返回。这个链路一旦大模型响应慢,用户的请求会一直挂着,直到超时。所以在网关设计里,降级和熔断必须是一等公民。
我在网关层做了一个三档降级策略。第一档是功能降级,当检测到模型可用性低于阈值或响应时间飙高时,把一些非关键的 AI 功能关掉。比如课程摘要功能平时是自动生成的,此时改成返回一个默认的“该课程摘要生成中”,引导用户稍后再试。第二档是模型降级,首选模型挂了,自动切换到备选模型。比如公网模型超时,切到内网私有化模型,虽然回答质量稍差,但功能还能用。第三档是人工兜底,AI 助教不可用时,前端提示转人工客服。
熔断我采用了固定时间窗口的滑动计数算法。窗口设 60 秒,如果窗口内错误率超过 40%,触发熔断,之后 30 秒内直接快速失败,不再调用大模型。30 秒后处于半开状态,放过去一部分请求探活,如果成功率恢复就关闭熔断。这个阈值要根据实际流量调整,培训系统的整体流量不大,40% 的错误率意味着 10 次里有 4 次失败,用户感知已经很明显了,所以这个阈值比较合理。
重试策略我要提醒一句:大模型接口超时后直接重试,效果往往不好,因为模型生成很吃资源,单次超时很可能是模型负载居高不下,重试会加大压力。我建议重试次数上限为 1 次,而且只在“非流式请求”上做,流式请求中途断流就不要重试了,直接告诉用户“生成中断”。重试时换一个模型比按原模型重试成功率更高,我在实际中试过,同样的 prompt 换个供应商,大概率能绕开局部故障。
3.5 限流与配额:防止单点拖垮全局
培训系统的 AI 升级完成后,最开心的往往是讲师和学员,最慌的一定是运维。因为 AI 接口是从内部系统直接打到外部供应商的,成本直接和调用量挂钩。如果不做配额控制,一个批量任务脚本就能把月度预算跑穿。
我在网关层引入了二级限流。第一级是按业务模块限流,每个模块的 AppKey 配置独立的 QPS 上限和日调用量上限。第二级是全局限流,所有模块共享一个总 QPS 和月度 token 预算。实现方式很简单,Redis 做一个计数器和令牌桶,每次请求前检查,超过就返回 429 状态码和明确的中文错误信息。
配额控制做的是预算管理。每个月初,管理员在配置中心给各模块分配月预算,比如课程中心 500 万 token 额度,考试中心 800 万 token 额度。网关层实时累加每个模块的 token 消耗,达到预算的 80% 时告警,达到 100% 时直接停止非核心场景的调用,紧急场景可以走白名单绕过。
这里有一个很实用的技巧:token 消耗统计要基于“实际额度和补全 token 之和”,而不是简单按请求数估算。因为同一个请求,不同模型生成的 token 数量差异很大,如果只按次数限制,成本控制会失真。每次模型返回后,适配器把usage信息上报给网关,网关异步写入日志系统,再定时汇总到预算表。异步的好处是不增加主链路延迟。
4. 存量系统集成:怎么把网关接到老代码里
统一 AI 能力网关的技术内核设计完了,接下来最现实的问题:怎么把它接到那套老的培训系统里。这里要面对的是碎片化技术栈、历史包袱和团队有限的精力。我的原则是:不追求一步到位,先用最轻的方式把链路打通,再做渐进式改造。
4.1 三种接入方式对比
对存量系统来说,接入新网关有三种典型路径。
第一种是侵入式 SDK 接入,在业务代码里引入网关提供的 SDK 客户端,通过本地方法调用,SDK 再跟网关通信。好处是改了代码以后可以直接给业务方提供类型安全的方法,也可以在 SDK 层做熔断、重试等能力;坏处是老系统模块复杂,所有需要 AI 能力的代码都要写新逻辑,工作量大,而且有些老模块用的是老版本 JDK,连 SDK 都跑不起来。
第二种是独立网关统一入口,不改业务代码内部结构,只把认证和路由改到网关层。适合老系统逐步迁移,但培训系统的业务方往往希望 AI 能力直接内嵌在现有页面里,这条路纯走网关入口对前端改动也不小。
第三种是我最终推荐的模式:混合模式。对外用网关统一收口流量,对内提供一个轻量 SDK 供新模块使用;老模块改造量大的,先用标准 HTTP 接口对接。SDK 和 HTTP 接口都直接指向同一个网关地址。这样新代码有好的开发体验,老模块不至于因为引入 SDK 导致依赖冲突。
具体操作上,我在培训系统的用户中心建了一个AiClient组件,内部封装了网关的 API 地址、AppKey、密钥等配置,业务侧只需要调用aiClient.chat(...)或aiClient.summarize(...)。老模块如果没法引这个组件,就直接发 HTTP 请求,网关配置 CORS 允许对应域名访问。
这里要特别提醒一个容易忽略的点:存量系统的内网访问地址和老系统的外网出口不一样。培训系统内部各模块之间的网络隔离策略比较严格,网关部署在哪台机器上、哪几台业务服务器可以访问它,这些网络策略要提前梳理,否则接口开发完了,联调时才发现网络不通,非常浪费时间。
4.2 渐进式改造:先冷后热,灰度放量
存量系统改造最大的风险是回归测试。培训系统里一个课程中心,关联的权限、审批、统计报表一大堆,你不能说改就改。所以我的落地策略是:先挑一个“冷”场景试点,把链路跑通,再逐步扩展到高频“热”场景。
我选的第一个试点场景是“课程摘要生成”。这个场景用户感知不强,即使 AI 生成的内容质量一般,也不会对核心业务造成致命影响。而且课程摘要是一次性任务,不需要多轮对话,出了问题的排查链路最短。我们用课程中心几百门课程的真实数据做联调,前前后后跑了一周,把网关路由、适配切换、降级策略验证了一遍。
跑通之后,第二个场景就选了考试模块的“主观题批改辅助”。为什么选这个?因为讲师痛感最强,反馈最积极。我们先把批改做成“半自动”模式:大模型先给出评分建议,讲师确认后再写入最终成绩,避免大模型直接篡改核心数据。这个灰度策略非常重要,既让用户感受到了 AI 的效率提升,又没有拿到最终决策权,风险可控。
之后再逐步把智能问答助教、学习路径推荐这些高频场景接进来。每一次接入都遵循一个流程:定义内部能力接口,在网关配好场景编码和路由策略,适配器完成该场景的 prompt 模板和参数优化,最后小流量放量。整个过程下来,业务代码的改动被压缩到最小,大部分逻辑收敛在网关配置和适配器里。
4.3 安全与审计:数据出境和敏感信息防护
培训系统里有多名员工的账号、学习记录和考试成绩,这类数据在 AI 调用过程中不可避免地要经过外部模型服务完成处理,所以安全合规是整个接入方案里不可回避的问题。
我的处理原则是:能本地处理的数据尽量本地处理,必须出外部的数据做脱敏。比如学员姓名、手机号、工号,在传入大模型前,统一用占位符替换;问答请求里的敏感词先走一遍关键词过滤,再决定是否放行到外部模型。培训系统中的考试题目,尤其是一些内部培训的案例分析题,涉及不少业务细节,我们在网关层加了内容过滤规则,疑似含有机密内容的请求直接接入内网私有化模型,不进入公网。
审计是不可省略的。每条请求,从网关收到的原文到发给模型的 prompt,再到模型返回的完整内容和最终返回业务方的数据,我都会在网关层记录日志。业务侧不一定需要看 prompt,但出问题的时候,比如某个学员反馈助教给出了不合适的回答,日志能帮你精确定位是哪一次调用、哪个模型出了岔子。
接入身份认证也要统一做。网关对外提供的接口全部要求带签名,签名算法是简单的 HMAC-SHA256,使用模块专属的 AppKey 和 Secret。签名过期时间设 60 秒,防止请求被重放。这套签名逻辑我也封装在 SDK 和演示文档里,业务侧接入时不需要自己实现加密逻辑,直接调用即可。
4.4 配置管理与模型灰度
AI 能力升级里有一个业务方很难理解、但技术团队必须重视的环节:模型版本的灰度切换。大模型供应商经常升级模型版本,同一款模型的 v1 和 v2,在回答风格、稳定性、安全性上可能有差异。直接全量切换风险很大,我倾向于在网关层做模型版本灰度。
我在查询配置中心里维护了一个模型流量分配表,每个模型条目可以配置多个版本,每个版本有权重。比如某款模型默认版本流量 80%,新版本 20%。新版本跑一段时间,观察错误率和人工抽检结果,没问题再把流量逐渐调高到 100%。
这期间有一个很宝贵的经验:监控不能只看错误率,更要看“用户反馈率”。有一次模型新版本错误率不高,但学员反馈“AI 助教讲课越来越像念稿子”,后来人工抽检才发现是新版本模型生成的内容太模板化。所以灰度切换期间,我建议网关对接一个简单的反馈接口,前端页面提供“点赞/点踩”,把数据落到日志里,作为模型版本质量评估的重要参考。
5. 常见问题与排查技巧实录
方案落地过程中踩了不少坑,这里挑一些典型问题整理出来,当作一份速查手册。这些问题单独看都不复杂,但串在一起非常消耗耐心,写出来给后面做类似系统的人参考。
5.1 高频故障症状与处理建议速查表
| 症状 | 可能原因 | 快速排查路径 | 解决方案 |
|---|---|---|---|
| 请求返回超时 | 模型供应商短时间内负载过高 | 查看网关日志中单次调用的耗时分布 | 开启备选模型降级;调低超时时间到 25 秒 |
| 返回内容 JSON 解析失败 | 模型在生成 JSON 时混入了非 JSON 文本 | 查看模型原始返回,检查前后缀是否多了多余字符 | 在适配层做“JSON 提取兜底”,截取大括号内的内容再解析 |
| 多轮对话后续答非所问 | 历史消息拼接过多或过少 | 查 Redis 缓存里的历史消息条数及 token 估算值 | 调整历史消息窗口;对早期历史做摘要压缩 |
| 成本快速上涨 | 某个模块生成了过长的补全内容 | 查网关配额表,按模块统计 token 消耗 | 调整maxTokens上限;对长文本场景接入摘要后处理 |
| 流式响应中途断流 | 网络连接被断开或模型侧异常 | 查网关注入的 SSE 连接状态和断流点 | 给前端提示“生成中断”,引导重试;在适配层实现 SSE 心跳重连 |
| 同一问题不同模型答案差异大 | 模型版本或 prompt 策略不一致 | 查看路由日志中实际选中的模型标识 | 固定场景绑定模型版本,或手动抽查调整 prompt 模板 |
| 内网私有化模型回答问题质量低 | 私有化模型参数量或微调数据有限 | 对比公网模型在相同 prompt 下的答案 | 把敏感程度不高的问题路由到公网模型;对私有模型做针对性微调 |
| 业务方反馈“功能时好时坏” | 熔断或限流触发后部分请求被拒绝 | 查 429 状态码和熔断日志 | 优化限流阈值;做前端友好提示,避免露出底层错误 |
表格里的内容都是线上真实遇到过的情况,可以对照排查。下面挑三个典型的展开说。
5.2 典型问题一:大模型返回 JSON 格式飘忽不定
主观题批改场景要求大模型输出结构化的评分 JSON,比如包含得分、评语、建议。但在实际测试中我们发现,大模型偶尔会在 JSON 前加一行“根据评分标准,结果如下:”,或者在 JSON 末尾附加一句解释。这段非 JSON 内容直接传给后端解析,ObjectMapper直接抛异常,整个功能挂掉。
排查后发现这不是偶发问题,不同供应商的处理风格差异很大。有的模型在 system prompt 里明确要求“只返回 JSON”时会遵守,但换个模型就不一定了。我的解决办法是在适配层写了一个“JSON 提取函数”,先用正则寻找第一个“{”和最后一个“}”,截取中间内容,再做解析;截取失败再尝试按段落拆分找 JSON 片段。这层兜底逻辑在公网大模型上很管用,几个主流模型生成的格式问题基本都能处理掉。
5.3 典型问题二:多轮对话的 token 浪费和上下文混乱
做 AI 助教时,业务方最早的实现方式是把所有历史消息一次性传进来,没做任何截断。跑了几天,成本高了不少,而且助教经常被很远的历史问题带偏。原因是有些课程相关的多轮对话很长,翻了十几页之后,早期的问题内容已经被讲得很细,但模型每轮都要重新读一遍,既浪费 token 又容易注意力发散。
我的改进方案是:网关层对历史消息做分层处理。最近 6 条消息完整保留,更早的消息合并成一段摘要,放在历史消息的最前面;如果消息总量超出模型窗口,就只保留最后 8 条消息的完整内容。具体实现时在 Redis 里给每个 session 存了两个 key,一个存原始消息流水,一个存“压缩后的上下文”。每次请求时先尝试用压缩上下文,如果没有摘要就从原始流水里生成一次。
这个方案上线后,多轮问答的 token 消耗降低了差不多 30%,而且助教的回答连贯性反而更稳定了。原因也好理解,模型处理较短的上下文时,注意力能更集中到最新的对话意图上。
5.4 典型问题三:熔断策略过于灵敏导致误伤
熔断策略上线初期,我把阈值设置得很敏感:60 秒窗口内错误率超过 20% 就熔断。结果有一次供应商做了一次小规模升级,有一两分钟的响应抖动,熔断被触发了,整整 30 秒内所有 AI 功能全部快速失败。业务方立刻炸了:“为什么 AI 助教突然不能用了?”
事后复盘,原因是我没有区分“错误类型”。供应商返回 429 限流错误和 500 服务内部错误,对熔断的参考价值完全不一样。429 说明供应商还能正常处理,只是流量超过了它的配额;500 才是真正的服务不可用。把两者混在一起统计,熔断阈值就失真了。后来我改成单独统计 5xx 错误率,4xx 不计入熔断,阈值调到 40%,误伤问题基本消除。
排查这类问题一定要用好网关日志。我在网关层记录了每次调用的状态码、耗时模型标识和错误信息,事后查问题时,能直接按时间范围筛选出熔断窗口的完整请求链。这套审计日志在第一期上线时不显眼,到了第二三期排查问题的时候,价值真的是翻倍体现。
项目回顾与实操心得
这个统一 AI 能力网关和适配层方案,从设计到第一场景上线大概花了三周,整体覆盖到学习路径推荐时又持续了一个月。回看整个过程,我认为最值得坚持的三个决策,一是引入适配层来隔离供应商变动,把模型切换成本降到最低;二是网关层坚持只做流量治理和日志审计,不做业务判断,这让网关在多个模块间保持通用;三是渐进式灰度接入,每次只改造一个场景,风险始终可控。
给后来者三个建议:第一,接口契约要尽早冻结,团队内部一旦开始写业务代码,再改接口的成本会指数级上升;第二,审计日志和成本配额表要第一天就做出来,不要等出了事故再补;第三,务必给流式非流式、降级策略和熔断阈值都留出可配置空间,因为模型供应商的稳定性是动态变化的,写死在代码里就是在给未来埋坑。
如果后续还想扩展,可以在适配层继续接入多模态能力,比如语音问答、视频内容分析,这些对培训系统来说都是很自然的能力延伸。到那时,这套网关和适配层依旧只需要加一个新适配器,业务侧的接入体验不会有任何变化。