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

相关推荐
加载中loading...14 分钟前
Linux线程安全(二)条件变量实现线程同步
linux·运维·服务器·c语言·1024程序员节
Wx120不知道取啥名18 分钟前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
UMS攸信技术1 小时前
汽车电子行业数字化转型的实践与探索——以盈趣汽车电子为例
人工智能·汽车
biomooc1 小时前
R语言 | paletteer包:拥有2100多个调色板!
r语言·数据可视化·1024程序员节
ws2019071 小时前
聚焦汽车智能化与电动化︱AUTO TECH 2025 华南展,以展带会,已全面启动,与您相约11月广州!
大数据·人工智能·汽车
Hello.Reader1 小时前
FFmpeg 深度教程音视频处理的终极工具
ffmpeg·1024程序员节
堇舟2 小时前
斯皮尔曼相关(Spearman correlation)系数
人工智能·算法·机器学习
Y.O.U..2 小时前
STL学习-容器适配器
开发语言·c++·学习·stl·1024程序员节
就爱敲代码2 小时前
怎么理解ES6 Proxy
1024程序员节
憧憬一下2 小时前
input子系统的框架和重要数据结构详解
arm开发·嵌入式·c/c++·1024程序员节·linux驱动开发