AI之Ollama
介绍
什么是llama
LLaMA
(Large Language Model Meta AI)是Meta开发的大规模预训练语言模型,基于Transformer
架构,具有强大的自然语言处理能力。它在文本生成、问答系统、机器翻译等任务中表现出色。LLaMA模型有多个规模,从几亿到上千亿参数,适用于不同的应用场景。用户可以通过开源平台如Hugging Face获取LLaMA模型,并根据需要进行微调。LLaMA的灵活性和可扩展性使其在自然语言处理领域具有广泛的应用前景。
什么是 ollama
Ollama
是一款用于本地安装和管理大规模预训练语言模型的工具。它简化了模型的下载、安装和使用流程,支持多种流行的模型如GPT-4和llama。Ollama通过易于使用的命令行界面和API,帮助用户快速部署和运行自然语言处理任务。它还支持多GPU配置和模型微调,适应各种计算资源和应用需求。总之,Ollama为研究人员和开发者提供了一个高效、灵活的本地化大模型解决方案。
下载
ollama
官网提供了各种平台的安装包,以下是下载地址:https://ollama.com/download
安装模型
ollama安装完成后就可以开始安装模型,先选择一个模型安装,ollama
提供了一个页面供用户查询可以安装的开源模型。
在页面查询到想要安装的模块之后使用如下命令安装模型并启动:
ollama run [模型id]
安装示例
这里以llama3:latest
为例:
ollama run llama3:latest
对话
安装完成后会自动跳转到对话,等待输入,可以输入帮助命令\?
查看帮助;
添加UI
这里介绍如何给上面运行起来的大模型添加UI,我们选择open-webui
给ollama
添加UI,ollama
安装大模型后会启动一个服务端,这里使用open-webui
调用服务;
什么是Open-WebUI
Open-WebUI 是一个开源的用户界面框架,旨在提供简便的工具和接口,帮助用户轻松地访问和管理各种深度学习模型,尤其是大规模预训练语言模型。以下是对Open-WebUI的简要介绍:
-
开源框架:
Open-WebUI
是一个开源项目,提供了灵活且可定制的用户界面,用于与各种深度学习模型进行交互。 -
模型管理: 通过
Open-WebUI
,用户可以方便地加载、配置和管理多个深度学习模型,包括GPT-4
、BERT
等大规模预训练模型。 -
用户友好: 它提供了直观的界面,简化了模型使用过程,使非技术用户也能轻松上手进行自然语言处理任务。
-
集成支持:
Open-WebUI
支持与多种后端深度学习框架(如TensorFlow
、PyTorch
)集成,提供高效的推理和训练功能。 -
扩展性强: 用户可以根据需求自定义和扩展界面功能,以适应不同的应用场景和任务需求。
总之,Open-WebUI
为用户提供了一个高效、直观的界面,使得大规模深度学习模型的使用更加便捷和高效。
下载
以下是Open-WebUI
的Github
地址,从这里将项目下到本地
https://github.com/open-webui/open-webui
部署
这里使用的是Docker
部署,只需要在项目文件下使用Docker
命令启动一个容器即可,当ollama
与Open-WebUI
部署在一台机器上,只需要运行:
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
如果ollama
部署在服务器上,可以使用如下命令在启动Docker
容器时指定ollama
的地址
docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://example.com -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
OLLAMA_BASE_URL
是指ollama
暴露的API地址,一般为服务器地址加 11434。如:OLLAMA_BASE_URL=<http://192.168.0.111:11434>
使用
部署完成后在浏览器打开http://localhost:3000,展示的是Open-WebUI
的界面,第一次加载可能有点慢,然后注册一个用户就可以开始使用;
模型选择
在对话框的左上角有下拉框,可以看到已下载的所有模型,也可以搜索其他模型直接安装;
接入应用
本节介绍如何使用代码与AI进行交互,由于使用的是.net
技术栈,这里使用Microsoft
的SemanticKernel
框架对接Ollama
的聊天服务,SemanticKernel
基于Open-Api
协议,所以只要是遵循Open-Api
的AI都可以接入,如:GPT
、Cohere
等;
LLM(大语言模型)的OpenAPI协议是一种用于描述和调用大语言模型服务的接口规范。OpenAPI(Open API Specification)是一个定义了如何描述、生产、消费和可视化RESTful风格的Web服务的规范。它提供了一种标准的、语言无关的方式来描述API,使得开发人员和机器能够理解API的功能和如何与之交互。
在LLM的上下文中,OpenAPI协议可以用来定义大语言模型的输入和输出格式、调用方式、参数设置等。通过遵循OpenAPI协议,开发人员可以更容易地将大语言模型集成到自己的应用程序中,而不需要了解模型的具体实现细节。
例如,一个遵循OpenAPI协议的LLM服务可能会定义以下接口:
输入:用户提供的文本、上下文信息等。
输出:模型生成的文本、预测结果等。
调用方式:通过HTTP POST请求发送输入数据,并接收返回的输出数据。
参数设置:如模型的超参数、生成文本的长度限制等。
通过这种方式,开发人员可以使用任何支持HTTP请求的编程语言来调用LLM服务,从而实现各种自然语言处理任务,如文本生成、文本分类、机器翻译等。
总之,LLM的OpenAPI协议为开发人员提供了一种标准的方式来与大语言模型进行交互,使得大语言模型的集成变得更加简单和高效。
Nuget
创建一个项目,这里使用的是WebApi
项目,先安装SemanticKernel
以及ollama connector
包
注意:ollama connector 还是 alpha 版本,请勿用于生产,搜索时勾选
包括预发布版
配置服务
在Program.cs
文件中添加如下代码,添加一个Ollama客户端OllamaApiClient
,并初始化Ollama
服务地址和使用的模型Id,模型Id是下载时的模型名称,这里使用的是llama3:latest
;
Program.cs
var endpoint = new Uri("http://localhost:11434");
var modelId = "llama3:latest";
builder.Services.AddSingleton(new OllamaApiClient(endpoint, modelId));
接入接口
注入客户端
创建一个AIChatController
,将OllamaApiClient
客户端注入到控制器中;
[Route("api/[controller]")]
[ApiController]
public class AIChatController : ControllerBase
{
private readonly OllamaApiClient _ollamaApiClient;
public AIChatController(OllamaApiClient ollamaApiClient)
{
_ollamaApiClient = ollamaApiClient;
}
}
创建接口
创建一个接口用于接收客户端参数,这个接口需要有几点需求:
- 接口接收数据的同时接收用户id,以辨别是哪个用户发送的消息;
- 同时需要将客户的消息关联上下文,使对话可以关联起来,而不是简单、毫无联系的一问一答,所以需要记录历史消息;
- 由于AI的返回都是逐字逐句的流式返回,这里使用推送的形式响应客户端请求,所以使用SSE推送数据给客户端;
参数
先实现第一点需求,接收用户id和消息,这里简单使用一个model接收数据,正式做法是使用jwt解析token获取id;
/// <summary>
/// 聊天参数
/// </summary>
public class AIChatParam
{
/// <summary>
/// 用户id
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 数据
/// </summary>
public string Data { get; set; }
}
历史记录
接下来实现第二点需求,将聊天的上下文做关联,这就需要将历史消息记录到某个媒介,然后打包发送到模型中,模型就可以根据上下文接着回答问题;
这里简单的创建了一个类,使用一个Dictionary在应用内存中维护这些历史消息,如果需要可以将消息记录放到数据库;
/// <summary>
/// 历史记录
/// </summary>
public static class AIChatHistory
{
/// <summary>
/// 数据
/// </summary>
private static Dictionary<int, List<Message>> _data = new Dictionary<int, List<Message>>();
/// <summary>
/// 获取历史记录
/// </summary>
/// <param name="userId">用户id</param>
/// <returns></returns>
public static List<Message>? GetHistory(int userId)
{
return _data.GetValueOrDefault(userId)??new List<Message>();
}
/// <summary>
/// 最新记录
/// </summary>
/// <param name="userId">用户id</param>
/// <returns></returns>
public static Message GetLatestHistory(int userId)
{
Message msg = null;
if (_data.TryGetValue(userId, out var userData))
{
msg = userData.Last();
}
return msg;
}
/// <summary>
/// 添加
/// </summary>
/// <param name="userId">用户id</param>
/// <param name="msg">消息</param>
public static bool Add(int userId, Message msg)
{
if (_data.TryGetValue(userId, out var userData))
{
userData.Add(msg);
}
else
{
var list = new List<Message>();
list.Add(new Message(ChatRole.System, "以下对话请使用中文"));
list.Add(msg);
_data.Add(userId, list);
}
return true;
}
/// <summary>
/// 移除
/// </summary>
/// <param name="userId">用户id</param>
/// <returns></returns>
public static bool Clear(int userId)
{
if (_data.TryGetValue(userId, out _))
{
_data.Remove(userId);
}
return true;
}
}
接入
做好了准备这里就可以接入了,这里创建一个接口,用于接收用户输入的参数,接口获取到模型的回答后将响应推送给客户端,步骤如下:
- 设置Content-Type;
- 载入历史记录;
- 调用
ollama
提供的接口; - 获取回答后开始推送消息
- 将回答存到用户的历史记录中;
[Route("api/[controller]")]
[ApiController]
public class AIChatController : ControllerBase
{
private readonly OllamaApiClient _ollamaApiClient;
public AIChatController(OllamaApiClient ollamaApiClient)
{
_ollamaApiClient = ollamaApiClient;
}
/// <summary>
/// Chat
/// </summary>
/// <param name="param">问答参数</param>
/// <returns></returns>
[HttpPost]
public async Task Chat(AIChatParam param)
{
Response.ContentType = "text/event-stream";
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
var userId = param.UserId;
AIChatHistory.Add(userId, new Message { Role = ChatRole.User, Content = param.Data });
var history = AIChatHistory.GetHistory(userId);
var req = new OllamaSharp.Models.Chat.ChatRequest()
{
Messages = history,
Stream = true
};
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("User:" + param.Data);
var sb = new StringBuilder();
var content = _ollamaApiClient.ChatAsync(req);
Console.Write("Asist:");
await foreach (var chatMessageContent in content)
{
var msg = chatMessageContent?.Message.Content;
sb.Append(msg);
Console.Write(msg);
await Response.WriteAsync($"data: {msg}\n\n");
await Response.Body.FlushAsync();
}
AIChatHistory.Add(userId, new Message { Role = ChatRole.Assistant, Content = sb.ToString() });
Console.WriteLine();
}
}
通过以上逻辑就可以实现获取模型的回答,同时推送到客户端;
清除上下文
有时,在对话的过程中我们可能需要重新问新的问题,不需要再关联之前的上下文了,这就需要将历史记录重新载入,由于这里使用的是Dictionary,我们只需要将用户的历史记录移除即可,如果历史记录存在数据库中就需要额外的逻辑控制历史记录;最终代码如下:
[Route("api/[controller]")]
[ApiController]
public class AIChatController : ControllerBase
{
private readonly OllamaApiClient _ollamaApiClient;
public AIChatController(OllamaApiClient ollamaApiClient)
{
_ollamaApiClient = ollamaApiClient;
}
/// <summary>
/// Chat
/// </summary>
/// <param name="param">问答参数</param>
/// <returns></returns>
[HttpPost]
public async Task Chat(AIChatParam param)
{
Response.ContentType = "text/event-stream";
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
var userId = param.UserId;
AIChatHistory.Add(userId, new Message { Role = ChatRole.User, Content = param.Data });
var history = AIChatHistory.GetHistory(userId);
var req = new OllamaSharp.Models.Chat.ChatRequest()
{
Messages = history,
Stream = true
};
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("User:" + param.Data);
var sb = new StringBuilder();
var content = _ollamaApiClient.ChatAsync(req);
Console.Write("Asist:");
await foreach (var chatMessageContent in content)
{
var msg = chatMessageContent?.Message.Content;
sb.Append(msg);
Console.Write(msg);
await Response.WriteAsync($"data: {msg}\n\n");
await Response.Body.FlushAsync();
}
AIChatHistory.Add(userId, new Message { Role = ChatRole.Assistant, Content = sb.ToString() });
Console.WriteLine();
}
/// <summary>
/// 清除上下文
/// </summary>
/// <param name="userId">用户id</param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Clear(int userId)
{
var res = AIChatHistory.Clear(userId);
Console.WriteLine("清除上下文");
return Ok(res);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)