본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 5장: 계획 수립 패턴 - 복잡한 작업의 분해와 실행
2026년 1월 25일·AI / ML·

5장: 계획 수립 패턴 - 복잡한 작업의 분해와 실행

Plan-and-Execute 아키텍처의 원리와 구현, 적응적 재계획 전략, 그리고 계획 수립 패턴이 에이전트 성능에 미치는 영향을 다룹니다.

16분903자6개 섹션
ai-agentllmarchitectureorchestration
공유
ai-agent-patterns5 / 10
12345678910
이전4장: 리플렉션 패턴 - 자기 평가와 반복 개선다음6장: 멀티 에이전트 패턴 - 협업과 조율의 아키텍처

계획이 필요한 이유

ReAct 패턴은 한 번에 한 단계씩 추론하고 행동합니다. 이 방식은 간단한 작업에 효과적이지만, 많은 단계를 거쳐야 하는 복잡한 작업에서는 한계를 보입니다. 중간에 방향을 잃거나, 비효율적인 경로로 진행하거나, 이미 달성한 것을 다시 시도하는 문제가 발생할 수 있습니다.

계획 수립(Planning) 패턴은 이 문제를 해결합니다. 먼저 전체 작업을 조감하여 실행 계획을 세우고, 그 계획에 따라 체계적으로 단계를 실행합니다. 벤치마크 연구에 따르면 Plan-and-Execute 아키텍처는 순차적 ReAct 대비 최대 92%의 작업 완료율과 3.6배의 속도 향상을 달성할 수 있습니다.

Plan-and-Execute 아키텍처

Plan-and-Execute는 두 개의 구분된 단계로 작동합니다.

Plan (계획): 전체 작업을 분석하고, 하위 단계들의 순서와 의존 관계를 정의합니다. 이 단계에서는 도구를 사용하지 않고 순수한 추론만 수행합니다.

Execute (실행): 계획에 따라 각 단계를 순서대로 실행합니다. 각 단계는 ReAct 패턴으로 독립적으로 수행될 수 있습니다.

기본 구현

plan_and_execute.py
python
import anthropic
import json
 
client = anthropic.Anthropic()
 
def create_plan(task: str) -> list[dict]:
    """작업을 분석하고 실행 계획을 생성합니다."""
    plan_prompt = f"""다음 작업을 수행하기 위한 단계별 계획을 수립하십시오.
 
작업: {task}
 
각 단계는 다음 정보를 포함해야 합니다:
- step: 단계 번호
- description: 수행할 작업 설명
- dependencies: 이 단계가 의존하는 이전 단계 번호 목록
- tools_needed: 필요한 도구 목록
 
JSON 배열로 응답하십시오.
예시:
[
  {{"step": 1, "description": "...", "dependencies": [], "tools_needed": ["search_web"]}},
  {{"step": 2, "description": "...", "dependencies": [1], "tools_needed": ["calculator"]}}
]"""
 
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=2048,
        messages=[{"role": "user", "content": plan_prompt}],
    )
 
    try:
        return json.loads(response.content[0].text)
    except json.JSONDecodeError:
        return [{"step": 1, "description": task, "dependencies": [], "tools_needed": []}]
 
def execute_step(
    step: dict,
    previous_results: dict[int, str],
    tools: list[dict]
) -> str:
    """계획의 한 단계를 실행합니다."""
    # 의존하는 단계의 결과를 컨텍스트로 구성
    context = ""
    for dep in step["dependencies"]:
        if dep in previous_results:
            context += f"\n[단계 {dep}의 결과]: {previous_results[dep]}"
 
    exec_prompt = f"""다음 작업을 수행하십시오.
 
작업: {step['description']}
 
{f'이전 단계의 결과:{context}' if context else ''}
 
제공된 도구를 활용하여 작업을 완료하십시오."""
 
    messages = [{"role": "user", "content": exec_prompt}]
 
    # ReAct 루프로 단계 실행
    for _ in range(5):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            tools=tools,
            messages=messages,
        )
 
        if response.stop_reason == "end_turn":
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text
            return "단계 완료"
 
        # 도구 호출 처리
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })
 
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})
 
    return "최대 반복 횟수 도달"
 
def plan_and_execute(task: str, tools: list[dict]) -> str:
    """Plan-and-Execute를 실행합니다."""
    # 1. 계획 수립
    plan = create_plan(task)
    print(f"[계획] {len(plan)}개 단계로 구성")
    for step in plan:
        print(f"  단계 {step['step']}: {step['description']}")
 
    # 2. 단계별 실행
    results = {}
    for step in plan:
        print(f"\n[실행] 단계 {step['step']}: {step['description']}")
        result = execute_step(step, results, tools)
        results[step["step"]] = result
        print(f"[결과] {result[:200]}...")
 
    # 3. 최종 종합
    synthesis_prompt = f"""다음 작업의 각 단계별 결과를 종합하여 최종 답변을 작성하십시오.
 
원래 작업: {task}
 
단계별 결과:
{json.dumps(results, ensure_ascii=False, indent=2)}
 
모든 결과를 통합하여 포괄적인 최종 답변을 제공하십시오."""
 
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        messages=[{"role": "user", "content": synthesis_prompt}],
    )
    return response.content[0].text

적응적 재계획

실행 중에 예상치 못한 상황이 발생하면 원래 계획을 수정해야 합니다. 이를 적응적 재계획(Adaptive Replanning)이라고 합니다.

adaptive_replanning.py
python
class AdaptivePlanner:
    def __init__(self):
        self.client = anthropic.Anthropic()
        self.plan = []
        self.results = {}
        self.current_step = 0
 
    def should_replan(self, step_result: str, step: dict) -> bool:
        """재계획이 필요한지 판단합니다."""
        check_prompt = f"""다음 단계의 실행 결과를 분석하십시오.
 
계획된 작업: {step['description']}
실행 결과: {step_result}
 
다음 중 해당하는 상황이 있으면 "REPLAN"을, 정상이면 "CONTINUE"를 응답하십시오:
1. 단계가 실패했거나 예상과 다른 결과를 반환했다
2. 새로운 정보가 발견되어 이후 계획을 수정해야 한다
3. 원래 가정이 잘못되었음이 드러났다
 
한 단어로만 응답하십시오: REPLAN 또는 CONTINUE"""
 
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=10,
            messages=[{"role": "user", "content": check_prompt}],
        )
        return "REPLAN" in response.content[0].text.upper()
 
    def replan(self, task: str, completed_results: dict) -> list[dict]:
        """지금까지의 결과를 바탕으로 남은 계획을 재수립합니다."""
        replan_prompt = f"""원래 작업과 지금까지의 결과를 바탕으로 남은 단계를 재계획하십시오.
 
원래 작업: {task}
 
완료된 단계와 결과:
{json.dumps(completed_results, ensure_ascii=False, indent=2)}
 
남은 작업을 완료하기 위한 새로운 단계들을 JSON 배열로 제시하십시오.
이미 완료된 결과를 최대한 활용하십시오."""
 
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=2048,
            messages=[{"role": "user", "content": replan_prompt}],
        )
        try:
            return json.loads(response.content[0].text)
        except json.JSONDecodeError:
            return []
 
    def run(self, task: str, tools: list[dict]) -> str:
        """적응적 재계획이 포함된 실행 루프입니다."""
        self.plan = create_plan(task)
        replan_count = 0
        max_replans = 3
 
        i = 0
        while i < len(self.plan):
            step = self.plan[i]
            print(f"\n[실행] 단계 {step['step']}: {step['description']}")
 
            result = execute_step(step, self.results, tools)
            self.results[step["step"]] = result
 
            # 재계획 필요 여부 확인
            if self.should_replan(result, step) and replan_count < max_replans:
                print(f"[재계획] 실행 결과에 따라 계획을 수정합니다.")
                remaining = self.replan(task, self.results)
                if remaining:
                    self.plan = list(self.plan[:i + 1]) + remaining
                    replan_count += 1
 
            i += 1
 
        return self.synthesize(task)
 
    def synthesize(self, task: str) -> str:
        """모든 결과를 종합합니다."""
        prompt = f"""작업: {task}\n결과: {json.dumps(self.results, ensure_ascii=False)}
모든 결과를 종합하여 최종 답변을 작성하십시오."""
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            messages=[{"role": "user", "content": prompt}],
        )
        return response.content[0].text
Warning

재계획 횟수에 반드시 상한을 두어야 합니다. 무한 재계획은 비용을 급격히 증가시키고, 에이전트가 원래 목표에서 벗어나게 할 수 있습니다. 실무에서는 2-3회가 적절합니다.

계획 표현 방식

계획을 어떤 형식으로 표현하느냐에 따라 에이전트의 실행 효율이 달라집니다.

선형 계획

단계들이 순서대로 실행되는 가장 단순한 형식입니다.

linear_plan.json
json
[
  {"step": 1, "description": "요구 사항 분석", "dependencies": []},
  {"step": 2, "description": "데이터 수집", "dependencies": [1]},
  {"step": 3, "description": "분석 실행", "dependencies": [2]},
  {"step": 4, "description": "보고서 작성", "dependencies": [3]}
]

DAG 기반 계획

의존 관계를 방향성 비순환 그래프(DAG)로 표현하여, 독립적인 단계를 병렬로 실행할 수 있습니다.

dag_plan_executor.py
python
from collections import defaultdict
import asyncio
 
class DAGPlanExecutor:
    def __init__(self, plan: list[dict], tools: list[dict]):
        self.plan = {step["step"]: step for step in plan}
        self.tools = tools
        self.results = {}
        self.completed = set()
 
    def get_ready_steps(self) -> list[dict]:
        """의존성이 모두 충족된 실행 가능한 단계를 반환합니다."""
        ready = []
        for step_id, step in self.plan.items():
            if step_id in self.completed:
                continue
            deps = set(step.get("dependencies", []))
            if deps.issubset(self.completed):
                ready.append(step)
        return ready
 
    async def execute_parallel(self) -> dict:
        """DAG 구조에 따라 병렬 실행합니다."""
        while len(self.completed) < len(self.plan):
            ready = self.get_ready_steps()
            if not ready:
                break
 
            print(f"[병렬 실행] {len(ready)}개 단계 동시 실행")
            tasks = [
                asyncio.to_thread(execute_step, step, self.results, self.tools)
                for step in ready
            ]
            results = await asyncio.gather(*tasks)
 
            for step, result in zip(ready, results):
                self.results[step["step"]] = result
                self.completed.add(step["step"])
                print(f"  단계 {step['step']} 완료")
 
        return self.results
Info

DAG 기반 실행은 독립적인 단계를 병렬로 처리할 수 있어 전체 실행 시간을 크게 줄입니다. 위 예시에서 단계 2, 3, 4는 동시에 실행되므로, 선형 실행 대비 약 3배 빠릅니다.

계층적 계획

큰 작업을 먼저 고수준 단계로 나누고, 각 단계를 다시 세부 단계로 분해하는 방식입니다.

hierarchical_planning.py
python
def create_hierarchical_plan(task: str, depth: int = 2) -> dict:
    """계층적 계획을 생성합니다."""
    prompt = f"""다음 작업을 계층적으로 분해하십시오.
 
작업: {task}
 
1단계: 3-5개의 고수준 단계로 나누십시오.
2단계: 각 고수준 단계를 2-4개의 세부 단계로 나누십시오.
 
JSON으로 응답:
{{
  "task": "원래 작업",
  "phases": [
    {{
      "name": "고수준 단계 이름",
      "description": "설명",
      "substeps": [
        {{"description": "세부 단계 1"}},
        {{"description": "세부 단계 2"}}
      ]
    }}
  ]
}}"""
 
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        messages=[{"role": "user", "content": prompt}],
    )
    return json.loads(response.content[0].text)

계획 수립의 실전 고려사항

계획 검증

생성된 계획이 실행 가능한지 사전에 검증합니다.

plan_validation.py
python
def validate_plan(plan: list[dict], available_tools: list[str]) -> list[str]:
    """계획의 유효성을 검증하고 문제점을 반환합니다."""
    issues = []
 
    # 1. 의존성 순환 검사
    visited = set()
    def has_cycle(step_id, path):
        if step_id in path:
            return True
        path.add(step_id)
        step = next((s for s in plan if s["step"] == step_id), None)
        if step:
            for dep in step.get("dependencies", []):
                if has_cycle(dep, path.copy()):
                    return True
        return False
 
    for step in plan:
        if has_cycle(step["step"], set()):
            issues.append(f"단계 {step['step']}에 순환 의존성이 있습니다.")
 
    # 2. 존재하지 않는 의존성 검사
    step_ids = {s["step"] for s in plan}
    for step in plan:
        for dep in step.get("dependencies", []):
            if dep not in step_ids:
                issues.append(
                    f"단계 {step['step']}이 존재하지 않는 "
                    f"단계 {dep}에 의존합니다."
                )
 
    # 3. 필요한 도구 가용성 검사
    for step in plan:
        for tool in step.get("tools_needed", []):
            if tool not in available_tools:
                issues.append(
                    f"단계 {step['step']}에 필요한 도구 "
                    f"'{tool}'을 사용할 수 없습니다."
                )
 
    return issues

언제 계획을 사용하고 언제 사용하지 않을 것인가

상황권장 방식
1-2단계로 해결되는 단순 작업ReAct (계획 불필요)
3-5단계의 중간 복잡도 작업선형 계획
독립적 하위 작업이 많은 경우DAG 기반 병렬 계획
매우 복잡한 프로젝트 수준 작업계층적 계획
실행 중 조건이 자주 변하는 작업적응적 재계획

다음 장 미리보기

6장에서는 멀티 에이전트 패턴을 다룹니다. 하나의 에이전트가 아닌 여러 전문화된 에이전트가 협업하여 복잡한 작업을 수행하는 방법을 살펴보겠습니다. 감독자(Supervisor) 패턴, 토론(Debate) 패턴, 그리고 에이전트 간 통신 프로토콜을 집중적으로 다룹니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#ai-agent#llm#architecture#orchestration

관련 글

AI / ML

6장: 멀티 에이전트 패턴 - 협업과 조율의 아키텍처

여러 전문화된 에이전트가 협업하는 멀티 에이전트 시스템의 설계 패턴, 감독자/토론/파이프라인 아키텍처를 코드와 함께 다룹니다.

2026년 1월 27일·15분
AI / ML

4장: 리플렉션 패턴 - 자기 평가와 반복 개선

에이전트가 자신의 출력을 평가하고 반복적으로 개선하는 리플렉션 패턴의 원리, 구현 방법, 실전 활용 전략을 다룹니다.

2026년 1월 23일·18분
AI / ML

7장: 메모리 시스템 - 에이전트의 기억과 학습

AI 에이전트의 단기, 장기 메모리 아키텍처를 이해하고, RAG 통합과 대화 히스토리 관리 전략을 코드로 구현합니다.

2026년 1월 29일·16분
이전 글4장: 리플렉션 패턴 - 자기 평가와 반복 개선
다음 글6장: 멀티 에이전트 패턴 - 협업과 조율의 아키텍처

댓글

목차

약 16분 남음
  • 계획이 필요한 이유
  • Plan-and-Execute 아키텍처
    • 기본 구현
  • 적응적 재계획
  • 계획 표현 방식
    • 선형 계획
    • DAG 기반 계획
    • 계층적 계획
  • 계획 수립의 실전 고려사항
    • 계획 검증
    • 언제 계획을 사용하고 언제 사용하지 않을 것인가
  • 다음 장 미리보기