멀티에이전트 시스템을 도입하는 팀이 빠르게 늘고 있습니다. 아이디어는 매력적입니다. 전문화된 에이전트들이 병렬로 일하고, 서로 핸드오프하며, 사람이 손댈 필요 없이 복잡한 작업을 완주한다—꿈 같은 얘기죠. 그런데 실제로 굴려보면, 꿈은 생각보다 빨리 끝납니다. 에러 로그조차 없이.
조용한 덮어쓰기: 가장 위험한 실패 모드
dev.to에 공유된 사례 하나가 멀티에이전트의 핵심 위험을 정확히 보여줍니다. 두 에이전트가 병렬로 리서치를 수행했습니다. 각자 다른 컨텍스트 윈도우, 그런데 공유 상태 파일은 하나. 둘 다 "성공" 리포트를 냈습니다. 그런데 나중에 완료된 에이전트 B가 에이전트 A의 결과를 같은 키에 덮어썼습니다. 에러 없음. 로그에도 overwrite 없음. 스웜은 데이터 절반을 잃은 채 다음 단계로 넘어갔습니다.
이게 멀티에이전트 시스템의 실제 실패 방식입니다. 요란하게 터지는 게 아니라, 조용히 성공한 척 합니다.
충돌 방지를 위한 세 가지 설계 원칙
이 문제를 해결하는 방법은 생각보다 구체적입니다. 첫 번째는 에이전트별 네임스페이싱입니다. state["findings"] 같은 공유 키 대신, state["agent_A"]["findings"]처럼 에이전트별 슬롯을 분리하고 오케스트레이터가 완료 후 병합합니다. 충돌이 '줄어드는' 게 아니라 구조적으로 불가능해집니다.
두 번째는 태스크 클레임 필드입니다. 작업을 시작하기 전 claimed_by 필드를 읽고, 비어있을 때만 자신의 ID를 기록하고 시작합니다. 두 에이전트가 동시에 체크해도, 나중에 커밋하는 쪽은 이미 채워진 필드를 보고 건너뜁니다. 단순하지만 대부분의 병렬 환경에서 작동합니다.
세 번째는 핸드오프 컨텍스트 설계입니다. 에이전트 A가 B에게 결과물만 넘기는 건 반쪽짜리 핸드오프입니다. B가 진짜 필요한 건 A가 실패한 경로, 빈 검색 결과, 신뢰할 수 없었던 데이터의 이유입니다. output, tried, partial, notes 네 필드를 구조화해서 넘기면 B가 A의 실수를 반복하지 않습니다.
MCP 도구 설계: 쓰기 작업은 별도로 취급하라
병렬 에이전트 환경에서 MCP 도구를 설계할 때 가장 많이 놓치는 부분이 있습니다. 읽기 도구는 여러 에이전트가 동시에 호출해도 안전하지만, 쓰기 도구는 다릅니다. 그런데 대부분의 MCP 서버 템플릿은 이 둘을 구분하지 않습니다.
안전한 패턴은 쓰기 도구에 caller_id 파라미터를 추가하는 겁니다. 서버가 이를 write key의 일부로 사용하면 에이전트 A와 B는 각자의 슬롯에 쓰고 충돌이 없습니다. 공유 상태를 수정해야 한다면 lock_key 메커니즘을 추가합니다. 글로벌 상태를 사이드 이펙트로 업데이트하는 도구—"태스크를 완료로 표시