用Ollama开发聊天程序实战

作者:澎涛 | 分类:科技 | 发布时间:2026-07-25 17:56:48 | 阅读:8 | 点赞:0 | 点踩:0
标签: 编程智能体

与Ollama服务通信支持以下三种方式:


1. cURL (命令行工具)

详细说明:cURL是一个利用URL语法在命令行下工作的文件传输工具。它是与Ollama交互最原始、最直接的方式,相当于手动构造并发送HTTP请求。你需要在终端(命令提示符)中逐条输入命令,并观察返回的原始JSON数据。


主要特点:


无需安装:大多数操作系统(Windows 10/11、macOS、Linux)都预装了cURL。


完全控制:你可以精确控制请求头(Header)和请求体(Body)的每一个字段。


即时反馈:适合快速测试API接口是否工作正常,查看原始响应结构。


缺点:手动处理复杂的JSON和流式响应比较繁琐,不适合构建大型应用。


详细使用示例

场景1:测试服务是否可用(列出已安装的模型)


curl http://localhost:11434/api/tags


返回示例:你会看到一个JSON数组,列出了所有已下载的模型名称、大小、修改时间等信息。


用途:确认Ollama服务已启动,并查看可用的模型名称(需要完整名称,如qwen3:4b)。


场景2:使用/api/generate端点进行单次文本生成

这个端点适合简单的问答或文本补全任务。


curl http://localhost:11434/api/generate -d '{ "model": "qwen3:4b", "prompt": "请用中文简单介绍一下人工智能。", "stream": false }'


参数解释:


-d:指定要发送的JSON数据。


"model":必须使用ollama list中显示的完整模型名称。


"prompt":你的提问内容。


"stream": false:设置为false表示等待完整响应后一次性返回;若为true,则会以Server-Sent Events(SSE)流式返回数据块。


返回示例:一个包含model、created_at、response(生成的文本)、done(是否完成)等字段的JSON对象。


场景3:使用/api/chat端点进行对话(支持多轮消息)

/api/chat端点的结构更清晰,适合构建聊天机器人。


curl http://localhost:11434/api/chat -d '{ "model": "gemma3:4b", "messages": [ {"role": "user", "content": "你好,请介绍一下你自己。"} ], "stream": false }'


参数解释:messages是一个消息数组,可以包含system(系统设定)、user(用户)和assistant(助手)角色的消息,实现上下文对话。


返回示例:响应中的message字段包含了助手的回复内容。


2. Python 库 (ollama)

详细说明:这是Ollama官方提供的Python SDK,它将底层的HTTP请求封装成了直观的Python函数。你不需要关心URL、JSON序列化或状态码,库会帮你处理这些细节。


主要特点:


高级封装:代码简洁,符合Python风格,易于学习和使用。


自动管理:自动处理JSON的序列化与反序列化,异常会以Python异常的形式抛出。


流式支持:通过简单的参数控制(如stream=True)即可实现流式响应,并迭代处理每个数据块。


生态集成:可以无缝集成到现有的Python AI项目(如数据分析、机器学习流水线)中。


详细使用示例

第一步:安装库


pip install ollama


第二步:编写Python代码


场景1:最简单的同步调用(使用/api/chat)


import ollama


# 调用chat接口,发送一条用户消息

response = ollama.chat(

  model='qwen3:4b', # 指定模型

  messages=[{'role': 'user', 'content': '为什么天空是蓝色的?'}]

)


# 直接打印助手的回复内容

print(response['message']['content'])

场景2:实现流式响应(逐字输出)

这对于提升用户体验非常重要,可以让你像在使用ChatGPT一样看到文字逐个生成。


import ollama # 启用流式输出 stream = ollama.chat( model='gemma3:4b', messages=[{'role': 'user', 'content': '写一首关于夏天的短诗。'}], stream=True # 关键参数 ) # 迭代处理每一个响应块 for chunk in stream: # 每个chunk包含一个'message'字段,其中'content'是本次新生成的部分文本 print(chunk['message']['content'], end='', flush=True) # 输出完成后换行 print()


场景3:维护多轮对话上下文

通过不断向messages列表中添加历史记录,模型就能“记住”之前的对话。


import ollama # 初始化对话历史 messages = [{'role': 'system', 'content': '你是一位知识渊博的历史老师。'}] while True: user_input = input("你: ") if user_input.lower() == 'exit': break # 将用户输入添加到历史中 messages.append({'role': 'user', 'content': user_input}) # 发送完整历史,获取回复 response = ollama.chat(model='qwen3:4b', messages=messages) assistant_reply = response['message']['content'] # 将助手的回复也添加到历史中,以便下一轮使用 messages.append({'role': 'assistant', 'content': assistant_reply}) print(f"助手: {assistant_reply}")


3. JavaScript/Node.js 库 (ollama)

详细说明:这是Ollama官方的JavaScript SDK,支持在浏览器和Node.js环境中使用。它基于现代的async/await异步编程模型,非常适合构建响应式的Web应用。


主要特点:


全栈支持:一套API既可以运行在前端浏览器中,也可以运行在后端Node.js服务器上。


Promise风格:完美融入现代JavaScript异步流程,可以使用async/await或.then()。


类型安全:如果使用TypeScript,可以获得完整的类型提示,减少错误。


易于集成:可以很方便地与React、Vue、Next.js等前端框架结合。


详细使用示例

第一步:安装库

在项目目录下初始化npm并安装:


npm init -y npm install ollama


第二步:编写Node.js代码


场景1:基本调用(使用/api/chat)

创建一个index.js文件:


jimport { Ollama } from 'ollama'; // 创建Ollama客户端实例,可指定主机地址 const ollama = new Ollama({ host: 'http://localhost:11434' }); async function basicChat() { try { // 发送聊天请求 const response = await ollama.chat({ model: 'gemma3:4b', messages: [{ role: 'user', content: 'Node.js和Python有什么区别?' }] }); // 打印助手的回复 console.log(response.message.content); } catch (error) { console.error('调用失败:', error); } } basicChat();


使用命令运行:node index.js


场景2:实现流式响应(实时显示)

通过监听async迭代器,可以逐块接收并处理响应,例如在Web页面中动态更新UI。


javascript


复制


下载


import { Ollama } from 'ollama';


const ollama = new Ollama({ host: 'http://localhost:11434' });


async function streamChat() {

  // 发起流式请求

  const streamResponse = await ollama.chat({

    model: 'qwen3:4b',

    messages: [{ role: 'user', content: '用JavaScript实现一个冒泡排序。' }],

    stream: true // 启用流式

  });


  // 遍历异步迭代器,获取每个数据块

  for await (const part of streamResponse) {

    // part.message.content 包含本次新生成的文本片段

    process.stdout.write(part.message.content); // 在控制台连续输出

  }

  console.log(); // 最终换行

}


streamChat();

场景3:在浏览器中使用

首先,通过<script type="importmap">或打包工具(如Vite)引入库。然后,在浏览器JavaScript中,用法与Node.js几乎完全相同,这使得前后端可以共享逻辑。


html


<!DOCTYPE html> <html> <head> <title>Ollama 浏览器聊天</title> </head> <body> <button id="chatBtn">问AI</button> <div id="output"></div> <script type="importmap"> { "imports": { "ollama": "https://cdn.jsdelivr.net/npm/ollama@0.5.0/+esm" } } </script> <script type="module"> import { Ollama } from 'ollama'; const ollama = new Ollama({ host: 'http://localhost:11434' }); document.getElementById('chatBtn').onclick = async () => { const output = document.getElementById('output'); output.innerText = '思考中...'; const response = await ollama.chat({ model: 'gemma3:4b', messages: [{ role: 'user', content: '推荐一本适合初学者的编程书。' }] }); output.innerText = response.message.content; }; </script> </body> </html>


总结与选择建议

方式 核心操作 代码量 适合场景 优点 缺点

cURL 手动拼接JSON,在终端执行 最少(单条命令) 测试API、调试、快速验证 直接、无需环境配置 难以处理复杂逻辑,不可用于生产

Python库 调用ollama.chat()函数 很少 后端服务、数据分析、脚本 高级封装、生态丰富、易于调试 仅限Python环境

JavaScript库 调用ollama.chat()异步方法 很少 Web前端、Node.js后端、全栈应用 异步高性能、前后端通用 需要对JavaScript异步编程有基本了解

下面用C#实现聊天程序

下面提供一个功能完整的C#控制台程序。它基于你提供的文章代码进行了重构和增强,支持从多个本地模型中自由选择,并优化了响应处理。


主要改进点:


可用的模型列表:动态获取并让你选择qwen3:4b或gemma3:4b(如果本地已拉取)。


正确的API调用:使用/api/chat端点(更适合多轮对话),并正确处理流式(stream: false)响应。


清晰的实体类:定义了与API响应匹配的ChatResponse类,方便解析。


友好的交互界面:支持连续对话(上下文未维护,仅作演示)和输入exit退出。


1. 创建实体类 (用于解析JSON响应)

在项目中新建一个ChatResponse.cs文件:


using System; using System.Text.Json.Serialization; namespace OllamaChat { // 对应 /api/chat 端点的响应格式 public class ChatResponse { [JsonPropertyName("model")] public string? Model { get; set; } [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } [JsonPropertyName("message")] public Message? Message { get; set; } [JsonPropertyName("done")] public bool Done { get; set; } } public class Message { [JsonPropertyName("role")] public string? Role { get; set; } [JsonPropertyName("content")] public string? Content { get; set; } } }


2. 主程序代码 (Program.cs)

将以下代码替换到你的Program.cs中:


using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Net.Http.Json;

using System.Text;

using System.Text.Json;

using System.Threading.Tasks;


namespace OllamaChat

{

  class Program

  {

    // 定义可用的模型列表(请确保这些模型已通过 ollama pull 下载到本地)

    private static readonly List<string> AvailableModels = new List<string>

    {

      "qwen3:4b",

      "gemma3:4b"

      // 如果你有其他模型,可以在这里添加,例如 "llama3.2:3b"

    };


    static async Task Main(string[] args)

    {

      Console.OutputEncoding = Encoding.UTF8; // 支持中文显示


      // 1. 检查Ollama服务是否可用

      using var client = new HttpClient();

      client.Timeout = TimeSpan.FromSeconds(120); // 推理可能需要较长时间

      try

      {

        var healthCheck = await client.GetAsync("http://localhost:11434/api/tags");

        if (!healthCheck.IsSuccessStatusCode)

        {

          Console.WriteLine("错误:无法连接到Ollama服务。请确保Ollama正在运行。");

          Console.WriteLine("请启动Ollama应用程序或运行 'ollama serve' 命令。");

          return;

        }

      }

      catch

      {

        Console.WriteLine("错误:无法连接到Ollama服务。请确保Ollama正在运行。");

        return;

      }


      // 2. 选择模型

      Console.WriteLine("=== Ollama C# 聊天程序 ===");

      Console.WriteLine("检测到的本地模型:");

      for (int i = 0; i < AvailableModels.Count; i++)

      {

        Console.WriteLine($"{i + 1}. {AvailableModels[i]}");

      }

      Console.Write("请选择模型的序号 (默认为1): ");

      string? inputChoice = Console.ReadLine();

      if (!int.TryParse(inputChoice, out int choice) || choice < 1 || choice > AvailableModels.Count)

      {

        choice = 1; // 默认选择第一个

      }

      string selectedModel = AvailableModels[choice - 1];

      Console.WriteLine($"你选择了模型: {selectedModel}\n");


      // 3. 开始聊天循环

      Console.WriteLine("输入你的问题,输入 'exit' 退出程序。\n");

      while (true)

      {

        Console.Write("你: ");

        string? userInput = Console.ReadLine();

        if (string.IsNullOrEmpty(userInput)) continue;

        if (userInput.Trim().ToLower() == "exit") break;


        // 调用Ollama API

        await SendChatRequestAsync(client, selectedModel, userInput);

        Console.WriteLine(); // 输出后换行

      }

    }


    private static async Task SendChatRequestAsync(HttpClient client, string model, string prompt)

    {

      try

      {

        // 构造请求体,使用 /api/chat 端点

        var requestBody = new

        {

          model = model,

          messages = new[]

          {

            new { role = "user", content = prompt }

          },

          stream = false // 设置为false以一次性获取完整响应

        };


        string json = JsonSerializer.Serialize(requestBody);

        var content = new StringContent(json, Encoding.UTF8, "application/json");


        // 发送POST请求

        HttpResponseMessage response = await client.PostAsync(

          "http://localhost:11434/api/chat",

          content

        );


        if (response.IsSuccessStatusCode)

        {

          // 解析JSON响应

          string responseJson = await response.Content.ReadAsStringAsync();

          var chatResponse = JsonSerializer.Deserialize<ChatResponse>(responseJson);


          if (chatResponse?.Message?.Content != null)

          {

            Console.WriteLine($"\n{model}: {chatResponse.Message.Content}");

          }

          else

          {

            Console.WriteLine("\n助手: (收到空响应)");

          }

        }

        else

        {

          string errorContent = await response.Content.ReadAsStringAsync();

          Console.WriteLine($"\n请求失败 (HTTP {response.StatusCode}): {errorContent}");

        }

      }

      catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)

      {

        Console.WriteLine("\n错误: 请求超时,模型推理时间过长。");

      }

      catch (Exception ex)

      {

        Console.WriteLine($"\n发生异常: {ex.Message}");

      }

    }

  }

}

3. 运行与使用步骤

确保环境:


已在本地安装Ollama。


已通过命令行拉取了模型,例如:


ollama pull qwen3:4b

ollama pull gemma3:4b

Ollama服务正在运行(可以在系统托盘中看到羊驼图标或通过ollama serve启动)。


创建项目:在Visual Studio或VS Code中新建一个控制台应用(.NET Core/.NET 5+)。


添加代码:将上述两个代码文件添加到项目中。


运行:按F5或dotnet run启动程序。根据提示选择模型,然后开始聊天。


这个程序为你提供了一个稳定、可直接运行的起点。你可以基于它进一步扩展,比如添加多轮对话的上下文记忆功能、实现流式输出等。希望这能帮助你顺利开始Ollama的C#开发!如果有其他问题,随时可以提出。


C#程序改进版,增加了流式应当,不用一次性输出,更加人性化。

using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Text;

using System.Text.Json;

using System.Threading.Tasks;

 

namespace OllamaChat

{

  class Program

  {

    private static readonly List<string> AvailableModels = new List<string>

    {

      "qwen3:4b",

      "gemma3:4b"

    };

 

    private static bool _showThinking = true;

 

    static async Task Main(string[] args)

    {

      Console.OutputEncoding = Encoding.UTF8;

 

      // 检查服务

      using var client = new HttpClient();

      client.Timeout = TimeSpan.FromSeconds(180);

      try

      {

        var healthCheck = await client.GetAsync("http://localhost:11434/api/tags");

        if (!healthCheck.IsSuccessStatusCode)

        {

          Console.WriteLine("错误:无法连接到Ollama服务。");

          return;

        }

        Console.WriteLine("✅ Ollama 服务连接正常\n");

      }

      catch

      {

        Console.WriteLine("错误:无法连接到Ollama服务。");

        return;

      }

 

      // 选择模型

      Console.WriteLine("=== Ollama C# 流式聊天程序 ===");

      for (int i = 0; i < AvailableModels.Count; i++)

      {

        Console.WriteLine($"{i + 1}. {AvailableModels[i]}");

      }

      Console.Write("请选择模型的序号 (默认为1): ");

      string? inputChoice = Console.ReadLine();

      if (!int.TryParse(inputChoice, out int choice) || choice < 1 || choice > AvailableModels.Count)

      {

        choice = 1;

      }

      string selectedModel = AvailableModels[choice - 1];

      Console.WriteLine($"你选择了模型: {selectedModel}\n");

 

      Console.Write("是否显示思考过程?(y/n, 默认为y): ");

      string? showInput = Console.ReadLine();

      _showThinking = string.IsNullOrEmpty(showInput) || showInput.ToLower() != "n";

 

      Console.WriteLine("\n输入你的问题,输入 'exit' 退出程序。\n");

      while (true)

      {

        Console.Write("你: ");

        string? userInput = Console.ReadLine();

        if (string.IsNullOrEmpty(userInput)) continue;

        if (userInput.Trim().ToLower() == "exit") break;

 

        await StreamChatAsync(client, selectedModel, userInput);

        Console.WriteLine("\n");

      }

    }

 

    private static async Task StreamChatAsync(HttpClient client, string model, string prompt)

    {

      try

      {

        var requestBody = new

        {

          model = model,

          prompt = prompt,

          stream = true

        };

 

        string json = JsonSerializer.Serialize(requestBody);

        var content = new StringContent(json, Encoding.UTF8, "application/json");

 

        using var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:11434/api/generate")

        {

          Content = content

        };

 

        using var response = await client.SendAsync(

          request,

          HttpCompletionOption.ResponseHeadersRead

        );

 

        if (!response.IsSuccessStatusCode)

        {

          string errorContent = await response.Content.ReadAsStringAsync();

          Console.WriteLine($"\n请求失败 (HTTP {response.StatusCode}): {errorContent}");

          return;

        }

 

        using var stream = await response.Content.ReadAsStreamAsync();

        using var reader = new System.IO.StreamReader(stream, Encoding.UTF8);

 

        string? line;

        bool hasContent = false;

        int chunkCount = 0;

        int tokenCount = 0;

 

        // ✅ 状态管理

        bool isThinkingPhase = true;   // 当前是否在思考阶段

        bool thinkingStarted = false;

        bool responseStarted = false;

 

        // ✅ 先显示模型名称

        Console.Write($"{model}: ");

 

        while ((line = await reader.ReadLineAsync()) != null)

        {

          if (string.IsNullOrEmpty(line)) continue;

 

          try

          {

            using var doc = JsonDocument.Parse(line);

            var root = doc.RootElement;

 

            // ✅ 处理思考过程(先显示)

            if (root.TryGetProperty("thinking", out var thinkingElement))

            {

              string thinkingChunk = thinkingElement.GetString() ?? "";

              if (!string.IsNullOrEmpty(thinkingChunk) && _showThinking)

              {

                // 第一次出现思考时,显示 "思考中...\n"

                if (!thinkingStarted)

                {

                  Console.ForegroundColor = ConsoleColor.DarkGray;

                  Console.Write("思考中...\n");

                  thinkingStarted = true;

                }

                // ✅ 逐字输出思考内容

                Console.Write(thinkingChunk);

              }

            }

 

            // ✅ 处理回答内容(在思考之后)

            if (root.TryGetProperty("response", out var responseElement))

            {

              string chunk = responseElement.GetString() ?? "";

              if (!string.IsNullOrEmpty(chunk))

              {

                // 如果还在思考阶段,切换到回答阶段

                if (isThinkingPhase && thinkingStarted && _showThinking)

                {

                  Console.ResetColor();

                  Console.Write("\n\n"); // 思考完成后换行

                  isThinkingPhase = false;

                }

                else if (isThinkingPhase && !thinkingStarted)

                {

                  // 没有思考过程,直接输出回答

                  isThinkingPhase = false;

                }

 

                // ✅ 逐字输出回答

                Console.Write(chunk);

                hasContent = true;

                chunkCount++;

              }

            }

 

            // ✅ 检查是否完成

            if (root.TryGetProperty("done", out var doneElement) && doneElement.GetBoolean())

            {

              if (root.TryGetProperty("eval_count", out var evalCount))

              {

                tokenCount = evalCount.GetInt32();

              }

 

              // ✅ 如果思考过程结束时没有换行,补充换行

              if (thinkingStarted && _showThinking && !isThinkingPhase)

              {

                // 已经换行了

              }

              else if (thinkingStarted && _showThinking && isThinkingPhase)

              {

                // 思考结束但没有回答(异常情况)

                Console.ResetColor();

              }

 

              Console.WriteLine($"\n\n[生成完成,共 {tokenCount} 个 token,{chunkCount} 个数据块]");

              break;

            }

          }

          catch (JsonException)

          {

            // 忽略无法解析的行

          }

        }

 

        if (!hasContent)

        {

          Console.WriteLine("(收到空响应)");

        }

      }

      catch (Exception ex)

      {

        Console.WriteLine($"\n发生异常: {ex.Message}");

      }

    }

  }

}

Lobster AI

cs

运行



————————————————

版权声明:本文为CSDN博主「weipt」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weipt/article/details/162956467


评论 (0)

登录 后发表评论

暂无评论