본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 05. 20. 16:24

2체의 Gemini 에이전트에게 "카레를 만들어라"라고 말하면 역할 분담이 생겨날까

요약

동일한 클래스로 생성된 두 개의 Gemini 에이전트가 고정된 역할 없이 대화를 통해 스스로 역할을 분담할 수 있는지 실험한 TypeScript 기반의 멀티 에이전트 프로젝트 결과입니다. 실험 결과, 에이전트들은 공정을 먼저 나열한 뒤 대화를 통해 실행 계획과 역할을 자율적으로 결정할 수 있음을 확인했습니다.

핵심 포인트

  • 동형(Isomorphic) 에이전트 간에도 대화를 통한 자율적인 역할 분담이 가능하다.
  • 프롬프트에 포함된 역할 힌트는 에이전트의 행동 방향에 강력한 영향을 미친다.
  • 종료 조건을 에이전트가 결정하도록(continue/end) 설계하면 대화의 깊이를 조절하기 용이하다.
  • MessageBus를 통한 통신과 메모리 기반의 대화 이력 관리가 멀티 에이전트 구현의 핵심이다.

TypeScript로 아주 작은 멀티 에이전트 (Multi-agent) 실험을 만들었다.

하고 싶었던 것은 단순했다. 동일한 클래스에서 생성한 2체의 에이전트에게 저녁 식사로 카레를 만든다라는 목표만 부여하고, 대화하면서 역할 분담을 할 수 있는지 확인하는 것이었다.

이 실험에서는 다음 사항을 충족하도록 구현했다.

  • 2체 모두 동일한 Agent 클래스에서 생성한다
  • 에이전트끼리는 MessageBus를 경유하여 대화한다
  • 대화 이력은 각 에이전트의 메모리 변수에 보관한다
  • 사용자는 HTTP API를 통해 대화 이력을 확인할 수 있다
  • docs/에는 전체 로그가 아니라, 저장 가치가 있는 합의 사항만을 남긴다

처음에는 역할을 상당히 강하게 유도하고 있었다.

  • Agent A: 재료 조달 및 준비
  • Agent B: 조리 공정

이 상태에서는 당연하게도 대화가 그 방향으로 흐르기 쉽다. 실제로 agent_a가 쇼핑이나 준비를 맡고, agent_b가 조리 담당이 되는 흐름이 금방 나타났다.

이는 "역할 분담이 일어났다"기보다는 "역할 분담을 프롬프트 (Prompt)로 절반 정도 결정해 두었다"에 가깝다.

다음에 발견된 문제는 대화 종료 조건이었다.

초기 버전에서는 에이전트가 done: true를 반환하면 비교적 금방 멈춰버려, 대화가 너무 짧아지기 쉬웠다. 이 경우 역할 분담의 과정이라기보다, 한 번 제안하고 한 번 승인하고 끝나는 경우가 많았다.

그래서 종료 조건을 다음과 같이 변경했다.

  • 각 턴에서 에이전트가 status: "continue" | "end"를 반환한다
  • 한쪽이라도 continue라면 대화를 계속한다
  • 양쪽 모두 end가 되었을 때만 종료한다
  • maxTurns는 안전장치로만 남겨둔다

이 변경을 통해 "대화를 어디서 끝낼 것인가"를 오케스트레이터 (Orchestrator)가 아닌 에이전트 측에 맡겼다.

이번에 가장 보고 싶었던 것이 바로 이 부분이었다.

고정된 역할 (Fixed role)을 없애고, 양측에 동일한 조건만을 부여하도록 했다.

  • 2체는 동등한 지위
  • 처음부터 고정된 역할은 없음
  • 대화하면서 역할 분담과 실행 계획을 결정함

즉, Agent A는 쇼핑 담당과 같은 전제를 코드에서 제외했다.

대화는 다음과 같이 진행되었다.

  • Agent A가 "역할 분담과 실행 계획을 논의하고 싶다"라고 운을 뗀다
  • Agent B가 "먼저 공정을 나열해 보자"라고 답한다. 공정으로는 장보기, 밑준비, 조리, 플레이팅, 뒷정리가 꼽힌다.
  • Agent A가 역할 분담안을 제안한다.
  • Agent B가 이를 승인하고, 재료나 시간축 확인으로 넘어간다.
  • 양측이 end를 반환하며 종료한다.

이번 실행 (run)에서는 최종적으로 다음과 같이 분담되었다.

  • Agent A: 재료 선정 및 장보기, 조리
  • Agent B: 밑준비, 플레이팅, 식후 뒷정리

여기서 중요한 것은 "A가 재료 담당, B가 조리 담당"이라는 초기 고정이 아니라, 공정을 나열한 뒤 그 자리에서 분담안이 제안되었다는 점이라고 생각한다.

이번 범위 내에서는 적어도 다음과 같은 점을 말할 수 있을 것 같았다.

  • 2체의 동형 에이전트라도 대화 속에서 역할 분담을 만들어낼 수 있다
  • 다만, 프롬프트에 심어둔 역할 힌트는 결과에 상당히 강하게 작용한다
  • 종료 조건을 continue/end로 설정하면 대화의 길이를 에이전트 스스로에게 맡기기 쉽다. 이력은 메모리 유지로도 충분히 돌아가지만, 열람용 API는 있는 편이 실험하기 쉽다

한편으로는 아직 상당히 소박한 수준이다.

  • 매번 Agent A부터 시작한다
  • 에이전트가 너무 예의 바르고 정돈되어 버린다
  • 실제 세계의 제약 사항 확인보다는 대화를 정리하는 방향으로 흐르기 쉽다
  • docs/에 저장하는 지식은 아직 중복되기 쉽다

구성(Configuration)은 매우 작다.

  • src/agent/Agent.ts: 에이전트 본체
  • src/bus/MessageBus.ts: 에이전트 간 중계
  • src/orchestrator/RunOrchestrator.ts: 실행 생성 및 대화 제어
  • src/server.ts: POST /runsGET /runs/:runId
  • src/cli.ts: 터미널에서 목표를 전달하여 실행

CLI에서의 실행은 다음과 같은 형태이다.

npm run dev -- 晩御飯としてカレーを作る

그러면 콘솔에 대화가 흐르고, 마지막에 run id, 상태, 저장된 문서가 출력된다.

이번 실험을 통해 알 수 있었던 것은, '다중 에이전트(Multi-agent)스러움'은 단순히 수를 늘린다고 해서 나타나는 것이 아니라, 종료 조건(Termination condition)이나 역할의 초기 조건에 상당히 의존한다는 점이었다.

고정된 역할(Fixed role)을 부여하면 그럴싸해 보인다. 하지만 그것은 분산 협조(Distributed coordination)라기보다는 유도된 분업(Induced division of labor)에 가깝다.

반대로, 고정된 역할을 제거하더라도 목표가 단순하다면 에이전트들끼리 공정을 분해하고 역할 분담안을 만들어낼 수 있었다. 이 차이는 작아 보이지만 중요했다.

다음에 시도한다면 다음과 같은 것들이 흥미로울 것이다.

  • 대화를 시작하는 에이전트를 무작위화(Randomize)한다
  • 3체 이상으로 늘린다
  • send_message, save_note, end_conversation과 같은 함수 호출(Function calling) 스타일의 인터페이스로 만든다
  • 현실적인 제약(Real-world constraints)을 더 많이 도입한다

적어도 이번 범위 내에서는, '2체에게 카레를 만들게 한다'라는 막연한 테마만으로도 역할 분담의 실험대로서 충분히 활용할 수 있었다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0