라이프사이클 콜백

라이프사이클 콜백

호출이 언제 시작해 언제 끝났는지, 각 스텝이 얼마나 걸렸는지, 도구가 성공했는지를 코드에서 직접 관찰하고 싶을 때가 있어요. 라이프사이클 콜백은 AI SDK 호출의 중요한 지점에서 나만의 코드를 실행하는 방법이에요. 로깅, 사용량 기록, 멀티 스텝 디버깅, 도구 실행 모니터링에 특히 유용하죠.

출처: 공식문서

본문

기본 사용법

generateText, streamText, embed, embedMany, rerank에 콜백을 옵션으로 넘기면 돼요.

import { generateText } from 'ai';

const result = await generateText({
  model: "xai/grok-4.6",
  prompt: 'What is the weather in San Francisco?',

  onStart({ callId, modelId }) {
    console.log('Generation started', { callId, modelId });
  },

  onEnd({ callId, usage, finishReason }) {
    console.log('Generation finished', {
      callId,
      finishReason,
      totalTokens: usage.totalTokens,
    });
  },
});

콜백은 동기·비동기 모두 가능하고, 콜백이 throw 하면 내부에서 잡혀서 AI SDK 호출은 계속 진행돼요. 다만 콜백이 라이프사이클의 일부로 실행되니 빠르게 유지하거나, 무거운 작업은 백그라운드 시스템에 넘기는 게 좋아요.

자동 OpenTelemetry 계측이 필요하면 텔레메트리를 쓰고, 특정 호출에 커스텀 코드를 붙이려면 이벤트 콜백을 쓰는 식으로 구분하면 돼요.

활용 사례

요청 로깅onStartonEnd로 호출 시작과 끝의 로그를 남겨요. callId가 라이프사이클 이벤트 전반에 걸쳐 동일하므로, 같은 요청의 로그를 연결할 수 있어 감사 로그나 대시보드, 기능별 사용량 추적에 잘 맞아요.

모델 성능 측정onLanguageModelCallEnd는 프로바이더 응답이 정규화·파싱된 뒤에 실행돼요. streamText에서는 첫 출력까지의 시간이나 출력 청크 사이 간격 같은 스트리밍 전용 타이밍도 함께 제공돼요. 성능 측정이 필요할 땐 onLanguageModelCallEnd로 프로바이더 작업을, SDK가 관리하는 로컬 도구 실행까지 포함한 타이밍이 필요하면 스텝 이벤트를 쓰면 돼요.

멀티 스텝 도구 호출 디버깅generateText/streamText에 도구를 쓰면 한 번의 사용자 요청이 여러 모델 호출(스텝)로 이어질 수 있어요. 모델이 한 스텝에서 도구를 호출하고, 결과를 받아 다음 스텝에서 최종 답을 내는 식이죠. onStepStart/onStepEnd로 각 스텝의 시작·끝을 잡으면 "모델이 도구를 불렀는지 곧바로 답했는지", "몇 스텝 걸렸는지", "어느 스텝이 토큰을 가장 많이 썼는지", "시간이 모델 응답에 갔는지 로컬 도구 실행에 갔는지"를 알 수 있어요.

const result = await generateText({
  model: "xai/grok-4.6",
  stopWhen: isStepCount(5),
  prompt: 'What is the weather in San Francisco?',
  tools: {
    weather: tool({
      description: 'Get the weather in a location',
      inputSchema: z.object({ location: z.string() }),
      execute: async ({ location }) => getWeather(location),
    }),
  },

  onStepStart({ stepNumber, messages, steps }) {
    console.log(`Step ${stepNumber} started`, {
      messageCount: messages.length,
      previousSteps: steps.length,
    });
  },

  onStepEnd({ stepNumber, finishReason, toolCalls, usage, performance }) {
    console.log(`Step ${stepNumber} finished`, {
      finishReason,
      toolCalls: toolCalls.map(toolCall => toolCall.toolName),
      totalTokens: usage.totalTokens,
      stepTimeMs: performance.stepTimeMs,
    });
  },
});

도구 실행 모니터링onToolExecutionStart/onToolExecutionEnd가 도구의 execute 함수 전후로 실행돼요. 도구 사용량·지연·성공 결과·도구 에러를 기록하는 데 쓰죠. toolOutput은 판별 유니언이라, toolOutput.type === 'tool-result'면 결과가 toolOutput.output에, 'tool-error'면 에러가 toolOutput.error에 담겨요.

임베딩·리랭킹 관찰 — 임베딩과 리랭킹은 onStart/onEnd만 있는 더 단순한 구조예요. 자주 임베딩하는지, 값을 몇 개 임베딩하는지, 리랭킹이 결과 집합을 어떻게 바꾸는지 파악하는 검색 파이프라인에 잘 맞아요.

생성 라이프사이클 이해하기

generateTextstreamText는 프롬프트·모델 호출·도구 호출·여러 스텝이 얽힐 수 있어 가장 풍부한 라이프사이클을 제공해요. 단일 스텝 생성은 보통 이 순서로 진행돼요.

onStart
onStepStart
onLanguageModelCallStart
onLanguageModelCallEnd
onStepEnd
onEnd

로컬 도구 실행이 있는 멀티 스텝 생성은 onStartonStepStartonLanguageModelCallStartonLanguageModelCallEndonToolExecutionStartonToolExecutionEndonStepEnd를 정지 조건이 충족될 때까지 반복한 뒤 onEnd로 끝나요.

여기서 핵심 구분이 있어요. onStepStart/onStepEnd스텝 전체를, onLanguageModelCallStart/onLanguageModelCallEnd그 스텝 안의 모델 호출만을 나타내요. 스텝에 로컬 도구 실행이 포함되면 스텝 시간이 모델 응답 시간보다 길어질 수 있으니 이 구분이 중요하죠.

런타임·도구 컨텍스트

라이프사이클 콜백은 호출을 관통하는 전체 runtimeContexttoolsContext를 받아요. 프롬프트나 도구 입력을 바꾸지 않고 애플리케이션 컨텍스트를 붙이기에 좋아요. 다만 텔레메트리 통합은 내보내기 전에 이 컨텍스트를 필터링할 수 있지만, 콜백은 전체 객체를 받으므로 콜백에서 비밀이나 민감 사용자 데이터를 로깅하지 않도록 주의해야 해요.

사용 가능한 콜백

generateTextstreamTextonStart, onStepStart, onLanguageModelCallStart, onLanguageModelCallEnd, onToolExecutionStart, onToolExecutionEnd, onStepEnd, onEnd를 지원해요. onStepFinish는 더 이상 사용하지 않으니 새 코드에서는 onStepEnd를 쓰면 돼요. embed/embedManyrerankonStart/onEnd만 제공해요.

각 콜백 이벤트가 주는 필드는 문서의 이벤트 데이터 참조에 상세히 정리되어 있어요. 예컨대 onStepEnd의 이벤트는 해당 스텝의 전체 StepResult예요. model, content, text, toolCalls, toolResults, finishReason, usage, performance, warnings, request, response 등을 담고 있죠. onEnd는 모든 스텝 결과와 응답 메시지, 모든 스텝을 합친 최종 사용량을 모아서 줘요.

더 알아보기