c# 调用 ollama 本地大模型

模型与算力

给 .NET 开发者一份用 C# 调 Ollama 本地大模型做对话集成的落地清单:从连通性自检、流式输出到超时与并发处理,按步骤排查。

你手上有个 .NET 项目,想接本地 Ollama 跑对话,结果要么连不上、要么等半天不肯吐字。问题通常不在模型,而在客户端写法:端点拼错、没开流式、超时太短、并发一股脑涌上去。这篇按顺序把每个环节该确认什么讲清楚,顺手给出先测速再定参数的思路,数字一律去站内对应页面核对当时的实际值。

你手上有个 .NET 项目,想接本地 Ollama 跑对话,结果要么连不上、要么等半天不肯吐字。问题通常不在模型,而在客户端写法:端点拼错、没开流式、超时太短、并发一股脑涌上去。这篇按顺序把每个环节该确认什么讲清楚,顺手给出先测速再定参数的思路。

需要提醒的是,很多数字(延迟、生成速度、并发上限)跟你的机器强相关,别人给的参考值意义有限。所以本文只讲判断标准和动作,具体数值一律到站内页面核对当时的实际值。

先确认能不能连上,再谈写代码

写代码之前先证明服务是活的。用浏览器或 curl 直接打一次本地接口,看到有响应再打开 IDE。这一步能省掉大量「是不是我代码写错了」的自我怀疑。

  1. 确认服务在跑本地跑一次拉取/对话命令,或直接打一次接口,能返回内容说明服务正常。不返回就先解决服务问题。
  2. 确认监听范围Ollama 默认只对本机开放。跨机器访问、容器访问、WSL 访问宿主,都要先改监听地址并重启服务,改完再从目标机器 ping 一次。
  3. 核对端点与端口本地 API 的端口、路径、OpenAI 兼容入口具体是什么,去 /ollama-local-api-address 这类站内页面核对,别凭记忆拼。
  4. 写个最小非流式请求C# 里先发一个最简单的非流式请求,能拿到完整 JSON 再往下做流式,一次只引入一个变量。

HttpClient 怎么配:超时、长连接、流式读取

对话请求耗时远高于普通接口,默认超时容易半路断。给对话单独设长超时,或者干脆设成无限、靠取消令牌来控制生命周期。同时复用单个 HttpClient 实例,不要每次请求 new 一个。

配置项容易踩的坑建议做法
超时用默认值,长回复被截断对话单独设长超时,或设无限 + 取消令牌
实例每次请求 new HttpClient全局复用单例,避免端口耗尽
流式读取等全部返回再处理用 ResponseHeadersRead,逐行读 NDJSON
返回形态把 SSE 当 NDJSON 解析按端点实际形态处理,见 /ollama-api-endpoints-2

流式输出要开 HttpCompletionOption.ResponseHeadersRead,逐行读 NDJSON,读到一行就解析一行往上抛。SSE 和 NDJSON 两种返回形态的处理方式不一样,接错会导致永远读不到内容,具体端点形态去 /ollama-api-endpoints-2 核对。

流式解析与 UI 刷新:别把界面卡死

读流要放在异步循环里,每解析出一条增量就触发一次回调。控制台直接写,桌面端用调度器切回 UI 线程。闭包捕获和跨线程更新控件是常见坑,先把数据拼在内存,再统一刷新,避免高频重绘。

  • 每解析一条增量就回调一次,不要攒到最后一起处理
  • 跨线程更新控件要用调度器切回 UI 线程
  • 最后一条消息可能带结束标记和统计字段,不处理会漏掉收尾信号
  • 把「首字延迟」和「每秒生成速度」分开记录,前者多半是模型加载,后者才跟硬件相关

想要一个可比较的性能基线,可以到 /tools/speed-test 走一遍,那里能测出你这台机器上的实际表现,再拿这个数字去决定并发和超时怎么设。

超时、取消与并发:上线前要过的一关

给用户可中断的取消按钮,把 CancellationToken 一路传到流读取,别留僵尸请求占着模型。本地单卡并发能力有限,同一时间只放一两个请求,具体上限由显存和模型大小共同决定。

取消令牌
从入口一路传到流读取,确保用户中断后请求真正停止
并发上限
由显存和模型大小决定,可用 /models/deploy 做测算
重试降级
流中断了是重发整段还是续接,取决于会话设计,先定规则再写代码
日志
记录请求耗时、返回 token 数和错误码,方便区分客户端与服务端问题

请求失败要有重试与降级策略。流中断了是重发整段还是续接,取决于你的会话设计,先定规则再写代码。日志里记录关键字段,出问题才分得清是客户端写法还是服务端负载。

上线前对照清单

下面这份清单按顺序过一遍,每一条都要能给出明确答案,含糊的地方就是后续出故障的地方。

  • 端点、端口、模型名三项与站内文档一致,且模型确实已经拉取到本地
  • 非流式打通后再切流式,两条路径都能走通
  • 超时、取消、重试三项都有明确策略,不是默认值
  • 在目标机器上实测一次首字延迟与生成速度,把结果记录成基线
  • 并发上限有依据,不是拍脑袋定的数字

需要强调一个适用范围:如果目标是多用户线上服务,本地单机部署的并发模型跟云端完全不同,上面这套清单更偏向单机或小团队内部工具。对外服务要先做容量评估,别直接把本地测试的并发数搬上线。

下一步把基线数字记下来,后续换模型或换硬件时才有对比依据。站内工具页能帮你测出这台机器上的实际表现。

常见问题

C# 调 Ollama 需要额外的 SDK 吗,还是直接发 HTTP 就行?

直接发 HTTP 就够,Ollama 提供的是本地 HTTP 接口,用 HttpClient 按它的端点发请求、按返回格式解析即可。官方和社区也有封装库,好处是省掉流式解析的样板代码,坏处是多一层依赖、升级节奏不由你控。小项目建议先手写一遍,搞清数据流再决定要不要换库。

为什么流式模式下一直收不到内容,非流式却正常?

多半是读取方式不对:流式要先把响应头读出来再逐块读正文,如果按普通方式等整个响应结束,就会一直卡住。另外返回可能是 NDJSON 而不是标准 SSE,按 SSE 的关键字去解析自然拿不到东西。先确认端点返回形态,再用逐行读取的方式处理,一般就通了。

本地接口要不要配鉴权?直接暴露有风险吗?

Ollama 默认不带鉴权,谁连上端口谁就能调用。只在本机跑问题不大,一旦跨机器、进容器或映射到公网就必须自己加一层反向代理做鉴权与限流。需要密钥相关做法可参考站内关于本地密钥与反向代理的整理,别把裸端口直接暴露出去。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。