헤르메스가 세컨드브레인을 검색할 때는, 사실 검색 도구를 떠올린 턴에만 검색합니다.
브레인 자체는 살아 있습니다. 52.5만 벡터짜리 색인에 물으면 답도 잘 찾아옵니다. 그런데 에이전트가 도구 호출을 잊는 순간, 있는 건 없는 거나 마찬가지가 됩니다. 사무실 지하에 문서를 잔뜩 쌓아놓고 책상 위에는 아무것도 붙여두지 않은 격입니다. 내려가서 꺼내 오지 않으면 끝내 안 읽히는 메모, 그게 제 세컨드브레인이었습니다.
그래서 시맨틱 회상을 매 턴 자동으로 주입되는 메모리 프로토콜 쪽으로 집어넣어 보려 했습니다. 그리고 시작하자마자 벽에 부딪혔습니다.

외부 메모리 슬롯은 하나뿐이었습니다
헤르메스의 메모리 시스템은 MemoryProvider라는 인터페이스 규격으로 만들어져 있습니다. 지금 기억을 SQLite에 쌓는 로컬 메모리도 프로바이더이고, 외부 벡터 DB도 프로바이더가 될 수 있습니다. 문제는 매니저 코드 주석에 또렷이 적혀 있었습니다. 외부 플러그인 프로바이더는 딱 한 개만 등록됩니다.
그래서 선택지가 두 개였습니다. 로컬 메모리를 브레인으로 갈아치우거나, 단념하거나요. 갈아치우는 순간 제가 누구이고 환경이 어떤지를 눌러 앉은 대로 짚어 주는 작고 확실한 로컬 계층이 사라집니다. 둘 다 살릴 방법이 필요했습니다.
해법은 감싸기였습니다. 슬롯에 들어가는 프로바이더 하나를 만들고, 내부에 기존 로컬 메모리 프로바이더를 멤버로 둡니다. 생명주기 훅은 거의 그대로 위임하고, 제가 직접 처리하는 함수는 회상 두 개뿐입니다. queue_prefetch가 턴 종료 뒤에 불려 올 때 그 질문을 들고 백그라운드 검색을 띄우고, 다음 턴의 prefetch는 캐시에 쌓인 걸 가만히 돌려주는 식입니다.
프로토콜 원문도 "Must be fast — recall in the background and return cached results"라고 안내합니다. 지금 턴을 기다리게 하지 말고 뒤에서 찾아서 다음 턴에 내밀라는 뜻이죠. 그래서 브레인이 주는 회상은 늘 한 턴씩 늦습니다. 시킨 음식이 다 되는 대로 식탁에 올라오는 이치이고, 주방 문 앞에서 접시를 받아 들고 서 있지 않을 뿐입니다. 한 턴 지연은 설계 단계에서 일부러 받아들이고 들어간 트레이드오프입니다.
코드를 쓰기 전에 재봤습니다 — 안 갈리는 임계값
파라미터를 감으로 박아 넣다가 혼난 적이 여러 번 있습니다. 그래서 프로바이더 첫 줄을 쓰기 전에 작은 프로브 스크립트부터 돌렸습니다. 실제 응답의 점수 분포를 먼저 재보는 게 목적이었습니다.
답이 있는 질문 8개에, 일부러 답 없는 질문 4개를 섞었습니다. 연구 코호트 규모나 프린터 오류 코드, 심사 초대 회신 정책처럼 브레인에 분명한 답이 있는 질문들을 날리고, 파스타 삶는 시간이나 타이타닉 줄거리 같은 걸 일부러 함께 넣었습니다. 임베딩 서버를 거쳐 벡터 DB에서 상위 3건의 코사인 점수를 받는 방식입니다.
예상은 관련/무관이 0.75와 0.82 사이에서 또렷하게 갈리는 모습이었습니다. 실측은 달랐습니다.
| 질문 종류 | 상위 점수 |
|---|---|
| 관련 질문 8개 | 최저 0.605 |
| 무관 질문 4개 | 최고 0.629 |
분포가 겹쳤습니다. 관련 질문의 최저점이 무관 질문의 최고점보다 낮았습니다. 상상하던 넓은 골짜기는 애초에 없었습니다. 저는 0.615에서 선을 그었습니다. 이보다 낮은 점수는 전부 쳐내는 식입니다. 선을 0.02만 밀어도 막히는 결과가 바뀌는 애매한 자리지만, 데이터가 그것 말고는 놓을 데가 없다고 하니 어쩔 수 없죠. 일주일치 관측을 쌓아 다시 조정하는 전제로 남겨뒀습니다.
한편 프로브 당시 레이턴시는 호출당 160~410ms였습니다. 이 김에 임베딩 서버가 쿼리에 "query: " 접두어를 자동으로 붙여준다는 것도 확인해 뒀습니다. 이런 건 가정하고 코드를 먼저 쓰면 나중에 원인도 모르는 버그가 됩니다.
지뢰 세 개
컴파일 의존성은 인터프리터에 따라 터집니다. 게이트웨이의 크론 워커는 Python 3.14에서 도는데, 메모리 라이브러리가 앉은 venv는 3.11입니다. 최근에도 rpds라는 컴파일 패키지가 이 접점에서 터지는 걸 봤습니다. 그래서 이번 브레인 클라이언트는 stdlib urllib만 쓰고, import 경로는 기존 shim과 똑같이 append만 합니다.
백그라운드 스레드의 문맥은 조용히 실종됩니다. 회상 검색을 아무 스레드에나 태우면 안 됩니다. 헤르메스의 프로필 격리는 ContextVar 기반이라, 컨텍스트 없이 시작한 스레드는 기본 프로필로 소리 없이 떨어집니다. 서브에이전트의 회상이 남의 책상에 주입되는 사고가 나는데도 로그에는 아무것도 남지 않습니다. 프로토콜이 제공하는 경유 방식으로만 스레드를 띄워야 합니다.
YAML 헤더가 스니펫 절반을 먹었습니다. 스모크 테스트에서 주입 결과를 열어 보고 눈을 깜빡였습니다. 회상 라인마다 앞에 "--- title ... ---" 머리가 붙어 나오고, 220자 스니펫의 절반이 메타데이터였습니다. 실제로 주입된 출력을 눈으로 확인하지 않았다면 한 달 뒤에 알 결함입니다. frontmatter를 벗겨 내는 정규식을 마지막에 한 줄 더 넣었습니다.
재시작 함정도 메모해 뒀습니다. 게이트웨이를 재시작해도 이미 열려 있던 세션은 옛 프로바이더를 그대로 씁니다. 검증은 반드시 새 세션에서 해야 합니다. 대시보드 경유 세션은 아예 별개 프로세스라 그쪽도 따로 재시작이 필요한데, 이건 이번엔 손대지 않았습니다.
자동 주입을 위한 안전장치
매 턴 컨텍스트에 내용을 자동으로 밀어 넣는 배관은, 필요할 때 부르는 도구랑은 마음가짐이 달라야 합니다. 고장 나면 모든 세션이 같이 고장 나니까요. 그래서 fail-open으로 만들었습니다. 임베딩 서버나 벡터 DB가 죽으면 예외를 삼키고 로컬 메모리 결과만 돌려줍니다. 불평을 안 합니다. export BRAIN_PREFETCH_ENABLED=0 한 줄로 브레인 경로만 즉시 끄는 스위치도 달아뒀습니다.
hermes config set memory.provider brain_memory # 활성 스위치
export BRAIN_PREFETCH_ENABLED=0 # 즉시 정지
주입 직전 게이트는 여러 겹입니다. 질문이 8글자보다 짧으면 건너뛰고, 인사말 같은 뻔한 패턴이면 건너뛰고, 같은 세션의 이전 검색이 아직 끝나지 않았으면 또 건너뜁니다. 문자 예산은 1200자로 묶고, 로컬 메모리에 이미 있는 줄은 겹침을 걷어 낸 뒤에 올립니다. 활성으로부터 일주일 뒤인 27일 아침에 일회성 관측 크론도 심어 뒀습니다. 기준은 미리 못 박아 뒀습니다. p95가 400ms를 넘거나 주입률이 20% 밑이면 걷는 쪽으로 검토합니다. 남길지는 제 기분이 아니라 데이터가 보고한 숫자가 정하게 되어 있습니다.
하루 돌려보니, 43%의 턴에만 들어갔습니다
10월 20일 새벽에 활성하고 나서 오늘 아침까지 회상 호출은 65번 쌓였습니다.

중앙값 레이턴시는 59ms, p95는 145ms로 프로브 때보다 오히려 빨랐습니다. 임베딩 서버가 늘 데워져 있는 덕 같습니다. 주입이 일어난 건 65번 중 28번, 43%입니다. 한 번 들어간 블록은 평균 718자였습니다.
제일 눈에 걸리는 숫자는 36번입니다. 검색은 3건씩 돌아왔는데 전부 0.615 앞에서 멈춰 선 경우입니다. 절반이 넘는 턴에서 브레인이 말을 꺼내려다 임계값에 밀려난 셈이죠. 임계값이 짠 것인지 회상 자체의 정확성이 약한 것인지, 아직은 모릅니다.
한계는 또렷합니다. 0.615는 질문 12개 기준의 선이라 다음 주 캘리브레이션에서 요동이 예정되어 있습니다. 세션 직후 던진 질문에는 첫 턴답을 못 붙여 줍니다. 대시보드 세션의 회상은 아직 반영 전이고, dense 검색만 쓰고 리랭커는 없습니다. 턴당 700자 넘게 컨텍스트를 쓰고 정작 답에 쓰이는지는 지금으로선 손으로 샘플링해야 하는 지점입니다.
27일에 관측 데이터를 뽑아서 남길지 걷을지 정하고, 그 결과를 또 여기에 올리겠습니다. 걷게 되어도 설정 파일의 프로바이더 이름 한 줄을 되돌리고 재시작하는 게 전부인 구조로 지었습니다. 배관은 얇게 깔수록 회수가 쉽습니다.
'공부 > 인공지능' 카테고리의 다른 글
| DeepSeek 1017을 2대의 GB10에 돌리다 — TensorFold와 1M 컨텍스트의 실체 (0) | 2026.10.20 |
|---|---|
| API server down 경보를 받고 서버를 의심했습니다 — 로그가 거짓말한 겁니다 (0) | 2026.10.18 |
| 음성 비서가 느린 진짜 병목은 LLM 첫 토큰이었습니다 — 헤르메스 데스크톱에 로컬 음성 스택 붙이기 (0) | 2026.10.17 |
| 얼굴 인식으로 '내가 방에 있는지' 판정하다 포기했습니다 — CCTV 재실 판정은 공간 마커 규칙으로 (1) | 2026.10.15 |
| 문패를 e-paper로 바꿨더니 배터리가 13배 오래 갔다 — ESP32 문패 구축기 (0) | 2026.10.14 |