介绍

什么是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提供了一个页面供用户查询可以安装的开源模型。

https://ollama.com/search

在页面查询到想要安装的模块之后使用如下命令安装模型并启动:

ollama run [模型id]

安装示例

这里以llama3:latest为例:

ollama run llama3:latest

对话

安装完成后会自动跳转到对话,等待输入,可以输入帮助命令\?查看帮助;

添加UI

这里介绍如何给上面运行起来的大模型添加UI,我们选择open-webuiollama添加UI,ollama安装大模型后会启动一个服务端,这里使用open-webui调用服务;

什么是Open-WebUI

Open-WebUI 是一个开源的用户界面框架,旨在提供简便的工具和接口,帮助用户轻松地访问和管理各种深度学习模型,尤其是大规模预训练语言模型。以下是对Open-WebUI的简要介绍:

  • 开源框架: Open-WebUI是一个开源项目,提供了灵活且可定制的用户界面,用于与各种深度学习模型进行交互。

  • 模型管理: 通过Open-WebUI,用户可以方便地加载、配置和管理多个深度学习模型,包括 GPT-4BERT 等大规模预训练模型。

  • 用户友好: 它提供了直观的界面,简化了模型使用过程,使非技术用户也能轻松上手进行自然语言处理任务。

  • 集成支持: Open-WebUI 支持与多种后端深度学习框架(如 TensorFlowPyTorch)集成,提供高效的推理和训练功能。

  • 扩展性强: 用户可以根据需求自定义和扩展界面功能,以适应不同的应用场景和任务需求。

    总之,Open-WebUI为用户提供了一个高效、直观的界面,使得大规模深度学习模型的使用更加便捷和高效。

下载

以下是Open-WebUIGithub地址,从这里将项目下到本地

https://github.com/open-webui/open-webui

部署

这里使用的是Docker部署,只需要在项目文件下使用Docker命令启动一个容器即可,当ollamaOpen-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技术栈,这里使用MicrosoftSemanticKernel框架对接Ollama的聊天服务,SemanticKernel基于Open-Api协议,所以只要是遵循Open-Api的AI都可以接入,如:GPTCohere等;

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;
}
}

创建接口

创建一个接口用于接收客户端参数,这个接口需要有几点需求:

  1. 接口接收数据的同时接收用户id,以辨别是哪个用户发送的消息;
  2. 同时需要将客户的消息关联上下文,使对话可以关联起来,而不是简单、毫无联系的一问一答,所以需要记录历史消息;
  3. 由于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;
}
}

接入

做好了准备这里就可以接入了,这里创建一个接口,用于接收用户输入的参数,接口获取到模型的回答后将响应推送给客户端,步骤如下:

  1. 设置Content-Type;
  2. 载入历史记录;
  3. 调用ollama提供的接口;
  4. 获取回答后开始推送消息
  5. 将回答存到用户的历史记录中;
[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之Ollama的更多相关文章

  1. 马里奥AI实现方式探索 ——神经网络+增强学习

    [TOC] 马里奥AI实现方式探索 --神经网络+增强学习 儿时我们都曾有过一个经典游戏的体验,就是马里奥(顶蘑菇^v^),这次里约奥运会闭幕式,日本作为2020年东京奥运会的东道主,安倍最后也已经典 ...

  2. 普通程序员如何转向AI方向

    眼下,人工智能已经成为越来越火的一个方向.普通程序员,如何转向人工智能方向,是知乎上的一个问题.本文是我对此问题的一个回答的归档版.相比原回答有所内容增加. 一. 目的 本文的目的是给出一个简单的,平 ...

  3. AI人工智能系列随笔

    初探 AI人工智能系列随笔:syntaxnet 初探(1)

  4. 游戏AI系列内容 咋样才能做个有意思的AI呢

    游戏AI系列内容 咋样才能做个有意思的AI呢 写在前面的话 怪物AI怎么才能做的比较有意思.其实这个命题有点大,我作为一个仅仅进入游戏行业两年接触怪物AI还不到一年的程序员来说,来谈这个话题,我想我是 ...

  5. MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义

    [转自网络]https://my.oschina.net/cers/blog/292191 PK Belongs to primary key 作为主键 NN Not Null 非空 UQ Uniqu ...

  6. 【AI开发第一步】微软认知服务API应用

    目录 介绍 API分类 使用‘视觉’API完成的Demo 点击直接看干货 介绍 从3月份Google家的阿尔法狗打败韩国围棋冠军选手李世石,到之后微软Build2016大会宣布的“智能机器人”战略.种 ...

  7. 扎克伯格开发的家用AI: Jarvis

    扎克伯格本周二在facebook发布了一篇文章,介绍自己利用个人时间开发的一套在自己家里使用的AI系统,并将它命名为Jarvis,对!就是电影钢铁侠里的AI助手Jarvis. 文章并没有讲细节的技术c ...

  8. 趣说游戏AI开发:对状态机的褒扬和批判

    0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...

  9. 自动绘图AI:程序如何画出动漫美少女

    序 全新的图形引擎与AI算法,高效流畅地绘出任何一副美丽的图像. IDE:VisualStudio 2015 Language:VB.NET/C# Graphics:EDGameEngine 第一节 ...

  10. 微软要如何击败Salesforce?Office365、Azure、Dynamics365 全面布局AI | 双语

    微软在上月宣布组建自己的 AI 研究小组.该小组汇集了超过 5000 名计算机科学家和工程师,加上微软内部研究部门,将共同挖掘 AI 技术. 与此同时,亚马逊,Facebook,Google,IBM ...

随机推荐

  1. SSL免费证书之Let’s Encrypt

    官网:https://letsencrypt.org/zh-cn 官网建议使用Certbot的方式进行安装,所以首先我们需要安装Certbot Certbot) 官网:Certbot (eff.org ...

  2. jackson 中对 null 的处理

    前情提要: 在项目中如何将null值转变为空字符串呢? @Configuration public class JacksonConfig { @Bean @Primary @ConditionalO ...

  3. 前端每日一知之css常用布局单位

    脑图在线链接 本文内容依据[js每日一题]公众号精彩文章总结而来

  4. openEuler欧拉使用rc.local实现开机自启动

    ​设置权限 chmod 775 /etc/rc.local 普通的单条是,直接写在rc.local里 /usr/local/nacos/bin/startup.sh -m standalone 复杂点 ...

  5. 中电金信:The Financial-Grade Digital Infrastructure

    01 Product Introduction   The Financial-Grade Digital Infrastructure is a digitally-enabled foundati ...

  6. Rocky Linux8升级9随记

    发现Rocky Linux已经升级了9.0版本,看着自己用着的8.5版本,跃跃欲试,于是就索性升级了.两者的支持年限没有太大的差别,先说我的想法:升不升级无所谓. 并不是9.0有什么特别牛的特性,只是 ...

  7. Spring JDBCTemplate 增删查功能 (简单易懂)

    需要的外部jar包: mysql-connector-javadruid 配置druid <!--配置数据库连接池--> <bean id="dataSource" ...

  8. Redis反序列化LocalDateTime时报错

    今天在整合redis和spring boot的时候,遇到了一个错误,记录一下. 报错如下: Could not read JSON: Cannot construct instance of `jav ...

  9. Qt编写安防视频监控系统22-摄像机搜索

    一.前言 摄像机搜索模块是后面新增加的,这个模块很有必要,一开始做视频监控系统的时候,那时候还没有研究出来一套纯Qt底层通信的onvif类,也就前几个月搞出来了,支持搜索设备信息和云台控制,所以马上加 ...

  10. Qt开源作品34-qwt无需插件源码

    一.前言 QWT,全称是Qt Widgets for Technical Applications,是一个基于LGPL版权协议的开源项目,可生成各种统计图.为具有技术专业背景的程序提供GUI组件和一组 ...