약 2일동안 열심히 클로드를 돌려서 ai를 사용한 연애 시뮬레이션 게임을 만들어 보았다.
https://github.com/sunm2n/DateWithAi
GitHub - sunm2n/DateWithAi
Contribute to sunm2n/DateWithAi development by creating an account on GitHub.
github.com
올라마를 통해서 ai를 로컬에서 돌리려는 시도를 했지만 모델 성능에서 한계가 느껴졌다.
그리고 챗봇 게임은 비슷한 응답을 다시 반환하는 경우도 많아서 redis를 활용하여 캐싱을 해두는 것도 향후 진행해 볼 예정이다.
아마 지금은 다른 프로젝트들로 인해 당분간은 리팩토링을 하지 못할거 같지만 추후에 어떤 방식으로 할지 고민을 해 보았다.
물론 제미나이 선생님과.
현재 구성하신 아키텍처(Spring + FastAPI + VectorDB)는 매우 표준적이고 훌륭한 구조입니다. 여기서 성능을 향상시키는 것은 어느 한 가지 기술을 바꾸기보다는, 데이터가 흐르는 각 단계의 병목을 찾아 최적화하는 방향으로 접근하는 것이 좋습니다.
연애 시뮬레이션 게임의 특성상 '응답 속도'가 사용자 경험에 치명적이므로, 체감 성능을 높이는 데 초점을 맞춘 전략들을 추천해 드립니다.
## 🚀 1. RAG 검색(Retrieval) 단계 최적화
LLM에 컨텍스트를 전달하기 전, VectorDB에서 얼마나 빠르고 정확하게 정보를 가져오느냐가 중요합니다.
가. Reranker 도입
가장 효과적인 방법 중 하나입니다. 현재는 VectorDB에서 유사도 높은 순서(Top-K)대로 문서를 가져와 바로 LLM에 전달하실 겁니다. 이 방식은 관련 없는 정보가 포함될 확률이 있습니다.
- 개선 방식:
- 1차 검색 (VectorDB): 일단 후보 문서를 넉넉하게 가져옵니다. (예: Top-K = 20)
- 2차 재정렬 (Reranker): 가져온 20개의 문서를 Reranker 모델(Cross-Encoder)을 사용해 사용자 질문과의 실제 관련성을 다시 정밀하게 계산합니다.
- 최종 선택: Reranker가 가장 점수를 높게 매긴 3~5개의 문서만 LLM에 전달합니다.
- 효과: 더 적고 정확한 컨텍스트를 LLM에 전달하므로, LLM의 응답 품질이 올라가고 처리할 토큰 수가 줄어들어 최종 응답 속도가 빨라집니다. BCEmbedding, Cohere Rerank 같은 모델을 FastAPI 서버에 추가할 수 있습니다.
나. 임베딩 모델 및 Chunk 전략 점검
- 경량 임베딩 모델 사용: 현재 사용 중인 임베딩 모델이 너무 무겁다면, 성능은 약간 타협하더라도 더 작고 빠른 모델(예: ko-sbert 계열)로 교체하는 것을 고려해 보세요.
- 의미 기반 Chunking (Semantic Chunking): 문서를 단순히 글자 수로 자르는 대신, 문맥과 의미가 완결되는 단위로 자르면 검색 정확도가 올라가 결과적으로 더 나은 성능을 냅니다.
## ⚡️ 2. LLM 추론(Inference) 단계 최적화
가장 시간이 많이 걸리는 구간이므로, 여기서의 최적화가 체감 성능을 크게 좌우합니다.
가. 스트리밍(Streaming) 응답
가장 먼저 도입해야 할 1순위 기술입니다. 사용자는 답변이 한 번에 '짠' 하고 나타날 때까지 기다려야 해서 지루함을 느낍니다.
- 개선 방식:
- FastAPI: LLM 모델의 응답을 스트림 방식으로 생성하도록 설정하고, 이를 StreamingResponse로 Spring 서버에 전달합니다.
- Spring: 전달받은 스트림을 WebClient를 이용해 비동기적으로 처리하고, 이를 다시 클라이언트(게임 화면)에 SSE(Server-Sent Events) 방식으로 전달합니다.
- 효과: 답변이 생성되는 대로 글자가 타자 치듯이 실시간으로 보입니다. 전체 응답 생성 시간은 같더라도 사용자는 훨씬 빠르다고 느끼며, 게임 몰입도가 극대화됩니다.
나. 더 작고 빠른 LLM 모델 고려
항상 최고의 모델(GPT-4 등)을 고집할 필요는 없습니다. 게임 캐릭터의 대화 생성과 같은 특정 목적에는 더 작고 빠른 모델을 **파인튜닝(Fine-tuning)**하거나, 경량화된 모델(e.g., Llama 3 8B, Gemma)을 사용하는 것이 비용과 속도 면에서 훨씬 효율적일 수 있습니다.
## 🧠 3. 적극적인 캐싱(Caching) 전략
게임에서는 반복적인 질문이나 상황이 자주 발생합니다.
- 임베딩 캐시: 동일한 사용자 질문(문자열)에 대한 임베딩 벡터 결과를 캐싱합니다. (Key: 질문 문자열 -> Value: 벡터)
- LLM 응답 캐시: 완전히 동일한 프롬프트가 LLM에 입력될 경우, 이전의 답변을 그대로 반환하도록 캐싱합니다. (Key: 최종 프롬프트 -> Value: LLM 답변)
Spring 서버 단에서는 Caffeine이나 Redis를, FastAPI 서버에서는 Redis를 활용하여 각 단계의 결과를 캐싱하면 불필요한 연산을 크게 줄일 수 있습니다.
## 🏛️ 4. 시스템 아키텍처 및 비동기 처리
현재 Spring과 FastAPI는 동기식 HTTP 요청으로 통신하고 있을 가능성이 높습니다.
- Spring WebClient 사용: 기존의 RestTemplate 대신 **비동기(Non-Blocking) 방식의 WebClient**를 사용하세요. FastAPI 서버의 응답을 기다리는 동안 Spring 서버의 스레드가 묶이지 않고 다른 요청을 처리할 수 있어, 전체적인 서버 처리량이 향상됩니다.
- 메시지 큐 도입 (고급): 사용량이 매우 많아질 경우를 대비해, Spring과 FastAPI 사이에 RabbitMQ나 Kafka같은 메시지 큐를 두는 것도 고려할 수 있습니다. 요청-응답 구조를 완전히 분리하여 안정성과 확장성을 크게 높일 수 있습니다.
## 최종 요약 및 추천 순서
- (1순위) 스트리밍(Streaming) 응답 도입: 사용자 체감 성능을 높이는 데 가장 효과적입니다.
- (2순위) 캐싱(Caching) 전략 적용: 임베딩 캐시부터 적용하여 반복적인 연산을 줄이세요.
- (3순위) Reranker 도입: LLM에 전달하는 컨텍스트의 질을 높여 응답 품질과 속도를 동시에 개선하세요.
- (4순위) Spring WebClient로 전환: 서버의 전반적인 처리 성능과 안정성을 높이세요.
Reranker 라는 말을 처음 들어봤다. 이 방식을 알았더라면 훨씬 더 초반 단계에서 좋게 설계를 했을 거 같다.
리팩토링을 하게 되거나 나중에 RAG를 사용한다면 그 때 적용시켜봐야겠다.
특히 이런 챗봇 게임은 응답 속도와 정확성이 중요하기 때문에 꼭 필요한 기술인거 같다.
그리고 임베딩 과정에서 Chunking을 어떻게 하냐를 되게 간과했던거 같다.
어차피 모델이 해주는데 이런 안일한 생각을 해버린 내 자신을 반성하게 되었다.
그리고 스트리밍 응답은 사실 구상은 해봤지만 초기 버전에서는 구현 난이도가 좀 있어보여 제외시켰다.
이 방식도 리팩토링을 하게 될 때 도입해 볼 예정이다.
그 외에도 다양한 개선 방식이 있지만 모두 다 해보는 것이 나의 학습에는 가장 좋은거 같다.
'tech > Ai' 카테고리의 다른 글
| [바이브 코딩] 바이브 코딩에 대한 현 시점 나의 견해 (1) | 2025.12.16 |
|---|---|
| [Ai] Ollama로 모델 돌려보기 (0) | 2025.09.11 |
| [Ai] Ollama 설치 (0) | 2025.09.11 |