그룹 채팅과 발화자 선택(Group Chat and Speaker Selection)
AutoGen GroupChat과 AG2 GroupChat은 N개의 에이전트(Agent)가 하나의 대화(Conversation)를 공유하는 구조입니다. 선택 함수(Selector Function)가 LLM, 라운드 로빈(Round-robin), 또는 사용자 정의(Custom) 방식 중 하나로 다음 발화자를 고릅니다. 이 구조는 떠오르는(emergent) 멀티 에이전트 대화의 원형(archetype)입니다. 에이전트는 정적 그래프(Static Graph) 안에서 자기 역할을 미리 알고 움직이는 것이 아니라, 공유 풀(Shared Pool)에 반응하며 행동합니다. AutoGen v0.2의 GroupChat 시맨틱(semantics)은 AG2 포크(fork)에 그대로 보존되었고, AutoGen v0.4는 이를 이벤트 기반 액터 모델(Event-Driven Actor Model)로 다시 작성했습니다. Microsoft는 2026년 2월에 AutoGen을 유지보수 모드(Maintenance Mode)로 전환하면서 Semantic Kernel과 합쳐 Microsoft Agent Framework(RC 2026년 2월)로 통합했습니다. GroupChat이라는 원시 요소(primitive)는 AG2와 Microsoft Agent Framework 양쪽에 모두 살아남았습니다. 한 번 익혀 두면 어디서든 그대로 쓸 수 있습니다.
유형: Learn + Build
언어: Python(stdlib)
선수 지식: Phase 16 · 04(멀티 에이전트 기본 모델, Primitive Model)
예상 시간: 약 60분
문제
정적 그래프(Static Graph) 기반의 LangGraph는 작업 흐름(Workflow)이 이미 잘 알려져 있을 때 훌륭하게 동작합니다. 그러나 실제 대화는 정적이지 않습니다. 어떤 순간에는 코더(coder)가 리뷰어(reviewer)에게 묻고, 어떤 순간에는 리서처(researcher)에게 묻고, 또 다른 순간에는 작가(writer)에게 묻습니다. 가능한 모든 핸드오프(handoff)를 코드에 그대로 박아 넣으려고 하면 엣지(edge)의 수가 폭발적으로 늘어납니다. 우리가 정말 원하는 것은 공유 풀(Shared Pool)에 반응하는 에이전트들이며, 그 위에서 어떤 함수가 "다음에는 누가 말할 차례인가"를 결정해 주는 구조입니다.
이것이 바로 AutoGen GroupChat이 해 주는 일입니다.
개념
형태
┌─── shared pool ────┐
│ m1 m2 m3 ... │
└─────────┬──────────┘
│ (everyone reads all)
┌───────┬─────────┼─────────┬───────┐
▼ ▼ ▼ ▼ ▼
Agent A Agent B Agent C Agent D Selector
│
▼
"next speaker = C"
모든 에이전트는 모든 메시지를 읽습니다. 매 턴(turn)마다 선택 함수(Selector Function)가 호출되어 다음에 말할 발화자(speaker)를 고릅니다.
세 가지 선택자(Selector) 유형
라운드 로빈(Round-robin). 정해진 순서를 그대로 도는 방식입니다. 결정적(deterministic)으로 동작하고, 에이전트 수 N에 대해 선형(linear)으로 확장되지만, 맥락(context)을 전혀 고려하지 않습니다. 주제가 법률 검토(legal review)여도 코더에게 차례가 돌아갈 수 있습니다.
LLM 기반 선택(LLM-selected). 최근 풀(recent pool)을 LLM에 넘기고, 다음 발화자로 가장 적절한 에이전트를 LLM이 직접 골라서 반환하게 하는 방식입니다. 맥락 인식(context-aware) 능력이 있지만 느립니다. 매 턴마다 LLM 호출이 한 번씩 추가되기 때문입니다. AutoGen의 기본값(default)입니다.
사용자 정의(Custom). 원하는 로직을 자유롭게 담은 Python 함수입니다. 흔한 형태는 LLM 기반 선택에 폴백 규칙(fallback rule)을 더하는 방식입니다. 예: "코더 다음에는 항상 검증자(verifier)에게 차례를 준다" 같은 규칙입니다.
ConversableAgent API
agent = ConversableAgent(
name="coder",
system_message="You write Python.",
llm_config={...},
)
chat = GroupChat(agents=[coder, reviewer, tester], messages=[])
manager = GroupChatManager(groupchat=chat, llm_config={...})
GroupChatManager가 선택자를 들고 있습니다. 한 에이전트가 자기 차례를 마치면 매니저가 선택자를 호출하고, 선택자가 다음 에이전트를 반환합니다. 종료 조건(termination condition)이 만족될 때까지 이 루프가 계속됩니다.
종료(Termination)
흔히 쓰는 패턴은 세 가지입니다.
- 최대 라운드(Max rounds). 전체 턴 수에 단단한 상한(hard cap)을 둡니다.
TERMINATE 토큰(Token). 에이전트가 약속된 문자열(sentinel)을 메시지에 담아 내보내면, 매니저가 그것을 보고 멈춥니다.
- 목표 도달 검사(Goal-reached check). 가벼운 검증자(verifier)가 매 턴마다 실행되며, 작업이 끝났다고 판단하면 대화를 멈춥니다.
AutoGen에서 AG2로의 분기와 Microsoft Agent Framework 통합
2025년 초, Microsoft는 AutoGen(v0.4)을 이벤트 기반 액터 모델(Event-Driven Actor Model) 중심으로 대대적으로 다시 쓰기 시작했습니다. 커뮤니티는 이미 자기 시스템에 통합해 둔 초기 도입자(early adopter)들의 코드를 보호하기 위해 v0.2의 GroupChat 시맨틱을 AG2라는 이름으로 포크(fork)하여 보존했습니다.
2026년 2월, Microsoft는 AutoGen을 유지보수 모드(Maintenance Mode)로 전환하고, 이벤트 기반 액터 모델은 Microsoft Agent Framework(RC 2026년 2월, Semantic Kernel과 통합 완료)로 흡수된다고 발표했습니다. GroupChat이라는 개념 자체는 두 갈래 모두에서 살아남았습니다. 구현 세부(implementation details)만 다릅니다. v0.2 호환 코드를 다루는 경우 AG2가 권장되는 업스트림(upstream)입니다.
GroupChat이 맞는 경우
- 떠오르는 대화(Emergent conversations). 가능한 다음 발화자(next-speaker)를 미리 모두 연결해 두고 싶지 않을 때.
- 역할이 섞이는 작업(Role-mixing tasks). 코더가 리서처에게 묻고, 리서처가 아키비스트(archivist)에게 묻고, 아키비스트가 다시 코더에게 묻는 식의 흐름입니다. 이 흐름은 유향 비순환 그래프(Directed Acyclic Graph; DAG)가 아닙니다.
- 탐색적 문제 해결(Exploratory problem-solving). "조립 라인(assembly line)"이 아니라 "브레인스토밍 회의(brainstorm meeting)"에 가까운 상황입니다.
실패하는 경우
- 엄격한 결정성이 필요한 작업. LLM 선택자는 일관되지 않을 수 있습니다. 같은 프롬프트(prompt)를 줘도 실행마다 다음 발화자가 달라질 수 있습니다.
- 아첨 연쇄(Sycophancy cascades). 에이전트가 가장 자신감 있게 말한 에이전트의 의견에 끌려가는 현상입니다. 이를 막으려면 반대 의견을 요구하는 프롬프트(counter-prompt)를 명시적으로 넣어야 합니다.
- 맥락 비대(Context bloat). 모든 에이전트가 모든 메시지를 읽기 때문에, 10턴만 지나도 맥락(context)이 매우 커집니다. 시야를 좁히기 위해 투영(projection)을 사용합니다(15강).
- 편중된 발화자(Hot speakers). 선택자가 특정 전문성을 선호한 나머지, 한 에이전트가 대화를 지배하는 현상입니다. 선택자 입력에 발화자 균형(speaker balance) 지표를 추가해 완화합니다.
그룹 채팅(Group Chat) vs 감독자(Supervisor)
두 패턴은 같은 원시 요소를 쓰지만 기본값이 다릅니다.
- 감독자(Supervisor): 한 에이전트가 계획(plan)을 세우고 나머지가 실행(execute)합니다. 선택자는 "다음에 무엇을 할지 계획자(planner)에게 묻기"입니다.
- 그룹 채팅(Group Chat): 모든 에이전트가 동등한 동료(peer)입니다. 선택자는 공유 풀 위에서 동작하는 하나의 함수입니다.
둘 다 04강의 네 가지 원시 요소를 사용합니다. 그룹 채팅은 기본값으로 LLM 기반 오케스트레이션(orchestration)과 풀 전체 공유 상태(full-pool shared state)를 사용합니다.
만들어보기(Build It)
code/main.py는 표준 라이브러리(stdlib)만으로 GroupChat을 처음부터 직접 구현합니다. 세 개의 에이전트(coder, reviewer, manager)와 라운드 로빈, LLM 기반 선택의 두 가지 변형(variant)을 함께 보여 주고, TERMINATE 토큰으로 종료하는 흐름을 보여 줍니다.
데모는 두 변형 각각에 대해 대화 기록(transcript)과 선택자의 결정 추적(decision trace)을 함께 출력합니다.
실행:
python3 code/main.py
사용해보기(Use It)
outputs/skill-groupchat-selector.md는 주어진 작업에 맞는 GroupChat 선택자를 구성합니다. 라운드 로빈, LLM 기반 선택, 사용자 정의 중 어떤 방식을 쓸지 정하고, 선택자에 어떤 입력(최근 메시지, 에이전트 전문성, 턴 수)을 넘길지 함께 정합니다.
산출물 만들기(Ship It)
체크리스트입니다.
- 최대 라운드 상한(Max rounds cap). 항상 둡니다. 일반적인 작업은 10~20 정도가 적당합니다.
- 발화자 균형 지표(Speaker-balance metric). 에이전트별 턴 수를 추적하고, 불균형이 임계값(threshold)을 넘으면 경고(alert)합니다.
- 종료 토큰(Termination token).
TERMINATE를 쓰거나, 별도의 검증자(verifier) 에이전트를 둡니다.
- 투영(Projection) 또는 범위 한정 메모리(Scoped memory). 메시지가 약 10개를 넘으면, 맥락 비대를 막기 위해 각 에이전트에게 자기 역할에 맞는 좁힌 시야(scoped view)만 주는 방안을 고려합니다.
- 선택자 로깅(Selector logging). LLM 기반 선택을 쓰는 경우, 선택자의 입력과 그 결과 선택(choice)을 모두 기록(log)합니다. 그렇지 않으면 사실상 디버깅(debugging)이 불가능합니다.
연습문제
- (쉬움)
code/main.py를 실행하세요. 라운드 로빈과 LLM 기반 선택 두 경우의 대화를 비교하세요. 각 방식에서 어떤 에이전트가 대화를 지배합니까?
- (중간) 선택자에 "에이전트별 최대 발화 횟수(max-speaks-per-agent)" 규칙을 추가하세요. 대화 기록이 어떻게 바뀝니까?
- (중간) 목표 도달 종료(goal-reached termination)를 구현하세요. 리뷰어가 "approved"를 반환하면 멈추도록 합니다. 라운드 상한(round cap)에 도달하기 전에 얼마나 자주 발동(trigger)됩니까?
- (중간) AutoGen stable docs의 GroupChat 문서(https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/design-patterns/group-chat.html)를 읽으세요.
GroupChatManager가 사용하는 기본 선택자(default selector)가 무엇인지 식별하세요.
- (어려움) AG2 저장소(https://github.com/ag2ai/ag2)를 읽고 v0.2 GroupChat과 v0.4 이벤트 기반(event-driven) 버전을 비교하세요. v0.4가 추가하는 구체적인 속성(처리량(throughput), 장애 내성(fault-tolerance), 조합 가능성(composability)) 중 어느 것입니까?
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| GroupChat | "한 채팅방 안의 에이전트들" | 공유 메시지 풀(shared message pool)과 선택 함수(selector function)의 결합입니다. AutoGen / AG2의 원시 요소(primitive)입니다. |
| 발화자 선택(Speaker selection) | "누가 다음에 말하는가" | 다음 에이전트를 고르는 함수입니다. 라운드 로빈, LLM 기반 선택, 사용자 정의 방식이 있습니다. |
| GroupChatManager | "회의의 진행자" | 선택자를 소유하고 턴 루프를 도는 AutoGen 컴포넌트(component)입니다. |
| ConversableAgent | "기반 에이전트" | AutoGen의 기반 클래스(base class)입니다. 메시지를 주고받을 수 있는 에이전트입니다. |
| 종료 토큰(Termination token) | "정지 단어" | 대화를 끝내는 약속된 문자열(sentinel string)입니다. 보통 TERMINATE를 씁니다. |
| 편중된 발화자(Hot speaker) | "한 에이전트가 지배함" | 선택자가 같은 에이전트를 계속 고르는 실패 모드(failure mode)입니다. |
| 맥락 비대(Context bloat) | "풀이 끝없이 커짐" | 각 에이전트가 모든 이전 메시지를 읽기 때문에, 턴이 늘수록 맥락이 함께 커지는 현상입니다. |
| 투영(Projection) | "좁힌 시야" | 맥락 비대를 막기 위해 역할별로 공유 풀을 좁혀 보여 주는 시야(view)입니다. |
더 읽을거리