ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

企业级LLM网关五级演进:从统一转发到平台化治理

企业级LLM网关五级演进:从统一转发到平台化治理 1. 从一次线上事故说起为什么企业需要一个LLM网关去年冬天的一个凌晨我被值班电话叫醒。客服系统里接入的智能问答模块突然开始返回大量无关内容同时账单显示过去两小时内token消耗量暴涨了十七倍。排查下来原因并不复杂某个业务线在调用大模型接口时把重试逻辑写成了无限循环而当时没有任何一层做统一的限流和熔断。更麻烦的是我们根本说不清这些请求分别来自哪个团队、走的哪个模型、花了多少钱——因为每个团队都是自己拿API Key直连的。那次事故之后我们决定认真做一件事构建一个企业级的LLM网关。所谓LLM网关简单说就是在业务应用和大模型服务之间加一层统一入口所有对模型的调用都经过它转发。它能做的事情包括统一计价与配额、按策略路由到不同模型、做内容安全治理、限流熔断、可观测性采集等等。这件事听起来像是个反向代理的活儿但真正做起来才发现它牵扯的东西远比想象中多。这篇文章想聊的就是我们在构建这套网关过程中经历的五个阶段的演进。从最开始一个简单的转发脚本到后来支撑全公司几十个业务线、日均千万级调用的平台中间踩过的坑、做过的取舍、想明白的道理我都会尽量讲清楚。如果你所在的团队也正在考虑给LLM调用做统一收口或者你是个后端工程师被安排来做这件事那这篇内容应该能帮你少走一些弯路。2. 第一级演进从裸调API到统一转发层2.1 最初的痛点API Key满天飞最开始的时候公司里几个团队各自做大模型相关的功能做法都很直接申请一个API Key在代码里写死然后调SDK。这种模式在项目初期没什么问题但很快就暴露出一堆麻烦。第一个问题是成本不可见。财务月底拿到账单只知道总共花了多少钱但不知道哪个团队花的、花在什么功能上。有一次某个团队做了个实验性功能跑了一周才发现消耗了预算的三成但已经来不及了。第二个问题是Key管理混乱。有的团队把Key写在配置文件里提交到了代码仓库有的团队几个人共用一个Key某个人离职了Key还在用。安全部门来审计的时候我们连一份完整的Key清单都拿不出来。第三个问题是模型切换成本高。今天用A家的模型明天想换成B家的或者想从通用模型换成某个垂直领域的模型每个调用方都得改代码、重新测试、重新上线。这个成本高到让很多团队宁愿将就着用不合适的模型。2.2 统一转发层的最小实现我们做的第一件事是搭一个最简单的转发服务。所有业务方不再直接调模型厂商的API而是调我们提供的统一接口。这个接口的签名和主流模型厂商的接口保持兼容这样业务方改造成本最低——基本上只需要把base_url换一下。这个转发层用Python写基于FastAPI核心逻辑就是接收请求、根据配置找到对应的上游、转发、返回结果。代码量不大但有几个细节必须处理好。第一是流式响应的透传。大模型的对话接口大多支持流式输出也就是Server-Sent Events。转发层必须原样透传这些事件不能等全部生成完再返回否则用户体验会差很多。这里要注意的是流式透传时不能对响应体做缓冲要用异步生成器逐块转发。第二是超时和重试。模型接口有时候会慢有时候会报错。转发层需要设置合理的超时时间并且在遇到可重试的错误时自动重试。但重试次数不能太多否则会放大问题——前面提到的那次事故就是重试逻辑失控导致的。第三是请求日志。每一条请求都要记录谁调的、调的哪个模型、输入输出token数、耗时、是否成功。这些日志是后续做计价和治理的基础。# 简化的转发核心逻辑示意 async def forward(request, upstream_config): async with httpx.AsyncClient(timeoutupstream_config.timeout) as client: upstream_resp await client.send(request, streamTrue) async def stream_generator(): async for chunk in upstream_resp.aiter_bytes(): yield chunk return StreamingResponse(stream_generator())这个阶段的目标很朴素先把入口收住让所有调用都经过我们。至于计价、路由、治理都是后面逐步加的。2.3 这个阶段的关键决策有一个决策我们当时讨论了很久转发层要不要做协议转换也就是说如果业务方用的是A厂商的接口格式而上游是B厂商要不要在中间做格式转换我们最后的结论是第一版不做。原因是协议转换会引入大量边界情况而且一旦转换出错排查起来非常困难。第一版只做透传业务方想用什么格式就用什么格式我们只负责转发。等入口收住了、调用量稳定了再考虑做统一协议。这个决策后来被证明是对的。因为当我们开始做路由的时候发现如果一开始就做了协议转换路由逻辑会变得极其复杂。保持透传让每一层职责清晰后面加功能时改动范围可控。3. 第二级演进计价与配额体系的搭建3.1 为什么计价是网关的核心能力入口收住之后第一个要解决的问题就是钱。LLM调用和传统API调用最大的区别在于它的成本是按token量浮动的而且不同模型、不同输入输出方向的价格差异很大。一个请求可能只花几厘钱也可能花几块钱。如果不做精细化的计价成本控制就是一句空话。计价体系要回答几个问题每个请求消耗了多少token这些token按什么单价计算哪个团队、哪个项目、哪个功能应该承担这笔费用预算还剩多少超了怎么办3.2 Token计数的三种方案Token计数是计价的基础。我们试过三种方案各有优劣。第一种是调用厂商的计数接口。很多模型厂商提供了token计数API输入一段文本返回token数。这种方案最准确但每次调用都要多一次网络请求延迟增加而且有些厂商不提供这个接口。第二种是本地用tokenizer计算。主流开源模型都有对应的tokenizer库可以在本地快速计算token数。这种方案快、不依赖网络但问题是不同厂商的tokenizer可能不一样用开源的tokenizer算闭源模型的token数会有偏差。第三种是从响应中读取。很多模型接口在返回结果时会带上usage字段里面包含输入输出token数。这是最省事的方案但只有请求成功时才有失败或超时的请求拿不到。我们最终采用的是混合方案优先从响应中读取usage如果响应没有usage用本地tokenizer估算对于流式响应在流结束时统计。同时在网关侧记录一个估算值和实际值的对比定期校准本地tokenizer的偏差。注意本地tokenizer估算的偏差在长文本场景下可能达到5%到10%如果对计价精度要求很高建议以厂商返回的usage为准本地估算只作为兜底。3.3 计价模型的设计计价模型的核心是一张价格表记录了每个模型每百万token的输入价格和输出价格。这张表需要支持动态更新因为厂商调价是常有的事。但光有价格表还不够还需要一套归属规则。一个请求进来怎么知道它该算到哪个团队头上我们的做法是在请求头里要求带上调用方标识网关根据这个标识去查归属关系。如果没带就归到未知分类并且触发告警。计价的计算逻辑本身不复杂输入token数乘以输入单价加上输出token数乘以输出单价。但有几个细节要注意。一是缓存命中。有些厂商对缓存命中的输入token有折扣价计价时要区分。二是批量调用。有些场景是批量提交任务计价时要按批次聚合。三是币种和汇率。如果厂商用美元计价而内部核算用人民币要处理好汇率转换和记录。3.4 配额与限流的实现有了计价就可以做配额了。配额的本质是给每个调用方设定一个预算上限超过就拒绝或降级。配额的计算有两种粒度按金额和按token数。按金额更直观但需要实时计算费用按token数更简单但不同模型的token价值不一样。我们两种都支持默认按金额。限流的实现用令牌桶算法。每个调用方一个桶桶的容量是它的配额令牌按时间匀速补充。请求进来先扣令牌扣不动就拒绝。令牌桶的好处是能应对突发流量同时保证长期速率可控。这里有个坑分布式环境下的令牌桶一致性。如果网关是多实例部署的每个实例各自维护令牌桶就会出现超发。我们的做法是用Redis做集中式的令牌桶用Lua脚本保证原子性。虽然增加了一点延迟但准确性有保障。-- Redis令牌桶的Lua脚本核心逻辑 local key KEYS[1] local capacity tonumber(ARGV[1]) local rate tonumber(ARGV[2]) local now tonumber(ARGV[3]) local requested tonumber(ARGV[4]) -- 计算当前令牌数并扣减 -- 返回是否允许通过3.5 这个阶段踩过的坑第一个坑是计价延迟。最开始我们是在请求结束后异步计价结果发现配额控制有延迟某个团队已经超预算了还在继续调用。后来改成请求前预扣、请求后结算超发的窗口就小多了。第二个坑是流式请求的token统计。流式响应是一块一块返回的如果中途断开已经产生的token怎么算我们的做法是只要上游开始返回就按已返回的内容计费即使客户端断开了也照算。这个规则要提前和业务方说清楚否则会有争议。第三个坑是价格表的版本管理。厂商调价后历史账单怎么算我们的做法是价格表带生效时间计价时按请求发生时的价格计算这样历史账单不会因为调价而变动。4. 第三级演进路由策略的精细化4.1 路由要解决什么问题统一入口和计价做完之后下一个问题是一个请求来了应该发给哪个模型这个问题看起来简单实际上要考虑的因素很多。成本、延迟、质量、可用性、合规每个因素都可能影响路由决策。而且这些因素之间往往是矛盾的——便宜的模型可能质量差质量好的模型可能贵延迟低的模型可能不稳定。我们的路由策略经历了从简单到复杂的过程。最开始是静态路由配置文件里写死某个调用方走某个模型。后来变成动态路由根据请求内容、调用方等级、当前系统状态动态选择。4.2 路由决策的输入维度一个成熟的路由策略需要考虑以下几类输入。请求特征请求的内容是什么是简单问答还是复杂推理输入长度多少是否需要多轮对话这些特征可以通过分析请求体得到。调用方特征调用方是谁它的等级是什么它的预算是多少它对延迟的敏感度如何这些信息从调用方标识和配置中获取。模型特征每个模型的成本、延迟、质量评分、当前可用性如何这些信息需要持续采集和更新。系统状态当前各模型的负载如何有没有模型在限流或故障整体预算消耗到什么程度了把这些维度组合起来才能做出合理的路由决策。4.3 几种实用的路由策略基于成本的路由优先选择满足质量要求的最便宜模型。适合对成本敏感、对质量要求不极致的场景比如内部工具、批量处理。基于延迟的路由优先选择响应最快的模型。适合实时交互场景比如客服对话、语音助手。基于质量的路由优先选择质量评分最高的模型。适合对准确性要求高的场景比如法律咨询、医疗问答。基于负载的路由根据各模型的当前负载做均衡避免某个模型被打爆。这个策略通常和其他策略组合使用。降级路由当主模型不可用时自动切换到备用模型。降级时要考虑备用模型的能力是否足够必要时可以降低功能或提示用户。我们的实现是把这些策略做成可组合的规则。每条规则包含匹配条件和动作请求进来后按优先级依次匹配命中就执行对应动作。4.4 路由配置的示例routes: - name: vip_customer_realtime match: caller_tier: vip latency_sensitive: true action: primary: model_a_fast fallback: model_b_fast max_latency_ms: 2000 - name: batch_processing match: caller_tier: internal request_type: batch action: primary: model_c_cheap fallback: model_d_cheap cost_limit_per_request: 0.01这个配置的意思是VIP客户的实时请求走快速模型延迟超过2秒就降级内部批量处理走便宜模型单次请求成本不超过1分钱。4.5 路由的灰度与回滚路由策略的变更是有风险的。如果新策略把大量请求导到了一个不合适的模型可能造成质量下降或成本飙升。所以路由变更必须支持灰度。我们的做法是新策略先对1%的流量生效观察一段时间的关键指标成功率、延迟、成本、质量评分没问题再逐步扩大比例。如果指标异常一键回滚到旧策略。灰度期间要特别注意指标的可比性。如果灰度流量和全量流量的请求特征差异很大指标对比就没有意义。所以灰度要按调用方或请求类型做分层保证对照组和实验组的请求分布一致。4.6 这个阶段的经验路由策略不是越复杂越好。我们一开始设计了一套非常复杂的规则引擎结果发现维护成本极高而且经常出现规则冲突。后来简化成少量高优先级规则默认策略的模式反而更稳定。另一个经验是路由决策要可解释。每个请求为什么走了这个模型要能查得到。我们在响应头里加了一个字段记录命中的路由规则名排查问题时非常有用。5. 第四级演进治理能力的体系化5.1 治理包含哪些内容到了这个阶段网关已经不只是个转发和计价工具了它开始承担治理职责。治理的内容很广主要包括内容安全、访问控制、审计合规、可观测性、故障隔离。内容安全是LLM网关特有的治理需求。大模型的输出是不可控的可能包含不当内容。网关需要在请求和响应两个方向做检查。请求方向检查用户输入是否包含敏感信息响应方向检查模型输出是否符合规范。访问控制是传统的API网关能力但在LLM场景下有新的要求。比如某些模型只能被特定团队使用某些功能只能在特定时间段调用某些请求需要审批才能放行。审计合规要求所有调用都有记录且记录不可篡改。这在金融、医疗等强监管行业尤其重要。可观测性包括指标、日志、链路追踪。LLM调用的链路比传统API长涉及网关、模型厂商、可能还有缓存和向量库没有完善的追踪很难排查问题。故障隔离是指当某个模型或某个调用方出问题时不能影响其他部分。这需要做好资源隔离和熔断降级。5.2 内容安全的实现内容安全我们做了两层规则层和模型层。规则层用关键词和正则表达式做快速过滤能拦住大部分明显的问题。规则库需要持续更新我们接入了内部的内容安全服务定期同步规则。模型层用一个小的分类模型做语义判断能识别规则层漏掉的隐晦内容。这个分类模型部署在网关侧延迟控制在几十毫秒内。请求方向的安全检查在转发前做不通过就直接拒绝。响应方向的安全检查在转发中做流式响应要逐块检查发现违规立即中断并返回提示。注意内容安全检查会增加延迟对延迟敏感的场景要权衡。我们的做法是分级检查高等级调用方走完整检查低等级调用方只走规则层。5.3 可观测性的落地可观测性我们用了三个支柱指标、日志、追踪。指标用Prometheus采集包括请求量、成功率、延迟分布、token消耗、成本等。每个指标都带标签可以按调用方、模型、路由规则等维度聚合。日志用结构化格式每条请求一条记录包含请求ID、调用方、模型、token数、耗时、状态等。日志写入后端的日志系统支持全文检索。追踪用OpenTelemetry每个请求生成一个tracespan包括网关处理、上游调用、安全检查等环节。这样排查问题时能看到完整的调用链路。这里有个细节流式请求的追踪。流式请求的耗时不好定义是首字节时间还是全部完成时间我们的做法是两个都记录首字节时间反映用户体验全部完成时间反映资源占用。5.4 故障隔离与熔断熔断是故障隔离的核心。当某个上游模型的错误率超过阈值网关自动熔断后续请求直接走降级策略不再尝试调用故障模型。过一段时间后再放少量请求试探如果恢复正常就关闭熔断。熔断的阈值设置很关键。太敏感会误熔断太迟钝会放大故障。我们的经验是错误率阈值设在20%到30%熔断持续时间从30秒开始逐步延长。除了熔断还要做舱壁隔离。不同调用方的请求用不同的连接池和线程池避免一个调用方的异常请求耗尽资源影响其他人。5.5 治理策略的配置化治理策略如果写死在代码里每次调整都要发版效率太低。我们把治理策略做成了配置化通过管理后台可以动态调整。配置化的挑战是一致性和安全性。配置变更要能快速生效但不能出现部分实例生效、部分没生效的情况。我们的做法是用配置中心推送每个实例监听变更收到后先校验再应用应用失败自动回滚。安全性方面配置变更要有审批流程和操作日志。谁能改什么配置改了之后影响什么都要有记录。6. 第五级演进平台化与生态建设6.1 从工具到平台前四个阶段做完网关已经是个功能完善的系统了。但它还是个工具——业务方要用得找我们开通、配置、对接。随着接入方越来越多这种模式的效率瓶颈就出来了。第五阶段的目标是把它变成平台业务方自助接入、自助配置、自助查看数据我们只负责平台的稳定运行和核心能力建设。6.2 自助接入的实现自助接入的核心是租户体系。每个业务方是一个租户租户有自己的API Key、配额、路由配置、治理策略。租户管理员可以在后台自己管理这些配置不需要找我们。租户体系要处理好几个问题。一是隔离租户之间的数据、配置、配额要严格隔离不能互相影响。二是继承租户可以有自己的子租户子租户继承父租户的部分配置。三是审计租户的所有操作都要有记录便于追溯。6.3 数据看板与分析平台化之后业务方需要自己看数据。我们提供了几个看板成本看板、用量看板、质量看板、异常看板。成本看板展示各租户、各模型、各时间段的成本分布和趋势。用量看板展示请求量、token量、调用方分布。质量看板展示成功率、延迟、内容安全拦截率。异常看板展示错误分布、熔断记录、限流记录。看板的数据来自网关的指标和日志通过ETL管道汇总到分析数据库再用可视化工具展示。这里要注意数据延迟和数据准确性的平衡。实时看板延迟低但可能有误差离线报表准确但有延迟我们两种都提供。6.4 生态能力的扩展平台化之后网关可以承载更多生态能力。模型市场把可用的模型做成一个市场业务方可以浏览、试用、申请。每个模型有详细的说明、价格、性能数据。Prompt管理业务方的Prompt可以托管在平台上支持版本管理、A/B测试、效果对比。这样Prompt的迭代就不用改代码了。评测服务平台提供模型评测能力业务方可以提交测试集对比不同模型的效果辅助路由决策。成本优化建议平台分析业务方的调用模式给出成本优化建议比如这个场景可以换用更便宜的模型、这个Prompt可以精简以降低token消耗。6.5 平台化的组织挑战技术上的平台化相对容易组织上的平台化更难。网关团队从服务提供方变成平台运营方工作方式要变。一是要建立SLA体系。平台对业务方承诺什么级别的可用性、延迟、支持响应要明确。二是要建立反馈机制。业务方的需求和问题要有渠道反馈平台要定期迭代。三是要建立成本分摊机制。平台的运营成本怎么分摊到各业务方要有规则。这些组织问题没有标准答案每个公司的情况不同。我们的经验是先把技术平台做好用数据说话再逐步推动组织层面的调整。7. 常见问题与排查技巧实录7.1 转发层常见问题问题一流式响应中断。表现是客户端收到一半就断了。排查思路先看网关日志确认是上游断开还是网关主动断开再看网络层确认有没有超时或连接重置最后看客户端确认是不是客户端自己断开的。常见原因是网关的超时设置比上游短或者网关的缓冲区满了。问题二请求体过大被拒。大模型的输入可能很长如果网关有请求体大小限制会直接拒绝。解决方法是调大限制或者对超长请求做特殊处理。要注意的是调大限制会增加内存占用要评估网关的资源。问题三并发高了之后延迟飙升。这通常是连接池不够或线程池不够。排查时看网关的连接数、线程数、队列长度。解决方法是调大池子或者做限流保护。7.2 计价层常见问题问题一token数对不上。业务方自己算的token数和网关算的不一致。排查时先确认用的tokenizer是否一致再看是否包含了系统提示词等额外内容。如果还是对不上可能是厂商的usage有延迟或误差。问题二配额扣减不准。表现是业务方明明没超预算却被限流或者超了预算还能继续调。排查时看令牌桶的实现确认Redis的原子性有没有问题确认预扣和结算的逻辑有没有漏洞。问题三价格表更新后历史账单变了。这是价格表版本管理的问题。要确保价格表带生效时间计价时按请求发生时的价格算。7.3 路由层常见问题问题一路由规则冲突。多条规则同时匹配一个请求不知道走哪条。解决方法是明确优先级并且做规则冲突检测配置时就发现冲突。问题二降级后质量下降。主模型故障切到备用模型但备用模型能力不够。解决方法是在降级时评估影响必要时降低功能或提示用户而不是静默降级。问题三灰度期间指标异常。灰度流量和全量流量的指标不可比。解决方法是做分层灰度保证对照组和实验组的请求分布一致。7.4 治理层常见问题问题一内容安全检查误杀。正常的请求被判定为违规。解决方法是优化规则和模型降低误杀率同时提供申诉渠道人工复核。问题二熔断误触发。短暂的网络抖动导致熔断。解决方法是设置合理的阈值和持续时间并且做熔断前的确认。问题三可观测性数据缺失。某些请求没有日志或追踪。排查时看采集链路确认是网关没产生还是采集没上报。常见原因是异步上报失败或采样率设置过低。7.5 排查技巧速查表现象可能原因排查方向流式响应中断超时设置不当、缓冲区满检查网关超时配置、缓冲区大小token数对不上tokenizer不一致、usage延迟对比tokenizer、检查usage来源配额扣减不准令牌桶非原子、预扣逻辑漏洞检查Redis脚本、预扣结算流程路由规则冲突优先级不明确、规则重叠检查规则优先级、做冲突检测内容安全误杀规则过严、模型偏差优化规则、人工复核熔断误触发阈值过低、抖动调整阈值、增加确认机制可观测性缺失采集失败、采样率低检查采集链路、调整采样率8. 我在实际构建中的几点体会做这套网关前后花了差不多一年时间从最初的一个转发脚本到现在支撑全公司的平台中间有很多取舍和反思。有几个体会比较深分享出来供参考。第一不要一开始就追求大而全。我们最开始也想过一步到位做个完美的平台但后来发现根本做不到——因为很多需求是随着使用才暴露出来的。五级演进不是刻意设计的而是被问题推着走的。每一级解决当前最痛的问题稳定了再考虑下一级。第二计价和路由是网关的核心价值。转发谁都能做但把计价做准、把路由做细才是真正体现网关价值的地方。这两块也是最容易出问题的需要投入足够的精力。第三治理能力要渐进式建设。内容安全、审计、可观测性这些能力一开始可以做得简单但不能不做。等到出事了再补成本会高很多。第四平台化是终点但不是目的。平台化的目的是让业务方更高效地使用大模型而不是为了平台而平台。如果业务方数量不多可能不需要那么复杂的平台能力保持简单反而更好。最后分享一个小技巧网关的配置变更一定要有一键回滚。我们踩过几次坑配置改错了导致大量请求失败如果没有回滚机制恢复时间会很长。现在我们的所有配置变更都支持一键回滚而且回滚操作本身也要有审计记录。这套系统还在持续演进后面可能会考虑做多模态的支持、做更智能的路由、做更细粒度的成本优化。但不管怎么演进核心思路是不变的把复杂留给自己把简单留给业务方。
返回列表