같은 부품(이름 붙은 에이전트와 handoff 도구)으로 만든 Supervisor, 계층(Hierarchical), Network(Swarm) 세 구조를 나란히 놓고, 각 파일이 실제로 던지는 질문이 어떤 경로로 흐르는지 탭을 바꿔 가며 넘겨 봅니다. 차이는 누가 다음 차례를 정하는가와 일을 마친 에이전트가 어디로 돌아가는가에 있습니다.
세 파일은 질문도 에이전트도 다르지만, 모두 '사내 데이터나 인사 정보를 묻는 질문 하나'가 들어와 담당에게 넘어가는 같은 성격의 흐름입니다. 모델의 판단에 따라 실제 경로는 달라질 수 있으므로 아래는 코드와 프롬프트가 의도한 경로입니다.
| 구조 | 파일의 질문 | 의도한 경로 | 바깥 대화에 남는 것 |
|---|---|---|---|
| Supervisor | 클라우드운영팀 자산 목록 알려줘 | supervisor → data_agent → supervisor → END | 서브 에이전트의 마지막 답 + 복귀 메시지 한 쌍. 안쪽 도구 호출은 버려짐 |
| Hierarchical | 클라우드운영팀 GPU 자산을 조회해서 분석하고 보고서로 만들어줘 | top → research_team(Lead → data_agent → Lead) → top → analysis_team(Lead → analyst_agent → Lead → report_agent → Lead) → top → END | 층마다 마지막 메시지만. 최상위에는 팀 Lead 의 요약이 닿음 |
| Network | 이PC 사원의 네트워크 방화벽신청할때 담당자 연차는 몇개야? | (START 분기) → reception → 넘겨받은 담당 → 거기서 END. 이 질문은 두 담당에 걸쳐 있어 갈래가 생김 | 모든 에이전트의 메시지가 그대로. 걸러 내는 단계가 없음 |
| 견줄 점 | Supervisor | Hierarchical | Network (Swarm) |
|---|---|---|---|
| 만드는 함수 | create_supervisor | create_supervisor 를 겹쳐 쓰고 팀은 compile(name=...) | create_swarm + create_handoff_tool |
| 다음 차례를 정하는 주체 | 관리자 한 명 | 층마다의 관리자 | 지금 일하고 있는 에이전트 |
| handoff 도구를 가진 쪽 | 관리자만 (자동 생성) | 각 층의 관리자 (자동 생성) | 모든 에이전트 (직접 만들어 넣음) |
| 배분 기준이 적힌 곳 | 관리자의 프롬프트 한 곳 | 층별 관리자의 프롬프트 | handoff 도구의 설명글과 각 에이전트의 프롬프트 |
| 에이전트가 일을 마치면 | 항상 관리자에게 복귀 | 자기 팀 Lead에게, 다시 그 위로 복귀 | 넘기지 않으면 그 자리에서 종료. 그 에이전트가 active_agent 로 남음 |
| 에이전트끼리 직접 연결 | 없음 | 없음 | 있음 |
| 여러 결과의 종합 | 관리자가 마지막에 종합 | 층마다 종합 | 종합하는 주체가 따로 없음 |
| 다음 질문의 출발점 | 매번 관리자 | 매번 최상위 관리자 | 직전에 일한 에이전트 (체크포인터 필요) |
| 모델 호출 수 | 배분과 종합만큼 추가 | 층마다 추가. 가장 많음 | 관리자의 배분·종합 호출이 없음. 접수 담당이 처음 넘기는 호출은 듭니다 |
| 조심할 점 | 관리자가 빈칸을 지어내거나 동시 handoff 를 시도 | 느리고 비싸며, 층을 오를 때마다 정보가 요약됨 | 복합 요구에 약하고, 누가 무엇을 했는지 추적하기 어려움 |
도구가 서너 개이고 성격이 같으며, 같은 질문에 도구 선택이 흔들리지 않는다면 나누지 않습니다. 나누는 순간 배분과 종합의 모델 호출이 붙습니다.
한 질문이 여러 담당에 걸치고, 결과를 하나의 답으로 종합해야 할 때의 기본 선택입니다. 흐름이 한곳을 지나므로 기록하고 검증하기 쉽습니다.
서브 에이전트가 많아져 관리자 한 명의 배분 기준이 길어지고 배분 자체가 흔들릴 때 씁니다. 만능 에이전트를 나눈 것과 같은 이유로 관리자를 나누는 것입니다.
상담 창구처럼 한 번에 한 담당과 길게 대화하고, 주제가 바뀔 때만 다른 담당으로 넘어가는 서비스에 어울립니다. 여러 담당의 결과를 합쳐야 하는 요청에는 맞지 않습니다.
lifespan 의 yield 앞에서 await build_app() 으로 final_scenario 의 그래프를 만들어 전역 graph_app 에 담습니다. 요청마다 MCP 서버에 접속하고 Supervisor 를 조립하지 않기 위해서입니다.Query(Pydantic, question 하나)로 검사하고, ainvoke 로 그래프를 끝까지 실행한 뒤 마지막 메시지의 글만 {"answer": ...} 로 돌려줍니다.체크포인터와 thread_id 가 없어 요청마다 새 대화입니다. 모든 모델 호출이 끝나야 응답이 나가고, 인증·시간 제한·오류 처리가 없습니다. from final_scenario import ... 가 그 파일 맨 바깥 코드를 함께 실행하고 경로가 상대 경로라 day6_practice 폴더에서 띄워야 합니다.
compile(name=...) 이 필수입니다. create_supervisor 는 받은 그래프의 이름이 없거나 기본값(LangGraph)이면 ValueError 를 냅니다. hierarchical_demo.py 46줄 주석이 이 점을 가리킵니다. 이름이 곧 상위 관리자의 handoff 도구 이름(transfer_to_research_team)이 됩니다.supervisor 입니다. create_supervisor 의 supervisor_name 기본값 때문에 최상위와 두 팀 Lead 가 모두 supervisor 라는 이름의 노드를 갖고, 팀이 올려 보내는 마지막 메시지에도 name='supervisor' 가 붙습니다. 복귀 메시지 한 쌍에 붙는 이름은 팀 이름(research_team)입니다. 출력을 읽을 때 경로와 함께 봐야 하는 이유입니다.recursion_limit 을 40으로 올렸고, 조회한 원본 데이터는 두 번 요약된 뒤 최상위에 닿습니다.ClientError 처리(84~89줄) 두 겹으로 대비했습니다. 다만 reception 의 프롬프트에는 이 문장이 없습니다.InMemorySaver 는 프로그램이 끝나면 사라지므로 새로 실행한 첫 질문은 언제나 reception 에서 출발합니다. active_agent 가 이어지는 것을 보려면 같은 config 로 질문을 이어서 넣어야 합니다.create_swarm 은 에이전트를 감싸지 않고 그대로 노드로 넣으므로, 각 에이전트가 만든 도구 호출과 결과가 공유 messages 에 모두 남습니다. 넘겨받은 에이전트는 앞 담당의 작업 내역까지 읽는 대신, 대화가 길어질수록 입력 토큰이 늘어납니다.day6_practice/supervisor_assembled.pyspecialist_agents.py 의 서브 에이전트 3종을 Supervisor 로 묶고, 호출 순서를 기록하는 run() 으로 질문 하나를 처리합니다.day6_practice/hierarchical_demo.py조회팀과 분석팀을 각각 Supervisor 로 묶어 compile(name=...) 하고, 두 팀을 최상위 Supervisor 가 지휘합니다. subgraphs=True 로 모든 층의 실행 경로를 찍습니다.day6_practice/network_demo.py접수, IT, 인사 에이전트가 관리자 없이 handoff 도구로 서로 넘기는 Swarm. 체크포인터가 active_agent 를 기억합니다.day6_practice/api_server.pyfinal_scenario 의 그래프를 FastAPI 의 POST /chat 으로 노출합니다.day6_practice/specialist_agents.pySupervisor 탭에 나오는 data_agent, research_agent, general_agent 의 정의. 이 자료에서는 코드를 싣지 않았습니다.