내가 속한 게임만들기 팀?엔 이미 위키가 있었다. 상태 배지도 있고(확정·잠정·미정·검토필요), 주제별로 정리된 문서도 있고, "이 질문은 이 문서를 보라"는 라우팅표까지 있었다. 그런데도 팀원들은 뭔가 궁금하면 위키를 열어보는 대신 회의나 카톡으로 묻는다... "메인유품이 뭐였더라", "이거 확정된 거 맞나" 같은 질문이 며칠 간격으로 똑같이 반복되는 상황이였다
이유는 명확했다.
위키는 "찾아가야 하는" 곳이고, 질문은 "그냥 떠오르는" 것이다.
그 사이의 마찰 하나가 잘 만든 위키를 죽은 문서로 만들기에 충분했다.
사실 우리 팀에서 이미 실패를 한번 겪었다. 예전에 노션으로 위키 구축하면서 이렇게 적어놨더라
문서가 틀려서 문제가 아니었다. 문서가 있다는 사실 자체가 아무 힘이 없었다. 아무도 그걸 근거로 쓰지 않으니까.
해당 문제를 막연하게 풀어야 겠다는 생각만 하던 차에, 토스 기술 블로그의 "AI에게 투자정보를 말하게 하기까지"라는 글을 봤다. 토스증권이 AI로 투자 정보를 서비스하면서 겪은 문제를 다룬 글인데, 읽다 보니 계속 우리 (내) 얘기처럼 느껴졌다.
벨로그 글 작성 당일 기준 어제 올라온 따끈따근 베이커리 (좋은 글이니 꼭 읽어보시길)

우리문서의 YAML 메타데이터
읽으면서 무릎을 쳤던 건 사실 진짜 치진 않았다 우리 위키가 이 세 관문 중 두 개를 이미 우연히 통과해놓은 상태였다는 거다. 상태 배지(확정·잠정·미정·검토필요)는 정확히 세 번째 관문 — 답변에 "평가 가능한 필드"를 같이 붙여두는 장치였다. 그리고 문서마다 붙은 ID와 라우팅표는 첫 번째 관문, "무엇을 근거로 삼을지"를 미리 걸러놓은 거였다. 아티클 표현을 빌리면 이런 거다
좋은 답변은 생성 단계가 아니라, 말할 수 있는 후보를 만드는 단계에서 절반 이상 결정된다.
우리 위키는 이미 "말할 수 있는 후보"를 잘 걸러놓고 있었다. 없는 건 딱 하나, 그걸 물어볼 수 있는 통로였다
그럼 들어주러 가자
이미 구축해둔 팀 위키가 있기 때문에 이걸 잘 활용할수있는 무언가를 만들어야 해서 우선 팀 컨플 문서를 어떻게 위키화 했는지 구조부터 봐야했다
원래는 잘 정리하기 위해서 정해돈 위키룰이 였지만 "꿩먹고 알먹기" 라고 AI한테 뭘 근거로 답해야 하는가 RAG를 야매로 구축해둔 셈이였다!!!
위키를 정리하면서 이미 "AI가 뭘 근거로 답해야 하는가"에 대한 답을 만들어놓은 셈
하나는 이미 있는 걸 쓰는 것. Slack에 태그를 걸면 AI가 답해주는 기성 기능들을 검토했다. 그런데 확인해보니 로컬 파일을 직접 읽지 못하고, 우리가 애써 만든 상태 배지("검토필요면 한쪽을 고르지 마라" 같은)를 강제할 방법이 없었다. 느슨한 회사 지식에 물어보기엔 충분한데, 우리 위키가 갖춘 규칙을 지키게 만들 수는 없는 구조였다.
그래서 직접 만들기로 했다. 대신 자유롭게 뭐든 찾아 헤매는 에이전트로는 안 만들었다. 아티클이 말한 두 번째 관문 응답 요건이 명확한 태스크엔 넓은 자율성보다 절차형 구조가 낫다는 걸 그대로 따랐다.
우리 봇이 해야하는 일은 뻔하다. 질문을 받아서, 근거를 좁히고, 정해진 규칙대로 답한다. 여기에 "알아서 검색하고 알아서 판단하는" 자유도까지 열어줄 이유가 없었다.
거창해 보이지만 실제로는 네 단계짜리 파이프라인만 만드는 심플한 작업이였다
Slack 멘션
→ 1.위키 진입점 문서(라우팅표)를 보고, 관련 있어 보이는 문서 ID를 고른다
→ 2.그 ID로 실제 파일 경로를 찾는다 (LLM이 경로를 직접 지어내지 않게, 코드가 확정한다)
→ 3.찾은 문서 내용 + "상태 다르면 다르게 답하라"는 규칙을 같이 넣어서 답을 만든다
→ 4.Slack 문법에 맞게 다듬어서 스레드에 답장
핵심은 1번과 2번을 나눈 거다. 처음엔 "질문 → 파일 경로"를 한 번에 시켰는데, 모델이 가끔 그럴듯한 이름을 지어내서 존재하지도 않는 파일을 찾으려 들었다. 그래서 모델은 "이 주제야" 정도만 판단하게 하고, 그 판단을 실제 파일과 연결하는 건 코드가 하게 나눴다. 모델에게 맞춰야 하는 정답이 있는 일과, 자유롭게 판단해도 되는 일을 구분한 셈이다
먼저 모델에게 시키는 부분. ID만 고르게, 스키마로 아예 못을 박아둔다.
const selectFilesDeclaration: FunctionDeclaration = {
name: "select_files",
description:
"질문에 답하기 위해 읽어야 할 게임가이드 문서를 1~2개 고른다. 라우팅표의 파일 ID만 고른다 — 파일 경로나 파일명을 지어내지 않는다.",
parameters: {
type: Type.OBJECT,
properties: {
ids: {
type: Type.ARRAY,
items: { type: Type.STRING },
description:
"라우팅표에 있는 그대로의 파일 ID. 예: ['04-05', '01-05']. 절대 폴더명이나 파일명을 지어내지 않는다.",
},
},
required: ["ids"],
},
};
const ids = await routeQuestion(question, routingDoc);
const paths = ids
.map((id) => fileIndex.get(id))
.filter((p): p is string => p !== undefined);
모델이 지어낼 수 있는 건 "이 주제가 뭘 것 같다"는 판단까지고, 그 판단이 실제로 뭘 가리키는지는 처음부터 모델 손을 떠나 있다.
3번 에서는 위키가 이미 갖고 있던 상태 배지 규칙을 그대로 프롬프트에 넣는다.

"검토필요면 한쪽을 고르지 마라", "모르면 모른다고 해라" — 위키 설계할 때 세운 원칙을 봇도 똑같이 지키게 만드는 거다. 그러니까 이 봇은 새로운 규칙을 발명한 게 아니라, 이미 있던 위키의 규칙을 그대로 옮겨온 것에 가깝다. 아티클 식으로 말하면 세 번째 관문이 이미 답을 갖고 있었던 셈 — 위키 자체가 "이 답이 확정인지 잠정인지"를 구조화된 필드로 매번 남기고 있었으니, 봇은 그 필드를 읽어서 말투만 바꾸면 됐다.
4번는 순전히 현실적인 이유인데, Slack은 흔히 쓰는 마크다운 문법을 못 읽는다. 별표 두 개로 굵게 쓰면 그게 그대로 별표 두 개로 보인다.

그래서 마지막에 한 번 더 다듬는 단계를 넣었다. 사소하지만, 이게 없으면 아무리 답이 정확해도 읽는 사람 입장에선 AI의 쌍따옴표 세례에 지래 겁을 먹어 도망칠 수 있기에

이 봇이 하는 일 자체는 화려하지 않다. 질문을 받아서, 근거 문서를 찾아서, 상태를 지켜가며 답하는 것뿐이다. 하지만 그 단순함이 핵심이었다. 정작 어려웠던 건 봇을 짜는 일보다, 흩어진 맥락을 하나로 모으고, 이미 있던 위키 설계 원칙을 봇의 규칙으로 그대로 옮겨오는 것이었다.
위키가 이미 옳았다면, 필요했던 건 위키를 더 정교하게 만드는 게 아니라 위키에 다가가는 거리를 줄이는 것이었다. "이거 확정된 거 맞나"라는 질문이 채널에 다시 올라올 때, 사람이 아니라 위키가 먼저 대답하는 것 — 그게 이 프로젝트로 바뀌길 바라는 것의 전부다.
참고한 아티클은 이런 문장으로 끝난다. "AI에게 투자 정보를 말하게 한다는 것은, AI가 말해도 되는 상태를 계속 운영하는 일이었습니다." 우리한테 맞게 바꿔보면 이렇지 않을까. 위키가 팀 대신 답하게 한다는 건, 위키가 항상 답해도 되는 상태로 정리돼 있게 계속 신경 쓰는 일이다. 봇은 그 상태를 그대로 옮겨 말할 뿐, 새로운 진실을 만들지 않는다. 그게 이 봇을 만들면서 제일 지키고 싶었던 원칙이다
생각보다 기대한 만큼 맥락에 대한 답을 잘해주는 공방 위키봇을 보여주며 마무리합니다