같은 LLM 베이스에서 갈라져 나온 두 카카오 봇을 한 단톡방에서 토론시키고, 그 결과를 엑셀 보고서로 자동 정리해 다시 카톡에 쏘기까지.
2026-06-06, 맥미니 로컬 환경에서 하루 만에 구현·검증한 기록.
📝 한줄 요약
카카오톡 단톡방에 @토론 [주제]라고 치면 → 회계사 봇과 세무사 봇이 3턴 주고받으며 토론하고 → 토론이 끝나면 [쟁점 | 회계 관점 | 세무 관점 | 통합 결론] 비교표 엑셀 보고서가 자동으로 단톡방에 업로드됩니다.
겉보기엔 "두 AI가 카톡방에서 토론하고 보고서까지 써준다"지만, 실제 구조는 그렇게 단순하지 않습니다. 카카오의 기술적 제약 때문에 우회 설계가 필요했기 때문입니다.
실제 업무에 활용하고 있는 에이전트이기 때문에 부득이하게 정보지운점 감안해주시면 감사하겠습니다.^^
바쁘시면 이것만:
카카오는 Bot-to-Bot이 안 됨 → 사회자 릴레이 + 카카오는 출력구만
무한 루프는 author_id 가드 + 사회자 호출 + router 분리로 구조 차단
회계·세무 기존 answer.py 재사용 → 토론 끝나면 엑셀 2시트 자동 업로드
🎯 이런 분들께 도움돼요
카카오톡(LOCO)으로 멀티봇·자동화를 붙이려는 분
"봇끼리 대화"가 무한 루프·계정 정지로 이어질 수 있다는 걸 알고 우회 설계가 필요한 분
이미 돌아가는 1:1 봇 두뇌를 단톡방 토론·보고서 파이프라인에 재사용하고 싶은 분
실제 접근성 때문에 업무에서 슬랙이나 디스코드 말고 카카오톡을 써야할것 같은 분
😫 문제 상황 (Before)
LOCO 프로토콜의 벽
카카오톡은 일반 오픈 API가 아니라 LOCO라는 자체 바이너리 프로토콜로 동작합니다. 텔레그램·디스코드처럼 "봇이 다른 봇의 메시지를 이벤트로 받는" 구조가 공식적으로 열려 있지 않기 때문입니다.
그래서 처음 떠올린 순진한 그림 — "단톡방에 봇 둘을 넣고 서로 메시지에 자동응답하게 한다" — 은 절대 가면 안 되는 길이었습니다:
회계사 답변 → 세무사가 그걸 메시지로 받음 → 세무사 답변
→ 회계사가 그걸 받음 → 회계사 답변 → 세무사가 받음 → ... (무한 루프)무한 핑퐁 + 카카오의 자동 응답 패턴 탐지 = 두 계정 모두 정지 위험.
해결 아이디어 — 카카오는 "출력 채널"로만 쓴다
핵심 발상의 전환:
대화 로직(두뇌)은 카카오 밖에서 돌리고, 카카오는 완성된 메시지를 발사하는 출력구로만 쓴다.
즉 봇끼리 직접 대화하게 두지 않고, 별도의 '사회자(moderator)' 프로세스가 중간에서:
회계사 두뇌를 호출해 답을 받고 → 회계사 계정으로 카톡에 발사
그 답을 세무사 두뇌에 입력으로 넘겨 답을 받고 → 세무사 계정으로 카톡에 발사
다시 회계사에게… (정해진 턴 수만큼)
두 봇은 서로의 메시지를 보지 않습니다. 다음 발화자를 항상 사회자가 호출하므로, 봇끼리 서로를 트리거하는 일이 구조적으로 불가능합니다. → 무한 루프 원천 차단.
즉 단톡방에 토론주제를 던지면
회계사가 해당 내용을 확인합니다.
회계사가 메인오케스트레이션(헤르메스)한테 토론주제를 던집니다.
확인한 헤르메스는 하위 애들인 회계사(오픈클로), 세무사(오픈클로) 한테 오픈클로 내에서 토론을 시킵니다. 즉 핑퐁핑퐁
내용들을 순차적으로 카카오 loco로 각 에이전트들이 발신해서 띄웁니다.
🛠️ 사용한 도구
항목
내용
환경
맥미니 로컬, OpenClaw 멀티봇, Hermes(오케스트레이션)
카카오 연동
agent-messenger kakaotalk 모듈 (2.19.1+)
회계 두뇌
accountant_yuseong_answer.py (K-IFRS·KASB 옵시디언 LLM Wiki)
세무 두뇌
tax_yuseong_answer.py (세법조문·판례 옵시디언 LLM Wiki)
사회자
debate_moderator.py (launchd ai.openclaw.debate-moderator, poll 15s)
보고서
debate_report.py + openpyxl, LLM anthropic/claude-sonnet-4-6
상시 서비스
회계 1:1 router + 세무 1:1 router + debate-moderator (3개 동시 가동)
🏗️ 아키텍처 (전체 구조)
한눈에 보기 — 3층 구조
층
역할
핵심 파일·프로세스
① 카카오 (출력·입력)
단톡방 UI, 메시지 송수신
chat_id 470828588411727, 회계·세무 계정 각각 send
② 사회자 (오케스트레이션)
트리거 감지, 턴 릴레이, 보고서 호출
debate_moderator.py
③ 두뇌 + 보고서 (로직)
RAG 답변 생성, 엑셀 정리
accountant_* / tax_* / debate_report.py
설계 원칙: 봇끼리는 직접 대화하지 않음. 카카오는 완성 메시지 발사만. 토론 순서는 사회자만 제어.
데이터 흐름 (Mermaid)
flowchart TB
subgraph KAKAO["① 카카오톡 단톡방 (토론방)"]
U["👤 이용자\n@토론 [주제]"]
OUT1["💬 회계사 발화"]
OUT2["💬 세무사 발화"]
OUT3["💬 회계사 발화"]
XLS["📊 엑셀 보고서"]
end
subgraph MOD["② 사회자 — debate_moderator.py"]
POLL["polling 15s\n(단톡방만)"]
GUARD["author_id 가드\n(봇 메시지 무시)"]
LOOP["턴 루프 TURNS=3\n회계 ↔ 세무 릴레이"]
CALL["debate_report.py 호출"]
end
subgraph BRAIN["③ 두뇌 (기존 answer.py 재사용)"]
ACC["accountant_answer.py\nK-IFRS·KASB 옵시디언 LLM WIKI"]
TAX["tax_answer.py\n세법조문·판례 옵시디언 LLM WIKI"]
end
subgraph RPT["③ 보고서 — debate_report.py"]
LLM["LLM 비교정리\nJSON 4필드"]
XL["openpyxl 2시트"]
end
subgraph ROUTER["※ 1:1 router (단톡방 미관여)"]
R1["accountant-router"]
R2["tax-router"]
end
U -->|새 메시지| POLL
POLL --> GUARD --> LOOP
LOOP -->|호출| ACC
LOOP -->|호출| TAX
ACC -->|send 회계사 계정| OUT1
TAX -->|send 세무사 계정| OUT2
LOOP -->|3턴 후| CALL
CALL --> LLM --> XL -->|upload| XLS
LOOP --> OUT3
R1 -.->|allowlist 1:1만| KAKAO
R2 -.->|allowlist 1:1만| KAKAO상세 ASCII 다이어그램
┌──────────────────── 카카오톡 "토론방" (chat_id) ────────────────────┐
│ 이용자: "@토론 [주제]" → 회계사 발화 → 세무사 발화 → 회계사 발화 → 📊 엑셀 │
└────▲──────────────▲─────────────────▲─────────────────▲───────────▲──┘
│ polling │ send(회계) │ send(세무) │ send(회계) │ upload
│ │ │ │ │
┌────┴──────────────┴──────────────────┴──────────────────┴────────────┴────────┐
│ debate_moderator.py (사회자) │
│ ① 단톡방만 polling → 이용자 @토론 감지 (봇 author_id 트리거 제외) │
│ ② 턴 루프: 회계 두뇌 ↔ 세무 두뇌 번갈아 호출, 상대 답을 다음 입력으로 전달 │
│ ③ 종료 → debate_report.py → 엑셀 생성 → 카카오 업로드 │
└────┬─────────────────────┬────────────────────────┬───────────────────────────┘
│ │ │
┌────▼─────────────┐ ┌─────▼──────────────┐ ┌──────▼─────────────────┐
│ accountant_ │ │ tax_ │ │ debate_report.py │
│ answer │ │ answer │ │ LLM 비교정리 → openpyxl │
│ (회계 두뇌) │ │ (세무 두뇌) │ │ → 2시트 엑셀 │
│ K-IFRS·KASB │ │ 세법조문·판례 │ │ │
└──────────────────┘ └────────────────────┘ └────────────────────────┘
※ 회계사·세무사 1:1 router → 이 단톡방 chat_id를 절대 polling하지 않음 (§ 무한 루프 방지)컴포넌트 역할표
컴포넌트
입력
출력
카카오와의 관계
이용자
—
@토론 [주제]
유일한 토론 트리거
debate_moderator
단톡방 새 메시지
두뇌 호출 + send + report 호출
유일한 단톡방 polling 주체
accountant_answer
질문 문자열
draft_answer JSON
회계사 계정으로 발사만
tax_answer
--q 질문
answer JSON
세무사 계정으로 발사만 (격리 config)
debate_report
발화 전문
xlsx 2시트
파일 upload
1:1 router ×2
allowlist 1:1 chat
1:1 자동응답
단톡방 미참여
핵심: 기존 두뇌(answer.py)를 재사용합니다. 새 토론 엔진을 만들지 않았습니다. 회계사·세무사의 지식이 업데이트되면 토론 품질도 자동으로 따라 올라갑니다.
▲ 단톡방 "토론방"에서 @토론으로 시작된 실제 토론. 회계사 → 세무사로 발화가 이어집니다.
🔧 작업 과정
1. 사전 검증 — 진짜 되는지부터 실측
추측으로 설계하지 않고, 불확실한 지점을 먼저 실제로 찔러봤습니다.
카카오가 단톡방을 다룰 수 있나?
사용 중인 CLI 도구(agent-messenger의 kakaotalk 모듈)에 단톡방 지원이 있는지 확인:
chat list --resolve-titles --search→ 단톡방(MultiChat) 명시적 지원 확인message list <chat-id>/message send <chat-id>→ 1:1이든 단톡방이든 chat_id 하나로 동일하게 동작member list <chat-id>→ 방 멤버 조회 가능
두 봇이 같은 방에 공존하나? (결정적 검증)
카톡 앱에서 단톡방 "회계세무토론방"을 직접 만들고 봇 둘을 초대한 뒤, 양쪽 계정으로 같은 방을 조회했더니:
chat_id: 사용자아이디 (type: MultiChat)
active_members: 3 → 사용자(00000000) + 회계사(0000000) + 세무사(00000000)*사정상 뒤 아이디는 가립니다.
두 봇이 같은 chat_id를 동시에 인식 ✓
member list로 세 user_id 전부 확인 ✓흥미로운 디테일: 세무사 눈엔 "회계사", 회계사 눈엔 "세무사"로 서로를 멤버로 정확히 인식
⚠️ 함정 하나: 카카오 LOCO는 메시지가 한 번도 안 오간 방은 chat list 스냅샷에 안 잡힙니다. 방만 만들고 가만히 두면 빈 배열이 나옵니다. 아무 메시지나 한 줄 쳐야 방이 잡힙니다.
2. 무한 루프를 막은 3중 안전장치
이 프로젝트의 진짜 핵심은 "토론을 시키는 것"이 아니라 "봇 둘을 안 꼬이게 만드는 것"이었습니다.
#
장치
내용
5.1
트리거는 사람만
사회자는 author_id가 이용자(00000000)이고 @토론으로 시작하는 것만 인식. 봇 author_id 메시지 무시 → 봇 발화가 또 토론을 부르는 일 불가능
5.2
발화자는 사회자 호출
봇 은 "상대 메시지를 보고 반응"하지 않음. for i in range(TURNS) 루프에서 사회자가 다음 발화자 직접 호출
5.3
1:1 router 분리
회계·세무 router는 allowlistChatIds만 polling. 단톡방 chat_id를 절대 넣지 않음 → 1:1처럼 자동응답하는 사고 차단
검증 (최악 시나리오 테스트)
state를 토론 직전으로 되돌려 봇 메시지 5개가 전부 "새 메시지"로 보이게 만든 뒤 사회자를 폴링시켰습니다 → 토론 재시작 0건. author_id 가드가 완벽히 작동함을 실측.
3. 까다로웠던 디테일 — 두 두뇌의 인터페이스가 다르다
회계사와 세무사 두뇌(answer.py)는 따로 만들어져서 호출 규약이 서로 달랐습니다. 사회자가 이걸 구분해서 호출하지 않으면 답이 빈 문자열로 나오거나 엉뚱한 계정으로 발사됩니다.
항목