
Firestore의 벡터 검색으로 AI 챗봇에 '커뮤니티의 문맥'을 주입하기
요약
AI 챗봇이 특정 커뮤니티의 말투나 뉘앙스를 반영하지 못하는 문제를 해결하기 위해 Firestore의 벡터 검색을 활용한 RAG 설계 방식을 소개합니다. 시스템 프롬프트의 토큰 비대화 문제를 방지하고, 사용자 발화에 맞는 문맥(Context)을 동적으로 주입하는 데이터 설계 전략을 다룹니다.
핵심 포인트
- 시스템 프롬프트에 모든 지식을 넣는 방식의 토큰 비대화 한계 지적
- Firestore의 벡터 검색을 활용한 RAG 기반의 문맥 주입 설계
- 공감 포인트와 심리적 인사이트를 결합한 데이터 벡터화 전략
- 의미적 유사성을 기반으로 한 뉘앙스 중심의 검색 구현
안녕하세요, takahashi(@stak_22)입니다.
AI 챗봇 신규 프로덕트를 개발하고 있습니다.
서론
특정 전문 영역이나 커뮤니티(도메인)를 위한 AI 챗봇을 개발하다 보면, **"사실은 올바르게 대답하지만, 말투나 뉘앙스가 '커뮤니티답지 않다'는 문제"**에 직면하게 됩니다.
전문 용어는 맞는데 사용법이 사전적이라 부자연스럽거나, 해당 영역 특유의 깊은 고민에 대해 피가 통하지 않는 교과서적인 정론만을 답변하는 경우 말이죠.
사용자가 원하는 것은 단순한 정보 그 자체뿐만 아니라, **"그 영역의 문화나 문맥(Context)을 이해한 바탕 위에서의 생생한 응답"**임에도 불구하고, AI가 너무 모범생 같아서 문맥을 파악하지 못하는 일이 자주 발생합니다.
이 기사에서는 이 과제를 해결하기 위해, 예시로서 도메인 고유의 '사용자 공감 포인트(Aruaru) + 그 배경에 있는 심리(Insight)'를 Firestore의 벡터 검색(Vector Search)으로 동적으로 가져와, 매 턴의 응답에 '커뮤니티다움'을 주입하는 설계를 소개합니다.
대상 독자
- 채팅 AI에 '다움'이나 도메인의 문맥을 부여하고 싶은 분
- 시스템 프롬프트(System Prompt)에 지식을 전부 때려 넣어 운영이 힘들어지고 있는 분
- Firestore의
findNearest를 실제 프로덕트에서 사용하고 싶은 분
다루지 않는 내용
- 벡터 DB 제품 비교 (Pinecone / Weaviate / pgvector 등)
- 임베딩(Embedding) 모델의 정밀도 비교
- LangChain 등 특정 프레임워크의 사용법
정적 시스템 프롬프트에 몰아넣는 방식의 한계
초기 구현에서는 도메인 고유의 지식(공감 포인트 + 인사이트)을 커다란 텍스트 블록으로 두고, 시스템 프롬프트의 끝에 전건을 연결하여 참조하게 했습니다. 조잡하지만 이미지는 대략 이렇습니다.
[system prompt body]
다음의 도메인 공감 포인트 데이터베이스를 참조하여 적절히 활용하십시오.
## 도메인 공감 포인트 데이터베이스
...
무리한 방식이라는 생각이 들면서도 일단 시도해 보았으나, 예상대로 별로 의미를 이루지 못했습니다. 사람에게도 이런 지시는 이해하기 어렵겠지요.
| 문제 | 내용 |
|---|---|
| 토큰 비대화 | 전건을 전달하므로 매 턴마다 통째로 포함됨. Gemini Context Caching으로 상당히 완화할 수 있지만 근본적인 해결책은 아님. |
| ... |
이 과제에 직면하여, **"전건을 전달하는 것은 포기하고, 지금 사용자의 발화에 꽂히는 문맥만을 그 순간에 핀포인트로 찾아내어 전달할 수밖에 없다"**는 결론에 도달했습니다. 이것이 이번 프로젝트의 출발점입니다.
해결책: 벡터 검색 + RAG
접근 방식으로는 왕도인 RAG (Retrieval-Augmented Generation)입니다.
사용자 발화
→ 임베딩 (Embedding) 화
→ 벡터 DB (Firestore)에서 유사도 검색
...
벡터 검색은 "RAG의 Retrieval 파트"의 한 구현일 뿐이지만, 이번과 같은 "뉘앙스가 비슷한 에피소드를 끌어오고 싶다"는 유스케이스에는 매우 궁합이 좋습니다. 완전 일치 검색이나 BM25로는 "의미적으로는 가깝지만 단어는 겹치지 않는 것"을 잡아낼 수 없기 때문에, 벡터 검색의 강점이 살아납니다.
수법과 설계
프로덕트에 통합함에 있어 충족해야 할 설계 요건을 「데이터」, 「검색」, 「프롬프트 주입」, 「운영」의 4가지 레이어로 분해하여 정리했습니다.
1. 데이터 설계: 인사이트를 포함하여 벡터화하기
Firestore의 컬렉션에는 문화를 상징하는 에피소드(공감 포인트 본문)뿐만 아니라, **"그 배경에 있는 사용자의 심리·동기(Insight)"**를 세트로 갖추도록 했습니다.
임베딩(Embedding, 벡터 데이터)을 생성할 때는 본문뿐만 아니라 공감 포인트 본문 + "\n" + 인사이트를 결합한 텍스트를 입력으로 사용합니다. 이를 통해 "단순한 표면적인 언어의 유사"가 아니라, "사용자가 품고 있는 감정이나 동기의 뉘앙스"에 가까운 것을 잡아낼 수 있게 되어 검색 정밀도가 크게 향상됩니다.
2. 검색 레이어: findNearest를 활용하기
벡터 DB를 별도로 준비하는 것이 아니라, 익숙한 Firestore가 지원하는 findNearest를 채택했습니다.
Terraform (hashicorp/google...
v5계)에서 벡터 인덱스 (Vector Index)를 정의할 때, __name__ 속성을 반드시 맨 앞에 두어야 하므로 주의가 필요합니다.
# 벡터 인덱스 정의의 핵심 부분
fields {
field_path = "__name__"
...
이를 수행하지 않으면, plan은 통과하더라도 apply 단계에서 에러가 발생합니다. 또한, 인덱스 빌드에는 수 분에서 수십 분이 소요되므로, 운영 환경에서는 "먼저 인덱스를 생성한 후 데이터를 seed한다"는 순서를 철저히 지키는 것이 안전합니다.
3. 프롬프트 주입: 최신 user 턴의 서두로 합성하기
검색을 통해 얻은 데이터를 어떻게 LLM에 전달할 것인가에 대한 인터페이스(Interface) 이야기입니다.
Gemini는 user 턴이 연속되어도 에러가 발생하지 않는 사양이기 때문에, 최종적으로는 **"최신 user 턴의 서두로서, 참고 정보를 합쳐서 하나의 턴으로 보낸다"**라는 심플한 설계로 결정되었습니다. 불필요한 토큰 소비도 억제할 수 있어, 이 방식이 가장 관리가 용이합니다.
프롬프트 내에는 다음과 같은 형태로 삽입합니다.
[참고: 관련 도메인의 컬처 (자연스러운 형태로 대화에 포함해도 좋음)]
・<공감 포인트 A> (배경 심리: <인사이트 A>)
・<공감 포인트 B> (배경 심리: <인사이트 B>)
...
"자연스러운 형태로 대화에 포함해도 좋음"이라고 한마디 덧붙이는 것만으로도, AI는 정보를 기계적으로 나열하는 것을 멈추고, 대화의 흐름에 맞춰 필요할 때만 문맥을 섞어주게 됩니다.
그럼에도 불구하고 약간 억지로 끼워 넣은 듯한 느낌을 완전히 지우지 못한 것은 여전히 과제로 남아 있습니다.
4. 운영 레이어: 안전한 출시를 위한 방어 기제
새로운 기능을 안전하게 운영 환경에 투입하기 위해 다음과 같은 메커니즘을 포함하고 있습니다.
Remote Config 등을 통한 기능 플래그 (Feature Flag): 동적인 벡터 검색 (신규 노드)과 기존의 정적 시스템 프롬프트 (기존 노드)를 콘솔에서 즉시 전환할 수 있도록 설계했습니다. 문제가 발생하면 코드 배포 없이 1초 만에 롤백할 수 있습니다. PoC 단계에서는 기능 플래그의 활용이 매우 유효합니다.
사일런트 폴백 (Silent Fallback): 만약 임베딩 (Embedding) 생성이나 벡터 검색이 실패하더라도, 에러로 인해 채팅 전체가 중단되는 대신 참고 정보가 없는 "일반 응답"으로 조용히 돌아가도록 가드(Guard)를 설치했습니다.
효과
말투 측면에서 체감되는 Before / After는 다음과 같습니다.
| 관점 | Before (정적 DB 전체 연결) | After (벡터 검색) |
|---|---|---|
| 토큰 소비 | 매 턴마다 DB 전체 분량이 포함됨 | 관련 3건 분량만 포함되므로 압도적으로 가벼움 |
| ... |
정량적인 평가는 앞으로 구체화해 나갈 예정이지만, 정성적으로는 "문맥에 맞는 '공감 포인트'를 AI가 깔끔하게 잡아내기 시작했다"는 체감이 확실히 나타나고 있습니다.
사실을 정확하게 전달하는 AI에서, **"그 업계의 문맥을 이해하는 AI"**로 나아가는 확실한 한 걸음이라는 느낌입니다.
트레이드오프 (Trade-off)
대단한 것은 아니지만, 우려되는 점도 일단 적어둡니다. 고려할 사항이 많지 않아 기존 프로덕트에도 쉽게 도입할 수 있는 방법이므로, 적극적으로 도입해 보는 것이 좋아 보입니다.
임베딩 생성 레이턴시 (Latency): 매 턴 응답에 임베딩 생성이 1회 추가되므로, 스트리밍 응답의 첫 번째 토큰(First Token)까지의 속도에 수백 ms 정도의 영향이 있습니다.
비용과의 관계: Firestore의 findNearest는 일반적인 읽기(Read)보다 조금 더 비쌉니다. 이번 규모(수백 건 × 768차원 × 사용자 수)에서는 현재 "오차 범위" 내에 있지만, 향후 만 단위로 스케일 업할 경우에는 비용 문제를 엄격하게 고려해야 합니다.
요약
채팅 AI에 "도메인에 정통한 말투나 문맥"을 부여하기 위한 구현 패턴으로, Firestore + 벡터 검색을 통한 동적 RAG를 소개했습니다.
- 정적 시스템 프롬프트에 모든 지식을 넣으면 토큰 비대화, 관련성 저하, 운영 부하가 발생함
- 벡터 검색으로 "현재 발화와 유사한 K건"을 매 턴 가져옴으로써, 필요한 문맥만 AI에게 전달할 수 있음
"사실을 정확하게 전달하는 것"뿐만 아니라, **"해당 영역의 컬처나 문맥을 이해한 상태에서 대화하는 것"**을 목표로 하는 AI 프로덕트에게 벡터 검색 기반의 RAG는 상당히 궁합이 좋은 접근 방식이라고 느낍니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기