端云协同LLM网关:统一入口动态路由本地与云端模型调度

发布时间:2026/11/2 8:53:51
端云协同LLM网关:统一入口动态路由本地与云端模型调度 说实话这个项目是在一阵烦躁里诞生的。在过去几个月我同时维护着好几个AI应用本地显卡上跑着开源模型云上又开了几家大模型API于是每天都在跟“用哪个模型更好”作斗争。最后我干脆做了一个端云协同LLM网关把所有接入收敛成一个统一入口把请求按隐私敏感度、上下文长度、成本和延迟要求动态路由到本地或云端模型。项目已经开源代码整理得能跑了现在最缺的不是新功能而是35位真实业务场景里的开发者帮我把它扔进生产环境里蹂躏一遍。这篇文章我会把这个网关为什么这么做、路由逻辑怎么设计、代码里有哪些关键取舍、以及我为什么要小范围招募测试者一次讲清楚。适合正在做LLM应用、被多模型接入搞到头疼、或者对“本地模型云端模型混合调度”感兴趣的开发者参考。1. 为什么我突然想做一个LLM网关被模型割裂逼出来的开源项目1.1 我的真实痛点API key散落、账单混乱、调用代码各写各的最早其实没有这么复杂。刚开始做AI应用时模型就一家SDK调一调完事。后来本地上了显卡装了Ollama和llama.cpp云端也开始同时用好几个不同厂商的大模型问题就全部涌出来了。首先是接入方式不统一。每个服务连不同的SDK有的用官方Python包有的走HTTP接口有的要封装流式响应代码风格千奇百怪。我要是想从A模型换到B模型得逐个项目改依赖、改环境变量、改超时和重试逻辑改完还可能因为某个参数语义不同而翻车。其次是成本失控。月底看到云厂商账单只知道总额完全分不清是哪个业务线、哪个模型、哪类请求烧的钱。想优化都不知道从哪里下手。最后也是最核心的痛点隐私和合规问题。有些业务数据根本不敢上传到云端模型但本地小模型的能力又不够导致同一个功能得写两套逻辑——一查内网资料走本地一查公开信息走云端维护成本直接翻倍。我把这些问题憋了一段时间后决定不再继续打补丁而是做一个入口所有业务只跟网关打交道网关去决定请求该去哪。1.2 端云协同的本质本地和云端不是竞品是分工很多人一聊到本地模型和云端模型脑子里就是“谁更强”的对比。我自己的结论是关键不是哪个更强而是哪个更合适。本地模型跑在自己的硬件上最大的两个优势是私密和离线而且不管调用多少次边际成本几乎可以忽略。但它受限于显存和算力7B、13B级别的模型能跑70B甚至更大参数的模型基本没戏推理速度也会随并发上升而急剧恶化。云端模型恰恰相反。模型版本新、参数大、单次推理质量高而且部署和运维都省心。但代价是数据要出网长上下文的调用费用肉眼可见一旦业务量上来账单数字是真能让人清醒的。端云协同这件事的本质就是不要替用户做“二选一”的决定。把请求拆细敏感的走本地复杂的走云端高频且简单的留在本地低频且困难的放上云。网关的价值就是把这个决策自动化业务方只关心“我要什么结果”不需要关心“这个结果由谁算出来”。1.3 LLM网关和传统网关有什么区别“网关”这个词在很多领域都有。做过后台的用过API网关搞过物联网的接过小米网关做通信的老哥们可能还弄过短信网关、反垃圾邮件网关流程引擎里也有BPMN网关。这些网关的共性是把多个上游聚合成一个入口然后做鉴权、路由、限流和协议转换。LLM网关骨子里也是网关但它多了一个传统网关没有的难点——路由决策不能只看请求的URL、IP或者Header还取决于请求里的自然语言内容、上下文长度、token成本、数据敏感等级甚至当前模型的实时负载。同一个用户发“帮我写一封邮件”发到本地小模型和云端大模型结果差异巨大而网关必须能识别出这种差异并做出合理判断。这也是这个项目和普通API网关本质不同的地方。它不是转发数据包而是在调度“推理能力”。2. 端云协同的调度逻辑让每个请求自己找到对的模型2.1 路由三步判断请求身份、查询意图、能力代价我在网关里实现了一套路由决策框架思路可以用一个比喻理解——每个请求进来我都会问它三个问题。这三个问题我习惯比喻成Key、Query、Value。Key我是谁。也就是请求的来源和身份。来自哪个业务线、打没打私有标签、数据敏感等级是高是低。这个信息可以由调用方在请求Metadata里显式声明也可以由网关根据来源IP、接口路径来自动推断。Query我要什么。也就是用户的真实意图。是在翻译、写代码、做摘要还是在闲聊。这部分需要网关对自然语言内容做一次轻量级理解我目前用一个很轻的embedding模型加上规则兜底把意图分成十几个大类不需要调大模型就能完成。Value我能付出什么代价。上下文有多长、需要多强的推理能力、能让用户等多久、愿意为这次调用花多少钱。上下文长度会在路由前做预检如果请求总token数超过本地模型的窗口就强制上云这一步必须前置否则几万token的请求落到本地模型上直接撑爆显存。三个维度叠加到一起一条路由规则才会真正生效。比如“私有知识库问答”这个场景在配置里就是Key是internalQuery是summary_or_qaValue是“上下文可能超过8k数据不能出内网”最终结果就是永远路由到本地模型。2.2 按成本、速度、质量分流策略配置与对比为了让策略可写我按大多数业务的实际需求归纳了三种典型模式成本优先、速度优先、质量优先。下面这张表可以比较直观地看出它们的区别。路由策略适用场景典型路由倾向示例成本优先日志分析、数据抽取、非核心链路本地模型 或 云端最便宜的档位大批量短文本分类速度优先实时对话、客服机器人、交互式工具本地低延迟模型 或 云端就近节点聊天、代码补全质量优先复杂推理、长文总结、代码生成云端大参数模型合同审查、架构设计策略写在一个YAML文件里由网关启动时加载。示例配置如下routes: - name: privacy-first match: key_tags: [internal, private] target: local:qwen2.5:7b - name: quality-default match: intent_type: [code, reasoning] target: cloud:claude-sonnet - name: cost-efficient match: intent_type: [classify, extract] target: cloud:gpt-4o-mini - name: fallback target: cloud:gpt-4o配置本身就是路由规则的可视化往上加规则比改代码直观得多。我在使用中还发现一件事不要完全迷信各种公开模型榜单来配置“质量优先”榜单分数只能代表基准测试集真实业务效果还是得看自己的场景。这也是为什么我一定要找真实开发者测试——公开榜单和实际业务之间隔着一整个数据集的距离。2.3 三个典型的端云分流场景我实际开发中遇到最多的分流场景有三个。第一个是隐私过滤。用户上传的文档可能包含个人信息或内部敏感内容不能直接丢给云端模型。网关的做法是先走一个本地规则引擎做敏感内容扫描如果命中敏感标签整个请求就钉死在本地模型如果文档脱敏后确实需要云端大模型做深度理解再从脱敏后的内容走云端。这等于在云和本地之间加了一道“隐私闸门”。第二个是上下文预检。本地模型的上下文窗口有限显存也有限。一个长文档先丢给embedding模型做切块每块控制在本地窗口范围内但如果用户明确要求“总结全文”切块后的分段摘要又不够这时候就先把全文走云端。上下文长度是最硬的路由条件不能靠猜必须提前算token数。第三个是高频低成本调度。有些业务调用量巨大但每个请求都很简单比如批量商品标题分类这种请求走云端成本非常高而一个7B模型其实完全能胜任。网关会统计每个意图类型的历史token消耗和延迟如果发现某类请求的月成本超过阈值就自动把它默认切换成本地模型。这是端云协同最实在的价值——流量结构一变路由策略跟着变账单也会立刻变得温柔。3. 网关内部怎么工作连接、流式转发与兜底机制的取舍3.1 统一接入层为什么对外暴露OpenAI兼容接口网关对外提供的是OpenAI兼容接口也就是/v1/chat/completions和/v1/embeddings。选这个协议没有悬念现在的LLM生态里OpenAI兼容接口是事实标准LangChain、LlamaIndex、各种开源客户端默认都支持。只要网关兼容它业务方几乎不需要改代码——把base_url指到网关上就完事。但兼容“看起来简单”和真的兼容是两回事。OpenAI SDK在流式场景下解析的是SSE格式每一行都是data: {...}。网关做流式转发时要把上游云端模型的SSE流原样转发给客户端中间不能丢失数据块也不能把不完整的数据行发出去。这里有个特别常见的坑网络层会把一个SSE事件拆成多个TCP包如果网关收到一半就把数据往下游扔客户端就会解析失败。所以我在转发逻辑里加了一个缓冲层攒到一个完整的data:行再往下游吐。这块代码看起来不起眼但决定了流式输出能不能稳定跑完一个长对话。async def forward_stream(payload: dict): async for raw_chunk in upstream_stream(payload): buffer.write(raw_chunk) while buffer.has_line(): line buffer.pop_line() if line.startswith(data:): yield f{line}\n\n3.2 云端模型归一化与本地模型接入各家云端模型的接口差异比我想象中更磨人。鉴权方式不一样模型命名不一样限流后的错误码不一样超时语义也不一样。更麻烦的是有的厂商流式输出时会把用量信息放在最后的chunk里有的则完全不提供。所以我抽了一层ModelProvider抽象把所有厂商差异都关在里面。对业务和路由器来说不管背后是哪个模型接口都是统一的class ModelProvider(Protocol): async def chat(self, messages, config) - ChatResponse: ... async def stream(self, messages, config) - AsyncIterator[Chunk]: ... def count_tokens(self, text) - int: ...本地模型的接入相对省心。Ollama和llama.cpp的server都暴露了OpenAI兼容端点本质上和云端模型走同一套协议。区别在于本地模型需要管理一个“运行状态”哪些模型已经加载到显存、当前排队请求数是多少、是否因为并发过高而触发保护。我在这里做了一个很简单的机制本地模型的同一个实例可以带一个最大并发数超过之后新请求直接排队而不是继续塞进去。不这么限制的话并发一高Ollama会疯狂排队延迟线性上升最后把显存也拖垮。3.3 稳定性三件套语义缓存、限流与回退链一个网关如果只是转发请求那它和普通的端口转发没有区别。真正让它能上生产环境的是三个稳定性机制。第一个是语义缓存。很多业务里的重复请求其实是同义改写比如“这个商品怎么退货”和“退货流程是什么”。如果用传统缓存根本命中不了。网关会对query做embedding计算向量相似度超过0.95就直接返回历史结果。这个机制在高频问答场景里能省下大量token实测命中率能做到30%以上。第二个是限流。云厂商限制最多的不是QPS而是每分钟token数。只限QPS的网关很容易“每分钟总共才发了200个请求结果把100万token的全部分配打光了”。所以我的限流策略是QPS和token速率双控先根据请求的预估token数做减法超过预算直接返回限流错误。第三个是回退链。每个路由目标都可以配置一个后备列表主模型挂了自动切到备选模型。我目前规定了同一个请求最多回退三次防死循环每次向上游发起真实请求前还要做一次幂等判断防止同一个问题在重试中被重复计费。回退不止要处理“连接失败”还要处理“上游返回了限流错误码”“上游超时半死不活”这类灰色状态。3.4 可观测性没有日志端云协同就是一团迷雾端云协同最容易出现的问题是流量一多之后没人知道请求到底去了哪里。所以我在网关里加了结构化请求日志每一条请求都记录来源业务、命中的路由规则、最终模型目标、输入输出token数、延迟、估算成本、是否命中缓存、是否发生回退。成本估算公式也不复杂按输入输出token分别计价cost input_tokens / 1000 * input_rate output_tokens / 1000 * output_rate测试阶段我会让测试者直接用SQLite跑查询比如“按业务标签统计本月成本Top10”一条SQL就能做成本归因。这个能力在纯云或纯本地模式下用不到但一旦混合调度它就成了判断路由策略是否合理的最重要依据。4. 为什么开源项目最缺的是35位真实开发者测试4.1 模拟压测和真实用户流量的差距到底在哪我自己做压测时脚本发的都是千篇一律的请求1000次并发请求可能就3种模板。但真实业务根本不是这样——有人发两万个token的长文有人一句话不到十个字有人在流式输出到一半时直接关掉页面有人一边发消息一边疯狂回车造成重复提交。压测只能验证容量的上限真实流量考验的是网关在“脏请求”面前的韧性。比如长请求和短请求混跑时的调度优先级比如上游响应慢时网关是及时释放还是继续傻等。这些场景只能靠真实业务来暴露我在模拟环境里把测试写得再漂亮也只是自娱自乐。这也是我坚持不搞大规模内测、只找35个人的原因。人少意味着每个测试者的反馈我都能真的跟进每个P0问题都能在当天定位到具体环境。开源项目早期最怕的不是反馈太少而是反馈太多太杂最后变成一团噪声反而不知道先改什么。4.2 端云混合场景最容易翻车的五个地方根据我自己的使用经验端云混合调度实际跑起来最容易出问题的场景有这几个本地模型在显存不足时会排队而不是报错并发一上来延迟就线性拉高客户端先超时断开网关却还在傻傻等上游返回。云端API的限流是按分钟token数计算的如果语义缓存命中率不高几个高并发请求就能把配额打光。SSE流式输出到一半客户端主动断开如果网关没有及时取消上游请求剩余token全部白烧。回退链配置不当会形成A失败退回B、B失败又退回A的循环账单翻倍结果还是错的。不同模型的分词器不同网关自己统计的token数和云端厂商账单里对不上导致成本归因失真。这些问题我都在代码里做了保护但没有真实流量验证之前我不敢说覆盖了所有情况。我期待测试者做的就是拿自己业务里最刁钻的请求来打这个网关。4.3 只找35人的协作效率反而更高我见过一些开源项目搞几百人的灰度测试群热闹是热闹但大多数反馈都停留在“不好用”“希望加个XX功能”这类模糊描述上真正能帮助项目定位问题的信息非常少。小而精的测试团队不一样。三个到五个人每个人我可以一对一拉群告诉对方网关的路由设计思路对方也能指着自己的业务场景告诉我哪里不合理。每个P0问题我能在半小时内拿到对方的完整环境信息并开始复现。这种协作方式在项目从“能跑”到“扛得住真实业务”的过渡阶段比任何规模的内测都有效。5. 测试安排清单、反馈闭环与协作方式5.1 第一阶段的实测清单从部署冒烟到专项压测整个测试周期预计三周分三个阶段。第一周是部署和冒烟测试。我会在仓库里提供完整的Docker Compose启动脚本测试者把云厂商的API Key填进环境变量把OpenAI SDK的base_url指到网关地址跑通一次普通的对话请求和一个流式对话请求就算冒烟过。这一步能最快验证接口兼容性。第二周是真实业务接入。测试者把网关设为自己应用的默认模型入口按自己的业务场景配置端云分流规则然后观察请求日志核验路由是否落到了预期目标。这一步会暴露最多的路由策略缺陷。第三周做专项压测和反馈报告。测试项目包括基础转发普通对话、流式对话、多轮上下文OpenAI SDK完全兼容端云路由配置至少一条分流规则验证请求落到正确目标回退演练临时禁用主目标模型验证fallback链路自动接管语义缓存重复发送一对同义改写请求验证缓存命中且token统计下降并发测试2050并发下观察延迟和限流表现稳定性测试连续运行24小时以上观察内存、连接数、错误日志趋势每一项我都提前写在TESTING.md里带checklist和预期结果测试者直接打勾即可。5.2 反馈模板问题分级与信息格式反馈质量决定了修复速度。我能做的就是降低反馈成本让测试者用最省力的方式把信息给全。问题分为四级。P0是崩溃、数据丢失、不可恢复错误P1是核心流程不可用P2是功能缺口或体验问题P3是优化建议。每一个问题都按下面这个模板提交环境信息操作系统/Docker版本/是否使用本地模型 复现步骤每一步都要可操作 预期结果你认为应该发生什么 实际结果实际发生了什么 日志片段网关日志或客户端报错信息看起来基础但真的能让issue处理速度快一倍。很多人反馈问题只写一句“流式对话会卡住”我得花半天去追问环境和复现步骤。模板把该要的信息一次要齐双方都省时间。5.3 反馈之后的处理节奏我承诺的处理节奏是P0问题24小时内响应先给出临时规避方案然后一周内出修复版本P1问题一周内出修复版P2问题进入issue列表排期P3建议汇总后统一评估。每个周末我会整理一份测试反馈汇总列出所有问题的状态已处理、进行中、待讨论。所有有效反馈的提交者名字都会写进项目的THANKS文件。这个机制虽然简单但它让测试者看得到自己的反馈被重视协作也能持续下去。6. 已知边界、Roadmap与参与方式6.1 当前版本的边界和“故意不做”的事情做开源项目明确边界和实现功能一样重要。这个版本里我明确不做这几件事不做多租户计费中心和用户体系。网关默认单团队使用鉴权用最简单的API Key目标是先跑通调度逻辑。不做模型下载和模型文件管理。本地模型权重由测试者自己准备网关只负责调用本地推理服务的OpenAI兼容端点。不承诺企业级的超高吞吐。目标场景是小团队和独立开发者先把单机部署的稳定性和调度质量做扎实。这些边界划出来是为了避免项目膨胀成一个“配置管理系统”最后连核心的端云调度能力都没打磨好。等调度逻辑稳定了再往外扩不迟。6.2 后续版本规划v0.2版本我计划做三件事。第一个是语义路由的自动学习从历史请求日志里自动归纳意图标签减少手写规则的维护量。第二个是多租户配额控制让一个网关可以给多个团队共享每个团队有独立的token预算。第三个是回退链灰度切换把5%的流量切到新模型观察P95延迟和结果质量再决定是不是全量切换。v0.3我考虑做一个Web控制台集中展示路由日志、token消耗和成本归因。不过这些计划的优先级最终都要跟着测试反馈走——如果测试中发现流式转发的稳定性更紧急那就先修稳定性功能往后排。6.3 想加入测试先看看这几个条件如果你正在做或准备做一个LLM应用并且符合下面任意几条就很适合来参与这个测试手上已经有LLM应用正在开发或者准备上线不是纯观望状态能用Docker部署基础环境编过YAML文件本地有一张能跑7B左右模型的显卡会非常理想没有显卡也可以先用纯云端模式参与每天愿意投入半小时到一小时调试和反馈申请时你只需要告诉我三件事你的项目场景是什么、预计调用量有多高、部署环境是什么样。我会一对一拉群对接敏感信息不要在公开issue里贴直接私聊。仓库里会准备好部署脚本、配置模板、测试清单和一份详细的README。不需要付费也不需要承担服务器成本我需要的只是真实业务流量的检验。说句心里话开源项目找测试者这件事最难的其实不是技术是信任。一个还没经过生产环境验证的网关能被别人接进自己的业务里是对这个项目最大的肯定。所以我不打算搞报名从速那一套就老老实实把代码开源、把反馈透明、把问题修到可追踪。希望这几个“真实测试门票”最后能被真正需要端云协同能力的开发者接住。