본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 22. 08:19

단일 도구에서 계획으로 — NVIDIA NIM을 활용한 멀티스텝 에이전트 (Multi-Step Agents)

요약

NVIDIA NIM을 활용하여 단일 도구 호출을 넘어선 멀티스텝 에이전트(Multi-Step Agents) 구축 방법을 설명합니다. ReAct(Reason + Act) 패턴을 통해 모델이 스스로 계획을 세우고 순차적으로 도구를 사용하는 루프를 구현하는 과정을 다룹니다.

핵심 포인트

  • 단일 도구 호출에서 계획 기반의 멀티스텝 추론으로의 전환
  • ReAct(Reason + Act) 패턴을 활용한 에이전트 루프 구현
  • NVIDIA NIM 엔드포인트를 사용한 순수 Python 기반 에이전트 구축
  • 도구 간의 의존성을 해결하는 멀티스텝 실행 프로세스 이해

파트 5에서는 모델에게 도구 목록을 제공하고 모델이 하나를 선택하게 했습니다. 시간을 물어보면 시계(clock)를 호출하고, AI Club에 대해 물어보면 검색기(retriever)를 호출합니다. 그것도 이미 에이전트(agent)이지만, 얕은 수준의 에이전트입니다. 모든 질문이 단 한 번의 도구 호출(tool call)로 답변되었습니다.

실제 질문은 그렇지 않습니다. _"다음 AI Club 모임까지 며칠 남았나요?"_라는 질문은 단일 도구만으로는 답할 수 없습니다. 모델은 지식 베이스를 **검색(search)**하여 클럽이 목요일에 모인다는 것을 알아낸 다음, "목요일"에 대해 날짜 계산을 수행하여 남은 일수를 세어야 합니다. 두 개의 도구가 순차적으로 실행되어야 하며, 두 번째 도구는 첫 번째 도구의 결과가 돌아오기 전까지는 실행될 수 없습니다.

이 포스트가 다루는 도약은 바로 이것입니다: 도구를 선택하는 것에서 **계획(plan)**을 실행하는 것으로의 전환입니다. 이 패턴에는 이름이 있습니다 — 바로 추론(Reason) + 행동(Act)의 합성어인 ReAct이며, 이는 여러분이 나중에 접하게 될 거의 모든 에이전트 프레임워크의 밑바탕이 되는 루프(loop)입니다. 우리는 동일한 호스팅 NIM 엔드포인트(endpoint)에서 순수 Python으로 이를 구축하며, 모델이 생각하는 과정을 지켜볼 수 있도록 트레이스(trace)를 출력합니다.

저는 USC의 NVIDIA Developer Champion인 B Torkian입니다. 시리즈의 파트 6입니다.

추가되는 사항

사용자 질문 (User question)
  → NIM 호출 (도구 스키마 포함)
  → 모델이 도구 호출 (Act)
...

파트 5에도 정확히 이 루프가 있었지만, 데모 질문들은 단 한 번만 루프를 돌았습니다. 파트 6에서는 의도적으로 여러 번 루프를 돌 수 있도록 두 가지를 변경합니다:

  1. 다른 도구의 출력에 의존하는 세 번째 도구: 따라서 단일 호출만으로는 작업을 완료할 수 없습니다.
  2. 가시적인 트레이스 (visible trace): 멀티스텝 추론(multi-step reasoning)이 읽을 수 있는 제어 흐름(control flow)으로 나타나도록 합니다.

파트 1의 채팅 호출(chat call), 파트 2의 검색기(retriever), 그리고 파트 3의 거부 가드레일(refusal guardrail)은 모두 변경 없이 그대로 유지됩니다.

여기서 "멀티스텝(multi-step)"이 실제로 의미하는 것

원샷(one-shot) 도구 호출은 다음과 같습니다:

질문(Q): AI Club은 언제 모이나요?
모델 → search_campus_info("AI Club meeting")"매주 목요일 오후 5시"답변(A): 목요일 오후 5시.

멀티스텝 계획은 다음과 같습니다:

질문(Q): 다음 AI Club 모임까지 며칠 남았나요?
모델 → search_campus_info("AI Club meeting day")"매주 목요일"
모델이 이를 읽은 후 → days_until_weekday("Thursday")"5일 후, 6월 18일"
모델이 이를 읽음 → 답변(A): 다음 모임은 이번 주 목요일인 6월 18일이며, 5일 남았습니다.

프레임워크에서 변경된 것은 아무것도 없습니다. 모델이 첫 번째 결과를 확인한 후 두 번째 도구가 필요하다고 판단했기 때문에, 루프(loop)가 한 번 대신 두 번 실행되었을 뿐입니다. 지능은 모델이 시퀀스(sequence)를 선택하는 데 있으며, 여러분의 역할은 모델에게 좋은 도구와 쓰러지지 않는 루프를 제공하는 것입니다.

1단계 — 설정을 그대로 가져오기

파트 1, 2, 5에서 사용했던 client, MODEL, knowledge_base, 그리고 retrieve_context가 필요합니다. Colab 노트북에는 간결한 필수 요구 사항 셀이 있으며, 독립형 part6_react_agent.py는 자체적으로 실행될 수 있도록 모든 것을 처음부터 정의합니다.

우리는 파트 5에서 했던 것과 동일하게 meta/llama-3.3-70b-instruct를 유지합니다. 여기서는 이것이 훨씬 더 중요합니다. 도구 하나를 선택하는 것은 관대하지만, 도구의 순서(sequencing)를 정하는 것(먼저 검색하고, 그다음에 계산하는 것)은 작은 모델이 맥락을 놓치는 지점이기 때문입니다. 호스팅된 엔드포인트(endpoint)는 동일하며, 파트 1~4와 모델 문자열만 다릅니다.

MODEL = "meta/llama-3.3-70b-instruct"
LOCAL_TZ = "America/Los_Angeles"   # 도구 간에 "오늘"이 일관되게 유지되도록 함

2단계 — 세 가지 도구, 그중 하나는 체이닝(chaining)을 강제함

이미 알고 있는 시계(clock)와 리트리버(retriever)입니다. 새로운 도구는 days_until_weekday이며, 이는 의도적으로 단독으로는 쓸모없게 설계되었습니다. 이 도구는 입력값으로 요일이 필요하며, 올바른 요일을 알아내는 유일한 방법은 먼저 지식 베이스(knowledge base)를 검색하는 것입니다.

WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday",
            "Friday", "Saturday", "Sunday"]

...

days_until_weekdaysearch_campus_info에 의존한다는 점이 이번 레슨의 핵심입니다. 이것이 바로 "도구 호출"을 "계획 수립"으로 바꾸는 요소입니다.

3단계 — 도구를 설명하고 순서를 암시하기

스키마(Schema)는 모델이 무엇을 호출할지 결정하기 위해 읽는 정보입니다. 멀티스텝 에이전트(Multi-step agent)의 경우, 설명(Description)은 단순히 목적만을 나타내는 것이 아니라 _순서(Sequence)_를 암시해야 합니다. days_until_weekday의 마지막 줄을 주목하세요:

tools = [
    {"type": "function", "function": {
        "name": "search_campus_info",
...

"보통 search_campus_info를 먼저 호출해야 합니다."라는 문구는 모델의 플래너(Planner)를 겨냥한 프롬프트 엔지니어링 (Prompt engineering)입니다. 모호한 도구 문서(Tool docs)는 도구를 잘못된 순서로 호출하거나 단계를 건너뛰는 에이전트를 만들어냅니다.

4단계 — 트레이스(Trace)를 활성화한 ReAct 루프

5부와 동일한 골격을 사용하지만, 주의 깊게 살펴볼 세 가지 요소가 있습니다. 더 큰 스텝 예산(Step budget), 출력되는 트레이스(Trace), 그리고 잘못된 호출이 루프를 중단시키지 않도록 도구 실행을 감싸는(Wrap) 방식입니다.

SYSTEM_PROMPT = (
    "You are a USC campus assistant that solves questions step by step using tools. "
    "Work in a loop: think about what you still need, call ONE tool to get it, read "
...

5부와 달라진 점과 그 이유는 다음과 같습니다:

  • MAX_STEPS = 5 — 원샷(One-shot) 루프는 3단계에서 멈출 수 있습니다. 플래너(Planner)에게는 검색하고, 계산하고, 때로는 스스로를 수정할 수 있는 여유 공간이 필요합니다. 상한선은 작고 명확하게 유지하세요. 중단 지점이 없는 에이전트는 가끔씩 통제 불능 상태(Spiral)에 빠질 수 있습니다.
  • 트레이스(Trace) — 매 반복마다 acting ->observe <-를 출력하는 것은 에이전트를 위한 가장 유용한 디버깅 (Debugging) 습관입니다. 에이전트가 오작동할 때는 거의 항상 잘못된 도구를 호출했거나 결과를 잘못 읽었기 때문이며, 트레이스는 정확히 어느 부분인지 보여줍니다.
  • 도구 호출 주변의 try/except — 모델이 인자(Arguments)를 작성하므로, 모델이 잘못된 인자를 작성할 수도 있다는 뜻입니다. 이를 포착하여 에러를 도구 결과로 다시 전달하세요. 그러면 에이전트는 프로그램이 충돌하는 대신 대개 다음 단계에서 복구될 것입니다.

5단계 — 실행 및 트레이스 읽기

for question in [
    "How many days until the next USC AI Club meeting?",  # search -> days_until_weekday
    "Is the USC GPU lab open right now?",                 # clock + search, then reason
...

트레이스에서 확인해야 할 사항:

  • 회의까지 남은 일수 (Days until the meeting) — 두 단계로 진행됩니다: search_campus_info가 "매주 목요일"을 반환하면, days_until_weekday("Thursday")가 남은 일수를 반환합니다. 모델은 두 번째 관찰 (observation)이 이루어진 후에야 답변합니다.
  • 지금 연구실이 열려 있는가 (Is the lab open right now) — 모델은 get_current_time에서 현재 요일과 시간을 가져오고, search_campus_info에서 공지된 운영 시간(월금, 오전 10시오후 6시)을 가져온 다음, 지금이 해당 시간 범위 내에 있는지 추론합니다.
  • 동아리 모임은 언제인가 (When does the club meet) — 한 번의 검색으로 완료됩니다. 유능한 에이전트는 필요하지 않은 도구로 계획을 채우지 않습니다.
  • 와이파이 비밀번호 (Wifi password) — 검색을 수행하지만 아무것도 찾지 못하면 거절 문구로 넘어갑니다. Part 3의 가드레일 (guardrail)이 멀티스텝 루프 (multi-step loop) 내부에서도 여전히 작동합니다.

모델의 동작은 완벽하게 결정론적 (deterministic)이지 않습니다. 일부 실행은 약간 다른 경로를 따를 수 있습니다. 이 점 또한 살펴볼 가치가 있습니다. 트레이스 (trace)를 통해 추측하는 대신 그 변동성 (variance)을 직접 관찰할 수 있습니다.

Step 6 — 실제로 구축한 것

이제 어시스턴트는 단계별로 추론할 수 있습니다:

  • Workshop 1은 모델에게 두뇌 (채팅 호출)를 부여했습니다.
  • Workshop 2는 모델에게 메모리 (검색/retrieval)를 부여했습니다.
  • Workshop 3은 모델에게 판단력 (가드레일)을 부여했습니다.
  • Workshop 4는 모델에게 이식성 (호스팅 또는 로컬)을 부여했습니다.
  • Workshop 5는 모델에게 손 (단일 도구 호출)을 부여했습니다.
  • Workshop 6은 모델에게 계획 (루프 내 도구 체이닝)을 부여했습니다.

이것이 LangGraph, CrewAI, AutoGen 등이 사용하는 아키텍처 (architecture)의 핵심입니다. 이들은 상태 머신 (state machines), 재시도 (retries), 하위 에이전트 (sub-agents), 대시보드 등을 추가하지만, 중심에는 방금 작성한 루프가 있습니다: 도구와 함께 모델을 호출하고, 모델이 요청하는 것을 실행하며, 결과를 다시 입력으로 제공하고, 이를 반복하는 것입니다. 일반적인 다음 단계는 다음과 같습니다:

  • 더 많은 도구 — 캘린더, 티켓팅 API, 웹 검색, 코드 샌드박스 (code sandbox).
  • 실제 플래너 (planner) — 한 번에 한 단계씩 결정하는 대신, 도구가 실행되기 전에 전체 단계 목록을 작성합니다.
  • 턴 간의 메모리 (Memory across turns) — 에이전트가 이미 찾아본 내용을 기억할 수 있도록 합니다.
  • 관측 가능성 (Observability)acting/observe 트레이스처럼, 로그가 남고 검색 가능한 형태입니다. 프로덕션 에이전트의 성패는 여기서 갈립니다.

이 시리즈 전체에서 한 가지만 기억한다면 다음과 같습니다: LLM은 내부 구조가 특이한 일반적인 Python 함수이며, 에이전트(agent)는 그 함수를 감싸는 while 루프입니다. 루프의 제어권은 당신에게 있습니다. 모델은 단지 빈칸을 채울 뿐입니다.

코드 가져오기

Repo: github.com/torkian/nvidia-nim-workshop
One-click Colab: part6_react_agent.ipynb 열기
Local Python: 리포지토리 내의 part6_react_agent.py (pip install -r requirements.txt 실행 후 python3 part6_react_agent.py 실행).

MIT 라이선스입니다. 저는 USC(University of Southern California)에서 이 작업을 수행하고 있습니다. 이 리포지토리를 포크(fork)하여 지식 베이스(knowledge base)와 도구(tools)를 당신의 학교, 동아리, 프로젝트에 맞게 교체해 보세요.

전체 시리즈

  • Part 1: 30분 만에 NVIDIA NIM으로 첫 번째 AI 앱 만들기
  • Part 2: 수동 RAG에서 실제 검색으로 — NVIDIA NIM을 활용한 임베딩 기반 RAG (Embedding-Based RAG)
  • Part 3: AI 앱이 거짓말을 하지 않도록 가드레일 (Guardrails) 추가하기
  • Part 4: 개인 GPU에서 NVIDIA NIM 실행하기
  • Part 5: 챗봇에서 에이전트로 — NVIDIA NIM을 활용한 도구 호출 (Tool Calling)
  • Part 6 (본 포스트): 단일 도구에서 계획으로 — NVIDIA NIM을 활용한 멀티스텝 에이전트 (Multi-Step Agents)

전체 시리즈를 한 번에 읽고 싶은 분들을 위해 Medium에 통합된 롱폼(long-form) 버전이 게시되어 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0