그래프 엔지니어링: 하나의 프롬프트로 1,000 개의 AI 에이전트를 병렬 실행하는 방법

@0xWast3
영어1일 전 · 2026년 7월 22일
146K
151
19
13
376

TL;DR

AI 에이전트를 위한 그래프 엔지니어링에 대한 기술적 심층 분석으로, 실제 의존성을 파악하고 병렬 실행을 통해 워크플로우를 확장하는 방법을 설명합니다.

2026년에 다중 에이전트 시스템을 구축하는 모든 개발자는 여전히 직선으로 작업하고 있습니다. 1단계, 다음 2단계, 그리고 3단계 - 각 단계는 이전 단계가 끝나기를 기다립니다. 이것이 왜 비효율적인지, 그리고 어떻게 해결할 수 있는지 알아보겠습니다.

wast3 - inline image

아무도 확인하지 않는 문제

여러분은 다단계 에이전트를 구축했습니다. 작동은 잘 됩니다. 하지만 느리기도 합니다.

모델이 병목이라고 생각할 것입니다. 하지만 아닙니다.

병목은 여러분이 그린 구조입니다. 체인 형태 - 1단계가 2단계를 기다리고, 2단계가 3단계를 기다리는 방식 - 은 절반의 단계가 서로 아무 관련이 없어도 순차적으로 실행되도록 강제합니다.

"이 문서를 요약한 다음, 날씨를 확인하세요"는 하나의 워크플로우로 위장한 두 개의 독립적인 작업입니다. 날씨 작업은 요약이 필요하지 않습니다. 원래부터 그랬습니다. 하지만 체인으로 작성했다면 어쨌든 기다려야 합니다.

이러한 불필요한 대기 시간이 수십 단계에 걸쳐 누적되면, 실행 시간의 대부분을 차지하게 됩니다.

1장 - 루프 vs 그래프

루프는 자기 개선의 한 단위입니다:

text
1시도해 보기 → 결과 확인 → 조정 → 다시 시도

이것이 기본 단위입니다. 하나의 에이전트, 하나의 지표, 수렴할 때까지 순환합니다.

루프에는 알려진 실패 패턴이 있습니다: 측정하는 것만 최적화하고 나머지는 무시합니다. 티켓을 빨리 마감하도록 조정된 지원 봇은 티켓을 빨리 마감하겠지만, 만족도는 조용히 추락합니다. 루프는 자신의 지표 밖을 볼 수 없습니다. 이것이 바로 에이전트 아키텍처에 나타난 구드하트의 법칙입니다.

그래프는 설계 자체로 이 문제를 해결합니다. 하나의 숫자를 쫓는 하나의 루프 대신, 서로를 감시하고 교정하는 루프 네트워크를 구축합니다. 노드 A의 출력이 노드 B로 전달됩니다. 노드 C는 독립적으로 실행되어 둘 다 확인합니다. 단일 지표가 전체 시스템을 움직이는 것이 아니라, 구조 자체가 그 역할을 합니다.

wast3 - inline image

에이전트 시스템에서 이는 한 가지 구체적인 변화를 의미합니다: 모든 것을 위에서 아래로 처리하는 하나의 에이전트를 작성하는 것을 중단하세요. 먼저 작업의 구조를 설계하세요 - 무엇이 무엇보다 먼저 실행되어야 하는지, 무엇을 동시에 실행할 수 있는지, 무엇이 실제로 기다려야 하는지.

2장 - 노드, 엣지, 그리고 그것들을 구분하는 테스트

그래프는 정확히 두 가지 구성 요소로 이루어집니다:

노드 - 하나의 작업 단위. 하나의 에이전트, 하나의 작업, 하나의 입력, 하나의 출력.

엣지 - 실제 의존성. 노드 B의 입력에는 노드 A의 출력이 필요합니다.

거의 모든 사람이 저지르는 실수: "그리고 나서"를 기본적으로 엣지로 간주하는 것입니다.

text
1"이 코드베이스를 읽고 변경 로그를 작성하세요"
2"가격 페이지를 가져와서 경쟁사 기능을 요약하세요"

워크플로우의 모든 "그리고 나서"에 대해 한 가지 질문을 해보세요:

다음 단계가 실제로 이전 단계의 출력을 읽나요?

→ 실제 엣지입니다. 순차적 순서를 유지하세요. 아니오 → 엣지가 아닙니다. 대기 시간은 낭비입니다. 병렬로 실행하세요.

두 작업 사이에 데이터가 교차되지 않는다면, 두 작업은 독립적입니다 — 그리고 순차적으로 실행하는 모든 독립적인 쌍은 공짜로 버리는 실행 시간입니다.

코드에 적용된 테스트는 다음과 같습니다:

python
1from dataclasses import dataclass
2
3@dataclass
4class TaskNode:
5 id: str
6 prompt: str
7 depends_on: list[str] # 이 노드가 실제로 필요로 하는 노드의 ID
8
9def has_real_edge(node_a: TaskNode, node_b: TaskNode) -> bool:
10 """
11 핵심 그래프 엔지니어링 테스트:
12 node_b의 프롬프트가 실제로 node_a의 출력을 필요로 하는가?
13 """
14 return node_a.id in node_b.depends_on
15
16# 예시: 대부분의 "체인"은 2-3개의 실제 의존성 그룹으로 축소됩니다
17nodes = [
18 TaskNode("audit_routes", "모든 API 라우트 파일 나열", []),
19 TaskNode("check_auth", "인증 미들웨어 적용 범위 확인", []),
20 TaskNode("fetch_weather", "오늘 날씨 가져오기", []),
21 TaskNode("summarize", "라우트 + 인증 결과 요약",
22 depends_on=["audit_routes", "check_auth"]),
23]
24
25# audit_routes, check_auth, fetch_weather 사이에는 엣지가 없습니다
26# 병렬로 실행됩니다. "summarize"만 실제 엣지를 가지고 있습니다 -- 기다립니다.

현재의 "A를 하고, B를 하고, C를 하세요" 에이전트는 기술적으로 이미 그래프입니다. 하지만 가장 나쁜 형태의 그래프입니다 - C가 중단되면 다운스트림에서 아무것도 실행되지 않는 단일 체인입니다.

3장 - 첫 번째 그래프 구축

wast3 - inline image

필요 사항:

  • Claude Code (Dynamic Workflows를 지원하는 최신 버전).
  • Max, Team 또는 Enterprise 플랜 - 워크플로우가 기본적으로 활성화되어 있습니다. Pro에서는 수동으로 활성화하세요.

실제 저장소를 여세요. 장난감 예제가 아닙니다 - 이점은 실제 규모에서만 나타납니다.

첫 번째 그래프를 시작하는 프롬프트:

text
1이 코드베이스의 모든 라우트 파일을 감사하는 워크플로우를 생성하세요.
2
3각 라우트 파일에 대해 독립적으로 확인하세요:
4- 인증 미들웨어 존재 여부
5- 모든 매개변수에 대한 입력 검증
6- 속도 제한 설정 여부
7- 오류 처리에서 스택 추적이 유출되지 않는지
8
9모든 라우트 파일에 대해 이러한 검사를 병렬로 실행하세요 —
10서로 의존하지 않습니다.
11
12모든 파일이 확인된 후, 심각도별로 그룹화된 하나의 통합
13보고서를 생성하세요: 치명적, 경고, 정보.
14
15통합 단계는 모든 검사가 완료될 때까지 기다려야 합니다.
16그 이전의 모든 단계는 기다리면 안 됩니다.

프롬프트 자체에 구조가 내장되어 있음에 주목하세요: 병렬 작업이 명시적으로 언급되고, 하나의 실제 의존성(통합이 모든 검사를 기다리는 것)이 명시적으로 명명되었습니다. 에이전트가 그래프를 추론하도록 기대하는 것이 아니라, 직접 설명하고 있는 것입니다.

내부에서 일어나는 일 - 오케스트레이션의 간소화된 버전:

python
1import asyncio
2from anthropic import Anthropic
3
4client = Anthropic()
5
6async def audit_route_file(filepath: str) -> dict:
7 """하나의 노드. 다른 모든 라우트 파일과 독립적으로 실행됩니다."""
8 response = await client.messages.create(
9 model="claude-sonnet-5",
10 max_tokens=1000,
11 messages=[{
12 "role": "user",
13 "content": f"""이 라우트 파일을 감사하세요:
14 - 인증 미들웨어, 입력 검증,
15 속도 제한, 오류 처리
16
17 파일: {filepath}
18
19 JSON 반환: {{"file": "", "issues": [], "severity": ""}}"""
20 }]
21 )
22 return {"file": filepath, "result": response.content[0].text}
23
24async def consolidate(results: list[dict]) -> str:
25 """하나의 실제 엣지 -- 모든 감사 노드가 완료될 때까지 기다립니다."""
26 response = await client.messages.create(
27 model="claude-opus-4-8",
28 max_tokens=2000,
29 messages=[{
30 "role": "user",
31 "content": f"""다음 {len(results)}개의 라우트 감사 결과를
32 심각도별로 그룹화된 하나의 보고서로 통합하세요:
33
34 {results}"""
35 }]
36 )
37 return response.content[0].text
38
39async def run_graph(route_files: list[str]):
40 # 확산 -- 모든 독립 노드가 동시에 실행됩니다
41 audit_tasks = [audit_route_file(f) for f in route_files]
42 results = await asyncio.gather(*audit_tasks)
43
44 # 수렴 -- 실제 의존성이 있는 하나의 노드
45 report = await consolidate(results)
46 return report
47
48# 40개의 라우트 파일, 하나의 프롬프트, 하나의 병렬 패스
49results = asyncio.run(run_graph([
50 f"routes/{f}.py" for f in ["auth", "users", "billing", "orders"]
51 # ...36개 더

각각 약 8초가 소요되는 40개의 순차적 API 호출은 5분이 넘습니다. 동일한 40개 호출을 병렬로 확산하면: 15초 미만으로, 가장 느린 단일 파일에 의해 제한되며 모든 파일의 합계에 의해 제한되지 않습니다.

4장 - 그래프가 실제로 실패하는 지점

그래프 엔지니어링은 세 가지 예측 가능한 지점에서 실패합니다. 발생하기 전에 알아두세요.

컨텍스트 붕괴. 1,000개의 노드를 확산하고 모든 1,000개의 출력을 하나의 통합 단계에 공급하려고 하면, 합성조차 시작하기 전에 컨텍스트 창을 초과하게 됩니다. 수정 방법: 수렴을 계층화하세요. 노드를 20-50개씩 배치로 그룹화하고, 각 배치를 요약한 다음, 원시 출력이 아닌 요약을 통합하세요.

python
1async def layered_consolidate(results: list[dict], batch_size: int = 30):
2 """계층별 수렴 -- 대규모 원시 출력을 절대 합성하지 않습니다."""
3 batches = [results[i:i+batch_size]
4 for i in range(0, len(results), batch_size)]
5
6 batch_summaries = await asyncio.gather(*[
7 summarize_batch(batch) for batch in batches
8 ])
9
10 # 최종 통합은 1,000개의 원시 결과가 아닌 요약을 대상으로 작업합니다
11 return await consolidate(batch_summaries)

거짓 독립성. 두 노드의 프롬프트가 서로를 참조하지 않기 때문에 독립적이라고 가정하겠지만, 두 노드가 모두 동일한 파일에 쓰거나 동일한 속도 제한 API를 호출할 수 있습니다. 이것은 숨겨진 엣지입니다. 수정 방법: 공유 데이터뿐만 아니라 공유 리소스도 감사하세요. 쓰기 충돌이 있는 두 노드는 데이터 의존성이 전혀 없더라도 엣지가 필요합니다.

무음 노드 실패. 체인에서는 하나의 실패가 모든 것을 중단시킵니다 - 짜증나지만 명백합니다. 그래프에서는 200개 중 하나의 실패한 노드가 완료된 것처럼 보이는 보고서 속으로 사라질 수 있습니다. 수정 방법: 모든 수렴 단계는 합성하기 전에 노드 수를 예상 수와 비교하고, 부분 데이터로 조용히 작업하는 대신 격차를 명시적으로 표시합니다.

python
1async def safe_consolidate(results: list[dict], expected_count: int):
2 if len(results) < expected_count:
3 missing = expected_count - len(results)
4 print(f"경고: {missing}개 노드가 무음 실패했습니다. "
5 f"보고서가 불완전할 수 있습니다.")
6 return await consolidate(results)

5장 - 실제 규모의 시스템으로 확장

wast3 - inline image

패턴이 40개 노드에서 작동한다면, 수백 개로 확장하는 것은 2장부터 그래프를 올바르게 구축했다면 재설계가 아닌 설정 변경입니다.

전체 프로덕션 구조:

text
1 오케스트레이터
2 |
3 +--------+-------+-------+--------+
4 v v v v v
5 노드 1 노드 2 노드 3 ... 노드 N
6 (병렬, 노드 간 엣지 없음)
7 | | | |
8 +--------+-------+-------+-------+
9 v
10 배치 요약 <- 계층별 수렴
11 (30개씩 그룹화)
12 v
13 최종 보고서 <- 하나의 진정한 엣지

오케스트레이터의 유일한 임무: 작업을 노드로 분해하고, 실제 엣지를 식별하고, 전달하는 것입니다. 자체적으로는 아무 작업도 수행하지 않습니다 - 그래프를 그릴 뿐입니다.

python
1async def orchestrate(task: str, resources: list[str]):
2 """
3 오케스트레이터 노드 -- 분해만 하고 실행하지 않습니다.
4 """
5 plan = await client.messages.create(
6 model="claude-opus-4-8",
7 max_tokens=2000,
8 messages=[{
9 "role": "user",
10 "content": f"""작업: {task}
11 사용 가능한 리소스: {resources}
12
13 그래프로 분해:
14 - 각 독립 노드 나열 (공유 엣지 없음)
15 - 노드 간 실제 의존성 나열
16 - 개수가 50개를 초과하는 경우 노드를 수렴 배치로 그룹화
17
18 다음을 포함하는 JSON 반환: nodes, edges, batch_groups"""
19 }]
20 )
21
22 graph = parse_plan(plan.content[0].text)
23
24 # 독립 노드를 병렬로 실행
25 node_results = await asyncio.gather(*[
26 execute_node(n) for n in graph["nodes"] if not n["depends_on"]
27 ])
28
29 # 그런 다음 실제 엣지만 존중하여 의존 노드 실행
30 final = await execute_dependent_chain(graph["edges"], node_results)
31
32 return final

이것이 그래프 엔지니어링이 나타내는 실제 변화입니다: 모든 단계를 직접 작성하는 사람에서 의존성 구조를 설계하는 사람으로 바뀌는 것입니다. 에이전트가 노드를 채웁니다. 여러분은 엣지를 소유합니다.

선 대신 그래프로 생각할 때 달라지는 것

40단계의 선형 에이전트는 40개의 순차적 실패 지점과 가장 느린 단일 단계의 40배 지연 시간을 가집니다.

동일한 40개의 작업 단위를 가진 그래프는 실제 의존성 수만큼의 병렬 실패 지점을 가지며(일반적으로 대부분의 워크플로우에서 3~5개), 지연 시간은 총 단계 수가 아닌 가장 느린 계층에 의해 제한됩니다.

이는 미미한 속도 향상이 아닙니다. 정확히 동일한 기본 작업을 실행하면서 5분이 걸리는 워크플로우와 15초가 걸리는 워크플로우의 차이입니다.

모델은 결코 병목이 아니었습니다. 여러분이 그린 선이 병목이었습니다.

이 글은 2026년 7월 기준 다중 에이전트 오케스트레이션 패턴에 대한 기술적 분석입니다. 코드 예제는 설명을 위한 것입니다 — 대규모 배포 전에 프로덕션 환경에 맞게 오류 처리, 속도 제한 및 재시도 로직을 조정하세요.

읽어주셔서 감사합니다.

YouMind에서 다시 만들기

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
크리에이터를 위해

당신의 Markdown을 깔끔한 𝕏 글로

직접 쓴 장문을 올릴 때 이미지, 표, 코드 블록을 𝕏에 맞게 정리하는 일은 번거롭습니다. YouMind는 전체 Markdown 초안을 깔끔하고 바로 게시할 수 있는 𝕏 글로 바꿔 줍니다.

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기