本文介绍基于ASP.NET Core
的Chatbot Restful API
开发,通过调用大语言模型的SDK,完成一个简单的示例。并且通过容器化进行部署.
安装
首先需要安装.NET环境,笔者在Ubuntu 22.04通过二进制包进行安装,Windows和Mac下都有installer可以用. 选择的版本是.NET 8
.
技术栈
我们分析一下要用到的技术栈,想要达到一个基本的demo需求,需要实现API,以及数据的查询存储,以及快速搭建运行环境.
因此我们选择postgresql
存储数据,docker
容器化实现快速部署. LLM
服务选用了OpenAI
.
Solution
首先创建一个solution,可以使用如下命令,或者使用IDE.
bash
dotnet new sln
然后新建webapi工程并添加到solution中
bash
mkdir WebApi
cd WebApi
dotnet new webapi
cd ..
dotnet sln add WebApi
完成创建后可以尝试restore并且简单build一下确认环境是否正常
bash
dotnet restore
dotnet build
创建控制器
新建的webapi
工程默认给了一个Program.cs
csharp
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
这个代码模板已经实现了一个简单的API,但不是我们需要的基于controller
的模式,为了使用controller,我们还要安装一些依赖包.
bash
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet tool uninstall -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
在Microsoft官方tutorial中给出的是:
bash
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet tool uninstall -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
因为我们不使用EntityFrameworkCore
,所以去掉了关于EntityFrameworkCore
的引用.
然后是创建controller,例如我们命名为ChatController
:
bash
dotnet aspnet-codegenerator controller -name ChatController -api -outDir Controllers
创建后,我们可以得到ChatController.cs
文件,稍作修改如下
csharp
using Microsoft.AspNetCore.Mvc;
namespace WebApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ChatController(IChatService chatService) : ControllerBase
{
[HttpPost]
[Route("[action]")]
public ActionResult<ChatResponse> Completions([FromBody] ChatRequest request)
{
var input = request.Text;
var result = chatService.Complete(request);
return Ok(result);
}
}
这时,如果我们直接运行工程,会发现在swagger的页面中,找不到刚才添加的Chat endpoint,因为还需要一些配置
在Program.cs
中,我们需要让服务映射我们新增的controller.
csharp
builder.Services.AddControllers();
app.MapControllers();
上面是两行需要增加的代码,使整个Program.cs
看起来这样
csharp
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
//entry point
app.Run();
这时,再运行就会发现swagger中已经有相关的endpoint。
OpenAI SDK
使用OpenAI SDK
非常简单,直接安装依赖包
bash
dotnet add package OpenAI-DotNet --version 8.3.0
准备好一个Open AI的key,配置到环境变量中,创建一个Factory获取ChatClient
(这是和OpenAI交互的客户端). 笔者使用的模型是gpt-3.5-turbo
,单纯是实惠,lol.
csharp
public abstract class OpenAiClientFactory
{
public static ChatClient Create()
{
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException("OpenAI API key not set");
return new ChatClient("gpt-3.5-turbo", apiKey);
}
}
有了ChatClient
后,就可以进行交互了. 这里涉及到的知识有AI对话点角色问题,涉及到3个角色:
- system
- assistant
- user
相关的概念可以查看文档.
进行一次AI对话就很方便了,直接调用complete方法即可
csharp
var prompt = $"""
You are an NLU expert help to do intent recognition, slot filling work. Now you are going to understand user input then give a proper result.
""";
var systemChatMessage = new SystemChatMessage(prompt);
var userChatMessage = new UserChatMessage(input);
var response = chatClient.CompleteChat([systemChatMessage, userChatMessage]);
这是一个模板代码,可以按照自己的需求修改.
容器化
为了快速运行,可以在项目中增加一个Dockrfile
Dockefile
FROM mcr.microsoft.com/dotnet/sdk:8.0
EXPOSE 5114
COPY . /app
WORKDIR /app
RUN dotnet restore && dotnet build
CMD [ "dotnet","run","--project","./WebApi" ]
通过docker快速打包后就能在所有支持docker的平台上快速演示demo.
在容器化方面,我们还可以使用docker compose来把数据库也和主程序一起启动。
Docker Compose 是 Docker 官方编排工具,用于定义和运行多容器 Docker 应用程序。使用 Docker Compose,你可以通过一个 YAML 文件来配置你的应用服务,然后使用一个简单的命令来启动和停止所有服务。
Docker Compose 的主要特点包括:
-
多容器编排:可以定义多个容器服务,并管理它们之间的依赖关系。
-
服务隔离:每个服务运行在独立的容器中,可以单独配置环境变量、卷挂载、网络等。
-
一键部署 :通过
docker-compose up
命令,可以一次性启动配置文件中定义的所有服务。 -
版本控制 :Docker Compose 文件(通常是
docker-compose.yml
)可以被版本控制系统跟踪,方便团队协作和持续集成。 -
环境一致性:在开发、测试和生产环境中使用相同的配置文件,确保环境一致性。
-
网络管理:可以定义服务之间的网络连接,使得服务之间可以通过服务名进行通信。
-
数据卷管理:可以定义数据卷,实现数据的持久化和共享。
-
扩展性:支持通过环境变量和扩展文件来扩展配置。
-
命令行工具:提供了丰富的命令行工具,用于管理服务的生命周期,如启动、停止、重建、日志查看等。
一个基本的 docker-compose.yml
文件示例如下:
yaml
version: '3'
services:
web:
image: "nginx:latest"
ports:
- "80:80"
volumes:
- "/var/www:/usr/share/nginx/html"
depends_on:
- db
db:
image: "postgres:latest"
environment:
POSTGRES_DB: "mydb"
POSTGRES_USER: "user"
POSTGRES_PASSWORD: "password"
在我们的例子中,还需要一个功能,就是在docker启动的时候,需要执行一段sql脚本初始化数据库,这个需求postgresql的image已经考虑到,只需要把要执行的脚本映射到一个特殊的路径即可。映射后,在container启动的时候,会执行sql.
yaml
- ./db.sql:/docker-entrypoint-initdb.d/init.sql
那么最终我们的docker-compose文件可能是这样:
yaml
version: '3'
services:
web:
build:
context: .
dockerfile: Dockerfile
environment:
- ConnectionStrings__DefaultConnection=Host=db;Port=5432;Database=db;Username=postgres;
- ASPNETCORE_ENVIRONMENT=Development
- OPENAI_API_KEY=sk-1234567890abcdef1234567890abcdef # REPLACE WITH YOUR OPENAI KEY
ports:
- "5000:5000"
depends_on:
- db
db:
image: bitnami/postgresql:latest
container_name: pg-container
restart: always
environment:
POSTGRES_USER: postgres
ALLOW_EMPTY_PASSWORD: yes
POSTGRES_DB: db
volumes:
- ./db.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
总结
使用ASP.NET Core
开发restful api相当方便,而借助容器的强大功能,部署到云环境进行快速验证也得以实现.