与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#程序改进版,增加了流式应当,不用一次性输出,更加人性化。
cs
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}");
}
}
}
}