本地跑起了 ollama,模型也能在命令行里聊,但一接进自己的代码就开始卡:端口写哪个、请求体里那些参数各管什么、为什么偶尔第一个字等很久、并发一上来就排队。这篇不讲协议兼容层怎么配,只按「先打通、再调参、最后上生产」的顺序,把调用流程和参数含义、以及超时并发降级日志这些工程细节一条条过一遍。
本地跑起了 ollama,命令行里也能聊,但一接进自己的代码就开始卡:端口写哪个、请求体里那些参数各管什么、为什么偶尔第一个字等很久、并发一上来就排队。这篇不讲协议兼容层怎么配,只按「先打通、再调参、最后上生产」的顺序,把调用流程、参数含义,以及超时、并发、降级、日志这些工程细节一条条过一遍。
下面每节都给判断标准和可执行动作。很多数字(比如各模型的资源占用、速度基线)不适合写死在文章里,会随时间和你机器配置变,需要时去站内页面核对当时的数字。
先把调用链路跑通:地址、路径与最小请求
服务默认监听本机某个端口,先确认进程起来了、端口没被改。用 curl 打一条最小请求,比在代码里猜错参数快得多。建议先跑通「模型列表」这类只读请求,确认服务本身活着,再打生成类请求,排查范围会小很多。
- 生成类路径
- 把提示词交给模型产出内容,请求体字段最全,是调参的主战场
- 模型列表路径
- 只读,返回本地已经拉下来的模型清单,用来确认模型名没拼错
- 嵌入类路径
- 把文本转成向量,字段和生成类不一样,别把采样参数塞进去
- 流式开关
- 一个布尔字段,决定是一次性返回完整对象还是逐行吐
- 模型名
- 必须是本地实际存在的那个,拼错会直接报模型不存在
- 确认服务在线curl 访问模型列表路径,能返回 JSON 数组就说明服务正常,这一步不涉及模型推理,最省事。
- 打一条非流式请求请求体里放模型名和一段短输入,流式开关关掉,先把返回结构看清。看的是字段名和各段内容对应关系,不是输出质量。
- 切流式把流式开关打开再打一次,观察是不是一行一行往外吐 JSON。逐行解析,遇到空行或结束标记就收尾。
- 封装成函数把地址、路径、请求体组装单独抽出来,后面调参和换模型都只动这一处。
参数别照抄:哪些影响输出,哪些影响资源
参数大致分两类:一类管「模型怎么挑下一个词」,一类管「这次最多说多少」。前者影响风格和稳定性,后者影响显存和等待时间。别把网上抄来的一套参数直接用到另一个模型上,模型换了,同一批参数的效果就变了。
| 参数类别 | 影响什么 | 调大之后 | 什么时候不适用 |
|---|---|---|---|
| 采样类 | 输出发散程度 | 更随机、更有创意 | 要求答案稳定复现时不要调高 |
| 长度类 | 单次输出上限 | 更慢、更占资源 | 短问答场景没必要留太大 |
| 停止词 | 在哪里收尾 | 提前结束、更省时间 | 模型自带模板已定义收尾时会被覆盖 |
| 系统提示词 | 怎么组织输入 | 约束语气和格式 | 输入本身就很明确时收益不大 |
- 有些参数只在特定路径生效,或者被模型自带的模板覆盖,写完先拿一条固定输入回跑两遍,确认参数真的起作用了再往下调
- 系统提示词、模板、停止词属于「怎么组织输入」,建议单独拎出来存配置,不要硬编码在调用函数里
- 采样类参数建议一次只动一个,动两个以上就分不清是哪个起的作用
- 长度类分输入侧和输出侧,超了表现不一样,排错时要分清是哪一侧溢出
超时和并发:本地服务最容易踩的两个坑
本地推理是串行占资源的,同一时刻能并行几条取决于显存和模型大小。先用几条请求压一下,量出你这台机器实际的并发上限,再定代码里的并发数。这个数字因机器而异,别人的经验值只能当参考,可以到 /tools/speed-test 这类工具里先测一遍基线。
| 超时阶段 | 含义 | 典型陷阱 | 处理方式 |
|---|---|---|---|
| 建连接 | 请求发出到服务响应 | 服务没起、端口写错 | 和首字耗时分开设阈值 |
| 首个字 | 请求到第一个 token | 首次加载模型明显偏慢 | 按冷启动单独放宽,别拿它定全局阈值 |
| 整体 | 从发出到返回结束 | 输出很长时必然超 | 按长度动态估,不写死一个数 |
- 并发数、排队策略、失败重试的退避节奏写进调用封装里,别让上层业务代码各自乱发
- 首次加载模型那次会明显偏慢,把它当成冷启动单独对待,不要用它来定超时阈值
- 重试要带退避,连续快速重试只会把本来就紧张的资源压得更死
- 记录每次请求的排队等待时间,排队变长通常比单请求变慢更早暴露容量问题
降级与兜底:本地跑不动的时候怎么办
降级不是临时打补丁,而应该是一开始就设计好的几档配置。常见顺序是:换更小的本地模型、减少输出长度和上下文、减少并发、转走远程接口。把这四级做成配置项,切换时改配置而不是改代码。
判断该不该降级看三个信号:单请求耗时、排队长度、失败率。连续几次超时或报错就自动切下一档,别等用户来投诉。远程兜底会引入网络和计费的不确定性,切换前先想清楚数据能不能出机器,这条往往比技术选型更重要。
- 如果业务本身允许「先给个能用的答案」,本地模型适合当兜底
- 如果要求高准确度,本地更适合当预处理器而不是最终出口
- 降级开关要有手动强制档位,方便线上出问题时快速锁定某一档
- 每次降级命中都要记日志,否则事后分不清是模型不行还是容量不够
日志留什么、怎么判断改完有没有变好
每次调用至少记这几样:时间、模型名、参数组合、输入输出长度、首个字耗时、总耗时、是否命中降级。缺了长度和耗时,慢和贵都复盘不了。参数改动当成版本管理的一部分,同一批固定输入跑前后对比,比凭感觉说「好像变好了」靠谱得多。
- 建回归集准备一小批覆盖典型场景的输入,不用多,够代表主要用法就行。
- 跑基线当前配置跑一遍,把输出和耗时记下来,作为对比基准。
- 改一个变量一次只改一个参数或换一个模型,再跑同一批输入。
- 对比判断看的是稳定性和一致性,不是单个漂亮输出。变差就回滚,别硬撑。
- 日志里别整段存用户原文,长度和摘要够用就行,具体记哪些字段可以对照站内关于日志字段的说法来定
- 耗时和长度这两个字段一定要留,否则调优全是凭感觉
- 参数组合建议用哈希或短 ID 标记,字段太多时表格里塞不下
- 回归集本身也要定期更新,业务变了输入分布就变了
想了解各模型在你的场景下的资源占用和速度基线,可以到 /rankings 和 /tools/speed-test 核对当时的数字,文章里写死没有意义。
下一步带上你的模型名和一段真实输入,去 /tools/speed-test 跑一遍延迟基线,再回到代码里定超时和并发阈值。→常见问题
ollama 的 API 需要 API key 吗?
默认在本机跑、监听本机端口的情况下通常不需要密钥,谁在本机能访问到端口谁就能调。所以要么保证只有本机可访问,要么在它前面加一层自己的网关做鉴权和限流。具体的鉴权行为和端口暴露选项,以你所用版本的文档为准。
为什么第一次调用特别慢,后面就快了?
多数情况是模型首次加载进显存或内存的开销,这一次数倍于后续请求很常见。处理办法是把冷启动和稳态分开对待:超时阈值按稳态定,服务启动后可以发一条预热请求把模型提前拉起来,别让第一个真实用户承担加载时间。
并发一高就报错或者卡住,怎么调?
先量出这台机器实际能同时处理几条,再把这个数字写进调用侧的并发限制里,超出的请求排队而不是硬发。同时把退避重试做好,连续失败就降级。本地服务本身不擅长扛突发流量,削峰只能在外层做。