Vercel AI SDK를 사용한 AI 에이전트 구축하기
요약
Vercel AI SDK를 활용하여 도구 호출 루프(tool-calling loops) 기반의 AI 에이전트를 구축하는 방법을 설명합니다. 다중 도구 사용, stopWhen 및 stepCountIs 설정을 통해 지원 분류 에이전트의 실행 흐름을 제어하는 기술적 가이드를 제공합니다.
핵심 포인트
- Vercel AI SDK의 도구 호출 루프 메커니즘 이해
- stopWhen과 stepCountIs를 이용한 에이전트 중단 조건 제어
- Zod를 활용한 다중 도구 정의 및 입력 스키마 설정
- 고객 조회부터 티켓 생성까지의 실전 지원 에이전트 워크플로우
Vercel AI SDK는 에이전트를 **도구 호출 루프 (tool-calling loops)**로 취급합니다. 모델이 텍스트를 생성하거나 도구를 호출하면, SDK가 해당 도구들을 실행하며, 모델이 답변을 하거나 **중단 조건 (stop condition)**이 발생할 때까지 루프가 계속됩니다.
이 포스트에서는 고객과 송장(invoice)을 조회하고, 내부 지식 베이스를 검색하며, 티켓을 생성하거나 상담원에게 에스컬레이션(escalate)하는 **지원 분류 에이전트 (support triage agent)**를 구축합니다. 이 내용은 Vercel AI SDK를 활용한 LLM 통합 포스트를 기반으로 하며, 다중 도구 (multiple tools), stopWhen, 그리고 **stepCountIs**에 초점을 맞춥니다.
SDK 네이티브 tool() 핸들러 대신 MCP를 통해 노출된 외부 도구에 대해서는 Node.js를 사용한 MCP 서버 포스트를 참조하세요.
사전 요구 사항
- OpenAI 계정
- 생성된 API 키
- 결제 활성화
- Node.js 버전 26
ai,@ai-sdk/openai,zod설치 (npm i ai @ai-sdk/openai zod)- Vercel AI SDK 통합 포스트를 통한 클라이언트 설정
멘탈 모델 - 단계(steps)와 도구 루프
**단계 (step)**는 한 번의 모델 생성(generation)을 의미합니다. 해당 단계에서 모델은 다음 중 하나를 수행합니다:
- 텍스트 (text) 반환 (루프 종료)
- 도구 호출 (tool calls) 반환 (SDK가 이를 실행하고 결과를 가지고 다음 단계를 시작)
지원 분류 에이전트의 전형적인 흐름: 사용자 질문 → 모델이 조회 도구 호출 (getCustomer, getInvoice, searchKnowledgeBase) → 모델이 티켓을 생성하거나 에스컬레이션 → 최종 답변. stopWhen은 쓰기 도구(write tools)가 실행되기 전이나 후에 루프를 종료할 수 있습니다.
stepCountIs(5)는 5개의 개별 도구 호출이 아니라, "5단계 (5 steps)"(5번의 모델 생성) 후에 중단하라는 의미입니다. 단일 단계에는 여러 개의 병렬 도구 호출이 포함될 수 있습니다.
stopWhen 없이 tools를 전달하면, SDK는 안전 장치로서 기본값으로 stepCountIs(20)를 설정합니다.
지원 분류 시나리오
프롬프트 예시:
고객 cus_1042가 송장 inv_8891에 대해 두 번 청구되었다고 합니다. 어떻게 해야 할까요?
현실적인 체인:
getCustomer- 플랜 등급 (plan tier), 열린 티켓 수 (open ticket count)getInvoice- 금액 (amount), 상태 (status), 결제 ID (payment IDs)searchKnowledgeBase- 중복 청구 및 환불 정책 (duplicate-charge and refund policy)createSupportTicket또는escalateToHuman- 실행 액션 또는 감시 중단 (sentinel stop)
데모는 인메모리 피스처 (in-memory fixtures: 고객, 송장, 지식 베이스 문서)를 사용하므로 데이터베이스 없이도 스크립트가 실행됩니다.
여러 도구 정의하기 (Defining multiple tools)
tool()과 Zod의 inputSchema를 사용하여 도구를 등록합니다. 명확한 description 값은 모델이 올바른 도구를 선택하는 데 도움을 줍니다.
import { tool } from 'ai';
import { z } from 'zod';
...
결과를 도출하기 위한 쓰기 도구 (write tools)를 추가합니다:
const createSupportTicket = tool({
description: '고객 및 정책 컨텍스트를 수집한 후 지원 티켓을 생성합니다',
inputSchema: z.object({
...
execute에서 구조화된 객체 (structured objects)를 반환하세요. SDK는 이를 다음 단계를 위한 도구 결과 (tool results)로 직렬화합니다. 모델이 예외를 던지는 대신 복구할 수 있도록 명시적인 에러 (예: { found: false, error: '...' })를 반환하세요.
generateText를 이용한 다단계 분류 (Multi-step triage)
모든 도구와 분류 규칙이 포함된 system 프롬프트를 전달합니다:
import { generateText, stepCountIs } from 'ai';
const { text, steps } = await generateText({
...
도구 호출 (tool calling)을 지원하는 모델을 사용하세요 (Vercel AI SDK 포스트의 웹 검색과 동일한 요구 사항입니다).
stopWhen - 루프가 중단되는 시점
stopWhen은 도구 루프 (tool loop)의 중단 조건을 정의합니다. 조건은 마지막 단계에 도구 결과가 포함되어 있을 때만 평가됩니다.
- 단일 조건은 해당 조건이
true를 반환할 때 중단됩니다. - 배열은 어느 하나라도 조건이
true를 반환할 때 중단됩니다 (OR 로직). stopWhen이 없으면 SDK는stepCountIs(20)를 적용합니다.
모델이 추가적인 도구 호출 없이 텍스트를 반환하면 루프는 자연스럽게 종료됩니다.
stepCountIs - 단계 수 제한
stepCountIs(n)은 steps.length가 n에 도달하면 중단합니다. 제어되지 않는 루프와 무제한적인 API 비용 발생을 방지하기 위해 모든 프로덕션 에이전트에 이를 사용하세요.
| 유스케이스 (Use case) | 권장 제한 (Suggested cap) |
|---|---|
| 단일 도구 사용 후 답변 | 2 (도구 단계 + 텍스트 단계) |
| ... |
동일한 프롬프트에 대한 엄격한 제한(Tight) vs 완화된 제한(Relaxed):
import { generateText, stepCountIs } from 'ai';
// 모델이 더 많은 컨텍스트를 원하더라도 3단계 후에 중단합니다
...
hasToolCall과 stepCountIs 결합하기
hasToolCall('toolName')은 모델이 최신 단계에서 특정 도구를 호출할 때 중단됩니다. 이를 stepCountIs와 결합하면 하드 캡(hard cap)과 감시 도구(sentinel tool)를 동시에 사용할 수 있습니다:
import { generateText, stepCountIs, hasToolCall } from 'ai';
const { text, steps } = await generateText({
...
escalateToHuman은 감시 도구로서 잘 작동합니다. 모델이 해당 케이스에 사람이 필요하다고 결정하는 즉시, 최종 텍스트 전용 단계를 기다리지 않고 루프가 중단됩니다.
steps 및 사용량 조사하기
결과값의 steps 배열에는 단계별 도구 호출(tool calls), 도구 결과(tool results), 종료 사유(finish reason) 및 사용량(usage)이 포함되어 있습니다. 이를 디버깅 및 비용 추적에 사용하세요:
const { text, steps, totalUsage } = await generateText({
model: openai('gpt-5.5'),
tools: supportTools,
...
streamText를 사용할 때는 onStepFinish를 전달하여 각 단계가 완료될 때마다 로그를 남길 수 있습니다.
ToolLoopAgent - 재사용 가능한 에이전트 정의
ToolLoopAgent는 스크립트와 API 라우트 전반에서 재사용할 수 있도록 동일한 루프를 래핑(wrap)합니다. generateText와 동일한 설정(tools, stopWhen, instructions)을 수용합니다.
import { ToolLoopAgent, stepCountIs } from 'ai';
const supportTriageAgent = new ToolLoopAgent({
...
스트리밍을 위해서는 .stream()을 사용하세요. Next.js 채팅 UI의 경우, AI SDK 에이전트 문서의 createAgentUIStreamResponse를 참조하세요.
도구를 사용한 스트리밍
streamText는 동일한 tools 및 stopWhen 설정을 지원합니다:
import { streamText, stepCountIs } from 'ai';
const result = streamText({
...
텍스트는 점진적으로 스트리밍됩니다. 도구 호출은 루프가 진행됨에 따라 텍스트 세그먼트 사이에서 실행됩니다.
프로덕션 참고 사항
- 항상
stopWhen을 설정하세요 - 모니터링 없이 프로덕션 환경에서 기본값인stepCountIs(20)에 의존하지 마세요. - 비용 (Cost) - 각 단계(step)는 추가적인 모델 호출입니다.
steps또는onStepFinish사용량을 로그로 남기세요. - 도구 오류 (Tool errors) - 모델이 재시도하거나 상위 단계로 에스컬레이션(escalate)해야 하는 경우, 예외를 던지는 대신
execute에서 구조화된 에러를 반환하세요. - 지침 (Instructions) - 정책 규칙은 사용자 프롬프트(user prompt)뿐만 아니라
system/instructions에 유지하세요. - 다른 곳에서의 동일한 패턴 - PR 리뷰 (
listPRs→getChecks→submitReview) 또는 직무 적합성 점수 산정(job-fit scoring)은 서로 다른 도구를 사용하지만 동일한 루프 메커니즘을 사용합니다.
데모 (Demo)
각 섹션에 대한 실행 가능한 스크립트는 vercel-ai-sdk-agents-demo 폴더에 있습니다. 코드 데모를 통해 접속할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기