코드 모드

코드 모드 (Code Mode)

모델이 도구를 하나씩 호출하는 대신, JavaScript나 TypeScript 코드로 여러 도구 호출을 직접 조합할 수 있다면 어떨까요. 코드 모드는 모델이 여러분의 AI SDK 도구를 호출하는 JavaScript/TypeScript를 직접 쓰게 해 줍니다. 생성된 코드는 격리된 QuickJS 샌드박스에서 실행되고, JSON으로 직렬화 가능한 결과를 돌려줘요.

출처: 공식문서

본문

코드 모드를 쓰면 모델은 도구를 하나씩 호출하는 대신 다음을 할 수 있어요.

  • 독립적인 도구들을 동시에(concurrently) 호출
  • 도구 결과를 변환하고 결합
  • 모델로 돌려보내기 전에 큰 도구 응답을 필터링
  • 여러 단계 작업에 JavaScript 제어 흐름 사용

코드 모드는 @ai-sdk/code-mode 패키지가 제공합니다.

코드 모드는 실험 기능이라 API가 향후 릴리스에서 바뀔 수 있어요. Node.js 22 이상이 필요하며, 브라우저나 엣지 런타임에서는 사용할 수 없습니다.

설치

pnpm add ai @ai-sdk/code-mode zod

generateText에서 코드 모드 사용하기

도구를 하나의 도구 세트에 정의하고 experimental_toolCallers로 코드 모드가 호출할 도구를 선택하세요.

import {
  DIRECT_TOOL_CALL,
  experimental_codeModeTool as codeModeTool,
} from '@ai-sdk/code-mode';
import { generateText, isStepCount, tool } from 'ai';
import { z } from 'zod';

const getInventory = tool({
  description: 'Get available inventory for a product.',
  inputSchema: z.object({ productId: z.string() }),
  outputSchema: z.object({ productId: z.string(), availableUnits: z.number() }),
  execute: async ({ productId }) => ({ productId, availableUnits: 42 }),
});

const getDemand = tool({
  description: 'Get requested units for a product.',
  inputSchema: z.object({ productId: z.string() }),
  outputSchema: z.object({ productId: z.string(), requestedUnits: z.number() }),
  execute: async ({ productId }) => ({ productId, requestedUnits: 31 }),
});

const tools = {
  code_mode: codeModeTool({
    executionPolicy: {
      timeoutMs: 30_000,
    },
  }),
  getInventory,
  getDemand,
} as const;

const result = await generateText({
  model: "xai/grok-4.6",
  tools,
  experimental_toolCallers: {
    getInventory: ['code_mode'],
    getDemand: ['code_mode'],
  },
  stopWhen: isStepCount(10),
  prompt: 'Compare inventory and demand for product sku_123.',
});

experimental_toolCallers의 키는 통제 대상 도구예요. 그 값은 허용된 호출자(caller)를 나타냅니다. 이 예시에서 getInventorygetDemandcode_mode로 사용 가능하지만, 모델이 직접 호출할 수 있는 도구로는 노출되지 않아요. 도구가 직접 호출될 수도 있어야 한다면 DIRECT_TOOL_CALL을 포함하세요.

experimental_toolCallers: {
  getInventory: ['code_mode', DIRECT_TOOL_CALL],
};

experimental_toolCallers 항목이 없는 도구는 기존의 직접 호출 동작을 유지합니다.

코드 모드 도구 설명에는 허용된 도구들의 입력·출력 스키마에서 생성한 TypeScript 시그니처가 포함됩니다. 설명·inputExamples·정확한 스키마는 모델이 올바른 코드를 쓰는 데 도움을 줘요.

위 예시에서 모델은 다음과 같은 프로그램을 생성할 수 있습니다.

const [inventory, demand] = await Promise.all([
  tools.getInventory({ productId: 'sku_123' }),
  tools.getDemand({ productId: 'sku_123' }),
]);

return {
  sufficient: inventory.availableUnits >= demand.requestedUnits,
  remaining: inventory.availableUnits - demand.requestedUnits,
};

제공된 각 도구는 전역 tools 객체를 통해 사용할 수 있어요. 유효한 JavaScript 식별자가 아닌 도구 이름은 대괄호 표기법을 씁니다.

const user = await tools['lookup-user']({ userId: 'user_123' });
return { id: user.id, plan: user.plan };

코드 모드 프로그램 작성

생성된 프로그램은 다음을 지원해요.

  • JavaScript와 타입이 벗겨진(type-stripped) TypeScript
  • 최상위 awaitreturn
  • 표준 JavaScript 제어 흐름과 데이터 변환
  • 동시 도구 호출을 위한 Promise.all
  • JSON.parseJSON.stringify
  • console.log·console.info·console.debug·console.error

모든 도구 호출은 비동기라서 반드시 await하거나 관찰해야 해요. 도구 호출이 아직 분리(detached)된 채 return하면 호출이 실패하고 진행 중인 작업이 중단됩니다.

프로그램과 도구 입력·출력은 JSON으로 샌드박스 경계를 오갑니다. JSON으로 직렬화 가능한 값만 return하세요. TypeScript 지원은 타입 구문 제거에 국한되며, 코드 모드는 타입 검사를 하거나 완전한 TypeScript 컴파일러를 제공하지 않습니다.

직접 실행

모델에 코드 모드를 노출하는 대신 프로그램을 직접 실행하고 싶다면 experimental_runCodeMode를 쓰세요.

import { experimental_runCodeMode as runCodeMode } from '@ai-sdk/code-mode';

const result = await runCodeMode({
  js: `
    const inventory = await tools.getInventory({
      productId: 'sku_123',
    });
    return {
      productId: inventory.productId,
      available: inventory.availableUnits > 0,
    };
  `,
  tools: { getInventory },
});

runCodeMode는 프로그램이 돌려준 값을 반환합니다. AI SDK 도구와 같은 샌드박스와 실행 제한을 사용해요.

도구 승인

코드 모드는 현재 AI SDK 도구 승인(approval) 흐름과 통합되지 않아요. 생성된 코드가 만든 도구 호출은 코드 모드 호출 안에 중첩되어 있어서, 생성을 멈추고 애플리케이션에 도구 승인 요청을 띄울 수 없습니다.

사용자 승인에 의존하는 도구는 코드 모드에 노출하지 마세요. 그런 도구는 모델이 직접 호출하도록 두는 게 좋아요. 중첩된 도구가 승인을 요구하면 실행되지 않고 거부됩니다.

실행 제한

모든 호출에는 런타임·메모리·소스 크기·결과·도구 페이로드·콘솔 출력·도구 호출 수에 대한 제한이 있어요. executionPolicy로 재정의할 수 있습니다.

const codeMode = codeModeTool({
  executionPolicy: {
    timeoutMs: 30_000,
    memoryLimitBytes: 64 * 1024 * 1024,
    maxResultBytes: 1024 * 1024,
    maxBridgeRequests: 100,
    maxInFlightBridgeRequests: 10,
  },
});

사용 가능한 제한은 다음과 같습니다.

  • timeoutMs: 전체 실행 시간
  • memoryLimitBytes: QuickJS 메모리
  • maxStackSizeBytes: QuickJS 스택
  • maxSourceBytes: 생성된 소스 코드
  • maxResultBytes: 반환되는 결과
  • maxConsoleOutputBytes: 합쳐진 콘솔 출력
  • maxToolInputBytes: 각 도구 호출의 입력
  • maxToolOutputBytes: 각 도구 호출의 출력
  • maxBridgeRequests: 전체 도구 호출 수
  • maxInFlightBridgeRequests: 동시 도구 호출 수

프로세스 전체에서 동시에 실행되는 코드 모드 워커 수를 제한하려면 experimental_setMaxWorkers를 쓰세요.

import { experimental_setMaxWorkers as setMaxWorkers } from '@ai-sdk/code-mode';

setMaxWorkers(4);

명시적 상한이 없으면 코드 모드는 사용 가능한 메모리에 따라 최대 32개 워커까지 하나를 골라요.

격리와 도구 접근

각 호출은 새로운 QuickJS 컨텍스트를 받아요. 샌드박스 코드는 다음에 접근할 수 없습니다.

  • process·require·module 같은 Node.js 전역
  • 호스트 파일 시스템이나 모듈 로더
  • fetch·WebCrypto·performance API
  • eval이나 동적 Function 생성

네트워크나 시스템 접근은 도구로 구현해 코드 모드에 명시적으로 제공해야 해요.

샌드박스를 심층 방어(defense in depth)로 취급하세요. 생성된 코드와 도구 인자는 신뢰할 수 없는 입력이에요. 도구는 QuickJS 샌드박스 바깥, 호스트 애플리케이션에서 실행되며, 제공된 도구가 노출하는 모든 기능을 생성된 프로그램이 사용할 수 있습니다. 각 도구 안에서 인가(authorization)를 적용하고 입력을 검증하세요.

도구 입력 스키마는 execute 함수가 실행되기 전에 검증됩니다. 중단 신호와 AI SDK 도구 실행 컨텍스트는 중첩된 도구 호출로 전달돼요.

더 알아보기

  • 도구(Tools) 통합과 도구 호출
  • 에이전트 만들기(Building Agents)에서의 다단계 실행
  • 구조화된 데이터 생성을 위한 스키마 정의