我以为接几个模型API就是多模型策略了,直到客服系统在上线当晚把预算烧穿

30秒速览

  • 多模型路由不是写几个if-else就能搞定的,生产环境一个晚上就能烧掉几万预算;Azure AI Studio的模型目录和推理端点天然支持多模型统一管控,配上APIM做意图路由,推理成本能实打实压下来30%以上;内容安全护栏要嵌入管线做输入输出双向屏蔽,PII检测比仇恨言论拦截更容易被忽略,但出了事就是大雷。

我以为接几个模型API就是多模型策略了,直到客服系统在上线当晚把预算烧穿

事情要从去年Q4说起。我们团队当时接到需求,要把公司电商平台的智能客服从原来那套基于规则和BERT小模型的方案,升级成能调用大语言模型的版本,同时还要接入多个模型厂商——Azure OpenAI GPT-4、Meta Llama 3、Mistral Large,甚至还有一个内部微调的Phi-3。理由很正当:不同意图用不同成本的模型,既能保证回复质量,又能控制推理开销。我当时想得很简单,写一个Python模块,根据用户意图做if-else判断,分发到对应的模型endpoint就行。测试环境跑了一周,看起来挺稳的。

上线那天晚上十一点,我正坐在屏幕前啃着夜宵,监控告警突然像放鞭炮一样响起来。一看账单面板,Azure OpenAI的API调用量在半小时内冲到了日预算的80%,但更恐怖的还不是这个。我们为“退货咨询”这个高频意图配置的Llama 3端点因为我们在同一个区域开了太多并发,触发了云服务商的速率限制,导致大量请求超时。而那个最便宜的Phi-3小模型因为部署在单台GPU虚拟机上,直接被流量打挂了。用户那边看到的不是“您的退货已受理”,而是清一色的“服务器繁忙,请稍后再试”。那天晚上我紧急把流量切回了GPT-4,成本直接翻了四倍。事后复盘,问题其实不是出在模型本身,而是我们完全没有一个真正意义上的“AI网关”概念——没有统一入口、没有动态路由、没有流量整形、没有安全过滤,就像给每个模型单独开了个水龙头,但总阀和水表全没装。

这次事故让我开始仔细琢磨,企业要想在生产环境平稳地跑多个大模型,需要的不是几个API调用脚本,而是一个能扛住流量冲击、同时把安全合规和成本控制融进骨头里的基础设施。后来我们在Azure AI Studio上重新搭了一遍架构,把整个多模型服务改造成了一个带智能路由和安全护栏的AI网关,总算把局面稳住了。下面我就把这次踩坑和重建的过程摊开来聊聊,特别是那几个差点让我通宵也搞不定的设计决策。

Azure AI Studio那个模型目录,打开之后我才发现里面藏着一个路由引擎

最开始我们只是把Azure AI Studio当作一个模型实验场,用它来对比不同模型的响应质量。但后来我发现,它底层那个模型目录(Model Catalog)和推理端点管理,本质上已经给多模型网关搭好了骨架。模型目录里不仅能直接部署Azure OpenAI的一系列模型,还能通过Serverless API形式接入Llama、Mistral、Cohere、Phi-3等近四十种模型,最妙的是所有这些端点都支持完全一致的Azure身份验证和网络隔离机制。这解决了之前我们那种“每个模型一个key,每个key一种调用方式”的混乱状态。

真正让我重新思考架构的,是看到AI Studio里可以把多个模型部署到一个共享的“AI项目”下,然后通过同一个推理端点做多模型路由。这个路由不是简单的负载均衡,而是你可以定义流量拆分策略。我当时第一反应是:这不就等于内置了一个轻量级API网关吗?于是我们立刻把之前的Python分发脚本扔了,改成了基于Azure AI Studio路由能力的方案。具体做法是,在AI Studio的项目里我们部署了三组模型端点:GPT-4(负责复杂对话、安抚投诉)、Llama 3 70B(处理退换货流程引导和常见问题)、Phi-3(做意图识别初筛和简单寒暄),然后用一个Azure API Management(APIM)实例作为统一入口,把请求先打到APIM,再转发到AI Studio的推理端点。你可能要问,为什么还要加一层APIM?因为AI Studio本身的流量拆分是静态权重,没办法根据请求内容做实时判断,而我们在APIM这一层写了个小型的意图路由策略,读取请求body里的用户消息,调一个我们之前训练好的轻量级意图分类器(部署在Azure Functions上),根据意图类别动态改写转发路径,指向不同模型分组。

代码大概长这样(Python,Azure Functions里的路由逻辑片段):

import azure.functions as func
import json
import requests

INTENT_TO_MODEL = {
    "退货": "llama3-group",
    "投诉": "gpt4-group",
    "咨询": "phi3-group",
    "闲聊": "phi3-group"
}

def main(req: func.HttpRequest) -> func.HttpResponse:
    body = req.get_json()
    user_msg = body['messages'][-1]['content']
    intent = classify_intent(user_msg)  # 本地轻量模型推理
    target_group = INTENT_TO_MODEL.get(intent, "phi3-group")
    # 调用Azure AI Studio推理端点,通过自定义header传递目标部署组
    resp = requests.post(
        "https://ai-studio-endpoint.cognitiveservices.azure.com/openai/deployments/{}/chat/completions?api-version=2024-02-15-preview".format(target_group),
        headers={"api-key": req.headers['x-api-key']},
        json=body
    )
    return func.HttpResponse(resp.text, status_code=resp.status_code)

你可能会发现,上面那个endpoint URL里直接用了部署组的名字当作路径参数,这是Azure AI Studio推理端点支持的一种用法:如果项目里部署了多个模型,同一个endpoint可以通过指定不同的deployment名称来路由至对应的模型。我们正是利用这个特性,把意图路由的决策点在APIM层做完,然后把流量精准分发到对应的deployment上。这样做的好处是,模型本身的endpoint是统一的,安全策略、网络隔离、认证只需要配一次,再也不需要维护一堆API key和地址。

除了意图路由,我们还利用AI Studio的流量拆分功能做了一些AB测试和灰度发布。比如模型版本升级时,我们先把10%的流量切到新版Phi-3部署,观察一整天延迟和错误率,确认没问题再全量切。这个过程直接在AI Studio的UI或CLI里操作,几秒钟就能生效,比之前我们手动改DNS或Nginx配置快了不知道多少。说实话,这个平台最打动我的不是模型数量多,而是它把模型管理做成了一个统一控制面,让一个团队里既懂AI又懂DevOps的工程师终于能把精力放到业务策略上,而不是天天给模型端点救火。

拦截仇恨言论还算简单,真正让我头疼的是敏感数字在回答中悄悄泄露

多模型路由跑稳之后,安全合规的问题就浮上来了。我们做的是电商客服,每天要处理大量带用户手机号、订单号、家庭住址的对话。最开始我们只在APIM层加了一个简单的正则过滤,把手机号和身份证号打码,但很快就发现这远远不够。有一次Llama 3在生成退货运单说明时,居然把用户之前的完整收货地址从对话历史里复述出来了,而那段历史里我们只是暂时做了前端掩码,后端日志里全是明文。安全部门找上门的时候,我问自己一个问题:一个企业级AI网关,难道只能靠开发者自己写正则来挡子弹吗?

Azure AI Studio里的内容安全(Content Safety)功能正好补上了这块短板。它不是一个简单的关键词黑名单,而是内置了微软多年在Xbox、Bing等服务上积累的多分类检测模型,能实时识别仇恨言论、暴力、色情、自残内容,而且支持自定义阻断阈值。我们启用它的时候,发现它还可以检测受保护的个人识别信息(PII)。对,就是那个让我头疼的地址和手机号。更厉害的是,这些安全过滤直接嵌入了模型推理管道里,你不需要额外部署一个微服务来做检测,只需要在创建或更新模型部署时配置安全策略。

我们是这样配置的:对于所有面向客户的模型部署(包括GPT-4和Llama 3),都启用了两层安全护栏。第一层是输入屏蔽,检查用户发来的消息是否包含仇恨言论或越狱尝试(很多用户喜欢对客服AI说“忽略之前指令”),如果命中高风险类别就直接返回预设的安全回复,模型压根不会收到这条消息。第二层是输出屏蔽,模型生成完回复后,内容安全服务会异步扫描整个响应,一旦检测到PII或者恶意代码注入,就自动替换成“***”或者触发告警。配置输入屏蔽的时候有个细节,我们把色情和暴力类的阈值设得比较低(0.2-0.3),因为电商场景偶尔会有用户描述商品使用问题,过于严格反而误拦;但对于仇恨言论和自残,阈值直接拉到0.01,宁杀错不放过。这些配置可以在Azure AI Studio的Content Filters界面里用可视化方式调,也可以通过CLI批量下发。

下面这段是创建部署时附加内容安全过滤的Azure CLI命令,实际用起来很方便,完全嵌入到了CI/CD流水线里:

az ml online-deployment create 
  --name phi3-deployment 
  --endpoint-name my-ai-gateway 
  --model azureml://registries/azureml/models/Phi-3-medium-128k-instruct 
  --instance_type Standard_NC6s_v3 
  --content-safety-name my-content-filter-policy 
  --set content_safety_config.enable_input_screening=true 
        content_safety_config.enable_output_screening=true 
        content_safety_config.sensitive_categories="Hate,Violence,Self-Harm,Sexual" 
        content_safety_config.pii_detection=True

说实话,PII检测在实测中准确率并不是100%,某些格式特殊的订单号还是会被漏掉。所以我们又加了一道防线:在APIM的出站策略里,用了一个极快的正则引擎做二次筛查,但这次只针对“高风险实体”,比如银行账号模版和增值税发票编号。两层结合下来,泄露事件从之前每月平均3-4次降到了零。这个安全护栏的另一个价值是合规审计。因为内容安全模块的每一次触发都会生成结构化日志,包含命中类别、严重分数、原始消息哈希等字段,我们可以把这些日志导入Azure Sentinel统一做安全态势分析。安全团队再也不用追着我问“上周那个投诉对话里有没有出现用户银行卡号”,他们自己看仪表板就行。

但这一切的前提是,你得接受安全护栏会吃掉一部分推理延迟。输入和输出双重检测给每个请求增加了大约50-80毫秒的额外耗时,在客服这种对延迟不特别敏感的渠道勉强能接受。如果你的业务是实时语音对话,这个代价可能就要重新评估了。好在AI Studio支持按部署粒度选择性启用安全特性,所以我们可以把高延迟对话和高风险对话分流到不同的护栏强度上,这种精细度才让我觉得这确实是生产级的东西,不是实验室玩具。

用意图路由把成本压掉30%后,老板让我把省下的钱拿去买更多并发

安全这关过了,接下来就是怎么在预算框架下让系统跑得更久。我们当初烧钱的最大头是直接把所有客服对话丢给GPT-4,而其中70%以上的用户请求其实只是一句“我的订单到哪了”或者“帮我改个收货地址”。这些请求用Phi-3或者Llama 3 8B完全能处理,而且成本只有GPT-4的十几分之一。于是我们在前面说的意图路由基础上,正式设计了一套智能分发的三层模型。

第一层是轻量意图过滤器,部署在Azure Functions上,用的是我们自己蒸馏出来的一个12M参数小模型,推理一次大概3毫秒,几乎不花钱。它能分出四大类:简单查询(运单、库存)、业务流程(退货、改地址)、情绪安抚(投诉、差评)、以及模糊闲聊。第二层是模型策略引擎,根据意图、当前各模型池的并发占用率、以及我们预设的各模型单价上限,动态选择最优部署。比如,当Llama 3部署的并发已经达到预设的80%水位,即使新请求是“退货”意图,策略引擎也会临时降级到Phi-3,避免触发速率限制导致请求堆积。第三层就是实际的模型推理,由Azure AI Studio的端点承载。

这套策略听起来复杂,但我们把核心逻辑写成了一个不到两百行的Python函数,部署在APIM的inbound policy里。APIM本身就支持用set-backend-service根据条件动态切换后端URL,但这里我们没用它的原生动态路由,因为我们需要更精细的负载数据。所以我们让APIM在转发前先向一个内部状态服务(一个小型Redis缓存)查询当前各模型池的健康度和并发数,然后决定发给哪个部署。Redis里的数据则由各模型部署的监控探针每2秒更新一次,探针读取Azure Monitor里实时的“active_requests”指标。代码片段如下:

import redis
import json

r = redis.Redis(host='redis-cache', port=6379, decode_responses=True)

def get_best_model(intent):
    # 获取各模型池实时状态
    status_json = r.get('model_pool_status')
    status = json.loads(status_json) if status_json else {}
    # 预设优先级,意图 -> 主选,备选
    priority = {"退货": ["llama3", "phi3"], "投诉": ["gpt4", "llama3"], "查询": ["phi3", "llama3"]}
    candidates = priority.get(intent, ["phi3"])
    for model in candidates:
        pool = status.get(model, {})
        if pool.get('active_requests', 0) < pool.get('max_concurrency', 10) * 0.8:
            return model
    return "phi3"  # 最后的兜底

这套路由策略运行一个月后,我们的综合推理成本下降了36%,而且因为流控机制让请求排队时间大幅减少,P99延迟甚至还改善了18%。老板看到成本报告后第一句话是:“你省下的这些GPU预算,能不能给运营团队多开一些并发额度,让他们在促销季不用排队?”这就是生产环境里一个好消息后面总跟着一个更难的需求。我们后来给这套网关加了基于用户标签的优先级队列,VIP客户的请求可以跳过常规流控,直接进入专属的高配模型池,当然这部分账单单独核算。这里又体现出Azure AI Studio的好处:你可以在同一个endpoint下创建多个deployment,然后通过流量拆分把VIP请求精准导到预留实例上,普通用户用共享的按量付费实例,完全不用改任何代码。

监控看板这一块,我们用了Azure Monitor和Grafana的组合,把成本、延迟、安全命中数、模型健康度全部拉通。最直观的是成本热力图,横轴是24小时时间,纵轴是不同模型分组,颜色深浅代表每分钟花费的金额。这个图一出来,业务方才知道原来凌晨三点还有那么多客服机器人在疯狂调用GPT-4回答“你好”,于是我们顺势加了一条规则:晚11点到早7点之间,所有对话强制走低成本模型,除非检测到投诉情绪。就这么一个小小的规则,每月又多省了几千块。

至于和企业内部权限审计系统的集成,我们在APIM上启用了OAuth2和Azure AD的集成,不同部门的开发者在创建模型部署或修改路由策略时,必须通过JWT令牌验证身份和RBAC角色。所有路由变更和安全策略修改会通过Azure Policy自动记录到审计日志,完全满足SOC2和ISO27001的审查要求。这让我们在最近一次合规评审中轻松过关,审计师甚至打趣说这是他们见过最干净的AI网关日志。

最后我想掏心窝子说几句,所谓生产就绪的AI网关,绝对不是买几个模型API就能搭起来的。你得把这几个能力当成硬指标:第一,模型接入必须是统一管控面,所有端点、密钥、网络策略一把抓;第二,安全护栏不能是外挂,得嵌在推理管线里,而且要有自定义阈值和PII检测;第三,路由策略必须依赖实时负载数据和意图理解,不是静态分流;第四,全链路监控必须覆盖成本和延迟,看得见才能省得下来;第五,权限和审计要原生支持企业目录服务。如果你正在用Azure AI Studio做多模型部署,我建议你把上面的检查项一条条过一遍,至少能避免我当初那种半夜烧钱的心跳体验。到现在我偶尔还会翻看那天晚上的告警截图,不是怀旧,是提醒自己:做架构决策的时候,别把省事的幻想当成工程现实。

发表评论