HD현대중공업 HR RAG 챗봇
사규 문서로 답하는 사내 HR 챗봇을 3주 만에 단독 구축·배포 (마이메타 협업 데모)
마이메타와 협업해 HD현대중공업 사규 기반 한국어 RAG 챗봇을 만들었습니다. FastAPI + ChromaDB + Next.js로 3주 만에 배포했고, 사규를 외부로 내보낼 수 없다는 본사업 제약을 데모 단계에서 아키텍처로 먼저 풀었습니다.
Long story short
기획안은 명확했지만 실제 사내 데이터 없이 RAG 성능을 검증해야 했습니다. 검색 품질은 정성 평가로 판단이 어려워서, AI로 시드 쿼리 30종을 만들고 기대 키워드 포함·금지어 미포함·기대 출처 인용·기대 컬렉션 히트 네 가지 기준으로 자동 채점하는 회귀 테스트 구조를 만들어 해결했습니다.
또 하나의 제약은 '사규는 외부로 나갈 수 없다'였습니다. 데모는 외부 LLM API(Gemini)로 시연하되, 본사업에서는 사내 GPU의 자체 LLM으로 데이터 송신 없이 전환해야 했습니다. 그래서 LLMProvider 추상화 레이어를 두고 "백엔드 어디서도 SDK를 직접 호출하지 말 것"을 코드 가드레일로 못박았습니다. 환경변수 하나로 구현체가 바뀌고, vLLM 전환은 파일 1개 추가로 끝납니다.
권한은 LLM에게 맡기지 않았습니다. retriever 단계에서 user_clearance보다 높은 민감도의 청크를 잘라내므로, 권한 밖 정보는 프롬프트에 아예 들어가지 않습니다. 검색 결과가 있어도 거리 임계값을 넘으면 "규정에서 확인되지 않습니다"로 분기해 얼버무리는 답변을 원천 차단했습니다.
한 일
도구
RAG 파이프라인 8단계
PDF에서 벡터 인덱스까지, 민감도 분류와 암호화를 통과해야 검색 대상이 됩니다.
- PyMuPDF 텍스트 추출 → Gemini Vision OCR 폴백 (tesseract 의존성 0)
- Presidio PII 마스킹 → 청킹 800자 / 120 overlap
- LLM 민감도 자동 분류 (public · internal · confidential · secret)
- 임베딩 3072차원 → Fernet 암호화 저장 → Chroma 인덱싱
- 실 사규 형식 PDF 7종 · 5개 컬렉션 · 58청크 인덱싱
권한 밖 정보는 LLM에 도달하지 않는다
4역할 × 4민감도 매트릭스를 config.yaml에 선언하고, retriever가 먼저 걸러냅니다.
- 임직원 / HR담당 / HR임원 / Admin × public / internal / confidential / secret
- 프롬프트 조립 전에 청크가 사라지므로 프롬프트 인젝션으로도 우회 불가
- top-1 cosine distance > 0.5 → no_context 분기, 환각 대신 "확인되지 않습니다"
- JWT + bcrypt, rate limit, WebSocket 30초 단명 티켓, 감사 로그(질의·인용·IP)
스트리밍, Diff, 출처 인용
답변이 어디서 왔는지 항상 보이도록 만든 사용자 경험.
- SSE 토큰 스트리밍 + 12ms/글자 적응형 타자기 (자체 ReadableStream 파서)
- "달라/변경/차이" 의도 감지 → 동일 문서 두 버전 diff + LLM 의미 요약
- 출처 칩(문서명·조항·페이지·distance) 백엔드 자동 부착
- domains/{id}/config.yaml + prompts/system.md — 다른 회사 도메인 추가 시 코드 무변경
트러블슈팅과 배운 점
RAG의 품질은 프롬프트가 아니라 retrieval 설계에서 결정된다는 걸 체감했습니다.
- Docker 한글 파일명 ASCII codec 에러 → LANG / LC_ALL=C.UTF-8
- 재배포 시 Chroma 인덱스 소실 → /app/data 볼륨 마운트. "상태가 있는 서비스는 저장 위치부터 설계"
- LLM 민감도 분류 비결정성 → sensitivity_override로 중요 문서는 운영자가 고정
- 본사업 로드맵: PADO 사내 검색 hybrid retrieval, vLLM 전환으로 데이터 외부 송신 0건
소셜에서 만나요
이호성 — AI Product Builder
