지식 그래프를 내리고 원문 저장소로 가기로 한 뒤, 남은 일은 그 저장소가 실제로 어떻게 돌지를 정하는 것이었다. 기록이 나오는 곳은 맥 두 대와 Windows 두 대, 그리고 그 위의 여러 워크스페이스다. 각 머신에서 생기는 대화·메모리·머신 문서를 한 곳에 모으고, 세션이 필요할 때 꺼내 쓰게 해야 한다. 설계의 첫 원칙은 쓰는 길에 LLM 을 두지 않는 것이었다.
Table of contents
Open Table of contents
배경 — 기록이 생기는 자리와 모이는 자리
모이는 자리는 MariaDB 의 데이터베이스 하나다. 조각마다 원문, 출처 종류, 머신, 워크스페이스, 세션, 시각, 그리고 1024차원 임베딩을 담는다. MariaDB 의 VECTOR 열에 임베딩을 넣으므로, 원문과 벡터와 메타데이터가 같은 행에 있고 별도 벡터 DB 를 두지 않는다.
출처는 종류로 나눴다. 대화 턴, 자동 메모리, 워크스페이스 메모리, 머신 문서다. 종류를 나눈 이유는 찾을 때 종류마다 다르게 다뤄야 하기 때문이다. 대화 턴은 오래될수록 덜 중요하지만, 메모리나 머신 문서는 오래됐다고 덜 중요해지지 않는다.
판단 — 쓰기에서 LLM 을 빼고 임베딩만 붙인다
쓰는 길을 설계하는 방법은 두 가지였다.
- 쓸 때 LLM 으로 요약·중요도·제목을 붙여 찾기 쉽게 만든 뒤 넣는다
- 쓸 때는 원문을 정해진 크기로 자르고 임베딩만 붙여 넣는다. 고르는 일은 찾을 때 한다
2번으로 선택한 이유는 쓰기 경로가 가장 자주, 가장 많이 도는 길이기 때문이다. 기록마다 LLM 을 부르면 호출 수와 실패 지점이 기록 수에 비례해 늘어난다. 그래프 시절 에피소드 하나에 평균 9.58회였던 호출이 바로 그 구조였다. 반면 임베딩은 기록마다 한 번이고, 모델도 사내 GPU 에서 돈다. 이 선택의 대가는 쓸 때 해 두지 않은 일, 즉 무엇이 중요한지 고르는 일을 찾을 때의 순위식이 떠맡게 된다는 점이다. 그 순위식은 따로 시험해 정하기로 했다.
임베딩 모델은 한국어 문장이 대부분이라 한국어 검색에서 검증된 모델로 정했다. 처음 쓰던 bge-m3-korean 을 snowflake-arctic-embed-l-v2.0-ko(568M, 1024차원)로 바꿨는데, 공개된 한국어 검색 평가 두 곳과 이 저장소의 자료로 한 시험에서 모두 같은 크기급 1위권이었기 때문이다. 모델은 사내 GPU 서버의 임베딩 서버가 OpenAI 호환 /v1/embeddings 로 낸다.
해결 — 수집·업로드·주입 세 경로
수집(호스트, 1분마다). 각 머신의 호스트 Python 이 대화 턴 파일을 읽어 데이터 폴더에 모은다. 턴의 시각은 처음 본 순간 동결한다. 파일 수정 시각을 그대로 쓰면, 옛 턴이 나중에 다시 저장될 때 시각이 바뀌기 때문이다.
업로드(컨테이너, 5분마다). 업로드는 Docker 컨테이너가 5분마다 깨어나 모은 것을 자르고 임베딩해 DB 에 넣는다. 맥의 cron 이 직접 띄운 프로세스는 네트워크가 막혀 있어 업로드를 컨테이너로 돌리게 됐다. 이미지는 amd64·arm64 다중 아키텍처로 한 번 빌드해 네 머신이 같은 태그를 받는다. 임베딩 서버가 요청을 한 줄로 처리하므로, 머신마다 업로드 시작 분을 1분씩 엇갈려 네 머신이 한꺼번에 몰리지 않게 했다.
주입(훅). 세션 쪽에서는 Claude Code 의 훅이 사람의 프롬프트마다 질문을 임베딩해 뜻이 가까운 조각 몇 개를 맥락에 넣는다. 세션이 검색 도구를 직접 부를 수도 있다. 주입은 세션이 묻지 않아도 일어나므로, 몇 개를 어떤 거리까지 넣을지가 곧 비용이고 품질이다. 시험에서는 후보를 넓게 받아 순위식으로 다시 매긴 뒤 상위 몇 개만 자르고, 그 뒤에 거리 상한으로 거르는 순서가 먼저 거르는 순서보다 쓸모 있는 줄의 비율이 높았다.
영향 범위 — 운영에서 지켜야 할 것
이 구조에서 생긴 운영상의 함정도 함께 적어 두었다.
- 업로드는 호스트의 소스가 아니라 이미지 안의 코드로 돈다. 코드를 고치면 이미지를 다시 빌드하고 컨테이너를 다시 만들어야 하고, 판정은 각 머신에서 컨테이너가 쓰는 이미지 id 로 한다
- Windows 의 SSH 세션에서는
docker build·pull이 자격증명 저장소에 막혀, 같은 아키텍처 머신에서 이미지를save→scp→load로 옮긴다 - Windows 작업 스케줄러의 마지막 결과 값으로는 성패를 알 수 없어, 업로드 로그와 컨테이너 종료 코드로 본다
정리
- 가장 자주 도는 경로에는 가장 싼 일만 둔다. 쓰기에는 자르기와 임베딩만 두고, 고르는 일은 찾을 때 한다
- 원문·벡터·메타데이터를 한 행에 두면 별도 벡터 DB 없이 같은 질의로 거르고 정렬할 수 있다
- 시각은 처음 본 순간 동결한다. 파일 수정 시각은 기록의 시각이 아니다
- 자동 주입은 묻지 않아도 일어나는 비용이다. 개수와 거리 상한을 설계의 핵심 값으로 둔다