[HowTo] 방법론 · 작성: 2026-07-25 18:39:42 · 수정: 2026-07-26 19:49:15 · 조회 34
하나의 LLM 호출로 대부분의 일을 처리하다가, 어느 순간부터 "이 작업은 프롬프트 하나로는 안 되는데"라는 지점을 만나게 된다. 입력이 너무 길거나, 단계마다 요구되는 능력이 다르거나, 비용과 속도를 동시에 맞춰야 하거나. 이때 여러 모델 호출을 역할별로 나눠 엮는 걸 오케스트레이션이라고 부르는데, 이름은 거창해도 하는 일은 단순하다. 작업을 쪼개고, 각 조각에 맞는 모델을 붙이고, 결과를 다시 합치는 것뿐이다.
단일 모델 호출로 충분하면 오케스트레이션은 그냥 일을 더 복잡하게 만드는 것밖에 안 된다. 다음 상황일 때만 도입할 가치가 있다고 본다.
반대로 "프롬프트를 좀 더 잘 쓰면 되는" 문제를 오케스트레이션으로 풀려고 하면 복잡도만 늘고 남는 게 없다. 일단 단일 호출 + 괜찮은 프롬프트로 어디까지 되는지 먼저 확인하고, 거기서 막히는 지점을 찾아서 그 부분만 쪼개는 게 순서다.
제일 흔한 방식은 단계를 순서대로 태우는 것이다. 앞 단계의 출력이 다음 단계의 입력이 된다.
입력 → [정보 추출] → [초안 작성] → [검토/교정] → 출력
뒤 단계가 앞 단계 결과에 의존할 수밖에 없을 때(초안이 있어야 검토를 하지, 검토부터 먼저 할 수는 없다) 쓰는 구조다. 별거 아닌 것 같아도 각 단계 사이에 "형식이 맞는지, 필요한 값이 다 들어있는지" 확인하는 지점을 하나씩 심어두면 앞에서 난 오류가 뒤로 조용히 넘어가는 걸 초반에 잡을 수 있다.
들어오는 요청 성격이 제각각이면 먼저 분류부터 하고 갈라 보내는 방식도 직렬의 일종이다.
입력 → [분류기] ─┬─ 단순 질의 → 저비용 모델
├─ 복잡한 추론 → 고성능 모델
└─ 코드 관련 → 코드 전용 흐름
분류기 자체는 정확도보다 속도가 중요한 경우가 많아서 제일 가벼운 모델로 충분한 경우가 대부분이다.
직렬 구조의 단점은 명확하다. 전체 지연시간이 각 단계 소요시간의 합으로 늘어난다. 3단계를 거치면 3번의 호출을 순서대로 기다려야 한다.
단계들이 서로 독립적이라면 굳이 순서대로 기다릴 이유가 없다. 동시에 실행하고 나중에 합치면 된다. 흔히 두 가지 목적으로 쓴다.
하나는 큰 작업을 독립적인 조각으로 나눠 동시에 처리하는 것이다.
┌─ [1장 요약] ─┐
긴 문서 ─┼─ [2장 요약] ─┼─→ [합치기] → 출력
└─ [3장 요약] ─┘
이걸 직렬로 하면 3번의 요약 호출을 순서대로 기다려야 하지만, 병렬로 하면 셋 중 가장 오래 걸리는 하나의 시간만 기다리면 된다. 전체 소요시간을 줄이는 게 목적이지, 호출 횟수나 비용 자체가 줄어드는 건 아니다.
다른 하나는 같은 문제를 여러 번(또는 여러 모델로) 풀게 해서 답을 교차검증하는 것이다.
같은 입력 ─┬─ [모델/시도 A] ─┐
├─ [모델/시도 B] ─┼─→ [다수결 · 교차검증] → 최종 답
└─ [모델/시도 C] ─┘
취약점 탐지처럼 하나의 판단을 잘못 내렸을 때 대가가 큰 작업에서, 여러 개의 독립적인 판단을 모아 다수결로 정하면 개별 호출의 실수가 상쇄되는 효과가 있다.
구조를 아예 고정해두지 않고, 상위 모델이 요청을 보고 그때그때 필요한 하위 작업을 나눠서 맡기는 방식도 있다. 하위 작업들이 서로 의존하지 않는다면 병렬로, 순서가 필요하다면 직렬로 실행하면 된다.
요청 → 상위 모델(작업을 몇 개로 나눌지 결정)
├→ 하위 작업 1
├→ 하위 작업 2 (서로 독립적이면 동시에)
└→ 하위 작업 3
→ 상위 모델이 결과 종합
이 방식은 유연한 대신, 상위 모델이 작업을 얼마나 잘 쪼개느냐에 전체 결과가 달려 있어서 여기엔 상대적으로 강한 모델을 앉히는 게 안전하다.
| 구분 | 언제 쓰나 | 장점 | 주의할 점 |
|---|---|---|---|
| 직렬 | 뒷 단계가 앞 단계 결과 없이는 시작할 수 없을 때 | 단계마다 중간 검증을 넣기 쉽고 흐름을 추적하기 쉽다 | 전체 지연시간 = 각 단계 소요시간의 합 |
| 병렬 | 조각들이 서로 독립적일 때 (문서 분할, 같은 문제 여러 번 풀기) | 전체 소요시간이 가장 오래 걸리는 조각 하나 수준으로 줄어든다 | 결과를 합치는 로직이 따로 필요하고, 호출 수만큼 비용은 그대로 나간다(속도만 빨라짐) |
실무에서는 대개 섞어 쓴다. 큰 흐름은 직렬(추출 → 처리 → 합성)로 짜되, 그 안에서 서로 독립적인 하위 작업이 있으면 그 구간만 병렬로 처리하는 식이다.
마지막으로, 한 번에 완벽한 결과가 안 나오는 작업(글의 자연스러움, 번역, 코드 정확성 같은 것들)은 만드는 쪽과 확인하는 쪽을 분리해서 도는 루프가 도움이 된다.
생성 → 확인(기준 충족?) ─┬─ 미충족 → 피드백과 함께 재생성 (반복)
└─ 충족 → 출력
확인하는 쪽이 피드백을 구체적으로 줄수록 몇 번 안에 수렴한다.
패턴 자체는 위에서 본 것처럼 단순하다. 그래서 처음 한두 단계짜리 파이프라인이라면 그냥 직접 짜는 게 제일 빠르다. API 호출을 순서대로 걸고 사이사이에 파싱과 검증 코드만 넣으면 되니, 별도 프레임워크가 끼어들 자리가 별로 없다.
단계가 늘어나고 재시도·병렬 실행·중간 상태 저장 같은 게 반복해서 필요해지는 시점부터는 매번 그 배관을 손으로 다시 까는 게 손해다. 이럴 때 쓰는 도구들은 대충 세 갈래로 나뉜다.
LLM 애플리케이션 프레임워크는 프롬프트 구성, 모델 호출, 단계 엮기를 하나의 라이브러리 안에서 해결하려는 쪽이다. LangChain이 제일 먼저 자리잡은 케이스인데, 프롬프트 템플릿·모델 호출·파서를 체인으로 이어 붙이는 걸 표준화해줘서 직렬 파이프라인을 빠르게 짤 때 여전히 많이 쓴다. 다만 체인이 길어지고 분기·재시도·중간 상태 저장이 필요해지면 체인 하나로는 표현이 뻑뻑해지는데, 이 지점을 보완하려고 같은 팀이 내놓은 게 LangGraph다. 각 단계를 노드로, 흐름을 엣지로 표현해서 분기와 상태를 그래프 형태로 다룬다. CrewAI는 역할(role) 기반으로 에이전트 팀을 짜는 추상화가 직관적이라 빠르게 프로토타입을 돌려볼 때 특히 많이 쓰인다. AutoGen(AG2)도 같은 계열이었는데, 이제는 Microsoft가 Semantic Kernel과 합쳐 내놓은 Microsoft Agent Framework 쪽으로 무게중심이 넘어갔다 — AutoGen 자체는 버그·보안 패치만 받는 유지보수 모드라, 신규로 시작한다면 굳이 AutoGen보다는 이쪽을 먼저 보는 게 낫다. 이런 프레임워크들은 프롬프트·모델 호출과 밀접하게 엮여 있어서 오케스트레이터 자체를 LLM 애플리케이션 코드 안에 넣고 싶을 때 잘 맞는다. 다만 프레임워크가 내부적으로 뭘 하는지 추상화 뒤에 숨어 있어서, 디버깅할 때 프레임워크 소스까지 같이 뒤져야 하는 경우가 종종 생긴다.
에이전트-도구 호출에 특화된 SDK는 Claude Agent SDK나 OpenAI의 Agents SDK처럼, 모델이 스스로 도구를 고르고 다음 행동을 정하는 자율적인 루프를 만드는 데 초점이 맞춰져 있다. 위에서 말한 "상위 모델이 그때그때 하위 작업을 나눠 맡기는" 유연한 구조를 짤 때 이쪽이 더 자연스럽다. 대신 흐름을 미리 고정해두는 단순한 직렬·병렬 파이프라인에는 오히려 과할 수 있다.
범용 워크플로 엔진은 애초에 LLM을 염두에 두고 만들어진 게 아니다. Temporal, Airflow, Prefect 같은 도구는 "오래 걸리는 작업을 안정적으로 재시도하고 상태를 남긴다"는 문제를 LLM이 나오기 훨씬 전부터 풀어왔다. 파이프라인 하나가 몇 분, 몇 시간씩 걸리거나 중간에 서버가 죽어도 이어서 실행돼야 할 만큼의 신뢰성이 필요하면 이쪽이 낫다. LLM 호출은 그냥 워크플로 안의 작업 하나로 끼워 넣으면 된다.
빠르게 프로토타입만 확인해보고 싶다면 n8n이나 Make 같은 노코드 도구로 직렬·병렬 흐름을 그림으로 그려서 며칠 안에 돌려볼 수도 있다. 다만 로직이 복잡해지고 나면 UI 안에서 관리하는 게 오히려 코드보다 불편해지는 시점이 온다.
어떤 도구를 고르든 위에서 말한 원칙 — 단계 사이 데이터 전달 방식, 검증·재시도, 로깅, 반복 횟수 제한 — 은 그대로 적용된다. 도구는 이 배관을 얼마나 대신 깔아주느냐의 차이일 뿐, 무엇을 얼마나 쪼갤지에 대한 판단을 대신해주지는 않는다.
오케스트레이션을 하는 실질적인 이유 중 하나가 이거다 — 모든 단계에 같은 모델을 쓸 필요가 없다는 것.
| 상황 | 추천 모델 성향 | 이유 |
|---|---|---|
| 분류 / 라우팅 / 짧은 추출 | 가장 가볍고 빠른 모델 | 호출은 잦고 난이도는 낮아서, 무거운 모델을 쓰면 지연시간만 늘어난다 |
| 작업을 나누는 상위 판단 (오케스트레이터 역할) | 추론이 강한 모델 | 여기서 틀리면 하위 단계 전부가 잘못된 방향으로 간다 |
| 코드 생성 / 리팩토링 | 코드에 강한 모델 + 실행·테스트 검증 단계 | 그럴듯하게 짜는 것과 실제로 동작하는 건 다른 문제다 |
| 긴 문서 요약 / 검색 기반 응답 | 긴 입력에서 안정적인 모델 | 문서가 모델 한계보다 크면 분할(병렬) 처리부터 고려한다 |
| 결과 검증 / 평가 역할 | 생성 쪽과 같거나 그 이상 수준의 모델 | 약한 모델이 검증하면 검증 자체가 부실해진다 |
| 비용이 제일 중요할 때 | 저가 모델 우선 시도 → 애매할 때만 상위 모델로 넘기기 | 트래픽 대부분이 쉬운 요청이면 이것만으로 비용이 크게 줄어든다 |
결국 오케스트레이션에서 중요한 건 "여러 모델을 쓴다"는 기술 자체가 아니라 작업을 어떻게 쪼갤지, 그 조각마다 직렬로 이어야 할지 병렬로 흩어도 될지, 그리고 어떤 모델이 필요한지를 먼저 그려보는 것이다. 구조는 문제 생김새에 맞춰 고르면 되고, 모델은 "여기서 틀리면 얼마나 치명적인가"와 "여기가 얼마나 자주 불리는가" 두 가지만 놓고 저울질해도 대체로 답이 나온다. 그리고 단일 호출로 되는 문제까지 굳이 쪼갤 필요는 없다는 것도 계속 기억할 만하다.