本地大模型部署:Ollama + C#
作为一个写了20年代码的老头子,我是怎么在本地跑起大模型的
说出来你们可能不信,我现在写代码已经离不开AI了。
不是那种科幻片里机器人要统治人类的 剧情啊,就是实打实的–每天写业务代码的时候旁边开着个本地大模型,有事没事问它两嘴,比google搜文档快多了。你们想想,年纪大了,记性不如从前了,什么C#的新特性啦、.NET的新API啦,有时候真记不全乎。
但是用云端的API吧说实话有几个问题:一是得联网,二是钱花得心疼(量大是真的贵),三是有些代码涉及内部业务,不太方便传出去。于是我就寻思–能不能在本地跑个大模型?
答案是:能!而且比我想象的简单多了。
这篇文章就是记录一下我的心路历程,从装环境到跑通第一个Hello World,保姆级教程,手把手带你们入坑。
先把Ollama装起来
我估计很多兄弟跟我一样,听到"本地大模型"第一反应是头皮发麻–这得买多贵的显卡啊?
其实吧,没那么夸张。现在开源模型做得越来越轻量了,普通的游戏显卡甚至纯CPU都能跑。我用的是Ollama这个工具,怎么说呢,它就是把"跑大模型"这件事做得像安装一个软件那么简单。
Windows安装(我是主力机)
在Windows上安装简直不能更简单:
- 去官网 https://ollama.com/download 下载安装程序
- 双击运行,等它安装完
- 就这么完了?
对就这么完了。安装程序会自动帮你配置环境变量,创建系统服务。你甚至不需要手动去做什么配置。
安装完成后,你可以在命令行试试:
ollama --version
能看到版本号就说明装好了。第一次运行的时候它会自己启动服务,默认监听11434端口。
Linux服务器安装
我另外有一台Linux服务器,有时候也在那边跑。安装方式也很简单:
curl -fsSL https://ollama.com/install.sh | sh
这个脚本会自动检测你的系统,下载对应的二进制文件,还会帮你配置systemd服务。装完以后可以用这些命令管理:
# 看看服务状态
systemctl status ollama
# 开机自启
systemctl enable ollama
# 重启服务
systemctl restart ollama
顺便说一句,如果你用的是Docker,官方也有镜像,一行命令就搞定:
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
我个人推荐直接装原生版本,性能会好一些,GPU加速也更容易发挥出来。
装完以后的配置
Ollama装好以后默认就能用,但有几个地方可以调教一下:
默认端口是11434,绑定地址是127.0.0.1。如果你想要局域网其他机器也能访问,可以改环境变量:
# Linux下
export OLLAMA_HOST=0.0.0.0:11434
# Windows下
set OLLAMA_HOST=0.0.0.0:11434
模型存在哪里?默认是用户目录下的.ollama/models。如果你的C盘空间紧张,可以改:
# 换个存放位置
export OLLAMA_MODELS=/data/ollama/models
选个趁手的模型
环境装好了,接下来就是挑模型。
你们可能问了,模型不都差不多吗?选一个下不就完了?
那可不对。选错模型的下场就是–要么跑不动(显存不够),要么回答质量太差(问它C#问题它给你扯Java),要么速度慢得让你怀疑人生。
几个好用的模型
日常开发首选:Qwen3:8b
没错,就是阿里巴巴达摩院开发的通义千问系列模型。实测下来,这个模型对C#的支持相当到位。你问它 async/await 怎么用,它能给你讲得明明白白;你让它帮你写个斐波那契数列,它能给你写出好几种实现方式。
关键是这个大小–8B参数,Q4_K_M量化后约4.9GB。我那台16GB内存的电脑跑起来毫无压力,RTX 3060显卡加持下每秒能出几十个token,速度感人。
复杂问题求解:DeepSeek-R1:8b
这个是推理型模型,特别适合处理那种"挠破头也想不出来"的问题。比如算法题啊、架构设计啊,扔给它准没错。
它有个特点–会先"思考"一下再回答你,把推理过程也展示出来。有时候看它是怎么分析的,比直接得到答案还有收获。
轻量备用:Llama 3.2:3b
如果你显卡都没有,就是纯CPU跑,那3B参数的Llama是个好选择。2GB左右,占用极低,写个简单的代码补全、问个技术概念完全够用。
量化是个啥?
可能有人要问了–什么是Q4_K_M?
这就涉及到一个很重要的概念:模型量化。
你们知道模型训练完以后,权重文件通常是 FP16(16位浮点数)甚至 FP32(32位浮点数)的。这精度是高,但太大了,一张RTX 4090也就能跑70B参数的模型。于是就有人研究怎么把精度降下来–从16位压到8位、4位,这就是"量化"。
量化级别从高到低大致是:F16(无损)> Q8_0 > Q6_K > Q5_K_M > Q4_K_M > Q4_0
Q4_K_M 是社区推荐最多的平衡方案–体积只有原来的四分之一左右,质量损失很小,性价比之王。我上面说的模型大小默认都是Q4_K_M量化后的结果。
顺便提一句,量化级别不仅影响模型大小,也会影响推理速度。量化位数越低,计算越快,但质量损失也越大。所以 Q4_K_M 在速度和质量之间取得了很好的平衡,这也是它成为社区推荐的原因。如果你追求极致速度且不在乎质量略降,可以试试 Q4_0;如果你显存充裕想要更高质量的回答,可以用 Q8_0 甚至 F16。
怎么拉模型?简单:
# 拉取默认版本(Ollama会自动选Q4_K_M)
ollama pull qwen3:8b
# 查看已下载的模型
ollama list
# 删除不用的模型
ollama rm llama3.2:3b
C#怎么跟Ollama对话
好了,环境装好了,模型也下好了。现在的问题是–怎么在C#里调用它?
方案一:OllamaSharp(强烈推荐)
这是我最推荐的方式。OllamaSharp是一个社区开发的SDK,微软官方都在用–Semantic Kernel、.NET Aspire都用它。质量绝对有保障。
先装包:
dotnet add package OllamaSharp
然后就可以开始写了。我先给你们看个最简单的例子:
using OllamaSharp;
// 初始化客户端,指向本地Ollama服务
var ollama = new OllamaApiClient(new Uri("http://localhost:11434"));
ollama.SelectedModel = "qwen3:8b";
// 问它一个问题
Console.WriteLine("请问:C#中async和await是什么?");
Console.Write("AI回答:");
await foreach (var stream in ollama.GenerateAsync("请用通俗易懂的语言解释C#中的async和await关键字"))
{
Console.Write(stream.Response);
}
看到没,这就是流式输出–AI回答的时候是一个字一个字蹦出来的,跟真人在打字一样。这个体验,比等它全部生成完再显示要好太多了。
方案二:自己写HttpClient
有的兄弟可能不喜欢依赖第三方包,那也没问题,Ollama本身提供的是标准的REST API,咱们自己写个HttpClient也能调。
using System.Net.Http.Json;
using System.Text.Json;
public class OllamaClient
{
private readonly HttpClient _http;
private const string BaseUrl = "http://localhost:11434";
public OllamaClient()
{
_http = new HttpClient
{
BaseAddress = new Uri(BaseUrl),
Timeout = TimeSpan.FromMinutes(5)
};
}
// 列出所有已下载的模型
public async Task<List<ModelInfo>> ListModelsAsync()
{
var response = await _http.GetFromJsonAsync<JsonDocument>("/api/tags");
var models = new List<ModelInfo>();
foreach (var model in response!.RootElement.GetProperty("models").EnumerateArray())
{
models.Add(new ModelInfo(
model.GetProperty("name").GetString()!,
model.GetProperty("size").GetInt64(),
model.GetProperty("modified_at").GetDateTime()
));
}
return models;
}
// 非流式生成(一次性返回)
public async Task<string> GenerateAsync(string model, string prompt)
{
var request = new
{
model,
prompt,
stream = false, // 关闭流式
options = new { temperature = 0.7 }
};
var response = await _http.PostAsJsonAsync("/api/generate", request);
var json = await response.Content.ReadFromJsonAsync<JsonDocument>();
return json!.RootElement.GetProperty("response").GetString()!;
}
// 流式生成
public async IAsyncEnumerable<string> GenerateStreamAsync(string model, string prompt)
{
var request = new { model, prompt, stream = true };
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/generate")
{
Content = JsonContent.Create(request)
};
using var response = await _http.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line)) continue;
using var json = JsonDocument.Parse(line);
var text = json.RootElement.GetProperty("response").GetString() ?? "";
yield return text;
// 检查是否已经完成
if (json.RootElement.GetProperty("done").GetBoolean())
break;
}
}
}
public record ModelInfo(string Name, long Size, DateTime ModifiedAt);
用起来是这样的:
var client = new OllamaClient();
// 看看本地有哪些模型
var models = await client.ListModelsAsync();
Console.WriteLine("本地模型列表:");
foreach (var m in models)
Console.WriteLine($" - {m.Name} ({m.Size / 1024 / 1024 / 1024:F1} GB)");
// 流式调用
Console.WriteLine("\n流式回答示例:");
await foreach (var chunk in client.GenerateStreamAsync("qwen3:8b", "用C#写一个快速排序算法"))
{
Console.Write(chunk);
}
代码写起来
光说不练假把式。我来带你们写一个完整的、能在实际项目里用的例子。
场景:做个本地代码助手
我写了一个简单的控制台程序,你可以问它C#相关的问题,它会实时显示回答。这个Demo包含了流式输出、错误处理、退出机制,是最基础的"可用"版本。
using System.Text;
using OllamaSharp;
Console.WriteLine("=== 本地 C# 代码助手 ===");
Console.WriteLine("输入你的问题,按回车发送。输入 exit 退出。\n");
// 初始化 Ollama 客户端
var ollama = new OllamaApiClient(new Uri("http://localhost:11434"));
ollama.SelectedModel = "qwen3:8b";
// 设置系统提示,让AI扮演C#专家
var chat = new Chat(ollama);
chat.SystemPrompt = @"你是一个资深的C#开发者,有20年的编程经验。
你的特点是:
1. 代码示例简洁、易懂、符合现代C#编码规范
2. 喜欢用async/await和多线程
3. 解释问题时喜欢用生活中的例子
4. 会指出潜在的性能问题和最佳实践";
while (true)
{
Console.Write("\n你: ");
var userInput = Console.ReadLine();
if (string.IsNullOrWhiteSpace(userInput))
continue;
if (userInput.ToLower() == "exit")
{
Console.WriteLine("再见!");
break;
}
Console.Write("\nAI: ");
var fullResponse = new StringBuilder();
try
{
// 流式输出
await foreach (var response in chat.SendAsync(userInput))
{
Console.Write(response);
fullResponse.Append(response);
}
Console.WriteLine(); // 换行
}
catch (Exception ex)
{
Console.WriteLine($"\n出错了: {ex.Message}");
}
}
这个程序跑起来大概是这样的:
=== 本地 C# 代码助手 ===
输入你的问题,按回车发送。输入 exit 退出。
你: 什么是async/await?
AI: 哈,这个问题问得好!让我用生活中...
再来一个:Web API 版本
有兄弟可能要做成Web服务给别人用。我写了一个ASP.NET Core的示例,支持流式输出(SSE)。注意:此示例需要 .NET 8 或更高版本:
using Microsoft.AspNetCore.Mvc;
using OllamaSharp;
var builder = WebApplication.CreateBuilder(args);
// 注册Ollama服务
builder.Services.AddSingleton(new OllamaApiClient(
new Uri(builder.Configuration["Ollama:Url"] ?? "http://localhost:11434"))
{
SelectedModel = builder.Configuration["Ollama:Model"] ?? "qwen3:8b"
});
var app = builder.Build();
app.MapPost("/api/chat", async (OllamaApiClient ollama, [FromBody] ChatRequest request) =>
{
var results = ollama.GenerateAsync(request.Prompt);
return Results.Stream(
stream: async yield =>
{
await foreach (var response in results)
{
var json = System.Text.Json.JsonSerializer.Serialize(new { content = response.Response });
await yield.WriteAsync($"data: {json}\n\n");
}
await yield.WriteAsync("data: [DONE]\n\n");
},
contentType: "text/event-stream",
cancellationToken: default
);
});
app.Run();
public record ChatRequest(string Prompt);
启动服务:
dotnet run
然后测试:
curl -X POST http://localhost:5000/api/chat \
-H "Content-Type: application/json" \
-d '{"Prompt":"用C#写一个单例模式"}'
你会看到回答像打字一样一行行出来。
踩过的坑
作为一个老程序员,什么坑没踩过。跟你们念叨念叨,省得你们再走弯路。
坑一:模型太大,显存不够
第一次我脑子一热,直接拉了个70B的模型。嗯,显存直接爆炸,加载都加载不进来。
后来学乖了–7B-8B是消费级显卡的安全范围,14B需要24GB显存,32B往上就得等专业卡了。不确定的时候先试试小的,不够再说。
坑二:模型加载慢
有时候启动模型要等十几秒甚至几十秒。这是因为模型要从硬盘加载到内存/GPU显存。
解决方案:Ollama有个keep_alive参数,控制模型在内存中保留多久。默认是5分钟。用完想立即释放:
// 直接调用HTTP API的方式(非OllamaSharp SDK)
var request = new { model = "qwen3:8b", keep_alive = "0m" };
var response = await httpClient.PostAsJsonAsync("http://localhost:11434/api/generate", request);
或者直接命令行:
ollama stop qwen3:8b
坑三:中文回答里有乱码
这个问题一般出现在Windows上,有时候流式输出的中文会显示成乱码。大概率是控制台编码问题。
解决方法:在Program.cs最开头加上:
Console.OutputEncoding = System.Text.Encoding.UTF8;
坑四:多线程并发
如果你的应用会有多个请求同时过来,要注意Ollama对并发的处理–它会用队列串行处理同一个模型的请求。
如果真的需要高并发,要么起多个Ollama实例(不同端口),要么加个信号量限制并发数:
public class OllamaSafeService
{
private readonly OllamaApiClient _ollama;
private static readonly SemaphoreSlim _semaphore = new(1, 1);
public OllamaSafeService(OllamaApiClient ollama)
{
_ollama = ollama;
}
public async IAsyncEnumerable<string> GenerateSafeAsync(string prompt)
{
await _semaphore.WaitAsync();
try
{
await foreach (var chunk in _ollama.GenerateAsync(prompt))
yield return chunk.Response;
}
finally
{
_semaphore.Release();
}
}
}
坑五:GPU没被用上
有的兄弟反映跑起来好慢,一问–他在用CPU跑,显卡白买了。
检查一下:
ollama ps
看Processor那一列,如果有GPU相关的信息说明用上了。如果全是CPU…检查一下驱动装好了没,NVIDIA驱动版本要在550以上。
坑六:请求超时
大模型生成内容是需要时间的,特别是CPU模式,可能要等好几分钟。如果你用的是默认的HttpClient超时时间,可能会遇到请求被断开的情况。
我之前就遇到过,正在等它生成一段代码,结果突然报错了。一查–超时了。
解决方法很简单,把Timeout调大:
var httpClient = new HttpClient
{
BaseAddress = new Uri("http://localhost:11434"),
Timeout = TimeSpan.FromMinutes(10) // 大模型生成可能很慢
};
坑七:上下文窗口
有些问题需要很长的上下文,比如让AI分析一段很长的代码或者文档。如果你直接问,它可能会"忘"了后面说的内容。
这是因为模型有一个上下文窗口大小的限制。默认通常是4096个token(小显存设备),不过Ollama会根据你的显存大小自动调整——显存越大,默认上下文窗口也越大。你可以理解为AI的"短期记忆"就这么多,超出部分会被"遗忘"。
解决方法:可以手动设置更大的上下文窗口。
在请求中指定:
// 在请求中指定
var options = new RequestOptions
{
Options = new Options
{
NumCtx = 8192 // 扩大上下文到8K
}
};
await foreach (var stream in ollama.GenerateAsync(prompt, options))
{
// ...
}
或者通过环境变量全局设置:
set OLLAMA_CONTEXT_LENGTH=8192 # Windows
export OLLAMA_CONTEXT_LENGTH=8192 # Linux
但要注意,上下文窗口越大,显存占用越高。如果你的显卡不够大,别硬撑。
坑八:网络访问不了
这个问题主要出现在国内–Ollama默认会尝试访问一些云端资源,在国内网络环境下可能会卡住。
解决方法:禁用云端功能。
注意:
OLLAMA_NO_CLOUD环境变量和server.json中的disable_ollama_cloud配置已在 Ollama 官方 FAQ 中正式记录。禁用云端功能后,将无法使用 Ollama 的云端模型和 Web 搜索功能。
方式一:环境变量
set OLLAMA_NO_CLOUD=1 # Windows
export OLLAMA_NO_CLOUD=1 # Linux
方式二:配置文件。在 ~/.ollama/server.json 中添加:
{
"disable_ollama_cloud": true
}
坑九:选错模型回答质量差
有时候你问AI一个问题,它给的回答牛头不对马嘴。先别急着骂AI,先看看是不是模型选错了。
比如你用的是Llama,问它C#的问题–Llama对中文的支持本身就一般,对C#库的了解也不如阿里系的模型。我测试下来,Qwen系列在中文代码这块确实做得更好。
再比如你问的是算法题,结果用了一个"对话型"模型,效果不如"推理型"模型。DeepSeek-R1就是专门做推理的,做数学题、逻辑题特别在行。
所以我的经验是:不同场景用不同的模型,别一个模型打天下。
坑十:模型卸载不掉
有时候你想换模型,结果旧的怎么都停不掉。或者是重启Ollama服务以后,模型还在内存里占着地方。
试试这些命令:
# 强制停止模型
ollama stop model-name
# 如果停不掉,重启整个服务
systemctl restart ollama # Linux
# 或者 Windows 服务管理器里重启 Ollama
# 最暴力的一招:kill掉进程再重启
taskkill /F /IM ollama.exe # Windows
pkill ollama # Linux
我一般不建议用kill这种方法,容易造成数据损坏。但如果真的卡死了,该用还是得用。
日常使用的一些小技巧
用了这么久,跟你们分享几个我日常在用的小技巧。
怎么判断模型有没有在跑
有时候不确定模型是在运行还是已经卸载了,可以直接敲:
ollama ps
这个命令会列出当前加载的模型。如果你想看本地有哪些可用模型:
ollama list
怎么知道模型详细信息
想看某个模型的具体参数?可以:
ollama show qwen3:8b
这个会告诉你模型的参数大小、量化版本、上下文长度等信息。
怎么快速切换模型
如果你有多个模型,想快速切换试试效果,可以这样做:
# 停止当前模型
ollama stop qwen3:8b
# 启动另一个模型
ollama run deepseek-r1:8b
本地模型也能有"记忆"
你们可能注意到了,OllamaSharp里的Chat类会自动记录对话历史。这意味着你可以这样连续对话:
var chat = new Chat(ollama);
chat.SystemPrompt = "你是一个C#专家";
// 第一轮
await foreach (var r in chat.SendAsync("什么是泛型?"))
Console.Write(r);
// 第二轮 - AI还记得上一轮的内容
await foreach (var r in chat.SendAsync("那 covariance 呢?"))
Console.Write(r);
这就是多轮对话,AI知道你在讨论什么。
调整生成参数
大模型生成内容的行为可以通过参数调整。这里说几个最常用的:
temperature(温度):控制随机性。0到2之间,越低越"保守",越高越"有创意"。一般代码生成用0.2到0.5,对话用0.7到1.0。
var options = new RequestOptions
{
Options = new Options
{
Temperature = 0.3 // 更确定性的输出
}
};
top_p:另一种控制随机性的方式,通常和temperature二选一。我一般习惯用temperature。
num_predict(最大生成长度):限制生成多少token,防止它废话太多。
让AI只输出代码
有时候你不需要AI解释那么多,只想要它生成的代码。可以设置:
chat.SystemPrompt = "你是一个C#开发者。直接给出代码,不要解释。如果需要解释,用注释写在代码里。";
或者用JSON格式输出,让它返回结构化的内容:
# Ollama支持JSON输出模式
ollama run qwen3:8b "用JSON格式返回一个用户对象,包含Name,Age,Email字段"
性能优化的经验之谈
跑了这么久,在性能这块也积累了一些经验。
GPU才是本体
我必须强调一点——如果有条件一定要用GPU跑。CPU和GPU的性能差距是数量级的。
拿我自己的机器举例:
- RTX 3060 跑 Qwen3:8b,每秒能出 30-50 个token
- 纯CPU跑同样的模型,每秒只有 3-5 个token
这是个什么概念?CPU模式下生成一个完整的函数要等几十秒,GPU模式可能几秒就出来了。体验完全不一样的。
所以如果你真的想认真用本地大模型,第一件事——检查你的显卡驱动装好没。NVIDIA驱动要在550以上,AMD需要ROCm,Apple Silicon自带M系列芯片天然支持。
内存比显存更重要
很多人以为显存够就行,其实内存也很重要。因为模型加载的时候,数据是先加载到内存,然后再拷贝到显存。
我的建议:
- 8B模型:至少16GB系统内存
- 14B模型:至少32GB系统内存
- 32B模型:至少64GB系统内存
如果内存不够,可能会用到交换区,那速度会变得很慢。
并发要谨慎
Ollama处理并发请求的能力有限。我之前做过测试,同时发3个请求,结果响应时间变成了原来的3倍——它实际上是串行处理的。
所以我的建议是:
- 如果是个人使用,直接串行调用就好
- 如果是Web服务需要并发,用消息队列做限流
- 或者多开几个Ollama实例,不同端口
预热模型
第一次调用模型的时候会比较慢,因为需要加载模型。但如果你频繁调用,速度就会快很多,因为模型一直在内存里。
如果你是做Web服务,可以在服务启动时先调用一次"预热":
// 在 Program.cs 中
var ollama = new OllamaApiClient(new Uri("http://localhost:11434"));
ollama.SelectedModel = "qwen3:8b";
// 预热 - 发送一个简单请求让它加载
await foreach (var response in ollama.GenerateAsync("hello"))
{
// 不需要处理响应,只是触发模型加载
}
Console.WriteLine("模型预热完成");
下一步打算啥?
这篇文章写的是最基础的用法。实际上玩了一段时间之后,我发现还有很多有意思的东西可以折腾:
多模态模型:有些模型能看图。比如你扔一张UI截图给它,它能帮你分析布局合不合理。这对于我们做桌面开发的挺有用。
Embedding向量化:Ollama支持生成文本嵌入向量,可以用来做本地知识库。比如把你公司的技术文档都向量化存进去,问问题的时候先检索相关文档再让AI回答,准确性会高很多。
模型微调:如果通用模型在某个垂直领域表现不好,可以试试用微调来让它更专业。不过这个需要一定数据量和算力门槛。
最后提一嘴——本地大模型虽然好用,但它毕竟是个"本地版",跟GPT-4这种顶级闭源模型比还是有差距的。我的使用习惯是:简单问题本地解决,复杂问题还是去问云端API。本地模型作为日常开发的辅助工具,那是相当称职。
好了,絮絮叨叨写了不少,希望对你们有帮助。有问题欢迎评论区聊聊,咱们下回再见。
更多推荐


所有评论(0)