본문으로 건너뛰기

지연 도구

대규모 도구 카탈로그는 execute_typescript 시스템 프롬프트를 비대하게 만듭니다. createCodeMode에 전달하는 모든 도구는 해당 프롬프트에서 완전한 TypeScript 타입 스텁이 됩니다. 도구가 50개를 넘으면 모델이 사용자 메시지를 보기도 전에 이 스텁만으로 유효 프롬프트가 수만 토큰에 이를 수 있습니다.

지연 도구는 점진적 공개로 이 문제를 해결합니다. 자주 사용하지 않는 도구에 lazy: true를 지정하면 초기 시스템 프롬프트에서 제외됩니다. 모델에는 짧은 "검색 가능한 API" 카탈로그에 도구 이름만 표시됩니다. 모델이 도구를 사용해야 할 때 discover_tools 형제 도구를 호출해 TypeScript 시그니처를 가져온 다음 execute_typescript 내부에서 사용합니다. 모든 샌드박스 바인딩은 항상 주입되며, 지연되는 것은 호출 가능성이 아니라 _문서_뿐입니다.

도구를 지연 도구로 표시하기

지연하려는 도구의 toolDefinition 설정에 lazy: true를 추가합니다.

import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";

// Always eager — documented upfront
const fetchWeather = toolDefinition({
name: "fetchWeather",
description: "Get current weather for a city",
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ temperature: z.number(), condition: z.string() }),
}).server(async ({ location }) => {
const res = await fetch(`https://api.weather.example/v1?city=${location}`);
return res.json();
});

// Lazy — kept out of the system prompt until discovered
const fetchArchive = toolDefinition({
name: "fetchArchive",
description: "Retrieve historical weather archive data for a date range",
inputSchema: z.object({
location: z.string(),
from: z.string(),
to: z.string(),
}),
outputSchema: z.array(z.object({ date: z.string(), temperature: z.number() })),
lazy: true,
}).server(async ({ location, from, to }) => {
const res = await fetch(
`https://api.weather.example/v1/archive?city=${location}&from=${from}&to=${to}`
);
return res.json();
});

즉시 로드되는 도구에는 계속 시스템 프롬프트에 전체 타입 스텁이 포함됩니다. 지연 도구는 이름으로만 표시됩니다.

서버 설정

즉시 로드되는 도구와 지연 도구를 모두 createCodeMode에 전달합니다. 지연 도구가 하나 이상이면 createCodeModediscover_tools 형제 도구도 반환하므로, chat()에 전달하는 tools 배열에 포함합니다.

// server/route.ts
import { chat, maxIterations, toServerSentEventsStream } from "@tanstack/ai";
import { createCodeMode } from "@tanstack/ai-code-mode";
import { createNodeIsolateDriver } from "@tanstack/ai-isolate-node";
import { openaiText } from "@tanstack/ai-openai";

const { tools, systemPrompt } = createCodeMode({
driver: createNodeIsolateDriver(),
tools: [fetchWeather, fetchArchive], // fetchArchive is lazy
});

// tools is [execute_typescript, discover_tools]
// — discover_tools is included automatically because fetchArchive is lazy

export async function POST(req: Request) {
const { messages } = await req.json();

const stream = chat({
adapter: openaiText("gpt-5.5"),
systemPrompts: ["You are a helpful weather assistant.", systemPrompt],
tools: [...tools],
messages,
agentLoopStrategy: maxIterations(10),
});

return toServerSentEventsStream(stream);
}

createCodeMode{ tool, discoveryTool, tools, systemPrompt }를 반환합니다.

필드타입설명
toolServerToolexecute_typescript 도구(하위 호환)
discoveryToolServerTool | nulldiscover_tools 도구. 지연 도구가 없으면 null
toolsArray<ServerTool>[tool] 또는 [tool, discoveryTool]chat({ tools })에 전개
systemPromptstring이에 해당하는 시스템 프롬프트

지연 도구가 없으면 discoveryToolnull이고 tools에는 execute_typescript만 포함됩니다.

discover_tools 흐름

모델이 지연 도구가 필요한 작업을 만나면 다음을 수행합니다.

  1. 도구 이름(external_ 접두사가 없는 이름만)을 사용해 discover_tools를 호출합니다.
  2. 해당 도구의 TypeScript 타입 스텁과 설명을 받습니다.
  3. 이제 문서화된 external_fetchArchive(...) 호출을 사용해 execute_typescript 코드를 작성합니다.

바인딩은 항상 샌드박스에 주입됩니다. 도구 검색은 문서만 가져오며 바인딩을 활성화하지 않습니다. 모델은 먼저 검색하지 않고도 external_fetchArchive를 호출할 수 있지만, 타입 시그니처 없이 코드를 작성하게 됩니다.

검색 가능한 API 카탈로그 조정

기본적으로 지연 도구는 설명 없이 이름만 시스템 프롬프트에 표시됩니다.

### Discoverable APIs

- external_fetchArchive
- external_runReport
- external_exportData

검색 여부를 결정하기 전에 모델에 각 도구의 기능을 알려주려면 lazyToolsConfig.includeDescription을 사용합니다.

import { createCodeMode } from "@tanstack/ai-code-mode";
import { createNodeIsolateDriver } from "@tanstack/ai-isolate-node";
import {
fetchWeather,
fetchArchive,
runReport,
exportData,
} from "./tools";

const { tools, systemPrompt } = createCodeMode({
driver: createNodeIsolateDriver(),
tools: [fetchWeather, fetchArchive, runReport, exportData],
lazyToolsConfig: {
includeDescription: "first-sentence", // 'none' | 'first-sentence' | 'full'
},
});

'first-sentence'를 사용하면 카탈로그는 다음과 같이 표시됩니다.

### Discoverable APIs

- external_fetchArchive — Retrieve historical weather archive data for a date range.
- external_runReport — Generate a summary report for a given time period.
- external_exportData — Export query results to CSV or JSON format.
효과
'none'(기본값)이름만 표시 — 프롬프트에 추가되는 내용이 가장 적음
'first-sentence'이름과 도구 설명의 첫 문장 표시
'full'이름과 전체 설명 표시

검색할 때는 항상 전체 타입 스텁과 입력/출력 스키마가 반환됩니다. includeDescription은 검색 전 카탈로그에만 영향을 줍니다.

일반 chat()에서 지연 도구 사용하기

Code Mode 외부에서 chat()과 직접 사용하는 지연 도구에도 동일한 lazyToolsConfig 옵션이 작동합니다. lazy: true로 표시된 도구는 모델이 요청할 때까지 __lazy__tool__discovery__ 카탈로그 설명에서 제외됩니다. lazyToolsConfigchat()에 직접 전달합니다.

import { chat, maxIterations, toServerSentEventsStream } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { fetchWeather, fetchArchive, runReport } from "./tools";

// Non-code-mode: lazy tools in a regular chat agent
export async function POST(req: Request) {
const { messages } = await req.json();

const stream = chat({
adapter: openaiText("gpt-5.5"),
messages,
tools: [fetchWeather, fetchArchive, runReport],
lazyToolsConfig: {
includeDescription: "first-sentence",
},
agentLoopStrategy: maxIterations(10),
});

return toServerSentEventsStream(stream);
}

includeDescription의 동작은 동일합니다. 'none'은 도구 이름만 나열하고, 'first-sentence'는 첫 문장을 덧붙이며, 'full'은 전체 설명을 덧붙입니다.

  • 'none'으로 시작합니다. 도구 이름을 잘 추론하는 모델에는 이름만 표시하는 카탈로그로 충분합니다. 모델이 관련 없는 도구를 자주 검색할 때만 'first-sentence'를 추가합니다.
  • 지연 도구도 항상 호출할 수 있습니다. 모델이 discover_tools를 호출했는지와 관계없이 external_* 바인딩은 샌드박스에 주입됩니다. 검색은 문서만 공개합니다.
  • 관찰 가능성에는 discoveryTool을 사용합니다. discoveryTool.name("discover_tools")을 확인해 도구가 연결되었는지 검증하거나, 분석을 위해 호출을 기록할 수 있습니다.
  • 기능이 아니라 사용 빈도로 나눕니다. 일반적인 요청에서 거의 필요하지 않은 도구를 지연 도구로 표시합니다. 대부분의 요청에서 사용하는 핵심 도구는 즉시 로드되는 상태로 유지해야 합니다.

다음 단계