챗봇에서 에이전트로 — NVIDIA NIM을 활용한 도구 호출 (Tool Calling)
요약
NVIDIA NIM을 사용하여 단순 챗봇을 도구 호출(Tool Calling)이 가능한 에이전트로 전환하는 방법을 설명합니다. LangChain 같은 복잡한 프레임워크 없이도 Python 루프와 NIM 호출만으로 에이전트의 핵심 메커니즘을 구현할 수 있습니다.
핵심 포인트
- 에이전트의 핵심은 모델의 도구 선택, 코드 실행, 결과 피드백의 루프 구조임
- NVIDIA NIM의 tools 파라미터를 통해 Python 함수를 모델에 설명 가능
- 복잡한 프레임워크 없이도 도구 호출 패턴을 통해 에이전트 구현 가능
- Llama-3.3-70b 모델을 사용하여 도구 호출 성능을 최적화함
파트 1부터 4까지 우리는 유용한 도구를 만들었습니다. 언제 정보를 검색해야 하는지, 언제 거절해야 하는지, 그리고 어떤 엔드포인트(endpoint)를 호출해야 하는지를 아는 USC 캠퍼스 어시스턴트입니다. 이것은 여전히 챗봇(chatbot)입니다. 모델은 문자열을 작성하고, 우리는 그것을 출력합니다. 모든 흥미로운 일은 단 한 번의 모델 호출 내에서 일어났습니다.
이 포스트는 그것을 에이전트(agent)로 변환합니다. 제가 말하는 _에이전트_는 구체적이고 작은 것을 의미합니다. 모델이 목록에서 도구(tool)를 선택할 수 있고, 여러분의 Python 코드가 그 도구를 실행하며, 그 결과가 다시 대화로 돌아가는 것입니다. 그게 전부입니다. LangGraph, AutoGen, LangChain은 필요 없습니다. 두 개의 함수, 하나의 루프(loop), 그리고 tools=...가 포함된 NIM 호출이면 충분합니다.
여러분은 모델이 시계를 확인할지, USC 지식 베이스(knowledge base)를 검색할지, 아니면 그냥 직접 답변할지를 스스로 결정하는 과정을 보게 될 것입니다. 일단 루프를 보고 나면, 그 위에 구축된 프레임워크 추상화(framework abstractions)들이 무엇을 숨기고 있는지 이미 알게 되므로 더 쉽게 읽힐 것입니다.
저는 USC의 NVIDIA Developer Champion인 B Torkian입니다. 시리즈의 마지막 파트입니다.
추가되는 사항
사용자 질문
→ NIM 호출 (tools 스키마 포함)
→ 모델이 최종 답변 또는 tool_calls 리스트를 반환
...
파트 1의 채팅 호출 형태가 그대로 이어집니다. 파트 2의 검색기(retriever)는 도구가 됩니다. 파트 3의 가드레일(guardrail) 패턴은 여전히 적용됩니다. 우리는 어시스턴트의 범위를 제한하며, 에이전트는 우리가 노출한 도구만을 사용할 수 있습니다.
여기서 "에이전트"가 실제로 의미하는 것
대부분의 마케팅 페이지에서는 _에이전트_를 "메모리나 루프가 있는 모든 것"을 의미하는 용어로 사용합니다. 이 포스트를 위한 정의는 더 좁으며, 미리 명확히 짚고 넘어갈 가치가 있습니다:
- 여러분은 JSON 스키마(
tools파라미터)를 통해 모델에게 소수의 Python 함수를 설명합니다. - 모델은 일반적인 메시지를 반환하거나, 실행하고자 하는 함수의 이름과 인자(arguments)가 담긴
tool_calls필드를 반환합니다. - 여러분의 코드가 해당 함수를 실행하고 그 결과를
tool역할(role)로 메시지 리스트에 추가합니다. - 여러분은 또 다른 NIM 호출을 수행합니다. 모델은 도구 실행 결과를 확인하고, 다른 도구를 호출하거나 최종 답변을 작성합니다.
이것이 전체 패턴입니다. 실제 프로덕션 에이전트(Production agents)는 계획(Planning), 재시도(Retries), 하위 에이전트(Sub-agents), 그리고 관찰 가능성(Observability)을 추가합니다. 하지만 그 중심에는 여전히 이 네 가지 단계가 있습니다.
1단계 — 설정 유지 및 모델 업그레이드
파트 1, 2, 3에서 사용한 모든 것 — client, MODEL, ask, knowledge_base, embed_texts, 그리고 retrieve_context가 필요합니다. 이 워크숍을 위한 Colab 노트북에는 압축된 필수 요구 사항 셀이 포함되어 있습니다. 저장소(Repo)에 있는 독립형 스크립트 part5_agent.py는 모든 것을 처음부터 정의하므로, 이전 셀 없이도 바로 실행할 수 있습니다.
미리 짚고 넘어갈 중요한 변경 사항이 하나 있습니다. 파트 1~4에서는 meta/llama-3.1-8b-instruct를 사용했습니다. 이 모델은 빠르고 저렴하며 채팅 및 RAG(검색 증강 생성)에 적합합니다. 파트 5에서는 meta/llama-3.3-70b-instruct로 전환합니다. 이유는 — 도구 호출(Tool calling) 기능이 더 큰 모델에서 눈에 띄게 더 안정적이기 때문입니다. 제가 두 모델을 모두 테스트해 본 결과, 8B 모델은 재실행 시 올바른 도구를 호출하는 데 일관성이 없었지만(어떤 실행에서는 호출을 거부하기도 함), 70B 모델은 매번 동일하게 동작했습니다. 모델이 단순히 답변하는 것을 넘어 도구 사이에서 _선택_을 해야 할 때는 속도보다 신뢰성이 더 중요합니다. 두 모델 모두 동일한 호스팅 엔드포인트에서 실행되며, MODEL 문자열만 변경됩니다.
MODEL = "meta/llama-3.3-70b-instruct" # 파트 1-4에서는 'meta/llama-3.1-8b-instruct'였습니다.
2단계 — 두 개의 아주 작은 도구 정의
import json
from datetime import datetime
from zoneinfo import ZoneInfo
...
두 개의 함수입니다. 일반적인 Python 코드입니다. 이 함수들은 모델에 대해 아무것도 알지 못하며, 모델 또한 이들이 존재하는지 아직 모릅니다. 이는 다음 단계에서 해결됩니다.
3단계 — JSON 스키마(Schema)를 통해 모델에 도구 설명
tools = [
{
"type": "function",
...
스키마는 모델이 보는 정보입니다. 이름, 설명, 그리고 파라미터 문서(Parameter docs)는 모델이 어떤 도구를 호출할지 결정하는 기준이 됩니다. 이 설명들을 진지하게 작성하십시오. 모호한 도구 설명은 혼란스러운 에이전트를 만듭니다.
available_tools 딕셔너리는 Python 측의 디스패치 테이블(Dispatch table)입니다. 항상 이 두 가지를 쌍으로 맞추십시오. 스키마는 의도(Intent)를 기술하고, 딕셔너리는 실행(Execution)을 제공합니다.
4단계 — 에이전트 루프(Agent loop)
def ask_agent(question: str) -> str:
messages = [
{
...
잠시 멈춰서 주의 깊게 살펴볼 네 가지 사항:
tools=...및tool_choice="auto"— 이것은 모델이 사용 가능한 도구가 있다는 사실과 스스로 선택할 수 있다는 것을 인지하는 방식입니다."auto"는 유용하다면 도구를 사용하고, 그렇지 않으면 직접 답변하라는 의미입니다.messages.append(message.model_dump(...))— 모델의 도구 호출(tool-call) 요청 자체가 대화의 일부가 됩니다. 이 단계를 건너뛰면 다음 NIM 호출 시 모델은 왜 당신이 도구 결과(tool result)를 보여주는지 알 수 없습니다.tool역할 (role) — 함수의 반환 값을 다시 보낼 때는 반드시role="tool"과 일치하는tool_call_id를 포함한 메시지여야 합니다. ID가 틀리면 모델은 해당 결과를 고립된 텍스트(orphan text)로 취급합니다.- 루프 제한 (3회 반복) — 명확한 중단 지점이 없는 에이전트는 때때로 무한 루프에 빠질 수 있습니다. 워크숍에서는 제한 수치를 눈에 띄게 작게 유지하고, 모델의 동작을 이해함에 따라 그 범위를 넓혀가세요.
5단계 — 실행하기
for question in [
"What time is it in Los Angeles?", # → get_current_time 사용
"When does the USC AI Club meet?", # → search_campus_info 사용
...
확인할 수 있는 결과:
- 시간 질문은 모델이
get_current_time을 호출하고 반환된 문자열을 바탕으로 답변하게 합니다. - AI 클럽 질문은 모델이
search_campus_info를 호출하고, 검색된 청크(chunks)를 읽은 뒤 이를 바탕으로 답변하게 합니다. - 와이파이 질문은 모델이
search_campus_info를 호출하고, 청크 중 어느 것도 비밀번호를 언급하지 않음을 확인한 뒤 거절 문구로 넘어갑니다. 이는 3부에서 다룬 가드레일(guardrail) 로직과 동일하지만, 다른 제어 흐름(control flow)을 통해 전달됩니다.
어떤 실행에서는 모델이 두 도구를 모두 호출할 수도 있습니다 (예: "지금 몇 시이고 클럽 모임은 언제인가요?"). 루프는 변경 없이 이를 처리합니다. 각 반복(iteration)마다 모든 도구 결과가 추가되고 다시 질문이 던져지기 때문입니다.
6단계 — 실제로 구축한 것
이제 전체 어시스턴트는 에이전트의 형태를 갖추었습니다:
- Workshop 1은 그것에게 두뇌(채팅 호출)를 부여했습니다.
- Workshop 2는 그것에게 기억(검색 (Retrieval))을 부여했습니다.
- Workshop 3은 그것에게 판단력(가드레일 (Guardrails))을 부여했습니다.
- Workshop 4는 그것에게 이식성(호스팅 또는 로컬)을 부여했습니다.
- Workshop 5는 그것에게 손(도구 호출 (Tool Calling))을 부여했습니다.
여전히 동작 방식에 대한 제어권은 당신에게 있습니다. 모델은 당신이 노출한 함수를 호출할 수 있을 뿐이며, 호출 시 인자(Arguments)를 선언해야 하고, 당신이 제어하는 루프(Loop) 안에서 작동합니다. 실제 시스템은 각 요소를 확장해 나가지만, 방금 구축한 것이 바로 그 중추(Spine)입니다. 가장 일반적인 후속 작업은 다음과 같습니다:
- 더 많은 도구 (캘린더, 티켓팅, 웹 검색, 코드 실행 샌드박스).
- 구조화된 출력 (Structured Outputs): 최종 답변이 산문이 아닌 JSON 형식이 되도록 합니다.
- 플래너 (Planner): 도구가 실행되기 전, 질문을 하위 질문으로 분해합니다.
- 관측 가능성 (Observability): 모든 도구 호출, 모든 인자, 모든 반환 값을 기록합니다. 프로덕션 에이전트의 성패는 여기서 갈립니다.
이 시리즈 전체를 통해 한 가지만 얻어간다면, 이것을 기억하세요: LLM은 내부 구조가 특이할 뿐인 일반적인 Python 함수입니다. 당신이 구축한 모든 것 — 검색 (Retrieval), 가드레일 (Guardrails), 배포 (Deployment), 도구 호출 (Tool Calling) — 은 그 함수를 감싸고 있는 일반적인 소프트웨어입니다. 프레임워크는 타이핑을 줄여줄 뿐, 모델 자체를 바꾸지는 않습니다.
코드 가져오기
Repo: github.com/torkian/nvidia-nim-workshop
원클릭 Colab: part5_agent.ipynb 열기
로컬 Python: 리포지토리 내의 part5_agent.py (pip install -r requirements.txt 실행 후 python3 part5_agent.py 실행).
MIT 라이선스입니다. 저는 USC(남가주대학교)에서 이 과정을 운영하고 있습니다. 포크(Fork)하여 지식 베이스와 도구를 당신의 학교, 클럽, 프로젝트에 맞게 교체하고, 당신이 어디에 있든 실행해 보세요.
전체 시리즈
- 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)
전체 시리즈를 한 번에 읽고 싶은 분들을 위해 Medium에 통합된 롱폼 (Long-form) 버전이 게시되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기