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é!
Đầ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
.
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
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.
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 {}
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,
];
}
}
ChatModule
sẽ sử dụng WorkflowService
để xử lý các yêu cầu trò chuyện từ người dùng.
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.
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.
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
.
Để 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
.
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 {}
Để lưu trữ lịch sử chat, bạn có thể sử dụng Prisma ORM.
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 {}
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.
Tạo file .env
để cấu hình môi trường của bạn.
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.
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!