← Hub ← 09
10
Chapter 10 · Multi-Agent

하나가
다 짊어지지
않는다.

탐색은 search agent. 평가는 eval agent. 보고는 report agent.
오케스트레이터가 셋을 조율한다.
각자의 시스템 프롬프트는 작고 분명해진다.

·
단일 vs 멀티

큰 LLM 하나
작은 LLM 셋

구분
단일 에이전트
멀티 에이전트
시스템 프롬프트
모든 규칙이 하나에
역할별로 분리, 짧고 명확
흐름 제어
코드에 하드코딩
오케스트레이터가 동적 결정
확장
시스템 프롬프트에 추가
새 에이전트 함수 추가
병렬 처리
불가
가능
·
아키텍처

오케스트레이터 +
역할별 에이전트.

오케스트레이터 전체 흐름 관리, 다음 에이전트 결정 🔍 search execute_sql retrieve_from_rag ⚖️ eval execute_sql get_confluence 📨 report save_excel send_email

각 에이전트는 자기 역할에 필요한 툴만 가진다.
컨텍스트가 좁아지므로 LLM이 헷갈릴 일이 줄어든다.

·
Code · 툴 그룹화

에이전트마다
필요한 툴만.

from tools_specs import make_tools
all_tools = make_tools()

search_tools = [t for t in all_tools if t["function"]["name"] in
                ["execute_sql", "retrieve_from_rag"]]

eval_tools   = [t for t in all_tools if t["function"]["name"] in
                ["execute_sql", "get_confluence_page_content"]]

report_tools = [t for t in all_tools if t["function"]["name"] in
                ["save_excel", "send_email"]]

전체 카탈로그에서 역할에 맞는 부분만 잘라 전달.
각 에이전트는 자기 영역 바깥의 툴은 호출할 수 없다 — 부주의한 호출도 방지.

·
Code · 에이전트 = 래퍼

에이전트는
그냥 래퍼 함수.

def run_agent(system_prompt, user_message, tools, max_steps=10):
    # Chapter 8의 handle_request와 동일한 피드백 루프
    ...

def search_agent(result):
    output = run_agent(search_system_prompt,
                       f"목표: {result['goal']}",
                       search_tools)
    result["resume_ids"] = output
    return result

def eval_agent(result):
    output = run_agent(eval_system_prompt,
                       f"이력서: {result['resume_ids']}\n목표: {result['goal']}",
                       eval_tools)
    result["eval_result"] = output
    return result

def report_agent(result):
    run_agent(report_system_prompt,
              f"평가 결과: {result['eval_result']}",
              report_tools)
    result["report_done"] = True
    return result
·
Code · 오케스트레이터

매 단계,
다음 에이전트를 묻는다.

def orchestrator(result):
    status = f"""
목표: {result['goal']}
search 완료: {'완료' if result.get('resume_ids') else '미완료'}
eval 완료:   {'완료' if result.get('eval_result') else '미완료'}
report 완료: {'완료' if result.get('report_done') else '미완료'}"""

    message = generate_response([{"role": "system", "content": orchestrator_system_prompt},
                                  {"role": "user",   "content": status}])
    decision = json.loads(message.content)
    return decision.get("next_agent", "end")


AGENT_MAP = {"search": search_agent, "eval": eval_agent, "report": report_agent}

def run_multi_agent(goal, max_steps=10):
    result = {"goal": goal, ...}
    for _ in range(max_steps):
        next_agent = orchestrator(result)
        if next_agent == "end": break
        result = AGENT_MAP[next_agent](result)
    return result
·
심화 · Planning Orchestrator

오케스트레이터에도
Planning을.

매번 묻는 방식

완료 상태 보고 → 다음 결정 → 실행 → 완료 상태 → 결정 → ...
완료 횟수만큼 LLM 호출.

Planning 방식

처음 한 번 호출 → ["search", "eval", "report"] 계획 수립 → 순서대로 실행.
LLM 호출 1번으로 끝.

def planning_orchestrator(result):
    if not result.get("plan"):
        # 처음 한 번만 계획 수립
        result["plan"] = make_plan_for_agents(result["goal"])["plan"]
        result["current_step"] = 0

    i = result["current_step"]
    if i >= len(result["plan"]): return "end"
    result["current_step"] = i + 1
    return result["plan"][i]
·
강의 전체 정리

열 장의
한 줄 요약.

역할 부여

system prompt가 LLM의 기본 행동을 정의한다.

대화는 누적

history list로 매번 통째로 다시 전달.

툴 = 함수

LLM이 못 하는 일을 대신하는 일반 파이썬 함수.

결정 권한 이양

코드가 결정 → LLM이 결정. JSON → tool_calls API.

설계 패턴

피드백 루프(ReAct) → Planning → Multi-Agent. 복잡도에 따라.

·
완강

축하합니다.
여기까지
출발선입니다.

이 다음은 도메인.
채용 평가가 아니라 여러분의 업무에 같은 패턴을 적용해보세요.

더 깊이 — MCP · LangGraph · AutoGen · OpenAI Assistants API.
모두 이 열 장에서 본 패턴의 변주.

← Hub
처음으로 돌아가기