前阵子帮朋友把他办公室里的三台 GPU 机器统一接入一个 AI 网关他最初的需求特别简单不要让产品经理每次换个模型地址就来找我改代码。结果我陪他改了三轮配置才意识到这事情看起来是“搭个代理转发一下”实际上牵扯到模型路由、负载均衡、超时策略、限流熔断和流式响应好几个完全不同层次的问题。最后落地的方案就是我接下来要详细拆的这套Olla一个面向自托管 AI 场景的智能代理与负载均衡器。听不懂这句话没关系你可以先把它想象成所有本地模型前面的一个专职服务员。你手上有 Ollama、vLLM、llama.cpp 服务每个服务各自守着不同的 GPU 和模型。Olla 做的事就是把这些后端统一收编到一个入口客户端只认一个地址按模型名把请求分发到合适的后端某个后端挂了自动切换流量不均时还能按权重或连接数做负载均衡。这篇文章适合已经在跑本地 AI 服务、但准备把单机改成多机集群的人也适合刚看完“Ollama Python 保姆级部署”教程、想再往前走一步的初学者。我会从架构规划、Docker Compose 部署、核心配置、压测验证、踩坑实录一直讲到 Kubernetes 横向扩容每个环节都给你可以直接抄的配置。1. 为什么我放弃了“每个服务直连 Ollama”的写法1.1 从一台机器到混合集群问题是怎么冒出来的早期大家跑本地 AI基本都是照着保姆级教程在一台机器上装好 Ollama然后写 Python 脚本直接连http://localhost:11434。一个人用没问题但一旦你把手上的模型开放给团队里几个应用麻烦就来了。我当时遇到的实际场景是这样的一台主力机器上有两块显卡分别跑 7B 和 32B 模型角落里还有一台旧 Mac mini专门跑一些 CPU 推理服务。公司内部用了三个应用一个是聊天前端一个是文档问答机器人还有一个是给研发用的代码补全插件。这三个应用全都把后端地址写死成某一个节点换模型就要改配置文件某个节点一挂三个应用立刻同时报错。更难受的是显卡负载完全不均衡。聊天前端整天有人用把那张跑 7B 的显卡挤满而跑 32B 的那张卡因为调用入口不公开一天也跑不了几个请求。你明明有算力却没法让它们互相兜底。这就是典型“单点直连”的问题客户端被迫感知拓扑后端状态一变客户端就得跟着改。1.2 Olla 帮我把“模型名”变成了一个稳定入口Olla 的解决思路是把“模型名”和“具体后端地址”彻底解耦。客户端发起请求时只写model: qwen2.5:7bOlla 根据路由规则决定这个qwen2.5:7b到底去哪个后端的哪个真实模型。这样带来几个立竿见影的好处Olla 核心能力对我的实际价值统一 OpenAI 兼容入口所有应用只需要配一个base_url不用分别维护后端地址模型名与后端解耦换模型、迁移节点、调整权重客户端零改动健康检查与自动故障转移某个后端挂了Olla 自动把流量切到存活节点负载均衡策略按权重、连接数或轮询把请求压到不同 GPU 上限流与熔断防止单个应用把整张显卡打满拖垮所有用户说白了Olla 就是把“代理”这层能力拔到了 AI 场景的专业高度。它不是一个简单的 TCP 转发工具而是理解“模型”“对话流”“推理时长”这些东西的七层代理。1.3 Olla 和 Nginx Proxy Manager、裸网关的差异很多人看到这里会问我用 Nginx Proxy Manager 反代一下不就行了NPM 我当然也在用它是很称职的 Web 反向代理但放到 AI 场景会有点水土不服。Nginx Proxy Manager 擅长的是按域名、路径转发请求它不知道model字段是什么意思也没法实现“同一个路径下根据请求体里的模型名不同分发给不同上游”。更麻烦的是AI 对话接口大量使用 SSE 流式返回Nginx 默认的缓冲行为会把流卡住导致用户体验变成“等了半天一句话不出来”。这些不是 NPM 的缺陷而是它压根不是为这个场景设计的。Olla 的定位是更贴近 AI 场景的七层代理。它懂得 OpenAI 兼容接口的语义能做模型路由、推理会话级别的负载均衡、流式透传还能把请求延迟、上游健康状态、限流触发次数这些 AI 场景特有的指标暴露出来。我的实际做法是NPM 负责域名和 TLS 证书Olla 专属处理 AI 流量两者是协作关系不是替代关系。2. 部署前的架构规划先算清楚再动手2.1 盘点你的上游Ollama、vLLM 还是 OpenAI 兼容服务Olla 需要知道上游是什么类型才能用正确的协议去探测和转发。我建议你先给自己写一张表把每台机器的角色列出来。上游类型默认监听常见启动方式接口风格Ollama11434ollama serve或 Docker/api/chat、/api/generate、同时提供/v1/chat/completionsvLLM8000python -m vllm.entrypoints.openai.api_serverOpenAI 兼容/v1/chat/completionsllama.cpp server8080llama-server -m model.ggufOpenAI 兼容/v1/chat/completions任意 OpenAI 兼容服务不定Docker 或裸进程/v1/chat/completions、/v1/embeddings我当时的第一版配置里主力机器跑的是 Ollama另一台测试机刚用 vLLM 起了 Qwen2.5-32B做高并发对比还有一台旧 Mac 用 llama.cpp 跑 CPU 推理。Olla 的一个贴心之处在于它对不同上游类型有一套适配层你不需要自己在代理里拼接不同的请求格式。2.2 模型路由规则不要只按“模型名”做路由最基础的路由一定是按模型名走但实践中你会发现模型名只是路由的一个维度。我后来把路由规则分成了三层第一层按模型名比如qwen2.5:7b可以同时路由到 gpu-01 和 gpu-02。第二层按请求类型chat/completions走文本生成节点embeddings走向量模型节点。第三层按请求头或上下文特征比如带X-Route-Group: small的请求固定走低延迟节点超长上下文的请求优先路由给显存大的机器。为什么要想这么细因为不同模型对硬件资源的需求差异太大了。一个 7B 量化模型可以在 8GB 显卡上跑一个 32B 模型就得拼显存而一个 embedding 模型可能只需要 CPU 就能搞定。如果你把所有请求一股脑打到同一个节点配置是简单了但硬件利用率会被你亲手按低。2.3 粗估并发与显存一张卡能抗多少请求负载均衡不是玄学背后是数学。我给大家一个最简估算方法。假设你在单张 4090 上跑 7B 量化模型推理速度大约 50 tokens/s。一次普通聊天请求输出 300 tokens那单个请求从开始生成到结束大约占用生成槽位 6 秒。如果 Ollama 设了OLLAMA_NUM_PARALLEL2这张卡同一时间能并行处理 2 个请求那每分钟能完成的请求数大约是2 × 60 ÷ 6 20也就是不到 0.4 请求/秒。这个吞吐单独看着很低但真实团队场景里用户不是每秒都在点发送。10 个人的小团队绝大多数时间处于阅读和打字状态这张卡其实是够用的。可如果你要做 20 请求/秒的对外服务平均每个请求 300 tokens那就需要大约 60 个并发生成槽位单张 4090 远远不够必须横向堆机器。这个估算的结论是负载均衡的前提是节点之间能力有差异或者节点数量多到单节点扛不住。如果你的场景只有一台机器做不做均衡都无所谓一旦有两台以上异构节点Olla 的权重策略才真正发挥作用。2.4 布好目录和配置文件后面改起来才不痛我想象中你的部署目录应该是这样的/opt/olla ├── docker-compose.yml ├── config │ ├── olla.yaml │ └── upstreams.d └── data配置文件单独放一个目录是为了升级镜像时避免覆盖你的自定义配置。data目录用来放 Olla 自己的持久化数据比如指标缓存、事件记录这个也可以直接放 Docker volume但我习惯在本地开一个真实目录方便备份和排查。2.5 环境变量与日志级别Olla 的环境变量不多我常用这几个OLLA_CONFIG指定主配置文件路径默认会找/etc/olla/olla.yaml。OLLA_LOG_LEVEL日志级别建议先debug跑半小时确认路由正常后改回info。OLLA_METRICS_ENABLED是否开启 Prometheus 指标端点部署阶段就建议打开后面压测会用到。日志级别一定要学会看。Olla 在debug模式下会把“请求命中哪条路由”“转发到哪个上游”“耗时多少”都打出来排错时比看任何文档都管用。3. Docker Compose 拉起 Olla一步到位3.1 最简单的 compose 文件下面是我当前在用的最简版本适合第一次跑通。我以olla/olla:0.5.2这个镜像名举例具体版本号你可以换成你实际拉到的。services: olla: image: olla/olla:0.5.2 container_name: olla restart: unless-stopped ports: - 8080:8080 environment: OLLA_CONFIG: /etc/olla/olla.yaml OLLA_LOG_LEVEL: info volumes: - ./config:/etc/olla - olla-data:/var/lib/olla volumes: olla-data:启动命令就两条cd /opt/olla docker compose pull docker compose up -d如果你之前已经跑过“Ollama Python 保姆级部署”这里唯一要适应的是思维转变以后 Python 脚本里不再直连 Ollama而是连 Olla。3.2 验证部署健康检查、日志、冷启动打开终端跑这几个命令确认 Olla 真的起来了docker compose ps docker compose logs -f olla curl http://127.0.0.1:8080/healthz正常情况下/healthz应该返回类似{status:ok}。注意冷启动阶段 Olla 会先解析配置文件建立到各上游的健康检查连接这一步通常几秒钟就完成。然后看它能不能拉到上游模型列表curl http://127.0.0.1:8080/v1/models如果能看到你所有 Ollama 节点上的模型清单说明上游识别成功整个链路已经通了。3.3 如何接入已有的 Ollama 节点接 Ollama 节点最容易踩的坑是 Ollama 默认只监听127.0.0.1:11434。当你把 Olla 跑在另一台机器或同一个 Docker 网络里时它会发现根本连不上。解决办法是让 Ollama 监听0.0.0.0:11434或者至少监听网卡对应 IP。用 Docker 跑 Ollama 的话命令是这样的docker run -d --gpus all \ -p 0.0.0.0:11434:11434 \ -v ollama-models:/root/.ollama \ ollama/ollama启动后先在本机验证curl http://192.168.1.10:11434/api/tags能看到模型列表再把它填进 Olla 的上游配置里。3.4 安全加固不要裸奔在公网Olla 默认不加密我强烈建议不要直接把 8080 端口暴露到公网。如果你要提供给公司外部或异地团队使用正规做法是放在 Nginx Proxy Manager 后面由它终止 TLS再配合基本认证或 IP 白名单。如果你的客户端统一用 OpenAI SDK通常会在请求里带一个api_key。Olla 可以配置一组服务端认可的 key也可以在转发时把内部 key 替换掉避免客户端直接拿到上游的真实凭证。本地内网环境你可以先把认证关掉但一旦出内网这一步必须补上。4. 核心配置拆解负载均衡算法、路由与限流4.1 上游定义与健康检查主配置文件的灵魂是upstreams段。下面是我实际用过的配置字段解释我都写在注释里server: listen: 0.0.0.0:8080 read_timeout: 300s write_timeout: 300s idle_timeout: 120s stream: true log: level: info metrics: enabled: true path: /metrics upstreams: - name: gpu-01 type: ollama url: http://192.168.1.10:11434 weight: 10 max_concurrent_requests: 4 keep_alive: 5m health_check: interval: 10s timeout: 2s path: /api/tags - name: gpu-02 type: vllm url: http://192.168.1.11:8000 weight: 8 max_concurrent_requests: 6 health_check: interval: 10s timeout: 2s path: /v1/models - name: cpu-node type: openai-compatible url: http://192.168.1.12:8080 weight: 2 health_check: interval: 15s timeout: 3s path: /v1/models routes: - model: qwen2.5:7b upstreams: [gpu-01, gpu-02] strategy: least_connections - model: qwen2.5:32b upstreams: [gpu-02] strategy: round_robin - model: bge-m3 upstreams: [gpu-01, cpu-node] strategy: weighted rate_limit: enabled: true global_rps: 100 per_route: - model: qwen2.5:32b rps: 10 burst: 20 circuit_breaker: enabled: true fail_threshold: 5 reset_after: 30s健康检查路径为什么不一样Ollama 的/api/tags能返回它当前加载过的模型列表vLLM 则用标准的/v1/models。如果你给一个 Ollama 上游配/v1/models在旧版本 Ollama 上未必稳定所以我习惯按类型来选探测路径。max_concurrent_requests是每个上游的并发上限。别把它理解成 Olla 的硬限制它更像是一个信号量超过上限的请求会排队而不是一股脑把所有请求都打进一个正在 OOM 边缘挣扎的进程。4.2 负载均衡算法对比与选择负载均衡算法没有银弹我整理了一张对比表算法工作方式适合场景不适合的场景round_robin请求轮流分发节点配置一致、平均延迟接近节点性能差异大慢节点会拖累整体least_connections优先分给当前活跃连接最少的节点LLM 推理时长差异大各节点连接数相近时收益不明显weighted按权重比例分发显卡性能差异大想人为控制流量比例权重拍脑袋拍错容易让好卡闲着我在文本生成场景里最常用的是least_connections。原因很简单一个 AI 推理请求不是一个“一次性 HTTP 请求”它可能持续十几秒甚至几分钟。轮询算法只知道“发出去一个请求”却不知道这个请求还占着 GPU 上的生成槽位。如果某台节点正在处理三个长文本此时轮询又给它塞了一个新请求它就被堵死了。least_connections能更好地反映真实负载。但如果是 embedding 请求每次请求都是毫秒级完成连接数波动极小这时候round_robin反而更简单稳定。4.3 路由规则模型名、请求头与长上下文策略配置里有一个很容易忽略的细节同一台机器上可能有多个模型同一个模型名也可能由多台机器提供。比如 7B 这个档位gpu-01 和 gpu-02 都有权重那我用least_connections就能实现自动均衡。32B 模型只有 gpu-02 跑得动那就只能单上游路由策略用什么都一样。embedding 模型我故意让 CPU 节点也参与权重只有 2原因是 embedding 计算量小CPU 能兜底不用白不用。如果你想做灰度路由可以给路由增加条件判断。比如客户端请求头里带X-Canary: stable时走正式节点带X-Canary: test时走新模型节点。这样你换模型版本时不需要改客户端的model字段改 Olla 配置就够了。4.4 限流、熔断与并发控制限流不好配不是因为这个功能复杂而是因为“限多少”很难拍脑袋。我给 32B 模型限 10 rps是根据前面那张显卡的估算吞吐来的。10 rps 乘以平均生成时长已经能吃掉不少显存了再高就可能把节点打挂。熔断解决的是另一种问题健康检查发现节点“活着”但节点已经被慢查询拖到响应超时。这时候继续发请求不仅没用还会加重雪崩。circuit_breaker的作用是当短时间内失败次数达到阈值就把节点临时踢出池子等 30 秒再放进来试探。这个机制我强烈推荐开启。4.5 可观测性指标端点与日志不用急着搭完整监控先把指标端点开起来。Olla 在/metrics提供标准 Prometheus 格式数据其中我重点看这几个olla_upstream_up上游当前是否在线。olla_upstream_request_duration_seconds到每个上游的请求延迟分布。olla_route_match_total每条路由命中次数能看出模型调用分布。olla_rate_limit_rejected_total被限流挡掉的请求数量。有了这几个指标后面压测时才不是盲人摸象。5. 用压测和故障演练验证部署5.1 先打基线直接打上游 vs 走 Olla部署完成不等于配置没问题一定要做一次对比验证。我用hey做简单的 HTTP 压测先直接打 Ollama 上游再打 Olla。hey -z 30s -c 10 -m POST \ -H Content-Type: application/json \ -d {model:qwen2.5:7b,messages:[{role:user,content:hi}],max_tokens:16,stream:false} \ http://192.168.1.10:11434/v1/chat/completions再打 Ollahey -z 30s -c 10 -m POST \ -H Content-Type: application/json \ -d {model:qwen2.5:7b,messages:[{role:user,content:hi}],max_tokens:16,stream:false} \ http://127.0.0.1:8080/v1/chat/completions注意我把max_tokens压到 16避免生成太多 token 导致压测时间过长。比较两次结果时重点看两个指标P95 延迟和吞吐。正常情况下走 Olla 的 P95 会比直连高几毫秒到十几毫秒这是代理层的正常开销如果 P95 从 2 秒变成 5 秒说明 Olla 的缓冲、超时或路由配置有问题。5.2 故障转移让一个上游当场“翻车”压测通过之后一定要演练一次故障转移。我的做法是直接把 gpu-01 上的 Ollama 容器停掉docker stop ollama然后立刻发一个请求curl http://127.0.0.1:8080/v1/chat/completions \ -H Content-Type: application/json \ -d {model:qwen2.5:7b,messages:[{role:user,content:你好}],stream:false}刚停掉的几十秒内Olla 可能还会尝试转发到 gpu-01因为健康检查有 10 秒间隔加上连续失败两次才会把节点标为不健康。等unhealthy_threshold触达后日志里会出现类似upstream gpu-01 unhealthy, removed from pool的记录随后请求自动转到 gpu-02。这个“窗口期”是有意设计的。如果健康检查太灵敏网络抖动一下就把节点踢掉反而会造成流量震荡。一般interval: 10s、unhealthy_threshold: 2是比较稳的组合也就是说一个节点要连续失败 20 秒才会被摘除。5.3 用日志和指标定位慢请求如果压测时发现某个请求特别慢先不要急着改 Olla按下面的顺序排查查 Olla 日志确认请求命中哪条路由、转发到哪个上游。直连那个上游用同样的请求体测试确认慢的是上游还是代理层。看olla_upstream_request_duration_seconds指标如果上游本身的 P99 就很高那是模型或显存问题如果上游很快但走 Olla 慢那大概率是流式缓冲或超时配置问题。这一套排查下来基本能覆盖 90% 的部署问题。6. 部署后最容易踩的四个坑6.1 流式输出被网关缓冲首字迟迟不来这是代理层最常见的 AI 场景坑。Olla 自己默认开启stream: true但如果你前面还套了一层 Nginx Proxy Manager而 NPM 站点配置里没关缓冲SSE 响应会被 Nginx 攒着等上游全部生成完才一次性吐给客户端。表现就是用户在聊天界面看到“正在输入”转圈半天然后整段文字一次性刷出来。这其实不是 Olla 的问题而是链路中的某个网关缓冲了 chunk。验证流式是否正常用 curl 直接请求curl -N http://127.0.0.1:8080/v1/chat/completions \ -H Content-Type: application/json \ -d {model:qwen2.5:7b,messages:[{role:user,content:你好}],stream:true}正常情况每个 token 会按行陆续输出如果等很久才一次性打印就把 Nginx 的proxy_buffering off;配到对应 location或者跳过 Nginx 先直连 Olla 测试。6.2 超时参数设错长生成任务被中途砍掉LLM 请求有一个明显特点从“收到请求”到“返回第一个 token”中间可能要经历 prefill 阶段尤其是长上下文可能耗时十几秒生成过程中如果模型在思考两个 token 之间也可能有停顿。如果你的read_timeout按普通 API 的习惯设成 30 秒或 60 秒长任务极容易被中途断掉。我的经验是read_timeout和write_timeout至少给到 300 秒idle_timeout放在 120 秒左右。注意区别read_timeout防止的是“整个请求生命周期过长”idle_timeout防止的是“连接空闲太久被回收”。这两者不要搞混。同时如果你用了 Nginx Proxy Manager也要把对应代理的proxy_read_timeout调大。链路里每一层都可能成为砍断长任务的那把刀。6.3 上游 OOM 与并发抢占多用户并发时最怕的不是 CPU 吃满而是显存 OOM。Ollama 默认会加载模型它有一套自己的调度机制但如果你同时让它跑两个大模型又叠加多个并发请求进程就可能被系统 OOM Killer 杀掉。解决手段分两层第一层在 Ollama 节点上设置环境变量限制并行数和模型加载数量OLLAMA_NUM_PARALLEL2 OLLAMA_MAX_LOADED_MODELS1 OLLAMA_MAX_QUEUE8第二层在 Olla 的上游配置里设置max_concurrent_requests从入口限制打到这个节点的并发量。这样即使客户端疯狂刷请求Olla 也会在内存里排队而不是让上游直接崩溃。还有一个小技巧给上游设置keep_alive: 5m让高频请求复用连接减少 TCP 和 HTTP 层的握手开销。效果在高并发压测时非常明显。6.4 端口、连接数与应用层的打架代理层转发高并发流量时Linux 系统本身也可能成为瓶颈。最常见的问题是临时端口耗尽现象是 Olla 日志里大量connect: cannot assign requested address。排查命令cat /proc/sys/net/ipv4/ip_local_port_range cat /proc/sys/net/ipv4/tcp_fin_timeout如果临时端口范围很小或tcp_fin_timeout过长可以适当调大端口范围和缩短 FIN 等待时间。但最好先确认 Olla 到上游的连接有做 keepalive否则连接复用不到位调系统参数只是治标。提示容器环境下要注意 Docker 本身的网络模式。默认桥接网络会做一层 NAT高并发下端口资源消耗比 host 模式快如果压测数据异常可以试试用network_mode: host直接跑 Olla。6.5 重启丢失配置持久化与备份要点Docker 容器重建时如果配置没有挂载出来确实会被清掉。我的习惯是把config目录放进 Git 仓库改配置前先记一笔变更记录。加上 compose 文件里的restart: unless-stopped机器重启后服务能自动拉起来但配置文件的版本管理只能靠自己。另一个容易忽略的是olla.yaml里如果填了 API key不要把真实 key 写到公开仓库。可以用环境变量引用Olla 支持在配置里读取环境变量比如${OLLA_UPSTREAM_KEY}这样敏感信息只存在.env文件里。7. 进阶Kubernetes 部署与多副本扩展7.1 K8s 下部署 Olla 的最小清单如果你已经用 Kubernetes 管理 GPU 节点把 Olla 迁进去是水到渠成的事。最小部署只需要三样东西ConfigMap 保存配置、Deployment 跑 Olla 实例、Service 做稳定访问入口。apiVersion: v1 kind: ConfigMap metadata: name: olla-config data: olla.yaml: | server: listen: 0.0.0.0:8080 log: level: info upstreams: - name: gpu-node-1 type: ollama url: http://gpu-node-1:11434 health_check: path: /api/tagsapiVersion: apps/v1 kind: Deployment metadata: name: olla spec: replicas: 2 selector: matchLabels: app: olla template: metadata: labels: app: olla spec: containers: - name: olla image: olla/olla:0.5.2 ports: - containerPort: 8080 volumeMounts: - name: config mountPath: /etc/olla volumes: - name: config configMap: name: olla-configapiVersion: v1 kind: Service metadata: name: olla spec: selector: app: olla ports: - port: 8080 targetPort: 8080这套配置跑起来后所有应用只访问http://olla:8080K8s Service 会根据 Endpoint 自动把请求轮询到两个 Olla Pod 上。7.2 多副本的配置同步与热更新多副本带来的第一个问题就是配置同步。Olla 的配置是静态文件每个 Pod 都从同一个 ConfigMap 挂载所以只要 ConfigMap 改一次再触发滚动更新所有副本就会拿到新配置。不要手动进每个 Pod 改文件那是反模式。另一个容易被忽略的细节是多副本部署后Olla 的内存限流统计是每副本独立的。假如你配置global_rps: 100两个副本加起来可能实际能接受 200 rps因为每个副本都在按 100 来数。如果要做严格的全局限流得靠外部存储或网关统一实现这一点很多人踩了坑之后才发现。7.3 更多玩法灰度路由、按 GPU 标签调度K8s 给你最大的好处是资源调度可以交给集群。GPU 节点可以用 node label 区分比如gpu-type: a100、gpu-type: 4090Olla 的上游地址直接写成对应的 headless service这样节点扩缩容时 Olla 配置不需要跟着改。灰度玩法的思路是这样的同一套模型服务先让 10% 流量走新节点用权重实现或者用请求头路由把内部测试流量引到新模型。Olla 的路由条件在这里非常灵活完全不需要重启整个集群。8. 如果让我重新搭一遍我会怎么做最后聊一点个人经验别一上来就追求“完美网关”。我自己第一次搭的时候恨不得把路由、限流、熔断、K8s 全套一次性铺上去结果排错排到怀疑人生。后来在干净环境里分阶段重搭反而两天就稳了。我建议你的顺序是先跑通最小链路一台 Olla、一个 Ollama 上游、一条路由然后把应用的base_url指过来。这一步验证的是“能不能通”。第二步加上第二个上游和健康检查人为关掉一个节点验证故障转移。第三步再加限流和指标。最后当你真的有了五六台异构节点、需要频繁调整模型版本时再考虑上 Kubernetes 和更复杂的路由策略。这套方案跑稳之后团队成员基本不需要感知后端地址。换显卡、加节点、切模型都只是动 Olla 配置的事。它不会替你解决模型本身的效果问题但能让你把有限的精力从“网络拓扑管理”里解放出来真正放到调模型、做业务上。这就是我眼中自托管 AI 基础设施该有的样子。