Cách Tạo Ra Một AI Của Riêng Mình Với NestJS

Cách Tạo Ra Một AI Của Riêng Mình Với NestJS
8k 3 tháng trước

Nếu bạn đã quen thuộc với việc lập trình và muốn bắt tay vào xây dựng một ứng dụng AI của riêng mình, thì bạn đã đến đúng chỗ. Trong bài viết này, mình sẽ hướng dẫn các bạn cách tạo ra một AI đơn giản bằng cách sử dụng NestJS, một framework mạnh mẽ cho Node.js. Hãy cùng mình đi từng bước từ cài đặt cho đến triển khai nhé!

1. Cài đặt NestJS CLI

Đầu tiên, chúng ta cần cài đặt NestJS CLI để có thể dễ dàng tạo và quản lý dự án.

npm install -g @nestjs/cli

Lệnh trên sẽ cài đặt CLI của NestJS trên toàn cầu.

Sau khi cài đặt xong, tạo một dự án mới với lệnh:

nest new my-ai-app

Lệnh này sẽ tạo một dự án mới tên là my-ai-app.

Cấu trúc thư mục dự án

Khi dự án được tạo xong, cấu trúc thư mục của dự án sẽ như sau:

src/
├── app.module.ts
├── chat/
│   ├── chat.controller.ts
│   ├── chat.module.ts
│   ├── chat.service.ts
├── workflow/
│   ├── workflow.module.ts
│   ├── workflow.service.ts
├── types/
│   ├── openai.type.ts

2. Tạo các Module và Service

2.1 Tạo Workflow Module

WorkflowModule sẽ là một module toàn cục (Global Module) cung cấp các dịch vụ liên quan đến AI cho toàn bộ ứng dụng.

workflow.module.ts

import { Global, Module } from '@nestjs/common';
import { WorkflowService } from './workflow.service';

// Đánh dấu module này là toàn cục (Global)
@Global()
@Module({
  providers: [WorkflowService], // Cung cấp WorkflowService
  exports: [WorkflowService],   // Xuất WorkflowService để sử dụng ở các module khác
})
export class WorkflowModule {}

workflow.service.ts

import type { ChatInterface } from './types/openai.type';
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import OpenAI from 'openai';

@Injectable() // Đánh dấu lớp này là một provider có thể được inject
export class WorkflowService implements OnModuleInit {
  private openai: OpenAI; // Đối tượng OpenAI để gọi API

  constructor(private configService: ConfigService) {} // Inject ConfigService để lấy các cấu hình

  // Getter để lấy model name từ cấu hình
  get modelName() {
    return this.configService.get('MODEL_NAME');
  }

  // Getter để lấy đối tượng OpenAI
  get openAI() {
    const openai = this.openai;
    if (!openai) {
      this.onModuleInit(); // Nếu openai chưa khởi tạo thì khởi tạo lại
      return this.openai;
    }
    return openai;
  }

  onModuleInit() {
    const API_KEY = this.configService.get('CHATAI_KEY');  // Lấy API key từ cấu hình
    this.openai = new OpenAI({
      apiKey: API_KEY,  // Khởi tạo đối tượng OpenAI với API key
    });
  }

  getTrainingBase({
    chatHistory,
    newChat,
  }: {
    chatHistory: ChatInterface[];
    newChat: ChatInterface;
  }): ChatInterface[] {
    // Tạo nội dung training cơ bản
    const content = `You are a helpful assistant that helps developers with their coding problems. The developer is asking for help with a coding problem.`;

    // Trả về lịch sử chat cùng với đoạn hội thoại mới
    return [
      ...chatHistory,
      {
        role: 'developer',
        content: content.trim(),
      },
      newChat,
    ];
  }
}

2.2 Tạo Chat Module

ChatModule sẽ sử dụng WorkflowService để xử lý các yêu cầu trò chuyện từ người dùng.

chat.module.ts

import { Module } from '@nestjs/common';
import { ChatController } from './chat.controller';
import { ChatService } from './chat.service';

@Module({
  controllers: [ChatController], // Khai báo controller cho module
  providers: [ChatService],     // Khai báo service cho module
})
export class ChatModule {}

ChatModule sẽ chứa các phần điều khiển và dịch vụ liên quan đến việc xử lý các yêu cầu trò chuyện.


chat.service.ts

import { ChatInterface } from '@/workflow/types/openai.type';
import { WorkflowService } from '@/workflow/workflow.service';
import { Injectable } from '@nestjs/common';
import OpenAI from 'openai';

@Injectable() // Đánh dấu lớp này là một provider có thể được inject
export class ChatService {
  private readonly openai: OpenAI;
  private readonly modelName: string;

  constructor(private readonly workflow: WorkflowService) {
    this.openai = this.workflow.openAI;     // Lấy đối tượng OpenAI từ WorkflowService
    this.modelName = this.workflow.modelName; // Lấy model name từ WorkflowService
  }

  async *generateChatResponse(userAsk: string): AsyncGenerator<string> {
    try {
      const chatHistory: ChatInterface[] = [];
      const newChat: ChatInterface = {
        role: 'user',
        content: userAsk,
      };
      const training = this.workflow.getTrainingBase({
        newChat,
        chatHistory,
      });

      const answer = await this.openai.chat.completions.create({
        model: this.modelName,
        messages: training,
        stream: true,
        max_tokens: 16000,
      });
      for await (const chunk of answer) {
        const content = chunk.choices[0]?.delta?.content || '';
        if (content) {
          yield content;
        }
      }
    } catch (error) {
      console.error(error);
      yield `Error: ${error.message}`;
    }
  }
}

ChatService sẽ xử lý việc gọi API từ OpenAI và trả về kết quả trò chuyện.


chat.controller.ts

import { Body, Controller, MessageEvent, Sse } from '@nestjs/common';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { ChatService } from './chat.service';

@Controller('Chat') // Định nghĩa route cho controller
export class ChatController {
  constructor(private readonly chatService: ChatService) {} // Inject ChatService

  @Sse('chat') // Định nghĩa SSE route
  async chat(@Body() { message }: { message: string }): Promise<Observable<MessageEvent>> {
    const chatStream = this.chatService.generateChatResponse(message);

    return from(chatStream).pipe(
      map((data) => ({
        data,
        id: new Date().toISOString(),
        type: 'message',
        retry: 15000,
      })),
    );
  }
}

ChatController sẽ nhận yêu cầu từ người dùng và trả về kết quả từ ChatService.


3. Cấu hình ứng dụng

Để sử dụng các cấu hình một cách toàn cục, chúng ta cần thêm ConfigModule vào AppModule.

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; // Import ConfigModule
import { ChatModule } from './chat/chat.module';
import { WorkflowModule } from './workflow/workflow.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // Cấu hình module để có thể sử dụng toàn cục
    }),
    WorkflowModule, // Import WorkflowModule
    ChatModule,     // Import ChatModule
  ],
})
export class AppModule {}

4. Thêm Prisma ORM

Để lưu trữ lịch sử chat, bạn có thể sử dụng Prisma ORM.

prisma.module.ts

import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: [PrismaService], // Cung cấp PrismaService
  exports: [PrismaService],   // Xuất PrismaService để sử dụng ở các module khác
})
export class PrismaModule {}

prisma.service.ts

import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect(); // Kết nối tới cơ sở dữ liệu khi module khởi tạo
  }

  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close(); // Đảm bảo ứng dụng đóng kết nối cơ sở dữ liệu khi tắt
    });
  }
}

Bạn cần tạo file /prisma/schema.prisma và viết các bảng model theo document của Prisma.


5. Cấu hình môi trường

Tạo file .env để cấu hình môi trường của bạn.

.env.example

CHATAI_KEY=your-openai-api-key
MODEL_NAME=gpt-4o
DATABASE_URL=postgresql://user:password@hostname:port/database

Đổi your-openai-api-key thành khóa API của bạn, và cấu hình URL của cơ sở dữ liệu theo thông tin của bạn.


Cảm ơn các bạn đã đọc

Với cấu trúc này, WorkflowModule sẽ có thể được sử dụng ở bất kỳ nơi nào trong ứng dụng của bạn. Việc sử dụng Prisma ORM giúp bạn dễ dàng quản lý dữ liệu và mở rộng ứng dụng AI của mình. Hy vọng bài viết này sẽ giúp bạn có một cái nhìn rõ ràng hơn về cách xây dựng và triển khai một ứng dụng AI cơ bản với NestJS.

Vậy đã he. Nếu các bạn cần hỏi thêm phần nào về bài viết hoặc cách sử dụng, có thể dùng mini app chat AI mà mình để ở góc dưới bên phải, hoặc Contact với mình nhaaa!

0%