跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C#AI算法

C#整合Ollama实现本地大语言模型调用

如何在 Windows 环境下安装配置 Ollama 本地大模型服务,并通过 C#语言集成OllamaSharp SDK 和 Semantic Kernel 进行开发。内容涵盖模型下载、命令行操作、HTTP 接口调用、简单对话、多轮对话、Function Call 工具调用、文本嵌入以及多模态图片理解等功能。文章提供了完整的代码示例,包括余弦相似度计算、图像 Base64 转换及请求重定向处理器实现,并对比了两种 SDK 的使用差异,最后总结了硬件要求、模型选择及接口兼容性等注意事项,旨在帮助开发者快速构建基于本地大模型的 C#应用程序。

魔法巫师发布于 2025/2/6更新于 2026/9/666 浏览
C#整合Ollama实现本地大语言模型调用

前言

近两年 AIGC 发展迅速,从单一的文本模型发展到多模态模型,使用门槛逐渐降低。Ollama 作为开源的大语言模型服务工具,允许用户在本地 PC 环境快速实验、管理和部署大型语言模型。本文将演示如何通过 C# 来使用 Ollama。

Ollama 简介

Ollama 是一个开源的大语言模型(LLM)服务工具,支持多种流行的开源大型语言模型,如 Llama 3.1、Phi 3、Qwen 2、GLM 4 等。它可以通过命令行界面轻松下载、运行和管理这些模型,无论是 CPU 或是 GPU 都可以运行,算力高的话推理速度更快。

安装

Ollama 的安装方式常用的有两种,一种是去官网下载,另一种是去 GitHub Release 下载,可以选择对应的系统版本进行下载。

安装完成之后可以修改常用的环境变量:

  • 通过 OLLAMA_MODELS 环境变量设置模型下载的位置,默认是在 C 盘,可以换成其他地址。
  • 通过 OLLAMA_HOST 设置 Ollama 服务监听的端口,默认的是 11434。

安装完成之后通过 version 查看,如果显示版本号则安装成功。

ollama --version

比较常用的指令不多,也很简单:

  • ollama list 列出本地下载的模型
  • ollama ps 查看正在运行的模型
  • ollama pull 模型标识 下载模型到本地,比如我要下载 qwen2:7b 则使用 ollama pull qwen2:7b
  • ollama run 模型标识 运行模型,如果已下载则直接运行,如果没下载则先下载再运行。

也可以将本地已有的 GGUF 模型导入到 Ollama 中去,操作很简单:

  1. 编写一个名为 Modelfile 的文件,写入以下内容:
FROM /path/to/qwen2-0_5b-instruct-q8_0.gguf
  1. 通过 Ollama 创建模型:
ollama create qwen2:0.5b -f Modelfile
  1. 运行刚创建的模型:
ollama run qwen2:0.5b

需要注意的是运行 7B 至少需要 8GB 的内存或显存,运行 13B 至少需要 16GB 内存或显存。这种量级的模型运行成本相对较低,适合做一些特定场景的推理任务。

命令启动

下载模型完成之后可以测试运行,通过 cmd 运行指令,比如运行起来 qwen2:7b 模型。这种方式比较简单,只能是文字对话的方式而且没有样式。

接口访问

Ollama 提供服务的本质还是 HTTP 接口,我们可以通过 HTTP 接口的方式来调用 /api/generate 接口。

curl http://localhost:11434/api/generate -d '{
  "model": "qwen2:7b",
  "prompt": "请你告诉我你知道的天气有哪些?用 json 格式输出",
  "stream": false
}'
  • model 设置模型的名称
  • prompt 提示词
  • stream 设置为 false 要求不要流式返回

因为是一次性返回所有内容,所以需要等待一会,如果需要流式输出可以设置为 true。等待一会后接口返回的信息如下所示:

{
    "model": "qwen2:7b",
    "created_at": "2024-09-04T06:13:53.1082355Z",
    "response": "...",
    "done": true,
    "total_duration": 70172634700
}

还有一种比较常用的操作就是嵌入模型,通俗点就是对文本或者图片、视频等信息进行特征提取转换成向量的方式,这时候需要使用 /api/embed 接口,请求格式如下所示,这里使用的向量化模型是 nomic-embed-text:

curl http://localhost:11434/api/embed -d '{
  "model": "nomic-embed-text:latest",
  "input": "我是中国人,我爱我的祖国"
}'

嵌入接口返回的数据格式包含 embeddings 数组,即向量数据。

可视化 UI

如果你想通过界面的方式通过 Ollama 完成对话服务,官方 Github 推荐的比较多,我选用的是 Open WebUI,简单的方式是通过 Docker 直接运行:

docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://你的 ollama 服务 ip:11434 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

启动成功后,在浏览器上输入 http://localhost:8080/,注册一个用户名登陆进来之后界面即可直接选择模型进行对话。

C#整合 Ollama

上面我们了解到了 Ollama 的基本安装和使用,明白了它的调用是基于 Http 接口来完成的。其实我也可以参考接口文档自行封装一套调用,但是没必要,因为有很多现成的 SDK 可以直接使用。

使用 Ollama SDK

这里使用的 C# 的 SDK 就叫 OllamaSharp,它的 Github 地址可以在其仓库找到。为什么选择它呢,其实也很简单,因为它支持 Function Call,这方便我们更早的体验新功能。安装它非常简单:

dotnet add package OllamaSharp --version 1.9.0
简单对话

简单的对话功能上手也没什么难度,都是简单代码:

string modelName = "qwen2:7b";
using var ollama = new OllamaApiClient(baseUri: new Uri("http://127.0.0.1:11434/api"));

Console.WriteLine("开始对话!!!");
string userInput = "";
do
{
    Console.WriteLine("User:");
    userInput = Console.ReadLine()!;
    var enumerable = ollama.Completions.GenerateCompletionAsync(modelName, userInput);
    Console.WriteLine("Agent:");
    await foreach (var response in enumerable)
    {
        Console.Write($"{response.Response}");
    }
    Console.WriteLine();

} while (!string.Equals(userInput, "exit", StringComparison.OrdinalIgnoreCase));
Console.WriteLine("对话结束!!!");

模型名称是必须要传递的,而且默认的是流式输出,如果想一次返回同样的是设置 stream 为 false。

多轮对话

如果需要进行分角色的多轮对话,要换一个方式使用,使用提供的 Chat 方式:

string modelName = "glm4:9b";
using var ollama = new OllamaApiClient(baseUri: new Uri("http://127.0.0.1:11434/api"));
Console.WriteLine("开始对话!!!");
string userInput = "";
List<Message> messages = [];
do
{
    //只取最新的五条消息
    messages = messages.TakeLast(5).ToList();
    Console.WriteLine("User:");
    userInput = Console.ReadLine()!;
    //加入用户消息
    messages.Add(new Message(MessageRole.User, userInput));
    var enumerable = ollama.Chat.GenerateChatCompletionAsync(modelName, messages, stream: true);
    Console.WriteLine("Agent:");
    StringBuilder builder = new();
    await foreach (var response in enumerable)
    {
        string content = response.Message.Content;
        builder.AppendLine(content);
        Console.Write(content);
    }
    //加入机器消息
    messages.Add(new Message(MessageRole.Assistant, builder.ToString()));
    Console.WriteLine();

} while (!string.Equals(userInput, "exit", StringComparison.OrdinalIgnoreCase));
Console.WriteLine("对话结束!!!");

多轮对话需要用 List<Message> 存储之前的对话记录,这样模型才能捕获上下文。

Function Call

高版本的 Ollama 支持 Function Call,当然这也要求模型也必须支持。其中 llama3.1 支持的比较好,美中不足是 llama3.1 对中文支持的不太好,所以我们简单的演示一下,这里使用的是 llama3.1:8b 模型。

首先需要定义方法,这样和模型对话的时候,框架会把方法的元信息抽出来发给模型,让模型判断调用哪个:

//定义一个接口,提供元信息
[OllamaTools]
public interface IMathFunctions
{
    [Description("Add two numbers")]
    int Add(int a, int b);
    [Description("Subtract two numbers")]
    int Subtract(int a, int b);
    [Description("Multiply two numbers")]
    int Multiply(int a, int b);
    [Description("Divide two numbers")]
    int Divide(int a, int b);
}

//实现上面的接口提供具体的操作方法
public class MathService : IMathFunctions
{
    public int Add(int a, int b) => a + b;
    public int Subtract(int a, int b) => a - b;
    public int Multiply(int a, int b) => a * b;
    public int Divide(int a, int b) => a / b;
}

有了上面的接口和实现类之后,我们就可以通过 Ollama 使用它们了:

string modelName = "llama3.1:8b";
using var ollama = new OllamaApiClient(baseUri: new Uri("http://127.0.0.1:11434/api"));
var chat = ollama.Chat(
    model: modelName,
    systemMessage: "You are a helpful assistant.",
    autoCallTools: true);

//给 Ollama 注册刚才定义的类
var mathService = new MathService();
chat.AddToolService(mathService.AsTools(), mathService.AsCalls());

while (true)
{
    try
    {
        Console.WriteLine("User>");
        var newMessage = Console.ReadLine();
        var msg = await chat.SendAsync(newMessage);
        Console.WriteLine("Agent> " + msg.Content);
    }
    finally
    {
        //打印本次对话的所有消息
        Console.WriteLine(chat.PrintMessages());
    }
}

这里需要设置 autoCallTools 为 true 才能自动调用方法。一般自动调用 Function Call 的时候会产生多次请求,但是我们使用的时候是无感知的,因为框架已将帮我自动处理了。

嵌入模型

上面我们提到过 Ollama 不仅可以使用对话模型还可以使用嵌入模型的功能,嵌入模型简单的来说就是对文本、图片、语音等利用模型进行特征提取,得到向量数据的过程。通过 Ollama SDK 可以使用 Ollama 的嵌入功能:

string modelName = "nomic-embed-text:latest";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://127.0.0.1:11434/api");
client.Timeout = TimeSpan.FromSeconds(3000);
using var ollama = new OllamaApiClient(client);
var embeddingResp = await ollama.Embeddings.GenerateEmbeddingAsync(modelName, "c#是一门不错的编程语言");
Console.WriteLine($"[{string.Join(",", embeddingResp.Embedding!)}]");

得到的就是向量信息。向量数据是可以计算相似度的,利用余弦夹角的概念可以计算向量的空间距离,空间距离越近,两个向量的相似度便越高。

比如我把下面两句话嵌入模型得到向量值,然后通过计算余弦夹角来比较它们的相似度:

var embeddingResp = await ollama.Embeddings.GenerateEmbeddingAsync(modelName, "c#是一门不错的编程语言");
var embeddingResp2 = await ollama.Embeddings.GenerateEmbeddingAsync(modelName, "c#是很好的语言");
Console.WriteLine("相似度:" + CosineSimilarity([.. embeddingResp.Embedding!], [.. embeddingResp2!.Embedding]));

//计算余弦夹角
public static double CosineSimilarity(double[] vector1, double[] vector2)
{
    if (vector1.Length != vector2.Length)
        throw new ArgumentException("向量长度必须相同");

    double dotProduct = 0.0;
    double magnitude1 = 0.0;
    double magnitude2 = 0.0;

    for (int i = 0; i < vector1.Length; i++)
    {
        dotProduct += vector1[i] * vector1[i];
        magnitude1 += vector1[i] * vector1[i];
        magnitude2 += vector2[i] * vector2[i];
    }

    magnitude1 = Math.Sqrt(magnitude1);
    magnitude2 = Math.Sqrt(magnitude2);

    if (magnitude1 == 0.0 || magnitude2 == 0.0)
        return 0.0;

    return dotProduct / (magnitude1 * magnitude2);
}
多模态模型

刚开始的对话模型都比较单一,都是简单的文本对话,随着不断的升级,有些模型已经支持多种格式的输入输出而不仅仅是单一的文本,比如支持图片、视频、语音等等,这些模型被称为多模态模型。使用 Ollama 整合 llava 模型体验一把,这里我是用的是 llava:13b。

我用这张图片对模型进行提问,代码如下所示:

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://127.0.0.1:11434/api");
client.Timeout = TimeSpan.FromSeconds(3000);
using var ollama = new OllamaApiClient(client);
string modelName = "llava:13b";
string prompt = "What is in this picture?";
System.Drawing.Image image = System.Drawing.Image.FromFile("1120.jpg");
var enumerable = ollama.Completions.GenerateCompletionAsync(modelName, prompt, images: [BitmapToBase64(image)], stream: true);
await foreach (var response in enumerable)
{
    Console.Write($"{response.Response}");
}

//Image 转 base64
public static string BitmapToBase64(System.Drawing.Image bitmap)
{
    MemoryStream ms1 = new MemoryStream();
    bitmap.Save(ms1, System.Drawing.Imaging.ImageFormat.Jpeg);
    byte[] arr1 = new byte[ms1.Length];
    ms1.Position = 0;
    ms1.Read(arr1, 0, (int)ms1.Length);
    ms1.Close();
    return Convert.ToBase64String(arr1);
}

我用提示词让模型描述图片里面的内容,然后把这张图片转换成 base64 编码格式一起发送给模型,模型返回的内容确实够强大,描述的信息很准确。

使用 Semantic Kernel

除了整合 Ollama SDK 以外,你还可以用 Semantic Kernel 来整合 Ollama。我们知道默认情况下 Semantic Kernel 只能使用 OpenAI 和 Azure OpenAI 的接口格式,但是其他模型接口并不一定和 OpenAI 接口格式做兼容。不过不用担心 Ollama 兼容了 OpenAI 接口的格式,即使不需要任何的适配服务也可以直接使用,我们只需要重新适配一下请求地址即可。

using HttpClient httpClient = new HttpClient(new RedirectingHandler());
httpClient.Timeout = TimeSpan.FromSeconds(120);

var kernelBuilder = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion(
       modelId: "glm4:9b",
       apiKey: "ollama",
       httpClient: httpClient);
Kernel kernel = kernelBuilder.Build();

var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

var history = new ChatHistory();
string? userInput;
do
{
    Console.Write("User > ");
    userInput = Console.ReadLine();
    history.AddUserMessage(userInput!);

    var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
        history,
        executionSettings: openAIPromptExecutionSettings,
        kernel: kernel);
    string fullMessage = "";
    System.Console.Write("Assistant > ");
    await foreach (var content in result)
    {
        System.Console.Write(content.Content);
        fullMessage += content.Content;
    }
    System.Console.WriteLine();

    history.AddAssistantMessage(fullMessage);
} while (userInput is not null);


public class RedirectingHandler : HttpClientHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var uriBuilder = new UriBuilder(request.RequestUri!) { Scheme = "http", Host = "localhost", Port = 11434 };
        //对话模型
        if (request!.RequestUri!.PathAndQuery.Contains("v1/chat/completions"))
        {
            uriBuilder.Path = "/v1/chat/completions";
            request.RequestUri = uriBuilder.Uri;
        }
        //嵌入模型
        if (request!.RequestUri!.PathAndQuery.Contains("v1/embeddings"))
        {
            uriBuilder.Path = "/v1/embeddings";
            request.RequestUri = uriBuilder.Uri;
        }            
        return base.SendAsync(request, cancellationToken);
    }
}

这里我们使用的是国产模型 glm4:9b,需要注意的是因为这里我们使用的是本地服务,所以需要适配一下服务的地址,通过编写 RedirectingHandler 类,并用其构造一个 HttpClient 实例传递给 Kernel。细心的同学可能已经发现了,这里我转发的 Ollama 服务的路径也变成了和 OpenAI 服务一样的路径,但是上面我调用 Ollama 服务用的是 /api/chat 和 /api/embed 这种地址的接口。这是因为 Ollama 为了兼容 OpenAI 的标准,专门开发了一套和 OpenAI 路径和参数都一样的接口,这一点是需要注意的。

同样的你可以通过 Semantic Kernel 使用嵌入模型的功能:

using HttpClient httpClient = new HttpClient(new RedirectingHandler());
httpClient.Timeout = TimeSpan.FromSeconds(120);

var kernelBuilder = Kernel.CreateBuilder()
    .AddOpenAITextEmbeddingGeneration(
       modelId:"nomic-embed-text:latest",
       apiKey:"ollama",
       httpClient: httpClient);
Kernel kernel = kernelBuilder.Build();
var embeddingService  = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
var embeddings = await embeddingService.GenerateEmbeddingsAsync(["我觉得 c#是一门不错的编程语言"]);
Console.WriteLine($"[{string.Join(",", embeddings[0].ToArray())}]");

这里需要注意的是 AddOpenAITextEmbeddingGeneration 方法是评估方法,将来版本有可能会删除的,所以默认的用 VS 使用该方法会有错误提醒,可以在 csproj 的 PropertyGroup 标签中设置一下 NoWarn 来忽略这个提醒。

<PropertyGroup>
   <OutputType>Exe</OutputType>
   <TargetFramework>net8.0</TargetFramework>
   <NoWarn>SKEXP0010;SKEXP0001</NoWarn>
</PropertyGroup>

总结与注意事项

本文介绍了如何通过 C#结合 Ollama 实现本地大语言模型的部署与调用,重点演示了在 C#应用中集成该功能的具体步骤。通过详细的安装指南与代码示例,帮助开发者快速上手。

首先我们介绍了 Ollama 的安装及基本设置和命令的使用。然后介绍了如何通过 Ollama 调用大模型,比如使用命令行、Http 接口服务、可视化界面。再次我们通过 C#使用了 Ollama SDK 来演示了对话模式、文本嵌入、多模态模型如何使用,顺便说了一下相似度计算相关。最后,我们展示了通过 Semantic Kernel 调用 Ollama 服务,因为 Ollama 对 OpenAI 的接口数据格式做了兼容,虽然还有部分未兼容,但是日常使用问题不大。

注意事项:

  1. 硬件资源:运行不同参数量级的模型对硬件要求不同。7B 模型建议至少 8GB 内存,13B 模型建议至少 16GB 内存。如果内存不足,推理速度慢且容易出错。
  2. 模型选择:不同模型擅长领域不同。Llama3.1 英文能力强但中文稍弱,Qwen2 和 GLM4 在中文场景下表现更佳。根据具体业务场景选择合适的模型。
  3. 接口兼容性:Semantic Kernel 对接 Ollama 时需注意路径重定向,确保请求被正确转发到 Ollama 的 API 端点。
  4. Function Call:使用 Function Call 功能时,需确保模型支持该特性,并正确配置 autoCallTools 参数以启用自动调用。

通过本文希望开发者可以快速入门本地大模型应用开发,掌握基础原理与使用方法,从而在实际项目中有效利用 AI 能力解决具体问题。

目录

  1. 前言
  2. Ollama 简介
  3. 安装
  4. 命令启动
  5. 接口访问
  6. 可视化 UI
  7. C#整合 Ollama
  8. 使用 Ollama SDK
  9. 简单对话
  10. 多轮对话
  11. Function Call
  12. 嵌入模型
  13. 多模态模型
  14. 使用 Semantic Kernel
  15. 总结与注意事项

更多推荐文章

查看全部
  • Open-Lovable 网页克隆工具及 cpolar 远程访问配置
  • 突破 LLM 上下文瓶颈:上下文内存虚拟化 CMV 的设计与实践
  • Java 可执行 JAR 包打包:三种 Maven 方案对比
  • Elasticsearch核心概念与Java客户端实战
  • Python 屏幕截图翻译工具实战
  • cJSON 1.7.19 源码剖析:数据结构、解析生成与注释规范
  • 基于 Spring Boot 与 WebSocket 的 Java 实时聊天室设计实践
  • 大模型时代人形机器人感知:视觉 - 语言模型应用
  • Spring Web MVC 入门:从概念到实践
  • Windows 系统下 vLLM 安装与配置实战
  • 基于 Whisper-large-v3 的短视频配音识别与字幕生成
  • 2026 年 3 月 18 日人工智能产业动态与前沿趋势
  • Java 数据类型、运算符与方法核心要点总结
  • RHEL 6 环境下 glibc 2.14 兼容性问题及解决方案
  • 前端函数防抖原理与实战实现
  • C++ 多线程开发完整指南
  • SpringBoot 原理:配置优先级、Bean 管理与自动配置
  • uv 虚拟环境管理:venv 创建、激活与 Python 版本指定
  • 基于单链表的队列完整实现
  • 相干伊辛机:医疗与医疗 AI 领域的组合优化新范式

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online