`ToolLoopAgent`
ToolLoopAgent
여러 단계에 걸쳐 툴을 사용하며 텍스트를 생성·스트리밍할 수 있는 재사용 가능한 AI 에이전트 클래스예요. 자율적이고 다단계인 에이전트를 만들 때 사용해요.
출처: 문서
본문
텍스트를 생성하고, 응답을 스트리밍하며, 여러 단계에 걸쳐 툴을 사용할 수 있는(추론 및 실행 루프) 재사용 가능한 AI 에이전트를 만들어요. ToolLoopAgent는 자율적이고 다단계인 에이전트를 구축하기에 이상적이에요. 행동을 취하고, 툴을 호출하고, 중지 조건에 도달할 때까지 결과에 대해 추론할 수 있는 에이전트죠.
generateText() 같은 단일 호출과 달리, 에이전트는 완료되거나 사용자 승인이 필요할 때까지 반복적으로 툴을 호출하고, 툴 결과를 수집하고, 다음 행동을 결정할 수 있어요.
import { ToolLoopAgent } from 'ai';
__PROVIDER_IMPORT__;
const agent = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are a helpful assistant.',
tools: {
weather: weatherTool,
calculator: calculatorTool,
},
});
const result = await agent.generate({
prompt: 'What is the weather in NYC?',
});
console.log(result.text);
에이전트에서 runtimeContext는 루프를 통해 흐르는 공유 런타임 상태예요. runtimeContext, toolsContext, 툴 context, 민감한 컨텍스트 필터링에 대한 안내는 Runtime and Tool Context 문서를 참고하세요. 툴이 명령어나 코드 실행 환경에 접근해야 한다면 generate() 또는 stream()에 experimental_sandbox를 전달하세요.
ToolLoopAgent가 실제로 동작하는 모습은 아래 예시들을 확인해 보세요.
Import
import { ToolLoopAgent } from "ai"
생성자 (Constructor)
Parameters
- model:
LanguageModel(필수) — 사용할 언어 모델 인스턴스 (예: 프로바이더에서 가져온 것). - instructions:
Instructions(선택) — 에이전트용 지침. 보통 시스템 프롬프트/컨텍스트에 사용돼요. - allowSystemInMessages:
boolean(선택) —prompt또는messages필드에role: "system"메시지를 허용할지 여부. 설정하지 않으면 시스템 메시지는 거부되는데, 프롬프트 인젝션 공격 위험을 만들 수 있기 때문이에요. 이상적으로는instructions옵션을 사용하세요. 시스템 메시지를 허용하려면true, 명시적으로 거부하려면false로 설정해요. - tools:
Record<string, Tool>(선택) — 에이전트가 호출할 수 있는 툴 집합. 키는 툴 이름이에요. 툴을 사용하려면 기본 모델이 툴 호출을 지원해야 해요. - toolChoice:
ToolChoice(선택) — 툴 호출 선택 전략. 옵션:'auto' | 'none' | 'required' | { type: 'tool', toolName: string }. 기본값:'auto'. - stopWhen:
StopCondition | StopCondition[](선택) — 에이전트 루프를 끝내는 조건. 기본값:isStepCount(20). 모든 툴 호출이 완료될 때까지 실행하게 하려면isLoopFinished()를 사용하되, 무한 루프에 빠질 위험에 주의하세요. https://ai-sdk.dev/v7/docs/reference/ai-sdk-core/loop-finished#isloopfinished 참고. - activeTools:
ActiveTools<TOOLS>(선택) — 결과에서 툴 호출·결과 타입을 바꾸지 않고 모델이 호출할 수 있는 툴을 제한해요. 기본적으로 모든 툴이 활성화돼요. 툴 이름은 툴 집합의 문자열 키로 제한돼요. - toolOrder:
ToolOrder<TOOLS>(선택) — 툴이 프로바이더로 전송되는 순서를 제어해요. 목록은 부분적일 수 있어요.toolOrder에 없는 툴은 나열된 툴 뒤에 알파벳순으로 전송돼요. 툴 이름은 툴 집합의 문자열 키로 제한돼요. - toolApproval:
ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT>(선택) — 에이전트용 승인 설정. 모든 툴 호출을 하나의 콜백에서 처리하려면GenericToolApprovalFunction을 전달하세요(toolCall,tools,toolsContext,messages,runtimeContext를 받아요). 또는 툴별 객체를 전달할 수 있는데, 각 키는 상태('not-applicable','approved','denied','user-approval'),{ type: 'denied', reason: 'blocked by policy' }같은 객체 형태, 또는 툴 입력과toolCallId,messages,toolContext,runtimeContext옵션을 받는SingleToolApprovalFunction일 수 있어요(툴 실행 옵션과 같은 형태지만abortSignal은 없고context는toolContext로 이름이 바뀜).RUNTIME_CONTEXT타입 파라미터는 에이전트의runtimeContext와 일치해요.GenericToolApprovalFunction또는SingleToolApprovalFunction은'not-applicable'과 같은 효과를 내기 위해undefined를 반환할 수 있어요.'not-applicable'은 기본 실행 경로이며 승인 메타데이터 없이 툴을 실행해요. 출력에 명시적인 자동 승인 요청/응답 파트를 원할 때'approved','denied'또는 그 객체 형태를 사용하세요. 객체 상태에는reason을 포함할 수 있어요: 자동 승인/거부는 이를 승인 응답으로 전달하고, 수동 사용자 승인은 인간 승인자를 위한 승인 요청으로 전달해요. 이 설정은 툴의needsApproval기본값보다 우선해요. - experimental_toolCallers:
Experimental_ToolCallers<TOOLS>(선택) — 각 툴을 호출할 수 있는 호출자 툴을 설정해요. 피호출(callee) 툴 이름을 키로 하고 값에 호출 가능한 툴 이름 목록을 담은 객체를 전달하세요. 구성된 툴을 모델이 직접 호출할 수 있게 유지하려면@ai-sdk/code-mode에서DIRECT_TOOL_CALL을 포함하세요. 로컬 전용 피호출 툴은 직접 모델 호출에서 숨겨지고 각 에이전트 단계에서 자체 로컬 호출자에 바인딩돼요. 프로바이더 호출자 이름은 프로바이더 네이티브 허용 호출자 옵션으로 변환돼요. - output:
Output(선택) — 응답을 타입 안전한 데이터로 파싱하기 위한 선택적 구조화 출력 스펙. - prepareStep:
PrepareStepFunction(선택) — 각 에이전트 단계마다 단계 설정을 변경하거나 상태를 주입하는 선택 함수. temperature, maxOutputTokens, 샘플링 제어, 패널티, 중지 시퀀스, seed, reasoning 같은 단계별 모델 호출 설정을 포함해요. 모델 호출 설정 오버라이드는 현재 단계에만 적용돼요. - include:
{ requestBody?: boolean; requestMessages?: boolean; responseBody?: boolean; rawChunks?: boolean }(선택) — 단계 결과에 어떤 데이터를 포함할지 제어하는 설정. requestBody, requestMessages, responseBody는generate()에, requestBody, requestMessages, rawChunks는stream()에 적용돼요. - repairToolCall:
ToolCallRepairFunction(선택) — 툴 호출을 파싱할 수 없을 때 자동 복구를 시도하는 선택 콜백. - experimental_refineToolInput:
ToolInputRefinement<TOOLS>(선택) — 파싱된 툴 입력을 정제하는 함수들의 선택적 매핑. 각 함수는 해당 툴의 타입화된 입력을 받아 같은 입력 타입 형태를 반환해야 해요. 정제된 입력은generate()와stream()모두에서 툴 실행, 출력 파트, 라이프사이클 콜백, 텔레메트리에 사용돼요. - onStart:
GenerateTextOnStartCallback(선택) — 에이전트 작업이 시작될 때, LLM 호출이 이루어지기 전에 호출되는 콜백. 로깅, 분석, 상태 초기화에 유용해요.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onStepStart:
GenerateTextOnStepStartCallback(선택) — 단계(LLM 호출)가 시작될 때, 프로바이더가 호출되기 전에 호출되는 콜백. 각 단계는 단일 LLM 호출을 나타내요.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onToolExecutionStart:
OnToolExecutionStartCallback(선택) — 툴의 execute 함수가 실행되기 직전에 호출되는 콜백.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onToolExecutionEnd:
OnToolExecutionEndCallback(선택) — 툴의 execute 함수가 완료(또는 오류)된 직후에 호출되는 콜백.toolOutput필드는 판별 유니언(discriminated union)이에요:toolOutput.type이'tool-result'이면output필드에 툴 결과가,'tool-error'이면error필드에 오류가 담겨요.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onStepEnd:
GenerateTextOnStepEndCallback(선택) — 각 에이전트 단계(LLM/툴 호출)가 끝난 후 호출되는 콜백.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onStepFinish:
GenerateTextOnStepFinishCallback(선택) — 더 이상 사용하지 않음.onStepEnd를 사용하세요. 이 이름은onStepEnd가 제공되지 않을 때만 대체로 사용돼요. - onEnd:
GenerateTextOnEndCallback(선택) — 모든 에이전트 단계가 끝나고 응답이 완성되었을 때 호출되는 콜백. 단계 결과, 총 사용량, 공유runtimeContext,toolsContext를 받아요.generate()또는stream()에도 지정했다면 두 콜백 모두 호출돼요(생성자가 먼저). - onFinish:
GenerateTextOnEndCallback(선택) —onEnd의 더 이상 사용하지 않는 이름. - runtimeContext:
CONTEXT(선택) —prepareStep과 라이프사이클 콜백에 전달되는 사용자 정의 공유 런타임 컨텍스트 객체. - toolsContext:
InferToolSetContext<TOOLS>— 툴 이름을 키로 하는 툴별 컨텍스트 맵. 툴이 하나 이상contextSchema를 정의하면 필수이며, 컨텍스트가 필요한 툴이 없으면 허용되지 않아요. - telemetry:
TelemetryOptions(선택) — 선택적 텔레메트리 설정.- includeRuntimeContext:
{ [KEY in keyof CONTEXT]?: boolean }(선택) — 텔레메트리에 포함할 최상위 런타임 컨텍스트 속성. 명시적으로true로 설정하지 않으면 제외돼요. 라이프사이클 콜백과 반환 결과는 여전히 전체runtimeContext를 받아요. - includeToolsContext:
{ [TOOL_NAME in ...]?: { ... } }(선택) — 툴별로 설정하는, 텔레메트리에 포함할 최상위 툴 컨텍스트 속성. 명시적으로true로 설정하지 않으면 제외돼요. 라이프사이클 콜백과 반환 결과는 여전히 전체toolsContext를 받아요.
- includeRuntimeContext:
- experimental_download:
DownloadFunction | undefined(선택) — 실험적: 툴 또는 모델 사용을 위해 파일/URL을 가져오는 커스텀 다운로드 함수. 기본적으로 모델이 특정 미디어 타입의 URL을 지원하지 않으면 파일을 다운로드해요. - maxOutputTokens:
number(선택) — 모델이 생성할 수 있는 최대 토큰 수. - temperature:
number(선택) — 샘플링 온도. 무작위성을 제어하며 모델로 전달돼요. - topP:
number(선택) — Top-p (nucleus) 샘플링 파라미터. 모델로 전달돼요. - topK:
number(선택) — Top-k 샘플링 파라미터. 모델로 전달돼요. - presencePenalty:
number(선택) — presence 패널티 파라미터. 모델로 전달돼요. - frequencyPenalty:
number(선택) — frequency 패널티 파라미터. 모델로 전달돼요. - stopSequences:
string[](선택) — 모델 출력을 중지시키는 커스텀 토큰 시퀀스. 모델로 전달돼요. - seed:
number(선택) — 결정적 생성을 위한 seed (지원 시). - maxRetries:
number(선택) — 실패 시 재시도 횟수. 기본값: 2. - providerOptions:
ProviderOptions(선택) — 추가 프로바이더별 설정. - headers:
Record<string, string | undefined>(선택) — 요청과 함께 보낼 추가 HTTP 헤더. HTTP 기반 프로바이더에만 적용돼요. - callOptionsSchema:
FlexibleSchema<CALL_OPTIONS>(선택) —generate()또는stream()호출 시 전달할 수 있는 커스텀 호출 옵션용 선택 스키마. - prepareCall:
PrepareCallFunction(선택) — 호출 옵션을 기반으로 호출별 설정을 준비하는 선택 함수. - id:
string(선택) — 커스텀 에이전트 식별자.
프로퍼티 (Properties)
- tools:
Record<string, Tool>— 이 에이전트에 설정된 툴 집합. 읽기 전용. - id:
string | undefined— 생성자에서 제공된 경우의 에이전트 식별자.
메서드 (Methods)
generate()
응답을 생성하고 필요에 따라 툴 호출을 트리거하며, 에이전트 루프를 실행해 최종 결과를 반환해요. GenerateTextResult로 해석되는 프로미스를 반환해요.
const result = await agent.generate({
prompt: 'What is the weather like?',
});
파라미터:
- prompt:
string | Array<ModelMessage>— 텍스트 프롬프트 또는 메시지 배열. - messages:
Array<ModelMessage>— 모델 메시지 목록으로 된 전체 대화 기록. - abortSignal:
AbortSignal(선택) — 호출을 취소하는 데 사용할 수 있는 abort 신호. - timeout:
number | { totalMs?: number; stepMs?: number; firstChunkMs?: number; chunkMs?: number }(선택) — 밀리초 단위 타임아웃. 숫자 또는 totalMs, stepMs, firstChunkMs, chunkMs 속성을 가진 객체로 지정할 수 있어요. firstChunkMs와 chunkMs는generate()에는 효과가 없고, 스트리밍 전용이라stream()에서 적용돼요. abortSignal과 함께 사용할 수 있어요. - experimental_sandbox:
Experimental_SandboxSession(선택) —prepareStep, 툴 설명 함수, 툴 실행에 그대로 전달되는 실험용 샌드박스 환경. 툴은 설명 함수 옵션과 실행 옵션에서 접근할 수 있어요. - options:
CALL_OPTIONS(선택) — 에이전트가 callOptionsSchema로 설정된 경우의 커스텀 호출 옵션. - onStart / onStepStart / onToolExecutionStart / onToolExecutionEnd / onStepEnd / onEnd (선택) — 생성자와 동일한 라이프사이클 콜백들. 생성자에도 지정했다면 두 콜백이 모두 호출돼요(생성자가 먼저).
- onStepFinish / onFinish (선택) — 더 이상 사용하지 않는 별칭 (
onStepEnd/onEnd사용).
반환값
generate() 메서드는 GenerateTextResult 객체를 반환해요 (자세한 내용은 generateText 참고).
stream()
에이전트의 추론과 툴 호출을 발생하는 대로 포함한 응답을 스트리밍해요. StreamTextResult를 반환해요.
const stream = agent.stream({
prompt: 'Tell me a story about a robot.',
});
for await (const chunk of stream.textStream) {
console.log(chunk);
}
파라미터:
- prompt:
string | Array<ModelMessage>— 텍스트 프롬프트 또는 메시지 배열. - messages:
Array<ModelMessage>— 모델 메시지 목록으로 된 전체 대화 기록. - abortSignal:
AbortSignal(선택) — 호출을 취소하는 데 사용할 수 있는 abort 신호. - timeout:
number | { totalMs?: number; stepMs?: number; firstChunkMs?: number; chunkMs?: number }(선택) — 밀리초 단위 타임아웃. firstChunkMs는 각 모델 호출 단계에서 첫 콘텐츠를 포함한 출력을 기다리는 시간을 제한해요. chunkMs는 이후 콘텐츠를 포함한 출력 청크 사이의 간격을 제한해요. abortSignal과 함께 사용할 수 있어요. - experimental_sandbox:
Experimental_SandboxSession(선택) —prepareStep, 툴 설명 함수, 툴 실행에 그대로 전달되는 실험용 샌드박스 환경. - options:
CALL_OPTIONS(선택) — 에이전트가 callOptionsSchema로 설정된 경우의 커스텀 호출 옵션. - experimental_transform:
StreamTextTransform | Array<StreamTextTransform>(선택) — 선택적 스트림 변환. 주어진 순서대로 적용되며 스트림 구조를 유지해야 해요. 자세한 내용은streamText문서를 참고하세요. - onStart / onStepStart / onToolExecutionStart / onToolExecutionEnd / onStepEnd / onEnd (선택) — 생성자와 동일한 라이프사이클 콜백들. 두 콜백이 모두 호출돼요(생성자가 먼저).
- onStepFinish / onFinish (선택) — 더 이상 사용하지 않는 별칭.
반환값
stream() 메서드는 StreamTextResult 객체를 반환해요 (자세한 내용은 streamText 참고).
타입 (Types)
ActiveTools
type ActiveTools<TOOLS extends ToolSet> =
| ReadonlyArray<keyof TOOLS & string>
| undefined;
에이전트 단계를 나열된 툴 이름으로 제한해요. undefined는 툴 제한이 없음을 의미해요.
InferAgentUIMessage
주어진 에이전트 인스턴스에 대한 UI 메시지 타입을 추론해요. 타입 안전한 UI와 메시지 교환에 유용해요.
기본 예시
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
const weatherAgent = new ToolLoopAgent({
model: __MODEL__,
tools: { weather: weatherTool },
});
type WeatherAgentUIMessage = InferAgentUIMessage<typeof weatherAgent>;
메시지 메타데이터가 있는 예시
두 번째 타입 인자를 제공해 각 메시지의 메타데이터를 커스터마이즈할 수 있어요. 에이전트가 반환하는 풍부한 메타데이터(createdAt, tokens, finish reason 등)를 추적하는 데 유용해요.
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
import { z } from 'zod';
// Example schema for message metadata
const exampleMetadataSchema = z.object({
createdAt: z.number().optional(),
model: z.string().optional(),
totalTokens: z.number().optional(),
finishReason: z.string().optional(),
});
type ExampleMetadata = z.infer<typeof exampleMetadataSchema>;
// Define agent as usual
const metadataAgent = new ToolLoopAgent({
model: __MODEL__,
// ...other options
});
// Type-safe UI message type with custom metadata
type MetadataAgentUIMessage = InferAgentUIMessage<
typeof metadataAgent,
ExampleMetadata
>;
예시 (Examples)
툴이 있는 기본 에이전트
import { ToolLoopAgent, isStepCount } from 'ai';
import { weatherTool, calculatorTool } from './tools';
const assistant = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are a helpful assistant.',
tools: {
weather: weatherTool,
calculator: calculatorTool,
},
stopWhen: isStepCount(3),
});
const result = await assistant.generate({
prompt: 'What is the weather in NYC and what is 100 * 25?',
});
console.log(result.text);
console.log(result.steps); // Array of all steps taken by the agent
에이전트 응답 스트리밍
const agent = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are a creative storyteller.',
});
const stream = agent.stream({
prompt: 'Tell me a short story about a time traveler.',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
출력 파싱이 있는 에이전트
import { z } from 'zod';
const analysisAgent = new ToolLoopAgent({
model: __MODEL__,
output: {
schema: z.object({
sentiment: z.enum(['positive', 'negative', 'neutral']),
score: z.number(),
summary: z.string(),
}),
},
});
const result = await analysisAgent.generate({
prompt: 'Analyze this review: "The product exceeded my expectations!"',
});
console.log(result.output);
// Typed as { sentiment: 'positive' | 'negative' | 'neutral', score: number, summary: string }
예시: 승인된 툴 실행
import { ToolLoopAgent, ModelMessage, ToolApprovalResponse, tool } from 'ai';
import { z } from 'zod';
const agent = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are an agent with access to a weather API.',
tools: {
weather: tool({
description: 'Get the weather in a location',
inputSchema: z.object({
location: z.string(),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
}),
}),
},
toolApproval: {
weather: 'user-approval',
},
});
const messages: ModelMessage[] = [
{ role: 'user', content: 'Is it raining in Paris today?' },
];
const result = await agent.generate({ messages });
const approvals: ToolApprovalResponse[] = [];
for (const part of result.content) {
if (part.type === 'tool-approval-request') {
approvals.push({
type: 'tool-approval-response',
approvalId: part.approvalId,
approved: true,
});
}
}
messages.push(...result.responseMessages);
messages.push({ role: 'tool', content: approvals });
const approvedResult = await agent.generate({ messages });
console.log(approvedResult.text);
더 알아보기 (Learn more)
- generateText
- streamText
- embed
- embedMany
- rerank
- generateImage
- experimental_streamTranscribe
- experimental_streamTranslate
- transcribe
- generateSpeech
- experimental_generateVideo
- experimental_evaluate
- uploadFile
- uploadSkill
- Agent (Interface)
- ToolLoopAgent
- createAgentUIStream
- createAgentUIStreamResponse
- pipeAgentUIStreamToResponse
- experimental_startBatch
- tool
- experimental_getBatchStatus
- dynamicTool
- experimental_getBatchResults
- experimental_cancelBatch
- createMCPClient
- experimental_getRealtimeToolDefinitions
- toolSearch
- experimental_listBatches
- MCP Apps
- Experimental_StdioMCPTransport
- jsonSchema
- zodSchema
- valibotSchema
- Output
- filterActiveTools
- ModelMessage
- UIMessage
- validateUIMessages
- safeValidateUIMessages
- Experimental_SandboxSession
- createProviderRegistry
- customProvider
- cosineSimilarity
- wrapLanguageModel
- wrapImageModel
- LanguageModelV4Middleware
- extractReasoningMiddleware
- simulateStreamingMiddleware
- defaultInstructionsMiddleware
- defaultSettingsMiddleware
- addToolInputExamplesMiddleware
- extractJsonMiddleware
- isStepCount
- hasToolCall
- isLoopFinished
- simulateReadableStream
- smoothStream
- generateId
- createIdGenerator
- DefaultGeneratedFile