AI에게 회의를 시켰더니 답답하다는 피드백이 돌아왔습니다
내부에서 쓰는 PM 겸 지식 관리 도구에 'AI 회의실' 기능을 붙였습니다. 사용자가 안건 하나를 던지면 마케팅, 운영, 리서치, 콘텐츠, 그리고 비서실장 역할까지 다섯 개의 역할이 각자 답하고 서로 의견을 주고받는 기능입니다.
발단은 단순했습니다. 혼자 결정하기 애매한 안건을 던졌을 때, 여러 관점을 한 번에 받아보고 싶었습니다. 마케팅 관점과 운영 관점은 종종 충돌하는데, 그 충돌을 사람이 매번 머릿속에서 시뮬레이션하는 것이 피곤했습니다. 그렇다면 역할별 LLM을 여러 개 두고 붙여보자는 생각이었습니다.
구현을 시작하고 나서야 진짜 문제가 뭔지 알았습니다. 에이전트를 다섯 개 만드는 것은 어렵지 않습니다. systemPrompt를 다섯 벌 준비하고 각각 호출하면 됩니다. 어려운 것은 그 다섯 개가 하나의 안건을 두고 '어떻게 진행하느냐'였습니다.
에이전트가 몇 개냐가 아니라 토론을 어떻게 굴리느냐가 문제였다
처음 떠올린 방식은 자유 토론이었습니다. 다섯 역할을 한 컨텍스트에 넣고 알아서 대화하게 두는 것입니다. 사람의 회의처럼 자연스럽게 오갈 것이라 기대했습니다.
결과는 예상과 달랐습니다. 누가 언제 끼어들지 순서가 정해지지 않으니 대화가 엉켰습니다. 한 역할이 길게 말하면 다른 역할이 묻혔고, 종결 시점도 불분명했습니다. 언제 결론이 나는지 코드가 알 방법이 없어서 무한정 돌거나 어정쩡하게 끊겼습니다. 자유 토론은 자연스러움은 있었지만 일관된 산출물을 뽑기 어려웠습니다.
그래서 반대편 옵션을 택했습니다. 라운드제입니다. 인간 회의의 골격을 그대로 코드로 옮겼습니다. 각자 답한다, 서로의 답을 보고 반박한다, 마지막에 한 명이 종합한다. 이 세 단계를 모드로 고정했습니다.
opinion, rebuttal, summary 세 모드로 회의의 골격을 강제했다
회의는 라운드 단위로 진행되고, 각 라운드는 세 가지 모드 중 하나로 동작합니다.
모드 | 무엇을 하는가 | 호출 방식 |
|---|---|---|
opinion | 참석 역할들이 동시에 안건에 각자 답한다 (1라운드) | 병렬 |
rebuttal | 이전 라운드의 모든 답을 컨텍스트로 받아 각 역할이 다시 답한다 (2라운드 이상) | 병렬 |
summary | 비서실장 한 명만 라운드들을 종합한다 | 단일 |
1라운드 opinion은 참석 역할들이 서로를 보지 않고 안건에만 답하는 단계입니다. 이 단계는 역할끼리 의존이 없으므로 병렬로 호출했습니다. CompletableFuture로 다섯 개를 동시에 던지고 모두 모이면 다음으로 넘어갑니다.
2라운드부터의 rebuttal은 직전 라운드의 모든 답을 컨텍스트에 넣어 각 역할에게 다시 묻는 단계입니다. 마케팅이 운영의 답을 보고 반박하거나 보강합니다. 이 단계도 각 역할 호출은 서로 독립이라 병렬입니다.
핵심은 summary입니다. 종합은 비서실장 역할만 할 수 있게 백엔드가 강제합니다. mode가 summary일 때 비서실장이 아닌 역할이 종합을 시도하면 막습니다. 이유는 간단합니다. 모두가 정리에 나서면 정리가 오히려 흐려집니다. 정리하는 목소리가 다섯 개면 그건 정리가 아니라 또 하나의 라운드입니다. 그래서 종합 역할은 코드 레벨에서 하나로 못 박았습니다.

회의를 저장하지 않고 결과만 남기기로 했다
설계하면서 의도적으로 단순화한 지점이 몇 개 있습니다. 첫째, 회의에서는 도구를 실행하지 않습니다. 텍스트로만 의견을 주고받습니다. 회의 중에 검색이나 파일 조작까지 붙이면 변수가 너무 많아져서, 이번 목적인 '관점 충돌 보기'에 집중하기 위해 잘라냈습니다.
둘째, 회의 자체를 영속화하지 않았습니다. 라운드 진행 과정을 그래프나 DB에 저장하지 않고, 사용자가 화면에서 결과를 보는 것으로 끝냅니다. 회의의 중간 과정은 대부분 노이즈이고, 그걸 다 저장하면 저장소와 비용만 늘어납니다. 남길 가치가 있는 것은 종합 결과이지 다섯 역할이 주고받은 모든 문장이 아니라고 봤습니다.
셋째, 역할 정의를 코드 밖으로 뺐습니다. 각 역할의 systemPrompt를 yml로 외부화하고 부팅 시 @PostConstruct에서 한 번 읽습니다. 새 역할을 추가할 때 코드를 고치지 않고 yml에 항목 하나를 더하면 됩니다. '법무' 역할을 붙이고 싶으면 프롬프트만 쓰면 되는 구조입니다.
같은 메커니즘을 일반 AI 채팅에도 그대로 썼다
회의실을 만들고 보니 이 구조가 회의실 전용이 아니었습니다. 일반 AI 채팅에도 agentId를 주입할 수 있게 했습니다. 특정 역할을 지정하면 그 역할의 systemPrompt를 컨텍스트 앞에 prepend하고, 비어 있으면 기본 비서로 동작합니다. 하위 호환을 지키면서 회의실과 채팅이 같은 패턴을 공유하게 됐습니다.
UI에서는 LLM 특유의 마찰을 줄이는 데 신경 썼습니다. 채팅 입력창에서 슬래시를 치면 역할 자동완성이 뜨고, 방향키와 Enter, Tab, Esc로 고를 수 있습니다. 메시지마다 누가 답했는지 뱃지로 표시합니다. 회의실에서는 안건을 입력하고 참석 역할을 체크박스로 고른 뒤(비서실장을 뺀 전원이 기본값), 1라운드가 자동으로 돌고 나면 '토론 더'와 '종합으로 마무리' 중에 선택합니다.
여기서 앞서 만들었던 다른 멀티에이전트 구조가 떠올랐습니다. 이전에 매니저 LLM이 작업을 여러 검색 에이전트에게 나눠주는 방식을 다룬 적이 있는데, 그건 '분배형'이었습니다. 이번 회의실은 역할들이 같은 안건을 두고 다투는 '토론형'입니다. 멀티에이전트라고 다 같은 게 아니라, 일을 쪼개 나눠주느냐 하나의 안건을 함께 논의하느냐로 갈립니다.

라운드제는 답답하다는 피드백이 돌아왔다
기능을 열고 나서 실제로 써본 뒤 돌아온 피드백은 예상 밖이었습니다. '라운드제는 각자 한 마디씩 하고 끝나서 답답하다, 사람 회의처럼 서로 주고받는 turn-taking을 원한다'는 것이었습니다.
이 지적은 정확합니다. 라운드제는 종결과 일관성 보장은 명확하지만, 자연스러움은 자유 토론이 더 강합니다. 제가 처음에 버렸던 자유 토론의 장점이 정확히 그 부분이었습니다. 종결을 얻는 대가로 자연스러움을 내준 셈입니다. 이건 다음 과제로 남겨뒀습니다. 어느 쪽이 옳다기보다, 도메인에 따라 무엇을 포기할지가 다릅니다.
다중 역할 LLM을 붙일 때 점검할 것들
에이전트 수를 먼저 정하지 말고 토론 진행 방식을 먼저 정한다. 종결과 일관성이 중요하면 라운드제, 자연스러움이 중요하면 자유 토론에 토큰 가드를 붙인다.
종합 역할은 하나로 강제한다. 모두가 정리에 나서면 정리가 흐려진다. 필요하면 종합을 복수화하는 변형도 있지만 기본은 하나다.
역할 정의는 코드 밖 설정으로 뺀다. 새 역할 추가가 배포 없이 되면 실험 비용이 크게 준다.
회의를 저장할지 결정한다. 대부분은 결과만 남기면 되지만, 회의 자체가 산출물인 법무나 이사회 성격이라면 전 과정 보관이 필수다.
참석 역할을 사용자가 고르게 한다. 역할이 다섯을 넘으면 1라운드 토큰 비용이 비례해 늘어난다. 참석 체크박스가 비용 가드 역할을 한다.
병렬 호출은 CompletableFuture로 충분하다. LLM 호출 비용이 크지 동기화 비용은 작다.
다중 역할 LLM은 '에이전트가 몇 개냐'가 아니라 '토론을 어떻게 진행하느냐'의 문제다.
종결을 얻으면 자연스러움을 내준다
이번 작업에서 얻은 것은 라운드제라는 정답이 아니라, 다중 역할 LLM의 진짜 변수가 어디 있는지였습니다. 에이전트를 몇 개 붙이느냐는 표면이고, 그것들을 어떤 순서로 말하게 하느냐가 산출물의 성격을 결정했습니다.
동시에 이 설계는 완결이 아닙니다. 종합을 한 역할에 몰아넣은 만큼 그 역할의 편향이 결과의 편향으로 이어질 수 있고, 라운드제의 답답함이라는 사용자 피드백도 아직 해결하지 못했습니다. turn-taking과 라운드제 사이 어딘가에 더 나은 지점이 있을 것이라 생각합니다.