`dynamicTool()`

dynamicTool()

dynamicTool 함수는 입력과 출력 타입이 컴파일 시점에 알려지지 않은 툴을 만듭니다. 다음과 같은 시나리오에 유용합니다.

  • 스키마가 없는 MCP(Model Context Protocol) 툴
  • 런타임에 로드되는 사용자 정의 함수
  • 외부 소스나 데이터베이스에서 로드된 툴
  • 사용자 입력에 기반한 동적 툴 생성

일반적인 tool 함수와 달리 dynamicTool은 unknown 타입을 받고 반환하여 런타임에 결정되는 스키마를 가진 툴을 다룰 수 있게 해 줍니다.

출처: 문서

본문

import { dynamicTool } from 'ai';
import { z } from 'zod';

export const customTool = dynamicTool({
  description: 'Execute a custom user-defined function',
  inputSchema: z.object({}),
  // input is typed as 'unknown'
  execute: async input => {
    const { action, parameters } = input as any;

    // Execute your dynamic logic
    return {
      result: `Executed ${action} with ${JSON.stringify(parameters)}`,
    };
  },
});

Import

import { dynamicTool } from "ai"

API Signature

Parameters (파라미터)

  • tool: Object — 동적 툴 정의입니다.
    • description: string | ((options: { context: Context; experimental_sandbox?: Experimental_SandboxSession }) => string) (선택) — 모델이 언제 어떻게 사용할 수 있는지에 대한 세부 정보를 포함한 툴의 목적 정보입니다. 고정 설명에는 문자열을, 각 모델 호출 전에 툴별 컨텍스트와 선택적 실험적 샌드박스에서 설명을 도출하려면 함수를 제공하세요.
    • deferLoading: boolean (선택) — toolSearch가 발견할 때까지 이 툴을 모델 컨텍스트 밖에 둡니다. toolDiscovery: 'conversation'와 함께 직접 호출 또는 코드 모드를 지원합니다. 발견된 툴은 다음 모델 스텝에서 사용 가능해집니다. 기본값은 false입니다.
    • title: string (선택) — deprecated 되었습니다. 소스별 툴 표시 메타데이터에는 providerMetadata를 사용하세요.
    • needsApproval: boolean | ((input: unknown, options: { toolCallId: string; messages: ModelMessage[]; context: Context }) => boolean | Promise<boolean>) (선택) — deprecated 되었습니다. generateText, streamText, ToolLoopAgent에서는 toolApproval로 승인을 구성하세요. 기존 needsApproval 사용은 호환성 폴백으로 여전히 동작합니다. 사용 시 불리언이거나 툴 입력과 실행 메타데이터를 받는 함수일 수 있습니다.
    • inputSchema: FlexibleSchema<unknown> — 툴이 기대하는 입력의 스키마입니다. 타입은 unknown이지만 검증을 위해 스키마가 여전히 필요합니다. 완전 동적 입력에는 z.unknown() 또는 z.any()를 가진 Zod 스키마를 사용할 수 있습니다.
    • execute: ToolExecuteFunction<unknown, unknown, Context> — 툴 호출의 인자로 호출되는 비동기 함수입니다. 입력은 unknown으로 타입이 지정되며 런타임에 검증/캐스팅해야 합니다.
      • toolCallId: string — 툴 호출의 ID입니다.
      • messages: ModelMessage[] — 언어 모델로 전송된 메시지입니다.
      • abortSignal: AbortSignal (선택) — 선택적 abort 신호입니다.
      • context: Context — 툴 실행에 전달되는 툴별 컨텍스트입니다. 이 값은 toolsContext의 일치 항목에서 옵니다.
    • outputSchema: Zod Schema | JSON Schema (선택) — 툴이 생성하는 출력의 스키마입니다. 검증과 타입 추론에 사용됩니다.
    • toModelOutput: ({toolCallId: string; input: unknown; output: unknown}) => ToolResultOutput | PromiseLike<ToolResultOutput> (선택) — 툴 결과를 언어 모델이 사용할 수 있는 출력으로 매핑하는 선택적 변환 함수입니다.
    • onInputStart: (options: ToolExecutionOptions<Context>) => void | PromiseLike<void> (선택) — 모델이 툴 입력 생성을 시작할 때 호출되는 선택적 함수입니다. 비스트리밍 컨텍스트에서는 onInputAvailable 직전에 호출됩니다.
    • onInputDelta: (options: { inputTextDelta: string } & ToolExecutionOptions<Context>) => void | PromiseLike<void> (선택) — 인자 스트리밍 델타가 준비될 때 호출되는 선택적 함수입니다. 툴이 스트리밍 컨텍스트에서 사용될 때만 호출됩니다.
    • onInputAvailable: (options: { input: unknown } & ToolExecutionOptions<Context>) => void | PromiseLike<void> (선택) — execute 함수가 제공되지 않아도 툴 호출을 시작할 수 있을 때 호출되는 선택적 함수입니다.
    • providerOptions: ProviderOptions (선택) — 추가 프로바이더별 메타데이터입니다.
    • metadata: JSONObject (선택) — 툴 자체에 대한 선택적 메타데이터입니다 (예: 소스). 결과 툴 호출의 toolMetadata로 전파되어 소비자가 툴 호출/결과 파트와 UI 메시지 파트에서 읽을 수 있습니다. 동적 툴의 소스(예: MCP 서버)가 스스로를 식별하는 데 유용합니다.

Returns (반환값)

generateText, streamText 및 기타 AI SDK 함수와 함께 사용할 수 있는 type: 'dynamic'의 Tool<unknown, unknown>을 반환합니다.

Type-Safe Usage (타입 안전 사용법)

정적 툴과 함께 동적 툴을 사용할 때는 올바른 타입 좁히기(type narrowing)를 위해 dynamic 플래그를 확인해야 합니다.

const result = await generateText({
  model: __MODEL__,
  tools: {
    // Static tool with known types
    weather: weatherTool,
    // Dynamic tool with unknown types
    custom: dynamicTool({
      /* ... */
    }),
  },
  onStepEnd: ({ toolCalls, toolResults }) => {
    for (const toolCall of toolCalls) {
      if (toolCall.dynamic) {
        // Dynamic tool: input/output are 'unknown'
        console.log('Dynamic tool:', toolCall.toolName);
        console.log('Input:', toolCall.input);
        continue;
      }

      // Static tools have full type inference
      switch (toolCall.toolName) {
        case 'weather':
          // TypeScript knows the exact types
          console.log(toolCall.input.location); // string
          break;
      }
    }
  },
});

Usage with useChat (useChat와 함께 사용)

useChat(UIMessage 형식)와 함께 사용하면 동적 툴은 dynamic-tool 파트로 나타납니다.

{
  message.parts.map(part => {
    switch (part.type) {
      case 'dynamic-tool':
        return (
          <div>
            <h4>Tool: {part.toolName}</h4>
            <pre>{JSON.stringify(part.input, null, 2)}</pre>
          </div>
        );
      // ... handle other part types
    }
  });
}

더 알아보기 (Learn more)