Articles
Guide 12분

채널 시스템으로 페르소나 분리하기 — 같은 프로젝트, 다른 역할

하나의 프로젝트에서 사고 파트너와 PM을 동시에 운영하는 방법. tmux 세션명으로 페르소나를 자동 적용하고, 역할 경계를 문서 레벨에서 강제하는 채널 설계.

중급 Claude Code 채널 tmux 페르소나 멀티에이전트 init prompt
이 글을 읽으면
  • 배경: AI에게 여러 역할을 시키면, 역할이 섞여서 어느 쪽으로도 제대로 동작하지 않게 되더라고요
  • 핵심 인사이트: 같은 프로젝트 안에서 세션명 하나로 완전히 다른 페르소나를 자동 적용할 수 있고, 역할 경계는 “알아서 하겠지”가 아니라 문서로 강제해야 지켜져요
  • 이런 분에게: AI에게 여러 역할을 맡기고 싶은데 페르소나가 자꾸 섞여서 고민인 분, 같은 코드베이스에서 서로 다른 AI 에이전트를 돌리고 싶은 분

하나의 봇에 두 개의 역할을 넣으면

혹시 AI에게 “너는 사고 파트너야, 근데 태스크 관리도 해줘”라고 해본 적 있으신가요?

저도 처음에는 하나의 세션에서 모든 걸 해결하려 했어요. 아침에는 어제 뭘 했는지 돌아보면서 대화하고, 오후에는 이번 주 태스크를 정리하고, 저녁에는 하루를 회고하고. 같은 AI한테요.

문제는 맥락이 오염된다는 거였어요.

회고를 하다가 “이번 주 태스크 상태가 어때?”라고 물으면, AI가 갑자기 PM 모드로 전환되면서 아까까지의 성찰적인 톤이 사라졌어요. 반대로 태스크를 정리하다가 “요즘 좀 지치는 것 같아”라고 말하면, 감정에 대한 응답 대신 “에너지 레벨을 고려한 태스크 재배치를 제안합니다”가 돌아왔고요.

사고 파트너와 PM — 이 둘은 근본적으로 다른 인지 모드를 요구해요. 사고 파트너는 발산이 필요하고, PM은 수렴이 필요하거든요. 한 세션에서 둘 다 하려니까 어느 쪽도 제대로 안 되더라고요.

Selforge에서 운영하는 두 에이전트의 역할이 명확히 달랐어요.

  • Sullivan (사고 파트너): 발산적 대화, 회고, 질문 던지기, 인사이트 발견. 톤은 부드럽고 열린 질문 위주.
  • 셀피 (PM): 태스크 관리, 시스템 운영, 진척 추적, 전략 제안. 톤은 간결하고 데이터 기반.

둘 다 같은 프로젝트 파일에 접근해야 했어요 — Obsidian Vault, 에이전트 정의, 스킬 파일, CLAUDE.md. 하지만 행동 패턴은 완전히 반대였고요. 하나의 세션에서 운영하면 페르소나가 맥락에 따라 흔들렸어요.

세 가지 분리 방법, 각각의 한계

이 문제를 해결하려고 세 가지 방법을 시도했어요.

첫 번째: 프로젝트를 아예 나누기. 사고 파트너용 프로젝트, PM용 프로젝트를 따로 만드는 거예요. 페르소나는 깔끔하게 분리되지만, 치명적인 문제가 하나 있었어요 — 파일 접근이 끊겨요. PM이 Vault에 있는 최근 노트를 참조해서 “요즘 이 주제에 시간을 많이 쓰고 계신데, 태스크에 반영할까요?”라고 제안하려면 같은 파일에 접근할 수 있어야 하거든요.

두 번째: 하나의 세션에서 역할 전환하기. “지금부터 PM 모드로 전환해”라고 말하는 방식이에요. 간단하긴 한데, 대화가 길어지면 이전 모드의 맥락이 섞여요. “아까 회고에서 말한 그 고민을 태스크로 만들어줘”같은 요청이 들어오면, PM과 사고 파트너의 경계가 완전히 무너졌어요.

세 번째: CLAUDE.md에 라우팅 규칙 넣기. “세션 이름을 확인해서 sullivan이면 사고 파트너로, selfy면 PM으로 동작해”라고 쓰는 거예요. 이론적으로는 완벽한데, 실전에서 AI가 이 라우팅을 간헐적으로 무시했어요. CLAUDE.md는 프로젝트 레벨 문서라서, 세션별 분기를 처리하기엔 우선순위가 낮았던 것 같아요.

세 가지 접근법의 구조적 한계를 정리하면 이래요.

접근법장점한계
프로젝트 분리페르소나 완전 분리파일 접근 단절. Vault, 에이전트 정의 등 공유 불가
단일 세션 역할 전환구현 비용 0맥락 오염. 대화가 길어지면 모드 경계 붕괴
CLAUDE.md 라우팅같은 코드베이스 유지간헐적 무시. CLAUDE.md는 프로젝트 레벨이라 세션 분기 불안정

CLAUDE.md 라우팅의 구체적인 문제가 뭐였냐면 — CLAUDE.md에 이런 규칙을 넣었어요:

## 채널 라우팅
- 세션 이름이 sullivan-channel이면 → sullivan.md를 읽고 사고 파트너로 동작
- 세션 이름이 pm-channel이면 → pm.md를 읽고 PM으로 동작

CLAUDE.md는 세션 시작 시 자동으로 로딩되니까 동작은 해요. 하지만 CLAUDE.md 안에는 프로젝트 규칙, 설계 원칙, 에이전트 카탈로그 등 다른 내용이 많잖아요. 라우팅 지시가 다른 컨텍스트에 묻혀서, AI가 때때로 라우팅을 건너뛰고 일반 세션처럼 동작하는 경우가 있었어요.

세션 초기화 프로토콜

결국 정리된 방법은, CLAUDE.md의 라우팅을 좀 더 강제력 있게 설계하는 거였어요.

핵심 아이디어는 간단해요. AI 세션이 시작될 때 “지금 내가 어느 채널에서 실행되고 있는지”를 먼저 확인하고, 그에 맞는 역할 문서를 읽게 하는 거예요.

비유하자면 이런 거예요. 출근했을 때 사무실 문에 “오늘 당신은 사고 파트너입니다”라는 표지판이 붙어 있고, 그 방에 들어가면 사고 파트너용 매뉴얼이 놓여 있는 거예요. 다른 방에는 “오늘 당신은 PM입니다” 표지판과 PM용 매뉴얼이 있고요.

이걸 가능하게 해주는 게 tmux라는 터미널 도구예요. tmux는 터미널 세션에 이름을 붙일 수 있거든요. sullivan-channel이라는 이름의 세션, pm-channel이라는 이름의 세션을 각각 만들어두면, AI가 “내가 어느 세션에서 돌아가고 있는지”를 이름만 보고 알 수 있어요.

CLAUDE.md에 넣은 세션 초기화 프로토콜이에요. 이 프로토콜은 세션의 첫 메시지에서 반드시 실행돼요.

**세션 초기화 프로토콜** (첫 메시지에서 반드시 실행):
1. echo "${TMUX:-not-in-tmux}"로 tmux 내부인지 확인한다.
   - not-in-tmux이면 → 일반 세션. 페르소나 없이 동작.
   - 값이 있으면 → tmux 내부. 2단계로 진행.
2. tmux display-message -p '#S'로 현재 세션 이름을 확인한다.
3. pm-channel → pm-channel 스킬을 로드하고, 셀피로 행동한다.
4. sullivan-channel → sullivan-channel 스킬을 로드하고, 설리반으로 행동한다.
5. 그 외 → 일반 세션. 페르소나 없이 동작.
6. 페르소나를 묻지 말고, 채널에 맞는 페르소나를 즉시 적용한다.

이전의 “세션 이름을 확인하고 분기하라”는 라우팅 규칙과 뭐가 다르냐면 — 명시적인 프로토콜 단계를 정해준 거예요. “확인하라”는 막연한 지시 대신, “이 명령어를 실행하라 → 이 결과가 나오면 이 파일을 읽어라”는 구체적인 절차를 줬어요. 특히 “페르소나를 묻지 말고 즉시 적용한다”라는 6번 규칙이 중요했어요 — 이게 없으면 AI가 “어떤 모드로 동작할까요?”라고 되물어보는 경우가 생기거든요.

채널-페르소나 아키텍처

Orchestrator-as-Persona 패턴

채널 분리를 설계하면서 한 가지 중요한 설계 결정이 있었어요. “이 세션에서 나와 대화하는 주체가 실제로 서브에이전트인가, 메인 세션 자체인가?”라는 질문이요.

두 가지 방법이 있었어요.

하나는 모든 메시지를 서브에이전트한테 전달하는 방식이에요. 메시지가 오면 → “셀피 에이전트”를 호출 → 응답을 받아서 전달. 깔끔해 보이지만, 매번 호출할 때마다 이전 대화 맥락이 사라져요. “아까 말한 그 태스크 말인데”라고 하면 “무슨 태스크요?”가 돌아오는 거예요.

다른 하나는 메인 세션 자체가 페르소나를 입는 방식이에요. 별도의 에이전트를 호출하는 게 아니라, 이 세션 자체가 “나는 셀피다”라고 동작하는 거예요. 대화 맥락이 유지되고, 응답도 빠르고, 무거운 작업이 필요할 때만 서브에이전트한테 넘기면 돼요.

결국 후자를 택했어요. 이 패턴을 “Orchestrator-as-Persona”라고 부르는데, Sullivan 채널에서 자연스럽게 이 방식으로 운영하고 있었거든요. Sullivan 채널의 메인 세션이 곧 Sullivan이었고, 메모 저장 같은 단순 작업만 서브에이전트가 담당했어요. 셀피 채널도 같은 구조로 만들었고요.

두 패턴을 구체적으로 비교하면 이래요.

A. Delegate-every-message

메시지 도착 → selforge-pm 에이전트를 서브에이전트로 호출 → 응답 전달
  • 장점: 에이전트 정의 파일만 있으면 동작
  • 단점: 매 호출마다 컨텍스트 리셋, 대화 흐름 단절, 지연, 비용 증가

B. Orchestrator-as-Persona

메인 세션 자체가 "나는 셀피다"로 동작 → 무거운 작업만 서브에이전트 위임
  • 장점: 대화 컨텍스트 유지, 빠른 응답, 자연스러운 페르소나
  • 단점: 별도의 하네스 문서가 필요

B 패턴을 선택한 이유가 하나 더 있었어요. 에이전트 정의(.claude/agents/selforge-pm.md)와 채널 하네스(.claude/skills/pm-channel.md)는 서로 다른 레이어의 문서라는 걸 깨달았거든요.

  • 에이전트 정의 = “이 에이전트는 무엇인가” (what) — 역할, 질문 전략, 정보 수집 프로토콜. 서브에이전트로 호출될 때의 행동 명세.
  • 채널 하네스 = “이 세션에서 어떻게 동작하는가” (how) — 페르소나, 톤, 메시지 라우팅, 크론 스케줄, 위임 규칙. 메인 세션이 페르소나를 입을 때의 운영 명세.

Sullivan 채널이 잘 돌아간 이유는 이 두 층이 모두 갖춰져 있었기 때문이었어요. PM 채널이 처음에 빈 껍데기였던 이유는 에이전트 정의만 있고 하네스가 없었기 때문이었고요. 에이전트를 만들었는데 채널에서 동작하지 않는다면, 하네스가 빠져있을 가능성이 높아요.

역할 경계를 문서로 강제하기

페르소나를 분리했으면, 경계도 분명하게 정해야 해요. 이게 생각보다 중요하더라고요.

셀피와 Sullivan의 영역을 이렇게 나눴어요.

  • 셀피 = 시스템, 실행, 전략, 개선
  • Sullivan = 사고, 감정, 성찰, 질문, 확장

“알아서 구분하겠지”라고 생각할 수도 있는데, AI는 맥락에 따라 역할이 쉽게 흐려져요. 셀피한테 “요즘 좀 지치는 것 같아”라고 보내면, PM이니까 에너지 레벨에 따른 태스크 재배치를 제안할 수도 있잖아요. 나쁜 응답은 아니지만, 그 순간 사용자가 원한 건 사고 파트너의 공감이었을 수 있어요.

그래서 하네스에 경계 침범 시 리다이렉트 문구를 넣었어요. 셀피한테 감정적인 메시지가 오면 — “이건 설리반이 더 잘 도와줄 수 있을 것 같아요.” 이 한 줄이 역할 경계를 유지하는 핵심이에요. AI가 “할 수 있으니까” 답하는 게 아니라, “이건 내 영역이 아니다”를 명시적으로 선언하는 거예요.

반대로 Sullivan한테 시스템 버그 얘기가 들어오면, Sullivan이 직접 코드를 수정하는 대신 셀피한테 이슈를 전달하도록 했어요. “이건 셀피가 처리할 이슈예요”라고 안내하고, 실제로 tmux를 통해 PM 채널로 넘기는 거예요.

역할 경계 설계의 핵심은 “하지 않는 것”을 구체적으로 나열하는 거예요.

셀피의 하네스(pm-channel.md)에 이런 규칙을 넣었어요:

**역할 경계**:
- 셀피 = 시스템 / 실행 / 전략 / 개선
- 설리반 = 사고 / 감정 / 성찰 / 질문 / 확장
- 겹치는 영역은 흐민이 어디에 보내느냐로 결정.
  셀피가 설리반 영역에 관여하지 않는다.

그리고 엣지 케이스 테이블에서 구체적인 대응을 정의했어요:

| 상황 | 대응 |
|------|------|
| 사고/감정 관련 메시지 | "이건 설리반이 더 잘 도와줄 수 있을 것 같아요." |

Sullivan 쪽에도 대칭적인 규칙이 있어요:

- 시스템·코드 수정이 필요한 이슈는 셀피에게 직접 전달한다
  — 이슈 배경과 원인을 정리하여 tmux send-keys로 pm-channel에 전달

특히 중요했던 건 컨텍스트 분리예요. Sullivan의 회고에서 나온 개인 목표(구직, 포트폴리오 등)를 셀피의 브리핑에 끌고 오면 경계가 무너지거든요. 셀피 하네스에 이걸 명시적으로 금지했어요:

**컨텍스트 분리 필수**: 설리반 회고/주간 리플렉션에서 나온 흐민의
개인 목표를 셀피 브리핑에 끌고 오지 않는다. 셀피 브리핑에는
셀피와 흐민이 함께 세운 Selforge 시스템 태스크만 포함한다.

채널 사이에 다리 놓기

채널을 분리했다고 완전히 단절시킨 건 아니에요. 같은 프로젝트니까, 채널끼리 소통할 수 있어야 해요.

tmux에는 다른 세션에 텍스트를 보내는 기능이 있어요. Sullivan이 시스템 버그를 발견하면, 셀피한테 “이런 이슈가 있어, 확인해줘”라고 직접 메시지를 보내는 거예요.

반대 방향도 가능해요. 셀피가 이번 주 태스크를 정리하면서 Vault의 최근 노트를 참조할 수 있고, Sullivan이 Vault에 저장한 인사이트를 셀피가 주간 리뷰에서 활용할 수도 있어요. 두 채널이 같은 프로젝트 파일을 공유하니까요.

요약하면 이런 구조예요 — 각자의 역할은 분명히 분리되어 있지만, 필요하면 서로에게 일을 넘길 수 있고, 같은 자료를 참조할 수 있어요.

채널 간 통신은 tmux send-keys로 구현했어요. 여기서 중요한 삽질 한 가지가 있었는데 — 텍스트와 Enter를 반드시 별도의 호출로 분리해야 해요.

# 반드시 별도 Bash 호출 2회로 분리
tmux send-keys -t pm-channel "메시지"    # 호출 1: 텍스트 전송
tmux send-keys -t pm-channel Enter       # 호출 2: Enter 전송 (반드시 별도)

하나의 명령에 텍스트와 Enter를 같이 넣으면, 긴 텍스트에서 Enter가 누락되는 문제가 있었어요. &&, ;, 줄바꿈 전부 안 돼요. 이건 실제로 겪어봐야 아는 종류의 문제예요.

전송 전에는 상대 채널이 준비됐는지 확인하는 것도 중요해요:

# 프롬프트(❯) 대기 중인지 확인
tmux capture-pane -t pm-channel -p | tail -5

상대 채널이 처리 중이면 완료될 때까지 기다렸다가 전송해요.

실제 사용 예시로는 이런 게 있어요:

  • Sullivan이 시스템 버그를 발견 → 이슈 배경과 원인을 정리해서 pm-channel로 전달
  • 셀피가 핫픽스 완료 → 결과를 sullivan-channel로 브리핑

이 설계에서 배운 것

이 채널 시스템을 만들면서 가장 크게 느낀 건 세 가지예요.

역할 경계는 문서 레벨에서 강제해야 해요. “알아서 구분하겠지”는 작동하지 않아요. AI는 맥락에 따라 역할이 흐려지기 쉬우니까, 하네스에서 “이것은 하지 않는다”를 구체적으로 나열하는 게 효과적이에요. 리다이렉트 문구까지 정해놓아야 실제로 분리돼요.

에이전트 정의와 채널 하네스는 별개의 레이어예요. 에이전트 정의가 “이것은 무엇인가”라면, 하네스는 “이 세션에서 어떻게 동작하는가”예요. 서브에이전트로만 쓸 거라면 정의만 있으면 되지만, 독립 채널로 운영하려면 하네스가 반드시 필요해요. “에이전트를 만들었는데 왜 채널에서 안 되지?”라는 혼란이 생긴다면, 이 구분을 확인해보세요.

두 번째를 만들 때 첫 번째의 구조가 보여요. Sullivan 채널은 자연스럽게 진화하면서 만들어졌기 때문에, “채널이 뭘로 구성되는지”가 암묵적이었어요. 셀피 채널을 만들면서 “채널 = 세션 이름 + 하네스 문서 + 역할 경계 + 크론 스케줄”이라는 공식이 명시적으로 드러났어요. 시스템을 이해하는 가장 좋은 방법은 두 번째 인스턴스를 만들어보는 것이더라고요.

마무리

채널 인프라 구축은 1편: 항상 대화할 수 있는 환경 만들기에서 다뤘어요. 이번 글에서는 그 인프라 위에 페르소나를 얹는 방법을 살펴봤어요.

사실 이 설계의 핵심은 기술적인 부분이 아니에요. “AI에게 역할을 줄 때, 어디까지가 이 역할이고 어디부터가 저 역할인지”를 사람이 먼저 명확하게 정의해야 한다는 거예요. 그걸 문서에 옮기는 건 그 다음 이야기고요.

다음 편에서는 페르소나를 분리했으니, 사고 파트너가 어떻게 대화해야 하는지 — 대화 규칙 설계에 대해 이야기할게요.