함수에서 Tool로: Vercel AI SDK로 도구 호출의 문 열기

Next.js에서 API Route를 작성하고 타사 인터페이스를 호출하는 데 익숙하다면, 이제 '스스로 인터페이스를 호출할 수 있는 대규모 모델 어시스턴트'를 만드는 데 반 걸음 남았습니다. 이러한 함수를 Tool로 포장하여 모델이 스스로 사용 시기를 결정하도록 하는 것입니다.
이 글에서는 MCP, Agent 프레임워크와 같은 '큰 단어'를 먼저 논의하지 않고 오직 한 가지만 수행합니다:
일반 함수 작성에서 '대규모 모델이 호출할 수 있는 도구 작성'으로 나아가도록 도와주고, 그 뒤에 있는 사고 모델을 명확히 설명해 드리겠습니다.
읽고 나면 다음을 할 수 있어야 합니다:
Vercel AI SDK의 도구가 무엇인지, 일반 함수와의 차이점을 알게 됩니다.
최소한으로 사용 가능한 도구를 작성하고 Next.js에서 실행할 수 있습니다.
도구의 입력 매개변수/반환 값을 설계하여 모델이 코드를 더 잘 사용할 수 있도록 하는 방법을 이해합니다.
대략적으로 알게 됩니다: 오늘은 Tool만으로도 많은 애플리케이션을 처리할 수 있으며, MCP와 같은 프로토콜은 나중에 다루어도 됩니다.
1. 왜 '도구' 계층이 필요한가?
먼저 간단한 요구 사항을 상상해보세요: '날씨를 조회하는 챗봇'을 만들고 싶습니다.
가장 순진한 방법은 다음과 같습니다:
사용자가 '베이징 오늘 날씨를 알려줘'라고 입력합니다.
서버에서 이 자연어 문장을 받습니다.
직접 if/else 또는 정규식을 작성하여 도시 이름을 추출합니다.
날씨 API를 호출하여 결과를 얻습니다.
결과를 프롬프트에 다시 넣고 모델이 문장으로 구성하여 사용자에게 보내도록 합니다.
이 방법은 사용할 수 있지만 명백한 문제가 있습니다:
로직이 코드에 하드코딩되어 있어 모델은 단지 '고급 템플릿 엔진'일 뿐입니다.
기능(환율 조회, 일정 조회)을 추가할 때마다 구문 분석 + 호출 로직을 추가로 작성해야 합니다.
모델은 '사용할 수 있는 기능'을 전혀 알지 못하므로 직접 '길을 열어'줘야 합니다.
도구 계층은 '당신의 if/else + 인터페이스 호출'을 모델이 볼 수 있는 기능으로 격상시킵니다.
다시 말하면:
함수를 작성하여 '무엇을 할 수 있는지', '어떤 매개변수가 필요한지', '무엇을 반환할지'를 정의합니다.
모델에게 알려줍니다: 이것들은 당신이 호출할 수 있는 도구입니다.
모델은 사용자 입력에 따라 어떤 도구를 호출할지, 어떤 매개변수를 전달할지 스스로 결정합니다.
이 추상화 단계는 바로 Vercel AI SDK의 도구가 도와주는 부분입니다.
2. Vercel AI SDK의 도구는 무엇인가요?
도구를 먼저 이렇게 이해할 수 있습니다:
'설명서가 있는 함수'로, 설명서는 모델을 위한 것이고 함수 본문은 자신의 코드를 위한 것입니다.
Vercel AI SDK에서 도구는 일반적으로 세 가지 정보를 포함합니다:
description: 이 도구가 무엇을 하는지 한 문장으로 설명합니다.parameters: JSON Schema(또는 Zod)를 사용하여 필요한 입력 매개변수를 설명합니다.execute: 실제로 실행되는 함수로, 예를 들어 HTTP API 호출, 데이터베이스 조회 등입니다.
대략 다음과 같은 모양입니다 (의사 코드, 먼저 정의를 느껴보세요):
import { tool } from 'ai';
const getWeather = tool({
description: '查询指定城市的实时天气',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名称,比如 Beijing' },
},
required: ['city'],
},
execute: async ({ city }) => {
// 这里就是你平时会写的业务代码
const res = await fetch(`https://api.example.com/weather?city=${city}`);
return await res.json();
},
});그런 다음 모델을 호출할 때 이 getWeather를 AI SDK에 전달합니다:
SDK는
description과parameters를 모델이 이해할 수 있는 '도구 목록'으로 변환합니다.모델은 사용자 의도를 이해할 때 'getWeather를 사용할까? 사용한다면 어떤
city를 전달해야 할까?'를 고려합니다.모델이 호출을 결정하면 SDK는 매개변수를
execute에 전달하고 반환 결과를 받은 후 모델이 최종 응답을 생성하도록 계속 진행합니다.
더 이상 '도시 이름 구문 분석', '언제 인터페이스를 호출할지 결정'하는 로직을 직접 작성할 필요 없이 '주도권'을 모델에 넘깁니다.
3. 최소한으로 사용 가능한 도구 예제
실제로 실행 가능한 최소 예제를 작성해 보겠습니다: 현재 시간 조회.
3.1 간단한 도구 정의
이미 Next.js 프로젝트에 ai 패키지(Vercel AI SDK)를 설치했다고 가정합니다. 서버 측에서 간단한 도구를 만듭니다:
// app/api/chat/tools.ts
import { tool } from 'ai';
export const getCurrentTime = tool({
description: '获取当前服务器时间(ISO 字符串)',
parameters: {
type: 'object',
properties: {},
},
async execute() {
return {
now: new Date().toISOString(),
};
},
});여기서 주목할 몇 가지 사항이 있습니다:
parameters는 빈 객체입니다: 이 도구에 입력 매개변수가 필요하지 않음을 나타냅니다.execute는 문자열 대신{ now: string }객체를 반환합니다. 이는 이후 구조화된 처리(예: 프론트엔드 표시, 후속 계산)를 용이하게 합니다.
3.2 API Route에서 도구 사용
다음으로, 모델이 대화 중에 이 도구를 호출할 수 있는 가장 간단한 채팅 API를 작성합니다. 구체적인 호출 세부 사항은 버전에 따라 약간 다를 수 있으므로 여기서는 '구조'와 '아이디어'만 강조하며 의사 코드는 다음과 같습니다:
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import { streamText } from 'ai'; // 假设使用 AI SDK 的某个调用方法
import { getCurrentTime } from './tools';
import { openai } from '@ai-sdk/openai';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const response = await streamText({
model: openai('gpt-4o'), // 你选的模型
messages,
tools: {
getCurrentTime,
},
});
return response.toAIStreamResponse();
}여기서 핵심 사항은 다음과 같습니다:
tools필드는 정의한 도구를 모델에 노출합니다.모델이 사용자 메시지를 받고 현재 시간이 필요하다고 판단하면 'getCurrentTime에 대한 호출을 시작'하고 SDK가 자동으로
execute를 실행합니다.도구 호출 결과는 모델에 다시 전달되어 시간에 따라 최종 응답을 계속 생성하도록 합니다.
3.3 간단한 프론트엔드 채팅 인터페이스
프론트엔드에서는 useChat과 같은 Hook을 사용하여 가장 간단한 채팅 UI를 만들 수 있습니다. 이 부분은 이 글의 핵심이 아니므로 자세히 설명하지 않겠습니다. 중요한 점은 프론트엔드 관점에서 일반 Chat API와 차이가 없으며, 모델이 이제 '스스로 시간을 조회'할 수 있다는 것입니다.
4. 도구와 일반 함수의 본질적 차이
여기서 궁금할 수 있습니다: 그럼 라우트에서 직접 new Date().toISOString()을 작성해도 시간을 조회할 수 있는데 왜 도구를 사용해야 하나요?
핵심 차이는 '누가 결정을 내리는가'에 있습니다:
일반 함수: 비즈니스 코드에서 명시적으로 호출하며, '언제 호출할지'를 전적으로 사용자가 결정합니다.
도구: '이 기능'을 노출하고 모델에게 '사용 방법'을 알려줄 뿐, '사용 여부'와 '사용 방법'은 모델이 결정합니다.
이로 인해 몇 가지 실제 이점이 있습니다:
적은 if/else 작성
자연어 구문 분석, 키워드 일치, 분기 선택을 직접 할 필요가 없습니다.
모델이 컨텍스트에 따라 특정 도구를 사용할지 여부를 스스로 판단합니다.
여러 도구 자동 오케스트레이션 지원
여러 도구(시간 조회, 날씨 조회, 일정 조회)를 정의하면 모델이 한 대화에서 필요에 따라 여러 도구를 호출할 수 있습니다.
도구가 증가함에 따라 '비즈니스 라우팅 테이블'을 유지할 필요 없이 모델이 스스로 '방법을 찾습니다'.
모델 간 통일
다양한 모델 공급업체의 Tool/Function Calling 인터페이스는 각기 다르지만 AI SDK가 이러한 세부 사항을 통일해 줍니다.
도구 정의 세트 하나만 유지하면 다양한 모델 간에 전환할 수 있습니다.
도구를 이렇게 볼 수 있습니다:
'모델 ↔ 코드' 사이에 '구조화된 메타데이터가 있는 함수' 계층을 추가하여 이 연결을 지능적이면서도 제어 가능하게 만듭니다.
5. 사용하기 좋은 도구 설계: 매개변수와 반환 값
도구 작성은 '실행만 되면 된다'가 아닙니다. 매개변수와 반환 값을 잘 설계해야 모델이 더 안정적이고 제어 가능하게 사용할 수 있습니다.
5.1 매개변수: 모델이 작업을 명확히 이해하도록 돕기
매개변수 정의 시 몇 가지 작은 원칙을 따르는 것이 좋습니다:
열거할 수 있는 것은 열거: 예:
unit: 'celsius' | 'fahrenheit'.필드로 분할할 수 있으면 분할: '도시+날짜'를 하나의 문자열로 합치지 말고 모델이 스스로 분할하도록 합니다.
description을 사용하여 모델에 몇 가지 예를 제공합니다: 모델이 실제로 참고합니다(또는 훈련 시 유사한 패턴을 보았을 것입니다).
예시:
const getWeather = tool({
description: '查询指定城市在指定日期的天气',
parameters: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称,比如 Beijing、Shanghai',
},
date: {
type: 'string',
description: '日期,格式为 YYYY-MM-DD,比如 2025-05-17',
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: '温度单位,默认 celsius',
},
},
required: ['city', 'date'],
},
async execute({ city, date, unit = 'celsius' }) {
// ...
},
});5.2 반환 값: 문자열 덩어리보다 구조화된 데이터 사용
execute에서 직접 '베이징 오늘 25도, 맑음'을 반환할 수 있지만 이는 많은 유연성을 잃게 됩니다:
프론트엔드에서 이 결과를 직접 후속 표시나 계산에 사용할 수 없습니다.
모델도 후속 호출에서 구조화된 정보를 편리하게 재사용할 수 없습니다.
더 권장되는 방법은 명확한 객체를 반환하는 것입니다:
async execute({ city, date, unit = 'celsius' }) {
const data = await fetchWeather(city, date, unit);
return {
city,
date,
unit,
temperature: data.temp,
condition: data.condition, // sunny, cloudy, etc.
};
}이렇게 하면 모델이 자체적으로 언어를 구성하여 사용자에게 응답할 수 있을 뿐만 아니라 후속 단계에서 이러한 필드를 계속 사용할 수 있습니다(예: 다른 도구 호출).
6. 여러 도구를 한 번에 연결: 모델이 직접 선택하도록
앞서 getCurrentTime만 사용했지만 이제 getWeather를 추가하여 함께 모델에 전달할 수 있습니다.
// app/api/chat/tools.ts
export const tools = {
getCurrentTime,
getWeather,
};모델 호출 시:
const response = await streamText({
model: openai('gpt-4o'),
messages,
tools,
});이때 사용자가 말하면:
'내일 상하이 날씨를 알려주고, 맑으면 현재 시간도 함께 알려줘.'
모델은 다음과 같은 작업을 수행할 기회가 있습니다 (로직 수준):
먼저
getWeather를 호출하여 '맑은지' 확인합니다.그런 다음
getCurrentTime을 호출하여 현재 시간을 가져옵니다.두 호출 결과를 통합하여 최종 응답을 생성합니다.
이러한 로직을 위해 '비즈니스 흐름도'를 작성할 필요 없이 다음만 있으면 됩니다:
도구를 잘 나열하고 매개변수를 명확히 정의합니다.
시스템 프롬프트에서 모델에게 '이런 요구 사항이 있을 때 도구를 잘 활용하라'고 대략 알려줍니다.
이것이 바로 '도구 호출'의 진정한 힘입니다: '프로세스 작성'에서 '능력 정의'로 전환됩니다.
7. 이 글의 마무리: 먼저 Tool을 충분히 익힌 후 프로토콜 계층 고려
이 글에서는 세 가지만 수행했습니다:
도구를 개념적으로 분해: '설명서가 있는 함수'이며 설명서는 모델을 위한 것입니다.
최소한의 도구 예제를 작성하고 Next.js API Route에서 사용하는 방법을 보여주었습니다.
매개변수와 반환 값을 설계하여 모델이 도구를 더 잘 사용하게 하는 방법을 설명했습니다.
Vercel AI SDK의 도구만으로도 다음을 만들 수 있습니다:
내부 고객 지원 도우미 / 티켓 도우미.
SaaS 제품의 스마트 쿼리 / 스마트 생성.
다양한 '조회', '계산', '기록' 업무 도우미.
그리고이 단계에서는 MCP와 같은 프로토콜 계층을 전혀 신경 쓸 필요가 없습니다. 이러한 프로토콜은 주로 '호스트 간, 프로젝트 간 도구 재사용' 문제를 해결하며, 도구 수와 호출 환경이 복잡해진 후에 천천히 도입하는 것이 더 적합합니다.
다음 글에서는 'Tool만 사용'을 기반으로 도구가 많아지고 호출 환경이 복잡해질 때 Tool만으로는 어떤 경계와 어려움에 직면하는지, 그리고 MCP와 같은 프로토콜이 왜 등장했는지에 대해 구체적으로 논의할 것입니다.
Google에서 팔로우
HeyBinyang을 Google 선호 소스로 추가
Google에서 내 업데이트를 더 쉽게 찾고 싶다면 이 사이트를 선호 소스로 표시할 수 있습니다.
공유
공유
이 글을 공유합니다.