작성일: 2026-07-15 (#03) 목적: "개발 채널에 외부 provider holographic 설정" 요청에서 출발해, 실제 컨테이너를 실측하며 holographic의 실동작·저장 타이밍·numpy 함정을 규명하고 dev 프로필에 활성화한 뒤, "memory provider는 채널 전체 대화를 저장하지 않는다" 는 근본 사실에 도달한 전 과정 기록. 성격: 학습/의사결정/실행 기록. 앞선 RAG 설계 논의는 Hermes RAG 논의 — 옵션 비교와 초기 결정, 실행계획서는 Hermes 문서 RAG 파이프라인 — 실행계획서. 관련 프로젝트 메모리: hermes-dev-holographic-provider, hermes-multi-profile-channel-agents.


0. 요약 (TL;DR)

  1. holographic = 메모리(RAG) provider (LLM 모델 provider 아님). 설치된 8개 플러그인 중 하나. 로컬 SQLite + FTS5 + HRR, API키·벡터DB 불필요.
  2. dev 프로필에 활성화 완료. 저장 경로 /opt/data/profiles/dev/memory_store.db, 저장 방식 = 실시간 fact_store 도구(SOUL.md에 지침 추가), auto_extract=false.
  3. ⚠️ HRR(의미검색)은 이 이미지에 numpy가 없어 런타임에서 비활성 → 실동작은 "FTS5 어휘검색 + Jaccard + trust"뿐. numpy 설치 전까지 의미검색 안 됨. → (c) FTS5-only로 먼저 켜고 HRR은 나중에 승격 결정.
  4. provider "동시 1개" 제약은 프로필 단위 → 채널별로 다른 provider 가능.
  5. 핵심 반전: memory provider(holographic 포함)는 멘션/DM으로 봇이 참여한 대화만 저장한다. 멘션 없는 채널 전체 대화는 어떤 provider를 켜도 저장되지 않는다 — 멘션 게이트(require_mention=true)가 에이전트보다 위에 있기 때문. 이는 Mattermost·Slack 등 전 플랫폼 공통. "채널 전체 저장"은 provider가 아니라 별도 인제스트 파이프라인의 영역.

1. 논의 흐름 (질문 → 결론)

순번 사용자 질문/요청 도달한 결론
Q1 개발 채널에 외부 provider holographic 설정하자 holographic=메모리 provider. dev 프로필에 활성화하는 작은 설정 변경(RAG 파이프라인 구축과 별개)
Q2 (auto_extract 선택 중) 세션 종료의 기준이 뭐야? on_session_end은 매 메시지 아님. idle 만료/압축/재시작/reset 때만. 기본 mode=none이라 idle로는 세션이 안 끝남
Q3 대화를 실시간으로 저장하는 방법은? 옳지 않은 방법이야? 오히려 정석. fact_store 도구로 대화 중 즉시 저장(holographic sync_turn은 no-op=도구 경로가 설계 의도). 세션종료 타이밍 함정을 우회
Q4 채널별로 다른 provider 설정 가능? 가능. memory.provider가 프로필별 config.yaml 문자열 1개 → "동시 1개"는 프로필 단위
Q5 FTS5+HRR은 기본 설정이야? FTS5는 항상. HRR은 코드상 기본이나 numpy 없어 런타임 비활성(FTS5 폴백)
Q6 1.numpy 설치해 HRR 살리자 2.numpy가 뭐야 3.데이터 위치는? numpy=수치계산 라이브러리(HRR 벡터연산 필수). 데이터=memory_store.db 한 파일(FTS5=가상테이블, HRR=BLOB 컬럼). 설치는 컨테이너 안에서만 유효
Q7 numpy 설치 때문에 이런 제안? 다른 방법은? 로컬 설치는? 그렇다. 호스트(Mac) 설치는 무용(컨테이너 격리+wheel 불일치). → (c) FTS5로 먼저 켜고 나중에 HRR 승격 선택
Q8 순서대로 진행 dev에 holographic 활성화 실행 완료(2~7단계)
Q9 봇 멘션 안 걸어도 대화가 저장돼야 하는 것 아냐? 아니오. 기본 require_mention=true → 멘션 없는 메시지는 에이전트 전에 drop → 저장 대상 없음
Q10 holographic 목적이 채널 전체 저장 아니었어? 멘션 저장은 활성화 없이도 됐잖아? 둘 다 맞음. memory provider는 채널 전체를 저장 못 함(설계상). 내장 메모리로 멘션 대화 기억은 원래 가능. holographic이 준 건 "구조화·검색"뿐
Q11 Slack도 마찬가지야? Mattermost만? 전 플랫폼 공통. Slack도 require_mention=true 기본 + Events API가 platform 단에서 app_mention만 전송

2. holographic의 정체 규명

  • hermes memory CLI 하위: setup / status / off / reset. provider 관리는 memory setup [provider](arg 주면 picker 없이 비대화형).
  • 설치된 memory provider 플러그인 8개: byterover, hindsight, holographic, honcho, mem0, openviking, retaindb, supermemory. 제약 문구: "Only one external provider can be active at a time."
  • holographic만 완전 무료·로컬(API키 0). 나머지는 클라우드/유료(mem0/honcho/retaindb/supermemory 등).
  • 소스(plugins/memory/holographic/): __init__.py, store.py, retrieval.py, holographic.py, README.md, plugin.yaml.
  • 저장 경로: $HERMES_HOME/memory_store.db. 테이블 facts, entities, fact_entities, memory_banks + FTS5 facts_fts.
  • 도구 2종: fact_store(add/search/probe/related/reason/contradict/update/remove/list), fact_feedback(신뢰점수 학습).
  • 설정 기본값(__init__.py): auto_extract=false, default_trust=0.5, hrr_dim=1024, db_path=$HERMES_HOME/memory_store.db.

3. 저장 타이밍 규명 — "세션 종료"란 무엇인가

holographic의 자동추출(auto_extract)은 on_session_end(messages) 훅에서만 발화한다. 이 훅은 매 메시지가 아니라 세션이 finalize되는 순간에만 호출된다.

run_agent.pyshutdown_memory_provider docstring: "call at actual session boundaries... NOT called per-turn — only at CLI exit, /reset, gateway session expiry, etc."

발화 지점(5):

트리거 근거 기본 상태
① 유휴 만료(idle) gateway/session.py:1590 updated_at + idle_minutes idle_minutes=1440(24h)이나 mode="none"이라 미작동
② 일일 경계(daily) at_hour=4 mode="none"이라 미작동
③ 컨텍스트 압축/session_id 회전 run_agent.py commit_memory_session 대화가 길어져 압축 시 발화
④ 게이트웨이 종료·재시작 run_agent.py shutdown_memory_provider 재시작 시 발화
⑤ 명시적 /reset 동일 수동

핵심: gateway/config.pySessionResetPolicy.mode 기본값이 "none"(2026-07 변경, "사용자가 대화 유지를 기대해서 놀란다"는 이유). → 기본 설정에선 유휴/일일로 세션이 안 끝나므로, auto_extract만 켜도 ③압축/④재시작/⑤리셋 때만 flush됨(주기적 축적 안 됨).


4. 실시간 저장 vs 배치 (기억을 언제 담나)

holographic이 대화 내용을 저장하는 3가지 경로:

방식 시점 판단 주체 근거
A. fact_store 도구 대화 중 즉시 봇(LLM) get_tool_schemas → FACT_STORE_SCHEMA, handle_tool_call
B. built-in 메모리 미러 봇이 MEMORY.md/USER.md 쓰는 즉시 on_memory_write — add를 fact로 미러
C. auto_extract 배치 세션 종료 시 일괄 자동(LLM 추출) on_session_end(위 3절 함정)
  • holographic의 sync_turn(매 턴 훅)은 의도적으로 no-op: "Holographic memory stores explicit facts via tools, not auto-sync." → 즉 holographic은 처음부터 도구 기반 실시간 저장(A) 을 1급 경로로 설계(auto_extract=false 기본인 이유).
  • 채택: A(실시간 fact_store). 세션종료 타이밍 함정을 우회, 크래시/재시작에 강함, 저장 품질 정밀. 단 봇이 도구를 부르도록 SOUL.md 지침이 필요 → dev SOUL.md에 추가함(8절).
  • "봇 판단 없이 100% 자동+실시간"은 holographic엔 없음(그건 sync_turn을 구현한 byterover 영역).

5. 채널별 다른 provider (프로필 단위)

  • memory.provider각 프로필 자신의 config.yaml에 저장(_save_memory_provider → 활성 프로필 홈). _get_current_memory_providerload_config()로 프로필 홈에서 읽음.
  • 각 채널 봇 = 독립 게이트웨이 프로세스 + 독립 MemoryManager → 자기 프로필 provider만 로드. memory_store.db도 프로필 홈에 격리.
  • "동시 1개" 제약 = 프로필 안에서 1개. dev=holographic, sales=mem0 처럼 채널별 상이 가능. 단 dev 하나에 holographic+mem0 동시는 불가.
  • 주의: 무료·로컬은 holographic뿐. 클라우드 provider를 채널마다 붙이면 키/비용·메모리(게이트웨이당 ~110MB) 부담. default 프로필은 미변경.

6. FTS5 + HRR 실측 — numpy 함정 (핵심 발견)

레이어 기본 여부 이 컨테이너 실측
SQLite 저장 항상
FTS5 어휘검색(BM25) 항상 생성·검색 1단계
Jaccard 재랭크 + trust 가중 항상
HRR 의미검색(1024차원) 코드상 기본(hrr_dim=1024) 비활성 (numpy 없음)

근거:

  • holographic.py:28-31: try: import numpy … except ImportError: _HAS_NUMPY=False. 이 컨테이너 numpy 미설치(ModuleNotFoundError).
  • store.py _compute_hrr_vector: "No-op if numpy unavailable"if not self._hrr_available: return. 저장 시 HRR 벡터 계산조차 안 함(facts.hrr_vector NULL).
  • retrieval.py:38-39: if hrr_weight > 0 and not hrr._HAS_NUMPY: → HRR 가중치를 0으로 재분배. search/probe/related 전부 if not hrr._HAS_NUMPY: 로 FTS5 폴백.
  • holographic.py encode_atom: SHA-256 결정론적 위상벡터 → 외부 임베딩 모델·GPU 불필요. 유일한 실질 의존성이 numpy 하나였고 그게 이미지에서 빠져 있던 것.

결론: dev에 holographic을 켜도 실동작 = "FTS5 어휘검색 + Jaccard + trust" 수준. "car"↔"automobile" 의미검색은 numpy 설치 전까진 안 됨. (앞 01-rag-design-discussion 문서의 "holographic=외부 임베딩 없이 의미검색, 인프라 0" 서술은 numpy 전제가 빠진 것.)


7. numpy 설치·영속화 논의

  • numpy = 파이썬 수치계산 라이브러리(N차원 배열+벡터/행렬 연산). HRR의 벡터 합성·코사인 유사도 계산에 필수. 임베딩 "모델"이 아니라 순수 수학 라이브러리.
  • 호스트(Mac) 설치는 무용: ① 컨테이너는 호스트 파이썬 패키지를 못 봄(격리) ② numpy는 C 확장이라 macOS(arm64) wheel과 컨테이너(linux arm64) wheel 비호환. 설치는 반드시 컨테이너 안에서.
  • 실측 제약: venv(/opt/hermes/.venv)는 hermes 유저 쓰기 불가(root 소유) + 이미지 레이어(볼륨 아님). venv에 pip 없음. uv/usr/local/bin/uv에 있음. 컨테이너 인터넷 OK. /opt/data는 grpcfuse 볼륨(영속).
  • 영속화 방식:
    방식 실체 영속성
    exec 설치 docker exec -u root … uv pip install numpy docker restart 생존 / recreate·재빌드 시 소멸
    커스텀 이미지 Dockerfile FROM …:latest + RUN uv pip install numpy, compose build 모두 생존(정석)
    볼륨+PYTHONPATH /opt/data 아래 설치 + PYTHONPATH 볼륨으로 영속(리빌드 없음). "로컬 설치"에 가장 근접
  • 결정: (c) 지금은 numpy·영속화 보류, FTS5-only로 dev 먼저 활성화. 의미검색이 아쉬운 순간에 (b)볼륨+PYTHONPATH로 HRR 승격.

8. 실제 적용 (실행 로그 + 검증)

대상: dev 프로필(@dev-bot, 채널 b15acwum5bfg9gbhzrfuhh8nee). default·기타 프로필·session_reset 미변경. 전부 -u hermes 실행.

단계 명령/작업 결과
1 사전확인 hermes -p dev memory status, gateway list provider none, dev gateway PID 866 running
2 활성화 hermes -p dev memory setup holographic memory.provider: holographic 기록(비대화형, 기본값)
3 상태 hermes -p dev memory status Provider: holographic, plugin installed ✓ available ✓
4 SOUL.md dev SOUL.md에 fact_store 실시간 저장 지침 추가 추가됨(사용자 명시 승인 후 — auto-mode 분류기가 페르소나 수정 차단)
5 재기동 dev python PID kill → s6 자동 부활 PID 866 → 2275, 로그 에러/트레이스백 없음
6 검증 임시 DB로 store 직접 구동 _HAS_NUMPY=False, FTS5 한국어 저장/검색 ✓, hrr_vector NULL ✓, facts_fts 존재 ✓
7 기록 프로젝트 메모리 + 이 글

config.yaml 기록 확인: dev memory: 섹션에 provider: holographic (auto_extract 등은 미기재=기본 false).

데이터 위치(dev 기준):

무엇 경로 형태
holographic 전체 /opt/data/profiles/dev/memory_store.db 단일 SQLite (활성화·첫 저장 시 생성)
├ FTS5 위 파일 내부 facts_fts 가상테이블
└ HRR 위 파일 내부 facts.hrr_vector BLOB + memory_banks.vector
내장 메모리(별개) /opt/data/profiles/dev/memories/MEMORY.md·USER.md 마크다운
(호스트에선) ~/.hermes/profiles/dev/memory_store.db /opt/data=~/.hermes 볼륨

SOUL.md에 추가한 지침(승인본):

## 메모리 사용 지침 (holographic fact_store)
이 개발 채널에서는 대화 중 아래를 발견하면 즉시 `fact_store`(action=add)로 저장하라:
- 확정된 결정·설정값·환경/버전·엔드포인트·규칙
- 반복될 문제의 원인과 해결책(에러 → 해법)
- 팀·프로젝트 고유 용어와 그 정의
답변 전, 질문과 관련된 과거 사실이 있을 법하면 `fact_store`(action=search 또는 probe)로 먼저 조회해 근거로 삼아라.
저장은 간결한 단일 사실 단위로. 비밀번호·토큰·개인정보 등 민감정보는 저장하지 마라.

9. 근본 재점검 — memory provider ≠ 채널 전체 저장 (반전)

사용자 목적이 "멘션 여부와 무관하게 채널의 모든 대화 저장"임이 드러났고, 이는 memory provider로 달성 불가함을 확인.

  • memory provider 훅(sync_turn, on_session_end, on_memory_write, fact_store)은 에이전트의 턴 안에서만 동작.
  • 그런데 멘션 게이트가 에이전트보다 위에 있음. mattermost/adapter.py:
    • MATTERMOST_REQUIRE_MENTION 기본 "true"(833행).
    • 849행 if require_mention and not is_free_channel and not has_mention: return멘션 없는 채널 메시지는 에이전트 실행 전에 drop.
    • allowed_channels(화이트리스트), free_response_channels(멘션 없이 응답할 채널 ID) 노브 존재.
  • 따라서 어떤 provider를 켜도 멘션 없는 메시지는 물리적으로 못 봄 → 저장 불가. provider 활성화는 수신 범위를 넓히지 않음.

3개 관문(앞 01-rag-design-discussion 5절 재확인): ①수신(멘션/DM만) ②저장(fact_store/auto_extract) ③파일(별도 색인). 첫 관문에서 대부분 걸림.

사용자의 두 지적, 둘 다 정확:

  1. "멘션 대화 저장은 활성화 없이도 가능" → 맞음. 내장 메모리(MEMORY.md/USER.md)는 항상 활성. holographic이 준 건 저장의 구조화·검색뿐, 범위는 내장과 동일(멘션/DM).
  2. "채널 전체 저장이 목적" → 그거라면 holographic은 도구가 아님. memory provider = 봇의 기억(참여한 대화), 채널의 로그가 아님.

"멘션 없이 저장" 억지 방법과 함정: MATTERMOST_FREE_RESPONSE_CHANNELS에 채널 추가(또는 require_mention=false) → 봇이 모든 메시지에 응답하게 됨. "조용히 관찰만 저장(observe-only)" 모드는 없음 → 공유 채널에서 봇이 잡담마다 끼어듦(비실용).

진짜 경로: "채널 전체 대화 저장"은 문서/메시지 RAG 인제스트 파이프라인(Hermes 문서 RAG 파이프라인 — 실행계획서) 영역. 플랫폼 API로 채널 히스토리를 봇 응답과 무관하게 수집→색인. 봇이 수다스러워지지 않고 멘션 게이트도 안 거침. 대신 별도 서비스 구축 필요.


10. 플랫폼 무관성 — Slack도 동일

멘션 게이트는 Mattermost 특성이 아니라 Hermes 전 플랫폼 공통 설계.

멘션 게이트 기본 free_response 노브 비고
Mattermost require_mention=true MATTERMOST_FREE_RESPONSE_CHANNELS allowed_channels 화이트리스트
Slack require_mention=true(adapter.py:4168) SLACK_FREE_RESPONSE_CHANNELS strict_mention 모드 추가; Events API가 platform 단에서도 app_mention만 전송(1098행) → 한 겹 더 게이트
Telegram/Discord/Teams 등 require_mention 브리지 존재 플랫폼별 동일 plugins/platforms/에 22개 어댑터 공통 관례
  • Slack 로직(adapter.py:2844-2849): free_response면 응답 / require_mention=false면 응답 / else return # Strict mode: ignore until @-mentioned again.
  • 핵심은 플랫폼 무관: 멘션 게이트가 에이전트 위에 있어, provider가 채널 전체를 저장하는 건 어느 플랫폼에서도 불가. 인제스트 파이프라인은 플랫폼별 수집 커넥터만 교체하면 되는 공통 설계라, Mattermost로 만들면 Slack 확장이 쉬움.

11. 결론 & 다음 방향

  • 현재 dev 구성: holographic 활성(FTS5 실동작, HRR 보류), 실시간 fact_store 저장, 멘션/DM 대화 범위. 무료·로컬.
  • holographic 처리 선택지:
    • (a) 유지 — 채널 전체 저장엔 기여 못 하나, 멘션/DM 대화 기억을 구조화·검색 가능하게 하는 이득은 실재. 나중에 인제스트 저장소로도 재활용 가능.
    • (b) 비활성(hermes -p dev memory off + config/SOUL 롤백) 후 인제스트 파이프라인으로 방향 전환.
  • 권장: (a)로 두되(해 없음), 진짜 목적인 "채널 전체 대화 저장" 인제스트 파이프라인을 별도 브레인스토밍. 요구사항 재정의 필요: 저장 범위/제외, 저장만 vs 검색까지, 규모, 플랫폼(Mattermost/Slack), 실시간성.
  • 미완료(사용자 확인 필요): 실채널 왕복 테스트(멘션 저장→회수)는 미검증. HRR 승격(numpy)은 보류.

12. 실측 근거 인덱스 (파일:라인)

  • plugins/memory/holographic/__init__.py: sync_turn no-op(222), on_session_end(237), on_memory_write(244), 설정 기본값 auto_extract/trust/hrr_dim(152-155)
  • plugins/memory/holographic/store.py: 스키마 facts/hrr_vector BLOB(17-27), facts_fts FTS5(48), add_fact→_compute_hrr_vector(227-228), _compute_hrr_vector no-op guard
  • plugins/memory/holographic/holographic.py: _HAS_NUMPY(28-31), encode_atom SHA-256(43)
  • plugins/memory/holographic/retrieval.py: 하이브리드 search FTS5→Jaccard→HRR(55,91), numpy 폴백(128,206,277), hrr_weight 재분배(38-39)
  • run_agent.py: shutdown_memory_provider(3305), commit_memory_session(회전/압축 flush)
  • gateway/config.py: SessionResetPolicy mode 기본 "none", idle_minutes=1440(369 부근)
  • gateway/session.py: idle_deadline updated_at+idle_minutes(1590)
  • plugins/platforms/mattermost/adapter.py: require_mention 기본 true(833), free_response(836), skip non-mention(849-851), allowed_channels 화이트리스트
  • plugins/platforms/slack/adapter.py: _slack_require_mention 기본 true(4168), strict_mention(4185), free_response(4192), app_mention 이벤트(1098), 게이팅 분기(2844-2849)
  • 환경 실측: numpy 미설치(ModuleNotFoundError), venv pip 없음, uv=/usr/local/bin/uv, /opt/data=grpcfuse 볼륨, venv hermes 쓰기불가(root)

부록. 현재 dev 프로필 상태 (2026-07-15 기준)

  • Provider: holographic (plugin installed ✓ available ✓)
  • gateway: running, PID 2275
  • config: /opt/data/profiles/dev/config.yamlmemory.provider: holographic
  • SOUL.md: fact_store 실시간 저장 지침 추가됨
  • 미변경: default·sales·finance·qna provider(none/각자), session_reset(mode=none)