← Hub ← 08 10 →
09
Chapter 09 · Planning

실행 전에
전체 청사진
먼저 그린다.

피드백 루프는 매 스텝 결정.
Planning은 시작 전에 한 번 결정.
복잡한 목표일수록 후자가 길을 잃지 않는다.

·
피드백 루프 vs Planning

결정 시점이
다르다.

구분
피드백 루프 (Ch 8)
Planning (Ch 9)
방식
직전 결과 보고 다음 결정
전체 계획 후 순서대로
판단 시점
매 스텝마다
시작 전 한 번
LLM 호출
많음
절약
장점
실패 시 대안 즉시
계획 명시적, 추적 쉬움
단점
매번 결정 비용
중간 실패 시 재계획 필요
·
2단계 구조

먼저 계획,
그 다음 실행.

목표 입력 1단계 · make_plan() LLM이 전체 단계를 JSON으로 ["search","eval","report",…] 2단계 · execute_plan() 각 단계를 순서대로, 안에서는 피드백 루프
·
Code · make_plan

계획은
JSON 한 덩어리.

planning_system_prompt = """당신은 채용 업무 실행 계획을 수립합니다.

규칙:
1. 실행 전에 전체 계획을 먼저 완성.
2. 평가기준표는 하나만 조회.
3. 이력서 비교가 필요하면 모든 이력서를 먼저 조회 후 평가.

출력 형식 (JSON만):
{
    "goal": "목표 요약",
    "plan": [
        {"step": 1, "tool": "툴이름", "desc": "이 단계에서 할 일"},
        {"step": 2, "tool": "툴이름", "desc": "..."}
    ]
}"""

def make_plan(goal):
    messages = [{"role": "system", "content": planning_system_prompt},
                {"role": "user",   "content": goal}]
    message = generate_response(messages, tools)
    return json.loads(message.content)
·
Code · execute_plan

각 step 안에
작은 피드백 루프.

def execute_plan(plan, goal):
    history = [{"role": "user", "content": f"목표: {goal}\n계획: {plan['plan']}"}]

    for step_info in plan["plan"]:
        step_num = step_info["step"]
        desc     = step_info["desc"]

        for inner in range(5):
            messages = [{"role": "system", "content": execution_system_prompt}, *history,
                        {"role": "user", "content": f"Step {step_num}만: {desc}"}]
            message = generate_response(messages, tools, "auto")

            if not message.tool_calls:                # 이 step 완료
                history.append({"role": "assistant", "content": message.content})
                break

            for tc in message.tool_calls:
                result = TOOLS[tc.function.name](**json.loads(tc.function.arguments))
                history.append({"role": "tool", "tool_call_id": tc.id, ...})

외곽 for = 계획대로 step 순회. 내부 for = step 안에서 tool_calls 반복.
step 종료 조건 = tool_calls 없음.

·
사용법

목표만 던진다.
계획부터 실행까지 자동.

goal = """소프트웨어 엔지니어링 채용공고를 분석하고
내용에 맞는 후보자를 RAG로 찾아서 이력서를 조회해줘.
그리고 평가결과를 엑셀에 저장해줘."""

# 1. 계획
plan = make_plan(goal)
# → {
#     "goal": "...",
#     "plan": [
#       {"step":1, "tool":"execute_sql",       "desc":"채용공고 조회"},
#       {"step":2, "tool":"retrieve_from_rag", "desc":"후보자 검색"},
#       {"step":3, "tool":"execute_sql",       "desc":"이력서 조회"},
#       {"step":4, "tool":"save_excel",        "desc":"평가결과 저장"}
#     ]
#   }

# 2. 실행
history = execute_plan(plan, goal)
Chapter 09 · 마무리

한 LLM이
전부 짊어진다.
그게 한계다.

평가·저장·보고서·엑셀 컬럼명까지 모든 규칙이 한 시스템 프롬프트에 쌓인다.
컨텍스트가 길어질수록 LLM이 헷갈리기 시작한다.

Next · Chapter 10
멀티 에이전트