코레오그래피된 Claude 동적 워크플로우 (Choreographed Claude Dynamic Workflows)
요약
Claude의 동적 워크플로우를 오케스트레이션(Orchestration) 방식과 코레오그래피(Choreography) 방식으로 비교 분석합니다. 중앙 제어 방식 대신 각 구성 요소가 이벤트에 반응하여 동작하는 하이브리드 모델의 구조와 이점을 설명합니다.
핵심 포인트
- 오케스트레이션은 중앙 제어를 통해 명확한 실행 경로와 예측 가능한 계획을 제공함
- 코레오그래피는 각 구성 요소가 이벤트와 시맨틱 계약에 반응하여 자율적으로 동작함
- 상위 의도는 오케스트레이션하고 실행은 코레오그래피하는 하이브리드 모델 제안
- IntentGraph, QuarkBehavior, SubAgents 등 구체적인 계층적 아키텍처 구조 제시
Claude 동적 워크플로우 (Claude Dynamic Workflows)에 대해 읽으면서, 저는 워크플로우 패턴 자체가 단일 에이전트 코딩 환경에 국한되지 않는다는 점을 발견했습니다. 흥미로운 점은 AI가 분류(classify), 팬아웃(fan out), 검증(verify), 필터링(filter), 비교(compare) 및 루프(loop)를 수행할 수 있다는 것뿐만이 아닙니다. 진짜 흥미로운 점은 이러한 패턴들이 어떻게 조정(coordinated)되는가 하는 것입니다.
Claude 스타일의 동적 워크플로우 (Dynamic Workflow)에서 시스템은 대개 고도로 오케스트레이션(orchestrated)된 느낌을 줍니다. 다음에 무엇을 할지, 어떤 하위 작업(subtask)을 실행할지, 어떤 도구(tool)나 에이전트(agent)를 호출할지, 결과를 어떻게 병합할지, 그리고 최종 답변이 언제 준비되는지를 결정하는 중앙 지능(central intelligence)이 존재합니다.
이 모델은 오케스트레이션이 시스템에 다음과 같은 이점을 제공하기 때문에 강력합니다:
- 중앙 제어 지점 (a central point of control)
- 명확한 실행 경로 (a clear execution path)
- 예측 가능한 계획 (a predictable plan)
- 명시적인 분기 (explicit branching)
- 더 쉬운 사용자 대상 진행 상황 확인 (easier user-facing progress)
- 최종 결과를 합성하기 위한 단일 장소 (a single place to synthesize the final result)
하지만 저의 아키텍처는 자연스럽게 이러한 동일한 패턴들을 코레오그래피(choreography) 방향으로 밀어붙입니다.
코레오그래피된 워크플로우 (choreographed workflow)에서는 모든 구성 요소에 무엇을 할지 지시하는 단일 컨트롤러(controller)가 항상 존재하는 것은 아닙니다. 대신, 각 시맨틱 동작(semantic behavior)이 이벤트(events)에 반응합니다. 하나의 동작이 전체 시스템을 이해할 필요는 없습니다. 오직 다음 사항들만 이해하면 됩니다:
- 자신이 검증하는 시맨틱 계약 (the semantic contract it validates)
- 자신이 방출하는 이벤트 (the event it emits)
- 자신이 수락하는 페이로드 형태 (the payload shape it accepts)
- 자신이 보완하는 에러 형태 (the error shape it enriches)
- 자신이 생성할 수 있는 종단 상태 (the terminal states it can produce)
이는 하이브리드 모델을 생성합니다.
상위 수준의 의도(high-level intent)는 여전히 오케스트레이션될 수 있습니다. 하지만 실행은 코레오그래피될 수 있습니다.
이 모델에서:
IntentGraph / graflow는 거시적 의도(macro-intention)를 정의합니다.QuarkBehavior는 가장 작은 단위의 시맨틱 동작을 정의합니다.AtomicBehavior는 시맨틱 동작들을 구성합니다.SubAgents는 이벤트에 반응합니다.ProofAgents는 정확성을 검증합니다.HealingAgents는 유효하지 않은 상태를 복구합니다.BenchmarkAgents는 비용, 지연 시간(latency) 및 실행 동작을 측정합니다.Governor는 최종 결과가 수용 가능한지 결정합니다.
오케스트레이터(orchestrator)는 무엇이 달성되어야 하는지를 말합니다.
코레오그래피된 에이전트(choreographed agents)들은 시맨틱 이벤트에 반응함으로써 어떻게 계속 진행할지를 스스로 찾아냅니다.
그것이 바로 Choreographed Claude Dynamic Workflows의 핵심 아이디어입니다. AI 코딩 에이전트(AI coding agents)에서 나타나는 동일한 워크플로우 패턴을 가져와, 이를 이벤트 기반의 시맨틱 동작(event-driven semantic behaviors)으로 재해석하는 것입니다.
1. Classify-And-Act (분류 및 실행)
Classify-And-Act는 AI 시스템에서 가장 기본적인 패턴입니다.
사용자가 메시지를 보냅니다. 시스템은 의도(intent)를 분류합니다. 그런 다음 런타임(runtime)은 어떤 동작(behavior)이 해당 페이로드(payload)를 처리해야 할지 결정합니다.
전통적인 오케스트레이션(orchestrated) 버전에서의 흐름은 다음과 같습니다:
IntentResolver -> Router -> Behavior -> Response
코레오그래피된 시맨틱(choreographed semantic) 버전에서의 흐름은 다음과 같습니다:
received.message -> classified.intent -> requested.behavior -> validated.behavior -> ended.behavior.in.success | ended.behavior.in.error
그 차이는 미묘하지만 중요합니다.
오케스트레이션 버전에서는 라우터(router)가 시퀀스(sequence)를 소유합니다.
코레오그래피 버전에서는 각 단계가 시맨틱 이벤트(semantic event)를 방출(emit)하며, 다음 동작이 해당 이벤트에 반응합니다.
메시지는 다음과 같이 분류될 수 있습니다:
CreateEntityIntentUpdateEntityIntentFindEntityIntentSendMessageIntentValidateFieldIntentHealInvalidPayloadIntentGenerateBehaviorIntentVerifyGeneratedCodeIntent
분류 후에 시스템은 단순히 함수를 호출하는 데 그치지 않습니다.
시스템은 시맨틱 동작을 선택하고, 페이로드를 검증하며, 원시 값(primitive values)을 추출하고, 필요할 때 타입을 변환한 뒤 다음과 같은 종료 이벤트(terminal events) 중 하나를 방출합니다:
ended.{Behavior}.in.successended.{Behavior}.in.error
이를 통해 모든 단계를 전체 흐름을 다시 작성하지 않고도 교체, 관찰, 복구(healed), 벤치마킹 또는 형식 검증(formally verified)할 수 있으므로 런타임이 더욱 모듈화됩니다.
2. Fanout-And-Synthesize (팬아웃 및 합성)
Fanout-And-Synthesize는 하나의 입력을 여러 개의 특화된 워커(specialized workers)에게 분산시킨 후, 나중에 시스템이 그들의 출력을 하나의 승인된 결과로 합성(synthesize)하는 패턴입니다.
Claude 스타일의 워크플로우 (workflow)에서는, 종종 중앙 에이전트 (central agent)가 문제를 하위 작업 (subtasks)으로 분할하고, 해당 하위 작업들을 병렬 워커 (parallel workers)에게 전송한 뒤 결과를 병합 (merge)하는 형태로 나타납니다.
코레오그래피된 시맨틱 워크플로우 (choreographed semantic workflow)에서 팬아웃 (fanout)은 이벤트 구독 (event subscription)을 통해 발생할 수 있습니다.
하나의 유입되는 페이로드 (payload)를 여러 개의 독립적인 시맨틱 동작 (semantic behaviors)이 관찰할 수 있기 때문에 이는 팬아웃 (fanout)입니다:
- 타입 검증기 (type validator)
- 기본 요소 추출기 (primitive extractor)
- 스키마 검증기 (schema validator)
- 시맨틱 검증기 (semantic validator)
- 언어별 컴파일러 (language-specific compiler)
- 증명 검사기 (proof checker)
- 벤치마크 실행기 (benchmark runner)
- 자가 치유 파이프라인 (self-healing pipeline)
합성 (synthesis) 단계는 단순한 텍스트 병합이 아닙니다.
그것은 시맨틱 리덕션 (semantic reduction)입니다.
런타임 (runtime)은 다음 사항들을 합성합니다:
- 어떤 값이 유효한지
- 어떤 변환 (conversion)이 성공했는지
- 어떤 동작 (behavior)이 실패했는지
- 어떤 언어별 타입 규칙 (language-specific type rule)이 실패를 유발했는지
- 어떤 치유 함수 (healing function)를 추가하거나 재사용해야 하는지
- 생성된 아티팩트 (artifact) 중 어떤 것이 수용 가능한지
코드 생성 (code generation)의 경우, 이 패턴은 더욱 강력해집니다.
동일한 시맨틱 의도 (semantic intention)를 여러 전문화된 에이전트 (specialized agents)에게 분산할 수 있습니다:
- 한 에이전트는 TypeScript를 생성합니다
- 한 에이전트는 Zig를 생성합니다
- 한 에이전트는 Haskell을 생성합니다
- 한 에이전트는 Prolog 규칙을 생성합니다
- 한 에이전트는 테스트를 생성합니다
- 한 에이전트는 시맨틱 동등성 (semantic equivalence)을 검증합니다
- 한 에이전트는 실행 비용 (execution cost)을 벤치마크합니다
- 한 에이전트는 최종 승인된 동작 (final accepted behavior)을 합성합니다
이것은 단순히 병렬 생성이 아닙니다.
시맨틱 책임 (semantic responsibility)에 대한 팬아웃 (fanout)입니다.
3. 적대적 검증 (Adversarial Verification)
적대적 검증 (Adversarial Verification)은 생성된 동작 (behavior), 함수 (function), 번역 (translation) 또는 구현 (implementation)을 즉시 신뢰하지 않는 워크플로우입니다.
다른 에이전트가 단 하나의 명시적인 목표를 가지고 후보(candidate)를 전달받습니다:
- 그것이 틀렸음을 증명하는 것
적대적 에이전트 (adversarial agent)는 친절한 리뷰어처럼 행동해서는 안 됩니다. 다음과 같이 행동해야 합니다:
- 공격자 (attacker)
- 컴파일러 (compiler)
- 타입 검사기 (type checker)
- 속성 기반 테스터 (property-based tester)
- 악의적인 사용자 (malicious user)
- 런타임 실패 시뮬레이터 (runtime failure simulator)
- 시맨틱 동등성 파괴자 (semantic equivalence breaker)
코레오그래피된 (choreographed) 구현 방식에서는 생성기 (generator)가 모든 적대적 검증기 (adversarial verifier)를 직접 호출할 필요가 없습니다.
생성기는 단지 후보 (candidate)가 생성되었다는 사실만을 발행 (publish)합니다:
DSL.pub("generated.candidate")
generated.candidate -> request.adversarialVerification.for.candidate
TypeAdversaryAgentSecurityAdversaryAgentSemanticEquivalenceAdversaryAgentLinearUsageAdversaryAgentRuntimeCrashAdversaryAgentPayloadCorruptionAdversaryAgentPromptInjectionAdversaryAgent
각 검증기는 다음과 같은 결과를 방출 (emit)합니다:
passed.verificationfound.counterexamplefound.securityViolationfound.semanticDriftfound.runtimeFailure
그래프는 다음과 같이 표현될 수 있습니다:
generated.candidate -> request.adversarialVerification.for.candidate
request.adversarialVerification.for.candidate
...
이 에이전트 중 하나가 실패를 발견했을 때, 단순히 "실패 (failed)"라고 반환해서는 안 됩니다.
반드시 반례 (counterexample)를 반환해야 합니다.
그 반례는 자가 치유 파이프라인 (self-healing pipeline)의 입력값이 됩니다:
found.counterexample -> execute.selfHealingPipeline
found.securityViolation -> execute.selfHealingPipeline
found.semanticDrift -> execute.selfHealingPipeline
...
4. 생성 및 필터링 (Generate-And-Filter)
Generate-And-Filter는 적대적 검증 (Adversarial Verification)보다 덜 공격적인 버전입니다.
하나의 답변을 생성하고 이를 신뢰하는 대신, 시스템은 여러 후보를 생성하고 결정론적 게이트 (deterministic gates)를 통해 이를 필터링합니다.
예를 들어, 새로운 QuarkBehavior를 생성할 때 시스템은 다음과 같은 여러 후보를 생성할 수 있습니다:
- 최소 구현 (minimal implementation)
- 엄격한 구현 (strict implementation)
- 빠른 구현 (fast implementation)
- 안전한 구현 (safe implementation)
- 선형 구현 (linear implementation)
- 언어 네이티브 구현 (language-native implementation)
그 후 필터가 다음 항목 중 하나라도 실패하는 모든 것을 제거합니다:
- 스키마 검증 (schema validation)
- 타입 검증 (type validation)
- 단위 테스트 (unit tests)
- 속성 기반 테스트 (property-based tests)
- 시맨틱 동등성 테스트 (semantic equivalence tests)
- 컴파일 (compilation)
- 린팅 (linting)
- 런타임 벤치마크 (runtime benchmark)
- 보안 정책 (security policy)
- 선형 소비 규칙 (linear consumption rule)
- 제로 트러스트 규칙 (ZeroTrust rule)
핵심 원칙은 간단합니다:
- 생성 (generation)은 저렴하다
- 수락 (acceptance)은 엄격해야 한다
그래프 형태로는 다음과 같습니다:
generated.candidate -> filtered.candidate -> accepted.candidate | rejected.candidate
더 완전한 파이프라인은 다음과 같은 모습일 수 있습니다:
accepted.intent -> generate.candidates
generate.candidates -> generated.candidate.1
...
필터 파이프라인 (filter pipeline) 자체는 독립적인 의미론적 게이트 (semantic gates)로 분해될 수 있습니다:
execute.filterPipeline -> execute.compileCheck
execute.compileCheck -> execute.unitTests
execute.unitTests -> execute.propertyTests
...
또는 완전히 코레오그래피 (choreographed) 될 수 있습니다:
ready.candidate.for.filtering -> filter.candidate.with.CompileFilterAgent
ready.candidate.for.filtering -> filter.candidate.with.TestFilterAgent
ready.candidate.for.filtering -> filter.candidate.with.SecurityFilterAgent
...
각 필터는 자체적인 결정을 발행 (publish) 합니다:
CompileFilterAgent -> pub("passed.filter.compile")
TestFilterAgent -> pub("passed.filter.tests")
SecurityFilterAgent -> pub("passed.filter.security")
...
모든 필수 필터를 통과하면, Governor가 아티팩트 (artifact)를 승격 (promote) 시킬 수 있습니다:
passed.allRequiredFilters -> accept.candidate.with.Governor
accept.candidate.with.Governor -> pub("promoted.artifact")
이를 통해 워크플로우 (workflow)는 결정론적 (deterministic)이 됩니다.
AI는 가능성들을 생성할 수 있지만, 런타임 (runtime)이 무엇이 시스템의 일부가 될 수 있는지를 결정합니다.
5. 토너먼트 (Tournament)
Tournament는 명확한 최적의 구현체가 없을 때 제가 사용할 워크플로우입니다.
단 하나의 솔루션을 요청하는 대신, 시스템은 경쟁하는 후보군 (candidates)을 생성하고 동일한 루브릭 (rubric)을 기준으로 점수를 매깁니다.
토너먼트는 다음과 같은 것들을 비교할 수 있습니다:
- 서로 다른 언어 (languages)
- 서로 다른 타입 인코딩 (type encodings)
- 서로 다른 직렬화 형식 (serialization formats)
- 서로 다른 힐링 전략 (healing strategies)
- 서로 다른 샤딩 전략 (sharding strategies)
- 서로 다른 이벤트 집계 윈도우 (event aggregation windows)
- 서로 다른 SQL-to-graflow 변환 계획 (translation plans)
- 동일한
QuarkBehavior의 서로 다른 구현체들
토너먼트는 단순히 승자만을 가리는 것이 아니라, 스코어카드 (scorecard)를 생성해야 합니다.
스코어카드는 다음 항목들을 측정할 수 있습니다:
- 테스트 성공 (test success)
- 의미론적 동등성 (semantic equivalence)
- 컴파일 성공 (compilation success)
- CPU 비용 (CPU cost)
- 메모리 사용량 (memory usage)
- 할당 횟수 (allocation count)
- 힐링 비용 (healing cost)
- 추적 가능성 (traceability)
- 생성된 코드 크기 (generated code size)
- 증명 난이도 (proof difficulty)
- ZeroTrust 준수 (ZeroTrust compliance)
- 선형 소비 안전성 (linear consumption safety)
토너먼트 그래프는 다음과 같은 형태를 띨 수 있습니다:
defined.problem -> open.tournament
open.tournament -> generate.candidate.with.TypeScriptAgent
open.tournament -> generate.candidate.with.ZigAgent
...
또는, 동일한 언어에 대해 서로 다른 전략을 사용하는 경우:
defined.problem -> generate.candidate.with.minimalStrategy
defined.problem -> generate.candidate.with.strictStrategy
defined.problem -> generate.candidate.with.fastStrategy
...
각 후보(candidate)는 동일한 의도를 가지고 토너먼트에 참여하지만, 서로 다른 전략을 사용합니다.
승자는 단순히 더 좋아 보인다는 이유로 선택되지 않습니다.
측정을 통해 선택됩니다.
전체 토너먼트 워크플로우 (workflow)는 다음과 같이 표현될 수 있습니다:
opened.tournament -> generate.candidates
generated.candidates -> request.benchmark.for.candidates
generated.candidates -> request.proof.for.candidates
...
토너먼트 패턴은 의견 (opinion)을 측정 (measurement)으로 전환합니다.
이는 시스템이 첫 번째로 나온 좋은 답변을 맹신하는 것을 방지합니다.
또한 다음과 같이 다양한 종류의 승자를 허용합니다:
- 프로덕션 (production)을 위한 최적의 구현체
- 형식 증명 (formal proof)을 위한 최적의 구현체
- 벤치마크 (benchmark)를 위한 최적의 구현체
- 문서화 (documentation)를 위한 최적의 구현체
- 이식성 (portability)을 위한 최적의 구현체
- 보안 (security)을 위한 최적의 구현체
이 지점에서 그래프는 더 높은 수준의 추상화 (abstraction) 단계로 진입합니다. 생성, 검증, 점수 산정 및 승격의 전체 프로세스는 의미론적 워크플로우 (semantic workflow)로 표현될 수 있습니다.
6. Loop-Until-Done
Loop-Until-Done은 의도 기반 힐링 (Intent-based Healing)을 가장 명확하게 표현한 것입니다.
대부분의 시스템은 에러를 흐름의 종료로 취급합니다.
이 모델에서 에러는 또 다른 하나의 이벤트 (event)입니다.
QuarkBehavior가 유효하지 않은 페이로드 (payload)를 수신했을 때, 즉시 최종 에러를 반환해서는 안 됩니다. 대신 시스템이 복구를 시도할 수 있도록 충분한 컨텍스트 (context)를 포함한 에러 이벤트 (error event)를 방출해야 합니다.
에러 페이로드 (error payload)에는 다음 내용이 포함되어야 합니다:
- 원본 페이로드 (original payload)
- 마지막으로 알려진 페이로드 상태 (last known payload state)
- 유효하지 않은 값 (invalid value)
- 예상되는 기본 타입 (expected primitive type)
- 예상되는
SemanticType - 실패한 의미론적 동작 (semantic behavior that failed)
- 내부 트레이스/스팬 (internal trace/span)
- 실패한 함수 (failed function)
- 이전 변환 시도 (previous conversion attempt)
- 복구 이력 (healing history)
- 거부 사유 (rejection reason)
그 지점부터, 해당 동작 (behavior) 또는 특화된 힐링 에이전트 (healing agent)가 페이로드를 수리하려고 시도할 수 있습니다.
표준 워크플로우 (canonical workflow)는 다음과 같습니다:
AtomicBehavior.pub("execute.{QuarkBehavior}")
execute.{QuarkBehavior}
...
이 루프 (loop)는 맹목적인 재시도 (blind retry)가 아닙니다.
맹목적인 재시도 (Blind retry)는 동일한 작업을 반복하며 다른 결과가 나오기를 바라는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기