오늘은 nest.js에서 openAi 사용에 대해서 적어보려고 한다.
우선 구조부터 말하자면
클라이언트에서 메세지 작성 -> 서버에 post요청 -> 메세지 형태 가공 -> openAi로 메세지 전송 -> 반환 받은 값 클라이언트로 전달
이 되겠다.
messages.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AiService } from 'src/ai/ai.service';
import { MessagesService } from './messages.service';
@Controller('messages')
export class MessagesController {
constructor(
private readonly aiService: AiService,
private readonly messagesService: MessagesService,
) {}
@Post()
async createMessage(@Body() content: { message: string }) {
const message = this.messagesService.createMessage(content);
const msg = await this.aiService.chat(message);
return msg;
}
}
body 데이터로 넘어온 값을 messagesService의 createMessage() 메서드로 전달하여 메세지의 형태를 가공한다.
이후 가공된 메세지를 aiService의 chat() 메서드로 보낸 뒤, 반환 값을 클라이언트에 전송한다.
messages.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class MessagesService {
createMessage(content: { message: string }) {
const message = [{ role: 'user', content: content.message }];
return message;
}
}
클라이언트에서 전송받은 메세지의 형태를 가공하는데, 다음과 같이 role과 content로 분리되어져 있다.
이 부분은 openAi 학습과 관련된 내용인데, 다음에 적어보도록 할 예정이다.
ai.service.ts
import { Injectable } from '@nestjs/common';
import OpenAI from 'openai';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AiService {
private readonly openai: OpenAI;
constructor(private readonly configService: ConfigService) {
this.openai = new OpenAI({
apiKey: this.configService.get('OPENAI_API_KEY'),
});
}
async chat(message: any) {
const chatCompletion = await this.openai.chat.completions.create({
messages: message,
model: this.configService.get('OPENAI_API_MODEL'),
});
return chatCompletion.choices[0].message.content;
}
}
OpenAI를 통해서 자신의 openAi 계정과 연결한다.
여기서 키 값은 ConfigService를 사용하여 .env로 관리하도록 한다.
추가적으로 주의 사항이 있는데,
이 코드에서 사용한 openai의 버전은 4.2.0 버전이다.
npm i openai@4.2.0
버전이 정말 중요한데,
연결을 시도하기 위해서 찾아봤던 글들의 대부분이 이 코드와 연결 방식이 달랐는데,
문제가 바로 버전에 있었다.
기존 openAi의 chat-GPT 3버전의 경우 openai 3.2.1버전으로 연동을 하니 잘 작동했었는데,
연결 방식도 좀 다르고, gpt-3.5-터보의 경우 잘 작동하지도 않았다.
그래서 버전이 매우 중요하다는 점... 기억해야한다.
(GPT-3.5 세대가 아닌 3세대를 사용하고자 한다면, 다른 글을 참조하길 바랍니다! openai 3.2.1버전으로 하니 잘 작동했었습니다. 코드는 따로 첨부하지 않습니다.)
위에서 .env 파일에 키값과 모델명을 기재해주고 연결하면 성공!
메세지 서비스에서 가공된 메세지 형식으로 openAi에 요청을 보낸 후, 해당 값을 반환한다.
자료를 찾는데에 시간이 걸렸지
(gpt-3버전 자료가 많았다. 버전 이슈로 한참 헤맸었고, 원인을 파악 한 후에는 nest.js자료는 거의 없어서 node나 js 자료를 찾아서 변형시켰다.)
코드 자체는 간단했다.
다만 여기서 ai를 자신의 입맛대로 학습을 시키는 과정이 필요하다.
(물론 gpt를 있는 그자체로 사용한다면 문제가 없다.)
다만, 팀 프로젝트에 쓰일 고객센터 겸용 챗봇을 만들어야 했기 때문에 학습에 대한 필요성이 존재했다.
찾아본 방법은 크게 두 가지로 나뉘었는데,
하나는 체인 방법, 다른 하나는 fine-tune 방법이 있었다.
체인은 기존의 대화내용들을 연결하여 전송함으로써, gpt와의 대화가 이어지는 듯한 느낌을 줄 수 있다.
(모델에 따라서 멍청하기도 하지만 실제로 이어지기도 한다.)
다만, 프로젝트에 필요한건, 고객과의 대화 누적 개념보다, 특정 키워드나 상황에 대한 ai의 페이지를 대변할 수 있는 일관적인 답변이 필요하여, fine-tune 쪽으로 시선을 돌리게 되었다.