Supervisor로 조립하기 전에 전문가 Agent 3개(데이터 조회, 규정 검색, 일반 응대)를 만들고, solo_test()로 하나씩 따로 불러 보는 실습입니다. 아래 콜 맵과 플레이어의 호출 순서, 인자, 결과, 토큰은 모두 실제로 돌려서 기록한 값입니다.
__main__이 solo_test()를 4번 부르고, 각 Agent는 자기 worker_llm(Bedrock Claude Sonnet 4.5)을 부르고, LLM이 고른 도구만 실행됩니다. 아직 Supervisor는 없습니다. 세 Agent는 서로 모르고, __main__이 직접 하나씩 부릅니다.
| 테스트 | Agent | LLM 호출 | 도구 호출 (인자) | 토큰 (LLM 호출별) | 결과 |
|---|
합계: LLM 번, 도구 번. 토큰은 각 LLM 응답의 usage_metadata.total_tokens입니다. 시스템 프롬프트와 도구 설명이 매번 같이 들어가서, 도구 4개를 가진 data_agent는 질문이 짧아도 한 번에 1,400토큰대가 나옵니다.
어떤 Agent를 부를지는 __main__이 정합니다(124~128줄에 직접 적음). 아직 라우터나 Supervisor가 없으니 질문이 Agent 담당 밖이어도(②) 그 Agent로 갑니다. 각 Agent가 쓸 수 있는 도구 목록도 create_agent(llm, [도구들])로 우리가 정합니다.
도구를 쓸지, 어떤 도구를, 어떤 인자로 부를지는 LLM이 정합니다. 근거는 시스템 프롬프트와 도구의 docstring입니다. ①에서 list_team_members를 고른 건 81줄 "팀 명단과 인원수는 list_team_members"와 30줄 docstring 덕분이고, ②에서 도구 없이 거절한 건 83줄 규칙 때문입니다.
도구가 있으면 START → model ⇄ tools → END 루프를, 도구가 없으면 START → model → END만 만듭니다(실제 그래프 확인: general_agent엔 tools 노드가 없음). model 노드는 LLM을 부르고, 응답에 tool_calls가 있으면 tools로, 없으면 END로 갑니다.
tools 노드는 LLM이 낸 tool_calls를 보고 해당 파이썬 함수를 그 인자로 실행한 뒤, 결과를 ToolMessage(tool_call_id=…)로 State에 넣습니다. 판단은 하지 않습니다. 그 결과를 들고 model로 돌아가 LLM이 최종 답을 씁니다.
invoke가 새 대화). 조립은 two_agent_supervisor.py에서 create_supervisor로 합니다.name=이 중요합니다. solo_test의 출력 [data_agent]가 이 이름이고, Supervisor로 조립하면 이 이름으로 transfer_to_data_agent 같은 handoff 도구가 만들어집니다.retrieve_docs는 아직 RAG가 아니라 키워드 사전입니다. 질문에 "연차", "재택", "출장"이 글자 그대로 들어 있어야 찾습니다(69~71줄). ③은 LLM이 인자를 '출장 숙박비 한도'로 넣어서 "출장"이 맞았습니다. LLM이 "숙박비 한도"처럼 줄여 넣으면 "관련 문서를 찾지 못했습니다"가 나옵니다. 4번째 세션에서 chroma_db 검색으로 바꿀 자리입니다.search_employee, get_asset, list_projects는 연결만 돼 있고 이번 질문들엔 필요 없어서 안 불렸습니다. 쓰지 않아도 도구 설명은 매 LLM 호출마다 프롬프트에 들어갑니다.[:150]). ③, ④의 답은 그보다 길어서 터미널에선 잘립니다. 플레이어의 State 칸에는 전체 답을 넣었습니다.temperature=0이어도 문장은 실행마다 조금씩 다를 수 있습니다. 도구 선택과 인자는 이번처럼 나올 가능성이 높지만 보장되지는 않습니다.