ASP.NET Core开发Chatbot API

本文介绍基于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个角色:

  1. system
  2. assistant
  3. 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 的主要特点包括:

  1. 多容器编排:可以定义多个容器服务,并管理它们之间的依赖关系。

  2. 服务隔离:每个服务运行在独立的容器中,可以单独配置环境变量、卷挂载、网络等。

  3. 一键部署 :通过 docker-compose up 命令,可以一次性启动配置文件中定义的所有服务。

  4. 版本控制 :Docker Compose 文件(通常是 docker-compose.yml)可以被版本控制系统跟踪,方便团队协作和持续集成。

  5. 环境一致性:在开发、测试和生产环境中使用相同的配置文件,确保环境一致性。

  6. 网络管理:可以定义服务之间的网络连接,使得服务之间可以通过服务名进行通信。

  7. 数据卷管理:可以定义数据卷,实现数据的持久化和共享。

  8. 扩展性:支持通过环境变量和扩展文件来扩展配置。

  9. 命令行工具:提供了丰富的命令行工具,用于管理服务的生命周期,如启动、停止、重建、日志查看等。

一个基本的 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相当方便,而借助容器的强大功能,部署到云环境进行快速验证也得以实现.

相关推荐
小陈phd2 小时前
OpenCV从入门到精通实战(九)——基于dlib的疲劳监测 ear计算
人工智能·opencv·计算机视觉
Guofu_Liao3 小时前
大语言模型---LoRA简介;LoRA的优势;LoRA训练步骤;总结
人工智能·语言模型·自然语言处理·矩阵·llama
向宇it4 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo5 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC6 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf20236 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
直裾6 小时前
Scala全文单词统计
开发语言·c#·scala
ZHOU_WUYI7 小时前
3.langchain中的prompt模板 (few shot examples in chat models)
人工智能·langchain·prompt
如若1237 小时前
主要用于图像的颜色提取、替换以及区域修改
人工智能·opencv·计算机视觉
ZwaterZ7 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue