모두의 연구소 X 테크포임팩트 활동으로, 누구나 리포터 Lab 에서 ‘비영리단체의 모금 활동 분석을 해주는 LLM 기반의 데이터 분석가 챗봇 서비스’를 개발하고 있다. 지난달에 람다 기반의 도커 컨테이너를 배포하는 CICD 파이프라인에 대해서 정리해보았다. 그렇지만 '카카오톡 챗봇' 이라는 형식과 '쉬운 유지보수'를 위한 개발팀의 고민과 선택들로 아키텍처가 많이 변경되었다 !!
지난 글 보기
누구나리포터랩 개발팀 팀원 이용선님과 전종민님과 함께 아키텍처는 아래와 같이 구성하였다. 아래의 아키텍처가 나오기 까지 정말 많은 고민이 담겨있었다 !! AWS Lambda 서비스를 이렇게 까지 고도화해서 아키텍처에 녹여내는 건 처음이다.
1. KakaoTalk 챗봇 개발을 위한 서버리스 기반 아키텍처
전반적인 흐름은 이와 같다.
1. 사용자가 카카오톡의 '누구나리포터랩' 챗봇 채널을 추가한다. 사용자는 이 챗봇 채널을 통해서 후원자 수, 후원금, 각 SNS 채널의 방문자 수 와 같은 '리포트'를 자연어로 질의할 수 있다. 예를 들어서 아래와 같이 질문을 할 것이다.
2. 사용자의 질문이 가면, 카카오톡 챗봇의 서버에서 사용자의 요청에 대해서 handling 을 담당하는 lambda 로 요청이 보내진다. 카카오 비즈니스의 챗봇 관리자 센터에서는 이를 '스킬'이라고 한다. 이 스킬 목록에 lambda 와 api gateway 로 배포한 request handler lambda 의 url 을 등록한다. 약간의 카카오 챗봇 개발 팁인데, 이 스킬을 fall-back block 에 등록해서 사용자의 모든 요청을 lambda api 로 보내도록 구축했다.
request handler lambda 는 위와 같이 사용자에게 기다려달라는 문구를 응답으로 보내고 있다. 그리고 이 lambda 는 AI Agent 에게 사용자의 질문을 그대로 전달한다. 한편, 사용자의 질문 1개가 처리되기 전까지 다른 질문에 대해서는 AI Agent 에 보내지 않도록 lock 상태를 걸고 이를 redis 를 통해 상태를 저장한다.
export async function setUserLock(
redis: Redis,
userId: string,
): Promise<boolean> {
// users:${userId}
const key = makeUserLockKey(userId);
const result = await redis.set(key, 60);
// result가 null이면 락 설정 실패
return result !== null;
}
또한 카카오톡 챗봇 빌더에서 callback url 를 생성하는데, user 의 요청에 매핑되는 callback url 의 정보도 redis 에 저장한다.
// chatId: 채팅에 부여되는 식별자, userId: KakaoTalk ID, callbackUrl: kakao Builder callback URL
async function saveChatInfo(
redis: Redis,
chatId: string,
userId: string,
callbackUrl: string,
) {
const key = `chats:${chatId}`;
const value = {
user_id: userId,
callback_url: callbackUrl,
};
await redis.hset(key, value);
}
3. AI Agent 는 LLM 을 기반으로, 사용자의 질문에 맞는 SQL 를 생성한다. (Text-to-SQL) 그리고 SQL 를 BigQuery 로 보낸다. BigQuery 에서 쿼리에 대한 결과값을 리턴 받으면, 이 쿼리에 대해서 분석한 내용을 생성한다. 이 답변은 바로 사용자에게 보여주기 위한 수준으로 생성이 된다. 답변은 callback handler lambda 에게 보내진다.
4. callback handler lambda 는 redis 에서 user_id 를 통해, callback url 값을 꺼내온다. 그리고 이 callback url 로 AI Agent 가 최종적으로 생성한 답변을 보낸다. 그리고 redis 에서 lock 을 해제한다.
5. 답변이 성공적으로 kakaotalk chatbot server로 전달이 된다면, 아래와 같이 사용자는 리포트를 확인할 수 있다.
2. Lambda 함수의 CI/CD - Serverless Framework 도입
Lambda 함수가 여러개로 분기되면서, Lambda 함수 CICD 관리가 필요했다. AWS Web Console 에서 배포를 수동적으로 할 수도 있으며, Docker Image 를 빌드해서 배포를 하는 방법도 있겠지만 IaC 로 관리할 수 있는 프레임워크를 찾았다. 바로 Serverless Framework 이다.
Lambda 서비스의 개발 , 테스트 및 배포를 위해서는 Serverless Framework 를 사용했다.
serverless.yml 파일에서 lambda 서비스와 lambda 함수 배포를 위한 API Gateway 설정도 모두 코드로 정의할 수 있다. 그리고 CLI 명령어로 테스트 및 배포도 가능하다.
테스트 환경 빌드하기
local 환경에서 배포하기 이전에 테스트를 하고 싶을때, 아래의 명령어를 사용할 수 있다.
$ serverless offline
명령어 한줄이면 local 환경에서 API 에 대해 테스트를 해볼 수 있다.
AWS 환경에 배포하기
Lambda 함수를 AWS 상에서 배포하고 싶을 때도 명령어 한줄이면 가능하다.
$ serverless deploy
deploy 와 함께 파라미터로 stage 정보 (dev/prd) 도 설정할 수 있다. Lambda 를 IaC로 간편하게 관리할 수 있다는 점에서 Serverless Framework 는 참 잘 도입했다고 생각이 든다. 따라서, github action 과 함께 사용하여 특정 branch 에서의 CI 과정이 성공하면 serverless deploy 명령어를 실행하도록 하여, 배포 과정을 자동화 할 수 있었다.
3. 앞으로의 과제
- 로그 수집을 통해 사용자의 요청과 답변 퀄리티, 채팅 정보를 관리한다.
- SQS를 도입하여, 요청을 유실하지 않고 순차적으로 AI Agent 에 보낼 수 있도록 보장한다.
- 에러 핸들링 케이스를 나누어서 사용자에게 어떻게 보여줄지 케이스별로 대응한다.
- kakao talk 챗봇의 답변은 텍스트 뿐만 아니라 카루셀, 사진 리스트 등 다양하다. 이를 llm 으로 분기처리 후 개발팀에서 case별로 형식을 만들어두어야 한다.