report jini draft 2026-06-08

Pantheon 페르소나 도구·세션 아키텍처 (전략 수립용)

Summary

페르소나(jini, wansu, raphael, nano, jarvis, asurada)는 각자 독립 Slack 봇 인스턴스로 부팅되고, 메시지가 들어오면 Pantheon bridge가 라우팅 → 세션 복원 → LLM 호출 → Slack 응답 순으로 처리한다. MCP 도구는 페르소나가 띄우는 LLM 프로세스의 환경에 정의된 것만 보이며, 사용자(hangman) Claude Code 본 세션의 MCP 연결과는 완전히 분리된다. 본 보고서는 이 구조를 5개 레이어로 분해해 도구 관리 전략 의사결정 근거를 만든다.

Context

왜 쓰는가

콜로세움 트래커 케이스에서 Jini는 Notion 접근 불가, Wansu는 reconnect 후 접근 가능이라는 비대칭이 관찰됐다. 단발 디버깅으로는 풀 수 있지만, 앞으로 도구를 어떻게 늘리고 운영할지 결정하려면 시스템 구조 자체를 이해해야 한다.

따라서 본 보고서는 이번 사고 해명이 아니라 향후 도구 관리 전략 의사결정용 인프라 보고서다.

표기 규약

전체 흐름 한 장

flowchart TB
  U["사용자 (Slack)"] -->|메시지| SL[Slack Cloud]

  subgraph Pantheon["Pantheon (단일 Python 프로세스: bridge.py)"]
    direction TB
    SH1["Bolt App #1 (jini bot)"]
    SH2["Bolt App #2 (wansu bot)"]
    SH3["Bolt App #3 (raphael bot)"]
    SH4["Bolt App #N (...)"]

    ROUTER["라우터<br/>parse_mention + auto_channels + northstar gate"]
    SESSION["세션 매니저<br/>persona_sessions + persona_memory"]
    DISPATCH["ask_llm dispatcher"]
    PROV_C["providers/claude.py<br/>subprocess: claude -p --resume"]
    PROV_X["providers/codex.py<br/>OpenAI API"]
    PROV_G["providers/gemini.py"]
  end

  subgraph LLM_PROC["LLM 프로세스 (페르소나마다 새로 spawn)"]
    direction TB
    LLM["Claude CLI / Codex / Gemini"]
    MCP["MCP 도구 셋<br/>(이 프로세스 환경에 정의된 것만)"]
  end

  subgraph USER_CC["사용자 Claude Code 본 세션 (별개 프로세스)"]
    direction TB
    USER_LLM["Claude CLI"]
    USER_MCP["MCP 도구 셋<br/>(사용자 OAuth/토큰)"]
  end

  SL --> SH1
  SL --> SH2
  SL --> SH3
  SL --> SH4
  SH1 & SH2 & SH3 & SH4 --> ROUTER
  ROUTER --> SESSION
  SESSION --> DISPATCH
  DISPATCH --> PROV_C & PROV_X & PROV_G
  PROV_C --> LLM
  PROV_X --> LLM
  PROV_G --> LLM
  LLM --> MCP

  LLM -->|응답 텍스트| DISPATCH
  DISPATCH -->|say + persona bot_token| SL
  SL -->|페르소나 명의 게시| U

  USER_LLM -.->|*완전히 분리*| USER_MCP

  style USER_CC stroke:#f85149,stroke-dasharray: 5 5
  style LLM_PROC stroke:#3fb950
  style Pantheon stroke:#58a6ff

핵심: 왼쪽 Pantheon 영역과 오른쪽 사용자 Claude Code 영역은 같은 머신에서 돌지만 별개 프로세스다. MCP 도구 연결도 각 영역이 별도로 보유한다.

레이어 분해

레이어 1: 메시지 수신 (페르소나별 독립 봇)

확정 사실 (bridge.py:46-47, 134-159, 655, 3396-3483)

핵심 함의: 페르소나는 각자 자기 Slack 앱이다. 봇 토큰을 분리해야 발화 주체가 보존된다. 그래서 jini가 hangman 토큰으로 post하면 주체 혼동이 생긴다 ([feedback_no_persona_via_user_token]).

레이어 2: 라우팅 (어느 페르소나가 응답할지)

확정 사실 (bridge.py:382-433, 1893-1906, 1998-2004)

라우팅은 프리픽스 → 채널 자동응답 → northstar 게이트 3단계로 결정된다.

단계 트리거 동작
1. 강제 프리픽스 !jini, !wansu 해당 페르소나가 무조건 응답
2. 자동응답 채널 JINI_AUTO_CHANNELS, WANSU_AUTO_CHANNELS 환경변수 태그 없는 메시지도 응답 후보
3. Northstar gate #northstar 또는 자동응답 채널 + 무태그 LLM이 응답할지 말지 스스로 판단 (NO_RESPONSE 키워드로 침묵 선택)

추정: 같은 채널에 여러 페르소나가 자동응답 후보로 있으면 각자 northstar gate를 돌고 응답 가치가 있다고 판단한 페르소나만 실제 답한다. 이게 dispatcher 2세대(project_dispatcher_agent)에서 다듬는 중인 영역.

레이어 3: 세션·메모리 (대화 연속성)

확정 사실 (bridge.py:1973-2041, persona_sessions.py, persona_memory.py)

세션 영속화는 3겹 구조다.

flowchart LR
  M["메시지 도착"] --> S1
  S1["1. sessions.json<br/>{persona}:{thread_ts}<br/>= claude session_id"]
  S2["2. persona_sessions<br/>채널별 상태<br/>(7d TTL)"]
  S3["3. persona_memory<br/>episodic JSONL<br/>(검색 가능)"]

  S1 -.->|--resume| LLM["LLM 프로세스"]
  S2 -.->|컨텍스트 주입| LLM
  S3 -.->|관련 회상| LLM

핵심 함의: 세션 영속성은 Pantheon 측 파일 시스템이 책임진다. 페르소나가 "기억한다"는 것은 Pantheon이 다음 호출에 과거 텍스트를 다시 주입한다는 뜻이지, LLM 프로세스가 살아 있다는 뜻이 아니다. LLM 프로세스는 매 메시지마다 새로 spawn된다.

레이어 4: LLM 호출 + 도구 접근 (핵심)

확정 사실 (providers/claude.py:64-190, bridge.py:1675-1777)

sequenceDiagram
  participant B as bridge.py
  participant P as providers/claude.py
  participant CLI as claude CLI (subprocess)
  participant MCP as MCP 서버들

  B->>P: ask_llm(persona, prompt, session_id)
  P->>CLI: subprocess.Popen(["claude", "-p", prompt, "--resume", session_id])
  Note over CLI: 새 프로세스. 환경변수·MCP config 상속
  CLI->>MCP: tools 호출 (있는 것만)
  MCP-->>CLI: 결과
  CLI-->>P: stream JSON (type=result)
  P-->>B: LLMResponse(text, session_id, usage)

확정 사실: 페르소나는 Claude CLI 서브프로세스로 매번 새로 뜬다 (providers/claude.py:64). MCP 도구 노출은 이 서브프로세스의 환경에 의해 결정된다.

추정: 페르소나가 보는 MCP 도구 목록은 Claude CLI가 인식하는 MCP config가 결정하는데, 그 config가 사용자 Claude Code 본 세션과 어디까지 공유되는지가 현재 보고서의 핵심 모호점. 두 가지 시나리오:

시나리오 결과
A. 페르소나도 ~/.claude/settings.json을 동일하게 읽음 본 세션에 연결된 MCP는 페르소나에도 보여야 함
B. 페르소나는 별도 MCP config 사용 페르소나용 도구를 명시적으로 추가해야 함

콜로세움 케이스 관찰: Jini는 Google Drive MCP 도구는 보이는데 Notion MCP 도구(notion-search, notion-fetch)는 안 보이고 authenticate만 보임. 추정: Notion MCP는 OAuth 흐름이 페르소나 측에서 미완성 상태로 부팅된 것. Google Drive MCP는 동일 OAuth임에도 정상 노출되므로 MCP 별 인증 상태 차이가 본질.

레이어 5: 응답 게시 (페르소나 명의 보존)

확정 사실 (bridge.py:2250~, tools/slack_send.py:20-27)

핵심 함의: 페르소나 명의는 봇 토큰 분리로 강제된다. 사용자 토큰으로 post하면 사용자 본인 명의로 보임 → 페르소나 정체성 무너짐.

MCP 도구 노출 매트릭스 (관측 기반)

콜로세움 케이스에서 jini 페르소나에 노출된 deferred 도구 목록을 관측 사실로 정리:

MCP 서버 페르소나 측 노출 사용자 본 세션 노출 원인 추정
Slack (slack-hangman) ✅ 전체 ✅ 전체 bot/user 토큰 모두 환경에 박혀 있음
Google Drive ✅ 전체 ✅ 전체 OAuth 인증 완료 상태로 부팅
Linear (hangman) ✅ 전체 ✅ 전체 API 키 환경변수 박혀 있음
GCal ✅ 전체 ✅ 전체 OAuth + 토큰 파일 존재
Notion ⚠️ authenticate ✅ 전체 페르소나 측 OAuth 토큰 미주입
Chrome DevTools (확인 필요)

핵심 패턴: Bearer/API 키 기반 MCP는 페르소나도 정상, OAuth 기반인데 토큰 파일이 페르소나에 안 보이면 실패. Google Drive는 같은 OAuth지만 토큰이 공유되는 위치에 있는 것으로 추정.

도구 관리 전략 옵션 3가지

이 구조 이해를 바탕으로 앞으로 도구를 어떻게 관리할지 3가지 옵션을 펼친다.

옵션 A: 페르소나 MCP를 사용자 본 세션과 완전히 공유

페르소나가 spawn될 때 사용자 ~/.claude/settings.json (또는 동등 위치)을 그대로 상속하게 만든다.

장점 단점
도구 추가 시 한 곳만 관리 페르소나가 사용자 본인 명의 API를 호출 → 발화/액션 주체 혼동 위험 (예: Notion에 jini가 항승 명의로 페이지 생성)
사용자가 본 세션에서 OAuth 한 번 하면 페르소나도 즉시 사용 보안 사고 시 blast radius

옵션 B: 페르소나별 전용 도구 셋 (현재 구조의 의도된 형태로 추정)

페르소나마다 필요한 MCP만 명시적으로 등록. OAuth도 페르소나 명의의 별도 인증 (예: Notion에 jini integration을 별도 생성).

장점 단점
발화·액션 주체 명확. Slack 봇 토큰 분리와 같은 철학 도구 추가마다 N개 페르소나에 각각 셋업
권한 최소화 (raphael은 read-only만 등) 운영 비용·복잡도 증가

옵션 C: 공유 도구 + 명의 분리 (하이브리드)

읽기 도구는 사용자 토큰 공유 (저비용), 쓰기 도구는 페르소나 명의 강제 (안전).

장점 단점
검색·조회는 빠르게 추가 가능 도구별로 읽기/쓰기 라우팅 룰을 따로 관리해야 함
사고 위험은 쓰기 경로에 집중 MCP 서버가 read/write를 도구 레벨에서 분리해줘야 깔끔

Risks (운영 관점)

위험 영향 대응 후보
페르소나 환경 부팅 시 어떤 토큰이 들어가는지 문서화되지 않음 신규 MCP 추가 시 디버깅 비용 증가 tools/ 아래에 페르소나별 env 점검 스크립트 추가
페르소나 LLM 프로세스가 매번 새로 뜸 → MCP OAuth handshake 매번 필요한 경우 지연·실패 응답 latency, 간헐적 도구 누락 OAuth 토큰은 파일 영속화 + 환경변수 주입 표준화
페르소나가 사용자 토큰으로 외부 API 호출 → 발화 주체 혼동 신뢰·감사 추적 어려움 옵션 B/C 채택 시 자동으로 해소

결정 위임 — 항승씨에게 묻고 싶은 것

전략 옵션을 좁히려면 사용자 가치 판단이 필요해요. 본 보고서에서 세 가지를 묻고 싶어요.

  1. 옵션 A vs B vs C 중 어느 철학에 가까운가? 직관적으로 끌리는 쪽이 있는지, 아니면 케이스별로 갈 생각인지.
  2. 페르소나가 "사용자 본인 명의로" 외부 시스템 액션을 하는 게 어디까지 허용되는가? (Notion 페이지 생성, Linear 이슈 close 등.) 지금까지의 룰은 Slack post는 절대 금지인데 다른 시스템은 명시되지 않음.
  3. Notion 케이스는 이번에 풀고 끝낼 것인가, 일반 패턴으로 풀 것인가? 지금 페르소나 측 Notion만 따로 고치면 Quick fix이고, OAuth MCP 일반 패턴으로 풀면 같은 사고 재발 방지에 더 가치.

모름·확인 필요

링크·참조