
我这两个月一直在做一件事给自己手头几个同时在跑的项目做一个统一的 LLM 访问层。一开始只是不想每个项目里都写一份 OpenAI SDK 调用代码后来发现事情远不止“封装 API”这么简单——云端 API 贵、延迟波动大、有些数据不能出内网、端侧跑个小模型又快又便宜但能力有限。于是这个项目慢慢长成了一个端云协同的 LLM 网关目前已经在内部跑通了完整链路现在想找 35 位真正在做 LLM 应用开发的开发者来一起做一轮真实场景测试。这个项目的核心定位很简单在端侧手机、PC、边缘盒子和云侧GPU 集群、公有云 API之间做一个智能的 LLM 请求分发层。应用层不需要关心请求最终被哪个模型处理网关会根据设备当前状态、网络情况、任务复杂度、成本预算这些因素自动决定是走本地小模型、还是转发到云端大模型。它解决的是一组很实际的问题想让 LLM 应用跑得便宜、跑得快、数据还能留在本地但又不想在应用代码里写一堆复杂的路由逻辑。如果你正在做 AI 应用、AI 硬件、端侧智能或者私有化部署相关的东西这个网关对你应该是直接能用的。我写这篇文章是想把项目的设计思路、核心实现、测试计划以及这段时间踩过的坑都摊开来讲也希望看到这篇内容的开发者有兴趣来当第一批真实测试用户。1. 端云协同网关的设计思路与核心场景拆解1.1 为什么不能直接“全上云端”或者“全跑本地”先说个我在项目初期反复纠结的问题为什么一定要做端云协同而不是干脆全云端或者全端侧。全云端是最省事的OpenAI、Claude、国内各家大模型 API 都很成熟SDK 一调就能用。但我在实际项目中遇到了几个绕不过去的坎。第一个是成本做过 AI 功能的人都懂token 费用看着单价不高一旦业务量上来每个月账单是实打实的钱。第二个是延迟和稳定性有些场景对响应时间特别敏感比如语音助手、实时翻译、交互式对话云端 API 的 P99 延迟经常让人崩溃。第三个是数据合规我手上有个医疗相关的项目患者的对话记录根本不能出内网全云端方案直接就毙了。全跑本地也有问题。端侧模型的能力天花板摆在那里跑个 Llama 3 8B 量化版或者 Qwen 2.5 7B处理简单问答、摘要、分类没问题但复杂推理、代码生成、长文本理解就明显拉胯。而且不是所有设备都能跑模型很多用户的手机和 PC 没有足够的算力。我做过一个测试在同一台 MacBook Pro 上跑 7B 模型生成速度大约 20 token/s而云端 GPT-4o 可以到 80 token/s 以上差距是数量级的。所以端云协同不是“选一个”而是“两个都要”——让简单请求留在端侧让复杂请求上云两者之间还要让用户无感切换。这个思路最初的灵感其实来自传统 CDN 和边缘计算内容分发靠边缘节点就近响应回源才到中心服务器。LLM 网关做的也是类似的事能本地处理的本地处理处理不了的再“回源”到云端大模型。1.2 场景拆解什么请求适合端侧什么请求必须上云在确定网关架构之前我先把自己手上的应用场景全部列出来逐个分析请求特征再决定路由策略。这里分享一下我整理的判断维度做这个网关的同学可以直接参考场景延迟敏感度数据敏感度任务复杂度推荐路由闲聊对话高中低端侧优先文本摘要低高中端侧/私有云代码生成中中高云端情感分析高高低端侧复杂推理低低高云端实时翻译高中中端侧优先失败上云知识问答中高高私有云/混合这个表看起来简单实际做的时候每个场景都要细抠。比如“文本摘要”看起来应该是数据敏感优先走本地但如果是长文档摘要端侧模型的上下文窗口根本装不下强行截断会导致摘要质量崩掉。所以网关的路由逻辑不能只看任务类型还要结合输入长度、模型上下文窗口、端侧可用内存这三个实时参数来综合判断。我在网关里把路由策略设计成了一个可配置的分层决策器第一层看数据合规策略数据绝对不能出内网的请求直接锁死到本地第二层看任务复杂度通过提示词长度、预期生成长度、任务类型标签来判断第三层看设备实时状态当前端侧推理服务的负载、可用显存/内存、电池状态。三层跑完才决定最终走哪条路。1.3 与传统 API 网关的本质区别很多人会问这不就是 API 网关加了个模型路由吗我一开始也觉得是但做深了发现差别非常大。传统 API 网关做的事情是流量管理鉴权、限流、负载均衡、灰度发布它面对的是多个后端服务每个服务是确定性的返回结构和行为都是可预期的。LLM 网关面对的是多个模型每个模型的行为是概率性的同一个提示词在不同模型、不同温度参数下会得到完全不同的输出。这意味着网关不能做简单的“转发”它必须理解请求内容对请求做分类、改写、裁剪甚至要对模型的输出做校验和兜底。另外LLM 网关还多了一个传统网关完全没有的维度token 成本管理。一个请求该花 0.1 元还是 1 元网关应该能给出预算控制。我在网关中实现了分用户、分业务的 token 配额不同业务线设置不同的成本上限一旦超限可以自动降级到端侧小模型或者限速。这个功能在传统网关里完全没有对应物也是实际业务中最容易被需要的。2. 网关核心架构与关键技术选型2.1 整体架构五大模块各司其职这个网关的架构我前后重构了三次最终稳定成五个核心模块。每个模块解决一类明确问题模块之间通过事件总线通信避免强耦合接入层负责接收应用请求统一鉴权、限流、格式转换。对外暴露 OpenAI 兼容的/v1/chat/completions接口这样接入方不需要改任何代码原来的 OpenAI SDK 直接换个 base_url 就能用。路由决策层核心引擎负责判断请求应该走端侧还是云侧以及选择具体哪个模型。这层拥有一套可配置的路由策略系统支持规则、权重、AI 分类器三种模式。端侧管理模块通过 WebSocket 长连接维护与端侧推理服务的关系。实时收集端侧状态、模型列表、当前负载还能远程下发放置在端侧的模型配置。云侧适配层统一封装各家云端 API 的差异包括 OpenAI、Anthropic、国内主流模型。实现了统一的超时重试、错误码映射、流式输出协议转换。可观测与成本模块记录每个请求的全链路 trace包括路由决策依据、端侧耗时、云侧耗时、token 消耗、预估费用。实时汇总成指标看板。2.2 技术栈选择与关键依赖技术选型上我没有追新全部选了经过验证的稳定方案。后端用 Python FastAPI这个选择主要考虑到 AI 生态基本都在 Python 这边后面要集成向量检索、RAG、模型推理框架都会方便很多。网关本身对性能要求没有核心业务那么极端FastAPI 的异步能力足够支撑几百路并发配合 Uvicorn 多 worker 部署实测单机可以稳定承载 300 并发请求。关键依赖方面路由决策引擎用了rule-engine这个库来跑规则匹配比我自己写 if-else 树清晰得多规则可以写成 JSON 配置下发不用改代码就能调策略。配置管理用了pydantic-settings所有配置项支持 YAML 文件与环境变量双重覆盖。异步任务队列用了arq用于处理流式请求的逐字转发和日志异步落盘。端侧那部分我单独写了一个轻量客户端用 WebSocket 和网关通信通过消息类型区分心跳、状态上报、推理请求、模型切换。通信协议用 Protobuf 序列化比 JSON 省流量在弱网环境下明显更稳。端侧 SDK 目前提供 Python 和 C 两个版本Python 版方便快速接入C 版用在资源受限的边缘设备上。2.3 配置驱动的路由策略设计网关里最灵活的部分是路由策略系统。我不想让每个业务方都来改代码才能调整路由行为所以设计成了配置即策略。一份 YAML 文件定义所有路由规则网关启动时加载也支持运行时通过管理接口热更新。一个简化版的策略配置长这样route_strategies: - name: privacy_lock priority: 100 condition: request_tags: [medical, legal] action: force_local - name: realtime_chat priority: 80 condition: task_type: chat device_capability_score: 60 action: local_first_with_fallback - name: complex_reasoning priority: 60 condition: task_type: reasoning action: cloud_only - name: cost_control_fallback priority: 40 condition: billing_tier: free monthly_token_usage: 1000000 action: degrade_to_local每条策略包含优先级、匹配条件、执行动作。网关按优先级从高到低匹配第一条命中的策略胜出。这个设计的核心思路是合规永远优先成本控制兜底性能和能力在中间段自由博弈。实际部署中发现配置化的好处不仅在于灵活更在于出了问题可以快速回滚——只需要下发一版新配置不用重新发布服务。2.4 为什么没有用现成的开源 LLM Gateway很多人会问Kong、APISIX 这些网关也有 AI 插件为什么不直接用这里要说下我的调研结论。现有的 API 网关确实加了一些 LLM 相关功能比如请求转发、API Key 管理但它们本质上是“流量管道”不感知模型能力差异不做请求内容分析没有端侧调度的概念。另外我也看过几个专门的 LLM Gateway 开源项目大多停留在云侧多模型路由这个层面把多个云 API 聚合到一个入口但没有解决端和云之间的协同问题。这个项目最重要的是“端云协同”四个字这是和现有所有方案的核心差异点。网关不仅要知道云端有哪些模型可用还要知道当前这个设备上有没有模型、是什么模型、现在负载怎么样。端侧模型和云侧模型之间还要能共享对话上下文一个会话可以在端侧模型跑几轮再无缝切换上云上下文不丢。这个能力我目前没有在任何一个开源项目里找到完整实现。3. 实操过程从零搭起端云协同 LLM 网关3.1 本地开发环境搭建与最小闭环我把完整搭建过程拆成四步依赖较少按顺序执行基本十分钟能跑起来。第一步是准备网关侧环境。先创建虚拟环境然后安装核心依赖目前项目锁定在 Python 3.10python -m venv venv-core source venv-core/bin/activate # Windows 用 venv-core\Scripts\activate # 核心依赖 pip install fastapi uvicorn[standard] websockets httpx pydantic-settings pip install rule-engine arq protobuf pyyaml # 数据库先上轻量的 SQLite后续迁移 PostgreSQL pip install sqlite-utils第二步是下载端侧推理运行时。端侧推理我目前优先支持 Ollama因为它对新手最友好一条命令装完就能跑模型。后续计划加 llama.cpp 原生集成和 MLC-LLMApple Silicon 上性能更好。curl -fsSL https://ollama.com/install.sh | sh ollama pull qwen2.5:7b-instruct-q4_K_M这个模型大约 4.7GB在 M 系列 MacBook Pro 16GB 内存上跑起来没有压力实测生成速度能有 25~30 token/s。第三步是把端侧客户端跑起来。我写了一个独立的小程序负责和网关通信它做的事情就是注册设备信息、周期上报状态、接收推理请求并调用 Ollamapython examples/simple_edge_client.py \ --gateway-url ws://localhost:8000/ws/edge \ --device-id mac-mini-test-01 \ --model qwen2.5:7b-instruct-q4_K_M启动后如果你在网关侧看一下日志会看到类似这样的注册消息{ type: edge_register, device_id: mac-mini-test-01, capabilities: { models: [qwen2.5:7b-instruct-q4_K_M], memory_available_mb: 8392, inference_backend: ollama } }第四步是启动网关主进程默认监听 8000 端口。启动后随便用 OpenAI SDK 打一个请求看它能不能自动路由到端侧模型from openai import OpenAI client OpenAI(base_urlhttp://localhost:8000/v1, api_keytest-key) resp client.chat.completions.create( modeldefault, # 网关会根据策略自动解析 messages[{role: user, content: 你好简单介绍一下你自己}], streamFalse, ) print(resp.choices[0].message.content)如果配置了“default 模型优先走端侧”这个请求会在本地 Ollama 上完成推理在网关日志里会看到一条路由记录标注edge而不是cloud。到这里最小闭环就跑通了。3.2 云端模型接入与多供应商适配端侧闭环通了之后接入云侧就相对简单了。为了兼容性考虑云侧适配层做了两层设计第一层是统一请求格式把各家 API 的差异请求体、鉴权方式、错误码封装掉第二层是统一的流式协议因为流式对话是 LLM 最常见的交互方式各家 SSE 事件格式不同网关要把它归一化成 OpenAI 兼容的格式再转发给客户端。我目前接入了三家OpenAI 官方 API、Anthropic Claude和国内一家主流模型服务商。配置很容易在cloud_providers.yaml里填好 API Key 和 base_url 就行cloud_providers: openai: type: openai_compatible base_url: https://api.openai.com/v1 api_key_env: OPENAI_API_KEY default_model: gpt-4o-mini claude: type: anthropic_compatible base_url: https://api.anthropic.com api_key_env: ANTHROPIC_API_KEY default_model: claude-3-5-sonnet-latest云侧接入有一个特别容易踩的坑流式响应超时。云端模型在长文本生成时如果中间有较长的思考停顿连接可能被误判为超时而断开。我的做法是在适配层把“首字延迟”和“字间延迟”分别设置超时时间首字延迟放宽到 60 秒字间延迟放宽到 30 秒同时启动一个心跳包定期向客户端发送 keep-alive 事件避免客户端代理层先切断了连接。3.3 端云切换的会话上下文同步机制这个功能是整个项目里技术挑战最大的一块。端到端的对话用户跟设备聊了五轮前五轮在端侧模型第六轮因为任务复杂被路由到了云侧怎么保证第六轮对话还能记住前五轮说了什么我的方案是做一个统一的会话状态管理层。对话上下文以标准格式存储在网关侧目前是内存Redis后续持久化到数据库每一轮对话结束时由端侧或云侧模型的响应触发上下文更新。无论下一次请求路由到哪里网关从统一存储里取出完整上下文按目标模型的要求重新组装 messages。这里有个关键问题要处理端侧小模型的上下文窗口通常只有 8K云侧大模型有 128K 甚至 200K。如果端侧已经积累了 6K 的上下文切到云侧自然没问题但如果云侧积累了 30K 的上下文切回端侧就装不下。我的方案是路由决策时必须带上上下文长度作为约束条件上下文超过端侧窗口的请求直接禁止路由到端侧。同时在端侧切换时自动做一轮摘要压缩用一个较小模型把长历史压缩成 500 token 左右的摘要塞进系统的 system prompt 里既保留核心信息又不超窗口。3.4 可观测性与成本统计的落地实现做 LLM 网关如果不做可观测性上线就是灾难。我之前被坑过一次——某个业务方反馈“模型怎么越答越差”排查半天才发现是路由策略静默失效所有请求都打到了一个小参数模型上而监控面板只看了整体成功率完全没发现。现在网关内置了一套完整的链路追踪机制每个请求从进入网关开始就生成一个 request_id连同路由决策的关键因子一起记录到日志里{ request_id: req_8f3k2d, route_decision: cloud, reason: task_typereasoning, input_tokens4521, device_availablefalse, target_provider: openai, target_model: gpt-4o-mini, latency_total_ms: 1842, latency_breakdown: {edge: 0, cloud: 1842, overhead: 37}, usage: {input_tokens: 4521, output_tokens: 312}, estimated_cost_cny: 0.16 }成本统计这里多说一句。各家 API 的价格计算方式不一样有的按百万 token 计价有的按调用次数计价有的有阶梯价。网关把这些都抽象成了统一的计价规则在配置里定义每款模型的单价然后按实际使用量实时累计。这个功能上线后我发现很多团队的 AI 成本存在明显的重复消费问题——同一个模型在多个应用里各自调用没有共享缓存同样的提示词同样的输入每次都重算一遍。所以在网关里加了一个语义缓存模块基于 embedding 相似度判断两个请求是否等价等价请求直接返回缓存结果。实测这个功能能省 20%~35% 的 token 费用。4. 测试计划与真实问题排查实录4.1 寻找 35 位开发者的测试目标与接入方式做这个项目最需要的就是真实场景测试。我需要的是正在做 LLM 应用的开发者也欢迎自己做 AI 硬件或者私有化项目的朋友。你不需要部署我的完整代码仓库会提供一个 Docker Compose 一键启动的网关侧环境端侧客户端也支持一键安装你只需要花半小时把网关接入到自己的现有项目里然后把平时的流量打进去观察路由行为。测试的核心目标有三个维度。第一是路由准确率端侧和云侧的分流是否符合场景预期有没有该上云的被挡在端侧、该留本地的被发到云上的情况。第二是端云切换平滑度对话过程中意外切换模型时用户是否能感知到切换响应是否中断上下文是否连贯。第三是成本和延迟的实际收益对比接入网关前后的账单和 P50/P95 延迟判断端云协同到底省了多少钱、快了多少。接入方式我已经做了尽量简化如果你用 OpenAI SDK只需要改一行代码# 原来的写法 client OpenAI(base_urlhttps://api.openai.com/v1, api_keysk-xxx) # 接入网关后 client OpenAI(base_urlhttp://localhost:8000/v1, api_keyyour-gateway-key)然后你正常调client.chat.completions.create()网关全接管。如果你用的是 LangChain 或 LlamaIndex它们的底层也是 OpenAI SDK同样只需要改 base_url。4.2 实测中发现的三个典型问题开发过程中我自己跑了很多轮测试踩了一些坑挑三个最典型的分享。问题一端侧模型被“高估”了。我在测试中发现小模型的稳定性远不如大模型同样的请求有时回答质量还行有时就直接崩溃。一开始网关把大量请求都路由到端侧结果用户反馈“时好时坏”。后来我加了一个机制每个端侧请求结束后网关会对输出做个质量评分评分方式是检查输出长度是否合理、是否有重复循环文本、是否包含低质量的“嗯”“啊”等填充词。连续三次低分网关自动把该设备在一段时间内的流量切到云端。问题二弱网环境下的端侧不可用。我的一个测试场景是手机连着不太稳定的 Wi-Fi端侧模型虽然跑在本地不需要网络但网关和端侧之间的管理通道走的是 WebSocket网络抖动会导致连接断开网关误判端侧不可用把本该走本地的请求全部发到了云端。解决办法是端侧客户端加了本地兜底缓存网络断开时缓存请求恢复后重放同时网关加入了 graceful degradation 机制状态上报超时不会立即判定离线而是等到两次心跳间隔 5 秒宽限期。问题三上下文切换导致“精分”。有一次测试中用户在端侧跟设备聊了十几轮家常突然问了一个数学题网关果断路由到云端大模型。但因为端侧上下文比较长压缩摘要时把“用户刚才说自己在准备考研”这个关键背景丢掉了云端大模型给出的回答风格非常正式跟之前端侧那种轻松的聊天风格完全不一致用户明显感觉到“换了个 AI 在跟我说话”。这提醒我上下文传递不仅要传信息还要传风格和语气。现在压缩摘要时会把系统提示词里的角色设定和语气要求一并传过去并约束下游模型“延续之前的语气”。4.3 常见问题速查与避坑建议整理了一份自己排查问题的速查表涉及几个最常遇到的坑其他开发者接入时可以直接对照现象可能原因排查方式解决办法请求全部打到云端端侧不生效设备能力评分过低或状态上报失败查看网关/v1/edges接口确认设备在线状态检查端侧客户端日志确认 Ollama 服务是否启动路由规则的优先级配置混乱多条规则互相覆盖但没有优先级概念开启网关debug_routetrue查看决策链日志给每条规则配独立优先级避免非精确匹配流式请求经常中断云侧适配层超时设置过短抓包看 SSE 连接的断点调整首字延迟和字间延迟超时时间端侧模型输出质量不稳定量化等级过低或模型参数不足对比同一请求端侧/云侧输出提升量化等级或为特定任务配置强制云侧路由token 费用超预算没有设置配额或语义缓存未开启查看成本看板定位高消耗业务配置业务级 token 配额开启语义缓存切换模型后对话出现“精分”上下文压缩丢失了风格信息检查压缩摘要中是否包含角色和语气标记压缩时保留风格描述字段4.4 后续版本规划与可扩展方向网关目前还在 0.2 版本阶段核心链路已经通了但离我理想中的形态还有距离。规划中的 0.3 版本主要有三件事。第一是更细粒度的端侧任务分解。现在一个请求只能整体路由到端侧或云侧0.3 会支持任务拆分——比如一个复杂的请求先由端侧模型做意图识别把任务拆成几个子任务简单子任务直接端侧解决复杂子任务上云最后再汇总结果。这在代码生成、文档分析这类多步骤任务里能显著节省云侧 token 消耗。第二是多设备联动调度。如果用户家里有多个端侧设备比如手机、电视、NAS、树莓派网关应该能识别这些设备的算力差异把一个任务拆分到多个设备上并行推理。这个想法目前还在验证阶段因为涉及异构设备间的模型同步和结果聚合工程复杂度不低。第三是插件化扩展机制。计划把 RAG、提示词优化、输出校验这些能力做成网关插件业务方按需加载不用自己再搭一套。目前网关已经预留了插件接口的框架设计核心改动是对外暴露事件回调钩子插件可以订阅请求前、响应后、路由决策前三个生命周期事件。我在测试中的一点体会做这个项目断断续续花了两个月最大的感受是端云协同 LLM 网关不是一个纯技术问题而是一个系统工程问题。技术上最难的其实不是路由算法、不是模型适配而是如何在不可靠的真实环境中维持体验的一致性——网络会抖端侧模型会抽风云端 API 会超时用户不会关心你路由到了哪只关心回答快不快、准不准、贵不贵。这三个目标互相牵制必须通过细粒度的可观测数据来不断修正策略。现在我每天都会花十分钟看网关的路由决策日志隔几天就会调整一版路由规则这个调优过程本身已经成为我最重要的经验来源。如果你也对端云协同 LLM 网关这个方向感兴趣非常欢迎来真实环境里跑一跑到时候把你的使用反馈丢给我我这个阶段最需要的就是这些来自实际场景的打磨意见。