本地把 Ollama 跑起来,再用 OpenAI 兼容的那套写法接进自己的应用,往往跑得通第一版就卡在第二版:偶发超时、并发一高就排队、流式断在半路、日志里看不出是哪一步慢。这篇不讲协议本身兼容了什么,只按落地顺序给你一份排查与配置清单,照着逐项过一遍,把接入从能用推到稳定。
本地把 Ollama 跑起来,再用 OpenAI 兼容那套写法接进应用,第一版通常挺顺:改个 base_url,原来调云端的那套 SDK 直接就能用。卡住的地方基本都在第二版——偶发超时、并发一高就排队、流式断在半路、日志里翻半天也看不出是哪一步慢。这篇不重复讲协议兼容了什么,只按落地顺序给一份配置和排查清单,照着从头过一遍,把接入从「能跑」推到「稳得住」。
先说一个前提:本地推理和云端接口是两种脾气。云端你调大并发,它自己扩;本地你调大并发,它可能直接卡死或者静默排队。所以下面每一项都围绕一个思路——把边界写死、把退路显性化、把现场留下来。具体到你这台机器速度如何,别凭感觉,到 /tools/speed-test 实测当时的数字再定。
先把地址和超时定死,别让它自己飘
地址和端口最容易散落在代码各处,今天写死在常量里,明天在环境变量里又覆盖一遍,出问题先找半天连的是哪。做法很简单:base_url 只允许从配置读,容器里跑要额外确认一层——Ollama 在宿主机、应用在容器里时,localhost 指的是容器自己,不是宿主机,这一步不确认,超时排查全是白费功夫。
| 配置项 | 怎么设 | 判断依据 |
|---|---|---|
| base_url | 单一来源,从配置/环境变量读 | 代码里搜不到第二个写死的地址 |
| 连接超时 | 短一些,只覆盖建连 | 连不上要快速失败,不要干等 |
| 读取超时 | 比云端给得宽 | 首次加载模型明显慢于后续请求 |
| 流式开关 | 流式和非流式分开设超时 | 流式要按「多久没有新块」判超时,不是按总时长 |
流式和非流式各测一次基线:到 /tools/speed-test 量出这机器的首字延迟和整体耗时,再拿这个数反推超时值,而不是拍一个整数上去。首字延迟决定读取超时下限,整体耗时决定你该不该开流式——超过用户能忍的等待,就该走流式。
并发不是调大就好,先看你显卡能同时吃几个
本地推理的并发上限主要由显存和模型体积决定,不是想调多大调多大。超了之后的表现不是「慢一点」,而是直接失败或者长时间排队——请求发出去,半天没回,你以为是慢,其实是在队列里等着没人处理。所以并发数得自己找拐点,不能抄云端的配置。
- 测单请求基线并发为 1 时,连发若干次,看单次耗时是否稳定;不稳定说明机器本身有别的负载,先清干净再测。
- 逐级加压并发从 1 加到 2、4、8,每档记录耗时和失败率。耗时不涨说明还没到拐点,某档开始明显跳升或出现失败,那就是上限附近。
- 按类型分档短问答、长上下文、带检索的请求分开排队,别共用一个池子,否则一个长任务能把短请求全堵在后面。
- 定重试策略失败重试用退避,不要立刻重发;同一台机器上立刻重发只会让队列更长。具体档位怎么设计,参考站内讲 429 那类文章的思路。
分档排队这件事,看着麻烦,其实是保护短请求的关键。用户问一句天气,不该排在某个长文档分析后面等一分钟。判断标准:如果你发现简单请求的耗时跟复杂请求一样长,那基本就是被同一个队列堵住了,该拆了。
本地扛不住时怎么退,退到哪
降级方向通常是本地小模型到本地大模型,再到远端接口,每一级都要有明确的触发条件,比如连续超时几次、排队超过多少秒。最怕的是「静默降级」:本地扛不住,悄悄转远端,日志里不留痕,等哪天数据合规被查出来,性质就从技术问题变成事故了。所以每次降级至少记一条:这次走了哪条路、为什么走。
- 触发条件
- 连续超时或排队超时的阈值,写成配置,不要写死在 if 里
- 路由表
- 模型名到真实后端的映射,集中一处维护,别在每个调用点各写各的
- 降级日志
- 每次走非首选路径都留一条,含原因和最终后端
- 兜底提示
- 无路可退时给用户明确反馈,而不是无限转圈
- 本地小模型排第一档,响应快,够用就先别升级
- 本地大模型第二档,慢但能力足,触发条件设在第一档失败之后
- 远端接口第三档,只有前两档都过不去才走,且必须记日志
- 每级的超时要独立设,别用同一个值套三种后端
- 降级后的结果要不要回写缓存,提前想清楚,否则命中脏数据很难查
日志留够字段,再用一小撮用例兜住改动
日志不够字段,排查就是猜。每次请求至少记这些:时间、模型名、输入输出长度、耗时、是否流式、走了哪条后端、失败原因。少一个字段,出问题时你就得多复现一次,而本地环境的复现成本往往比看日志高得多。这些字段不用搞得像监控系统,一个结构化日志行就够。
| 字段 | 为什么必须留 | 缺了会怎样 |
|---|---|---|
| 模型名 | 区分本地和远端 | 不知道请求最后落到哪个后端 |
| 耗时+是否流式 | 判断是排队还是推理慢 | 分不清瓶颈在哪一段 |
| 后端路径 | 记录降级是否发生 | 合规问题查不出 |
| 失败原因 | 归类超时/显存/连接 | 只能盲目重试 |
| 输入输出长度 | 解释耗时突增 | 以为是性能退化,其实是长文本 |
评测别大动干戈,固定十几条覆盖真实场景的问题就够,改配置或换模型后跑一遍,比凭感觉靠谱。记录失败样本时,把当时的配置一起存下来,否则过两天没人能复现。想看别人的中转站和工具怎么选的,到 /sites 核对当时的对比,价格类数字到 /price-watch 看,别信听说。
什么情况下不适用:纯个人本地实验阶段,只记失败就够了,不用上全套字段;但一旦有人依赖这个接口做事,字段就得补齐,因为那时候排查成本不是你自己承担。
下一步先拿这台机器跑一次速度基线,把首字延迟和并发拐点定下来,再回来改超时和并发配置,顺序反了会白折腾一轮。→常见问题
本地跑通了,接进应用却报连接被拒绝,先查什么?
先确认应用和模型服务是不是在同一网络命名空间里:容器里写的本地地址指向的是容器自己,不是宿主机。再确认端口没被防火墙或代理环境变量拦掉。用最简单的请求方式单独打一次接口,能通再加业务逻辑,能省很多来回。
为什么并发一高就变慢,加机器有用吗?
本地推理大多受限于显卡显存和计算资源,并发上来之后请求会排队,表现为整体变慢而不是报错。先测出你这台机器从并发 1 往上加到耗时明显跳升的那个点,把上限设在那附近。想让上限更高,得换更小的量化版本或更强的卡,而不是调大应用侧的线程数。
改配置或换模型之后,怎么知道效果没变差?
建一个十几到几十条的小用例集,覆盖你实际会问的几类问题,每次改动后跑一遍,看通过率和耗时变化。用例不用追求全面,够覆盖主要场景就行,重点是固定下来、每次跑同一套,这样才有可比性。站内 /tools/quality-monitor 有监控思路可以参考。