AI Agent Orchestration이란: Multi-Agent에서 정말 어려운 것은 공유 상태와 Handoff다
Agent 하나만 사용할 때는 coordination 문제가 그리 복잡하지 않습니다. task를 주고, tool을 연결하고, test를 실행하게 한 다음 결과를 확인하면 됩니다. 진짜 복잡해지는 시점은 Codex, Claude Code, Cursor, 그리고 자동화 worker까지 같은 repository를 동시에 건드리기 시작할 때입니다.
이때 처음 드러나는 문제는 어떤 model이 더 똑똑한지가 아닙니다. 아주 평범한 협업 문제입니다. Claude는 Codex가 아직 시작하지 않았다고 생각해 같은 파일을 수정하고, 다른 Agent는 scheduled task가 설정됐다는 이유만으로 이미 실행된 것으로 간주합니다. chat에서 request를 보냈지만 receiver는 실제로 받아들이지 않았을 수도 있습니다. 앞선 Agent는 오래된 commit을 기준으로 분석했고, 다음 Agent가 이어받을 때 repository는 이미 바뀌었지만 이전 state를 그대로 믿고 계속할 수도 있습니다.
이런 문제는 orchestrator를 하나 더 추가한다고 자동으로 사라지지 않습니다. Routing은 “다음 task를 누구에게 줄 것인가”만 해결합니다. Multi-agent workflow를 안정적으로 만들려면 한 층이 더 필요합니다. 지금 owner가 누구인지, 어떤 state가 canonical인지, handoff가 실제로 수락됐는지, 실행 환경에 필요한 capability가 있는지, 완료를 누가 검증했는지를 분명히 해야 합니다.
GitHub에서 본 microcredit-agent-testbed의 central message board는 이 문제를 꽤 노골적으로 다룹니다. Hermes, Claude Code, Codex / ChatGPT를 별도 lane으로 나누고, 하나의 공개 issue와 canonical world model로 coordination합니다. 비즈니스 도메인보다 흥미로운 건 그 안의 protocol입니다. request를 보낸 것과 ownership을 실제로 수락한 것은 다르고, schedule을 설정한 것과 job이 성공적으로 실행된 것도 다르며, configuration success와 이후 실제 run success도 분리합니다.
이 사례를 보면서 Agent Orchestration을 보는 관점도 조금 바뀌었습니다. 예전에는 “task를 어떤 role에 나눠 줄 것인가”에 더 초점을 맞췄다면, 지금은 이렇게 봅니다. routing은 입구일 뿐이고, shared state와 handoff가 정리돼야 orchestration이 진짜 engineering이 됩니다.
Orchestration은 Agent 수를 늘리는 일이 아니라 ownership을 명확히 하는 일이다
OpenAI의 현재 Orchestration and handoffs 문서는 두 가지 패턴을 나눕니다. 하나는 specialist가 handoff를 받아 다음 작업을 실제로 이어가는 방식이고, 다른 하나는 manager가 최종 책임을 유지한 채 specialist를 tool처럼 호출하는 방식입니다.
이 차이는 중요합니다. 결국 multi-agent system이 자주 흐리게 만드는 핵심 질문에 답하기 때문입니다. 현재 결과의 owner는 누구인가?
manager가 계속 책임진다면 specialist는 함수에 가깝습니다. 정해진 일을 하고 결과를 돌려줍니다. 반대로 handoff가 일어나면 control이 실제로 이동합니다. 많은 workflow가 점점 어지러워지는 이유는 code 수준에서는 delegation이 일어나는데 state 수준에서는 ownership이 명확하지 않기 때문입니다. “누군가 작업 중”이라는 건 알지만 “지금 누가 책임자인가”는 답하기 어렵습니다.
Coding Agent의 단순한 흐름을 예로 들면 이렇습니다.
issue
-> triage agent
-> implementation agent
-> review agent
보기에는 깔끔하지만 운영 질문은 곧바로 생깁니다. implementation agent가 수락한 뒤에도 triage agent가 같은 scope를 수정해도 되는가? review agent는 read-only인가, 아니면 직접 patch해도 되는가? implementation agent가 DONE이라고 말하면 task가 끝난 것인가? review가 실패하면 WORKING으로 돌아가야 하는가, 아니면 새 request를 만들어야 하는가?
이런 rule이 prompt 안에만 있으면 결국 model이 “대충 이해해 주길” 기대하게 됩니다. 저는 지금은 handoff마다 작은 coordination contract를 두는 편이 낫다고 봅니다. 최소한 request id, sender, receiver, owner, deliverable, source revision, dependencies, acceptance test, required capability, fallback, status를 명시하는 식입니다.
이건 Agent workflow를 또 다른 Jira로 만들기 위한 게 아닙니다. chat history를 다시 읽어야만 알 수 있는 애매한 state를 줄이기 위한 겁니다.
제가 사용한 state model은 일부러 작게 잡았습니다.
REQUESTED
-> ACKNOWLEDGED
-> WORKING
-> DONE
-> VERIFIED
중요한 건 다섯 개 label이 아니라 각 의미를 건너뛰지 않는 것입니다.
message를 보냈다는 사실은 REQUESTED까지만 증명합니다. ACKNOWLEDGED는 receiver가 직접 수락해야 합니다. scheduled job을 만들었다고 DONE이 되는 것도 아닙니다. Agent가 “끝났다”고 말하면 DONE으로 갈 수는 있지만, VERIFIED에는 test output, diff, log, 다른 concrete evidence가 필요합니다.
처음 보면 보수적으로 느껴질 수 있지만 Agent 수가 늘수록 오히려 workflow가 가벼워집니다. 새 worker가 긴 chat을 다시 읽으며 “아마 이렇게 됐겠지”라고 추측할 필요가 없기 때문입니다. canonical state와 최근 receipt만 읽으면 됩니다.
이 rule을 작은 validator로 직접 검증했다
protocol을 문장으로만 남기고 싶지 않아 Content Lab 안에서 첫 번째 실험을 만들었습니다. 위 third-party repository의 code는 실행하지 않았고, synthetic fixture만 사용해 coordination rule 자체를 검증했습니다.
첫 번째는 coordination-contract validator입니다. 일부러 흔한 잘못된 state를 만들었습니다. 두 Agent가 같은 scope의 active owner를 동시에 주장하는 경우, receiver acknowledgement 없이 task를 DONE으로 표시한 경우, handoff가 stale source revision을 가리키는 경우, merge capability가 필요한데 read/write만 있는 환경에 task를 할당한 경우, completion에 verification evidence가 없는 경우, 같은 external action에 이미 active request가 있는데 또 request를 만든 경우, superseded된 request가 여전히 active인 경우입니다.
8개의 fixture가 모두 예상대로 탐지됐습니다.
이 결과가 production conflict rate를 낮췄다는 증거는 아닙니다. 그렇게 말할 수는 없습니다. 다만 더 작고 유용한 결론은 낼 수 있습니다. Agent의 지능 문제처럼 보이는 실패 중 상당수는 model 바깥에서 deterministic하게 검사할 수 있는 coordination error입니다.
capability mismatch가 대표적입니다. 흔히 “이 Agent는 GitHub access가 있다”고 뭉뚱그려 말하지만 read, edit, push, merge, deploy, secret access, outbound messaging는 전부 다른 capability입니다. interactive session에서 push할 수 있다고 해서 다른 환경의 scheduled worker도 같은 일을 할 수 있는 건 아닙니다.
그래서 저는 모호한 has-access 플래그보다 task가 필요한 capability를 선언하고 runtime이 실제로 보유한 capability를 명시하는 방식을 더 선호합니다.
Claude Code의 현재 subagents documentation도 비슷한 방향입니다. subagent는 독립 context를 가지고 tool과 permission을 제한할 수 있으며, repository change를 격리하고 싶다면 worktree도 사용할 수 있습니다. 문서 역시 독립적인 탐색, tool isolation, bounded result에는 subagent가 유용하지만, 여러 phase가 긴밀한 shared context를 필요로 하고 자주 왕복해야 한다면 main session이 더 적합할 수 있다고 설명합니다. Multi-agent는 많을수록 좋은 구조가 아닙니다. 실제 경계가 다른 일만 분리하는 편이 낫습니다.
두 번째 실험은 receipt replay였습니다.
append-only event log를 REQUESTED에서 VERIFIED까지 진행시키고 중간에 잘못된 전이를 넣었습니다. REQUESTED -> WORKING은 거부됩니다. “request를 보냈다”는 사실만으로 “receiver가 수락했다”를 추론할 수 없기 때문입니다. REQUESTED -> DONE도 거부됩니다. schedule이나 trigger는 execution proof가 아닙니다. receiver가 아닌 actor가 ACKNOWLEDGED를 기록하는 것도 거부되고, VERIFIED에 evidence가 없으면 통과하지 못합니다.
7개의 replay check가 모두 통과했고, derived state를 삭제한 뒤에도 event log만으로 최종 VERIFIED state를 다시 만들 수 있었습니다.
long-running agent workflow에서는 이 점이 중요합니다. state가 하나의 chat 안에만 있으면 context compression, 새 session, 다른 machine으로 전환하는 순간 다음 Agent는 summary를 믿을 수밖에 없습니다. request, acknowledgement, done, verification receipt가 durable하다면 summary를 믿지 않아도 됩니다. fact를 읽으면 됩니다.
GitHub은 control plane에는 잘 맞지만 runtime database는 아니다
여기까지 해보니 개인 개발자나 작은 team에는 GitHub이 이 layer의 control plane으로 꽤 잘 맞는다는 생각이 들었습니다.
GitHub이 orchestration platform이라서가 아닙니다. state가 versioned되고, change에 author와 timestamp가 있고, commit을 revision으로 쓸 수 있고, issue / PR에 evidence를 남길 수 있으며, repository file은 Codex나 Claude Code가 직접 읽을 수 있기 때문입니다. 첫날부터 dedicated orchestration backend를 만들지 않아도 canonical state를 repository에 고정하는 것만으로 cross-session problem을 많이 줄일 수 있습니다.
제 Content Lab도 같은 방식을 사용합니다. active topic, active Dossier, previous command, next command, lifecycle state를 persist합니다. Dossier history는 DISCOVERED, SCORED, RESEARCHING, READY_FOR_TEST처럼 append되고, 하나의 chat이 “지금 어디까지 했는지” 기억하는 데 의존하지 않습니다. 이번 실험에서도 active pointer, explicit next command, append-only state history가 같은 GitHub-backed workflow 안에서 잘 동작한다는 점을 확인했습니다.
하지만 runtime state를 전부 Git에 넣는 건 권하지 않습니다. high-frequency lock, millisecond lease, message queue, temporary token, sensitive credential는 Git이 잘하는 일이 아닙니다. GitHub에 더 잘 맞는 건 session을 넘어 남아야 하고 나중에 audit할 수 있는 durable facts입니다.
저는 두 층으로 봅니다.
runtime state
-> lock, stream, short-lived message, transient execution state
canonical coordination state
-> owner, revision, handoff, receipt, evidence, final decision
앞의 것은 database, queue, SDK session에 둘 수 있습니다. 뒤의 것은 며칠 뒤 다른 Agent가 봐도 “실제로 무슨 일이 있었는지” 이해할 수 있어야 합니다.
OpenAI의 현재 Running agents 문서도 state continuation을 명시적인 design choice로 다룹니다. history를 직접 관리하거나, session을 쓰거나, server-managed continuation을 쓰거나, interrupted run을 resume하는 방식입니다. Agents SDK의 observability guidance는 model call, tool call, handoff, guardrail 등을 trace합니다. 결국 execution state와 auditable state 모두 설계 대상이며 prompt에만 맡겨선 안 된다는 뜻입니다.
가장 먼저 보완해야 할 것은 “누가 무엇을 했는지”에 대한 evidence chain이다
이전 글에서 말했던 원칙은 지금도 같습니다. Agent 하나가 안정적으로 task를 끝낼 수 있다면 architecture를 멋있어 보이게 하려고 다섯 개로 쪼갤 필요는 없습니다. OpenAI의 현재 guidance도 가능하면 one agent로 시작하고, capability isolation, policy isolation, prompt clarity, trace legibility에 실제로 필요해질 때 specialist를 추가하는 방향입니다.
다만 여러 Agent를 사용하기 시작했다면 저는 예전보다 훨씬 빨리 shared state를 넣습니다.
더 복잡한 supervisor를 먼저 만들자는 이야기가 아닙니다. planner와 reviewer를 늘리자는 것도 아닙니다. 먼저 애매함을 없애는 겁니다. 같은 scope의 owner는 누구인지, receiver가 정말 acknowledge했는지, task가 어떤 revision을 기준으로 하는지, execution environment가 무엇을 할 수 있는지, DONE을 뒷받침하는 evidence가 무엇인지, 나중에 receipt만으로 실제 순서를 재구성할 수 있는지를 고정합니다.
이 layer는 화려하지 않습니다. demo에서 더 “똑똑해 보이게” 하지도 않습니다. 하지만 오래 운영할수록 Agent 하나를 더 추가하는 것보다 중요해집니다.
Multi-agent system에서 가장 위험한 건 model 하나가 틀리는 것만이 아닙니다. 각각의 local decision은 모두 그럴듯한데 global state는 이미 false가 된 상황입니다. 한 Agent는 “아무도 아직 안 했다”고 생각하고, 다른 Agent는 “나는 이미 끝냈다”고 생각하고, automation은 “schedule이 실행됐다”고 생각하고, reviewer는 오래된 revision을 검증합니다. 각 단계는 말이 되는데 전체 workflow는 틀릴 수 있습니다.
그래서 지금 Agent Orchestration이 무엇인지 묻는다면 저는 더 이상 “여러 Agent가 나눠서 일하는 것”이라고만 답하지 않습니다.
저라면 이렇게 표현합니다. task, state, permission, evidence를 추측에 의존하지 않는 workflow로 조직하는 것.
Routing은 다음 단계를 누구에게 줄지 결정합니다. Ownership은 지금 누가 책임자인지 정합니다. Handoff는 context와 boundary를 넘깁니다. Receipt는 receiver가 실제로 받았다는 사실을 증명합니다. Capability boundary는 할 수 없는 환경에 task를 넘기는 실수를 막습니다. Trace와 evidence는 나중에 왜 현재 state가 되었는지 설명할 수 있게 합니다.
Coding Agent를 실제 프로젝트에 더 깊게 넣고 있다면, 관련 글 CLAUDE.md가 너무 길어졌을 때 AGENTS.md, Skills, CI의 경계를 어떻게 나눴는가는 그 이전 layer를 다룹니다. 그 글이 “rule을 어디에 둘 것인가”라면, 이 글이 다루는 질문은 그다음입니다. 여러 Agent가 동시에 일하기 시작했을 때, 어떻게 하나의 신뢰할 수 있는 현실을 공유할 것인가.