AI·

위키에게 물어보는 QA 봇 만들기 — 슬랙 소켓 모드 + RAG

팀 위키를 근거로 질문에 답하는 슬랙 QA 봇을 만든 기록. 소켓 모드 연결, 스크럼봇과의 분리, ingest·lint·chat용 시스템 프롬프트를 파일로 쪼갠 이야기, 그리고 컨텍스트 한계와 마주친 순간까지 정리합니다.

스크럼봇이 위키를 사람에게 먼저 전해 줬다면(지난 글), 이번엔 반대입니다. 사람이 슬랙에서 물어보면, 위키를 근거로 답해 주는 QA 봇을 만들었습니다. "그거 어디 문서에 있었지?"를 검색이 아니라 대화로 푸는 게 목표였죠.

위키가 있으면 물어볼 수 있어야 한다

QA 봇의 동작은 이렇습니다.

  1. 사용자가 슬랙에서 봇에게 질문한다
  2. 봇이 위키에서 관련 문서를 찾는다
  3. 그 문서를 근거로 사내 LLM이 답을 생성한다

핵심은 위키 데이터를 근거로만 답한다는 것입니다. 모델이 아무 말이나 지어내는 게 아니라, 팀이 실제로 쌓은 문서 위에서 답하게 하는 것 — 흔히 말하는 RAG(검색 증강 생성)입니다. 답변 생성에는 사내 게이트웨이의 더 큰 오픈 모델을 썼습니다.

① 사용자 질문 (슬랙) ② 위키에서 근거 검색 wiki/ ③ 근거 문서 선별 ④ 사내 LLM 답변 생성 ⑤ 답변 · 근거 기반
질문 → 위키 근거 검색 → 선별 → 생성 → 답변 (RAG)

소켓 모드로 연결

슬랙 봇을 붙이는 방법은 두 가지입니다. 외부에서 접근 가능한 URL로 이벤트를 받는 방식(HTTP), 그리고 봇이 슬랙으로 먼저 연결을 여는 소켓 모드입니다.

사내 서버는 외부에 포트를 열기 부담스러웠기에 소켓 모드를 택했습니다. 서버가 슬랙에 아웃바운드로 연결을 유지하니, 인바운드 포트를 열지 않고도 메시지를 주고받을 수 있습니다. 사내망 안에서 돌리기에 딱 맞았습니다.

스크럼봇과는 다른 봇이다 — 분리

여기서 실수할 뻔했습니다. 처음엔 스크럼봇과 QA 봇을 같은 설정으로 묶으려 했는데, 곧 헷갈리기 시작했습니다. 둘은 성격이 다른 봇이었습니다.

  • 스크럼봇: 정해진 시각에 서버가 먼저 DM을 보낸다(아웃바운드)
  • QA 봇: 사용자가 말을 걸면 답한다(양방향, 소켓 모드)

그래서 설정에서 두 봇을 명확히 나눴습니다. 토큰도, 모드도, 활성화 여부도 따로 관리하게요. 나중에 헷갈리지 않으려면 초반에 갈라 두는 게 맞았습니다.

{
  "scrum_bot": { "bot_token": "<스크럼봇 토큰>", "mode": "schedule" },
  "qa_bot":    { "bot_token": "<QA봇 토큰>",   "mode": "socket" }
}

시스템 프롬프트를 파일로

QA 봇을 붙이고 나서 시스템 프롬프트를 손보다가, 더 큰 문제를 발견했습니다. LLM을 쓰는 곳이 여러 군데(요약·검수·채팅)인데, 각자의 지시문이 코드 여기저기 흩어져 있었습니다.

그래서 용도별로 파일을 쪼갰습니다.

system_prompts/
├── ingest.md   # 원문을 요약할 때 — "사서처럼 압축하고 연결하라"
├── lint.md     # 요약 품질을 검수할 때
└── chat.md     # QA 봇이 답할 때 — "위키 근거로만, 없으면 없다고"

이렇게 하니 지시문을 코드 배포 없이 다듬을 수 있고, 각 용도의 톤도 뚜렷해졌습니다. 예를 들어 ingest.md는 "짧게, 명령어·설정은 보존하며 요약", chat.md는 "위키에 근거가 없으면 지어내지 말고 없다고 답하라"처럼요.

컨텍스트 한계와 마주치다

잘 되는가 싶더니, 다른 팀원이 질문하자 이런 오류가 떴습니다.

사내 API 오류: 400 — This model's maximum context length is
131072 tokens, but the input is 350730 tokens.

모델이 한 번에 받을 수 있는 입력은 12만 8천 토큰인데, 관련 문서를 너무 많이 밀어 넣어 35만 토큰을 보내 버린 것이었습니다. "근거를 많이 주면 좋겠지"가 오히려 한계를 넘겨 버린 셈입니다.

이건 다음 글의 주제로 이어집니다. 근거를 무작정 다 넣는 게 아니라, 정말 필요한 문서만 골라 넣어야 한다는 것. 위키 구조 자체를 손봐야 할 신호였습니다.

기다리는 동안 뭐라도 보여주기

답변 생성은 몇 초씩 걸렸습니다. 그동안 아무 반응이 없으면 사용자는 "죽었나?" 싶습니다. 그래서 봇이 즉시 반응하게 했습니다.

  • 질문을 받으면 곧바로 "답변을 위한 생각 중…"을 띄운다
  • 생성이 이어지는 동안 문구를 로테이션해서, 멈춘 게 아니라 일하고 있다는 걸 보여준다

작은 장치지만, 체감 대기 시간을 크게 줄여 줬습니다.

다음 문제: 너무 느리다

QA 봇은 동작했지만, 두 가지 숙제를 남겼습니다.

  1. 컨텍스트를 넘길 만큼 근거를 과하게 넣고 있었다
  2. 그래서 답변이 느렸다

결국 "어디서 무엇을 찾을지"를 봇에게 제대로 알려주지 못한 게 근본 원인이었습니다. 마지막 글에서 위키 구조를 재편하고 검색 인덱스를 붙여 이 문제를 푼 이야기를 다룹니다.