본문으로 건너뛰기

라이프사이클 경계

generic interrupt가 필요하지만 어느 단계에서 일시 중지할지는 모릅니다. 너무 일찍 일시 중지하면 모델에 검토할 초안이 없습니다. 너무 늦게 일시 중지하면 도구가 이미 실행되었습니다.

이 페이지를 끝까지 읽으면 INTERRUPT_BOUNDARY_PHASES에서 한 단계를 선택하고 해당 단계의 ctx.phase 가드를 작성할 수 있습니다.

정의, 등록 및 해결 단계는 Generic Interrupts를 참조합니다.

네 단계

onInterruptBoundary는 하나의 에이전트 반복에서 이러한 각 지점마다 실행됩니다. 일시 중지하려면 { interrupts }를 반환합니다. 실행을 계속하려면 아무것도 반환하지 않습니다.

단계실행 시점일반적인 질문
beforeModel이 반복의 onConfig 이후, 어댑터 호출 이전토큰을 사용하기에 사용자에게서 충분한 정보를 받았습니까?
afterModel모델 스트림 종료 이후, 도구 실행 이전이 초안 또는 도구 호출을 허용해도 됩니까?
beforeTools어시스턴트 도구 호출 메시지가 messages에 들어온 이후, 실행 이전이러한 도구를 실행해도 됩니까?
afterTools도구가 완료되고 결과 메시지가 messages에 들어온 이후이러한 결과를 모델에 전달해도 됩니까?

엔진은 동일한 단계에서 모든 미들웨어가 보낸 모든 요청을 하나의 인터럽트 배치로 결합합니다. 이 배치는 하나의 interrupt 결과와 함께 현재 실행을 종료합니다.

import type { ChatMiddleware } from '@tanstack/ai'
import { reviewPlan } from './interrupts'

export const requestReview: ChatMiddleware<unknown, typeof reviewPlan> = {
name: 'request-review',
onInterruptBoundary(ctx) {
if (ctx.phase !== 'beforeModel') return
if (ctx.parentRunId) return
if (ctx.iteration !== 0) return
return {
interrupts: [
reviewPlan.interrupt({
key: 'initial-plan',
reason: 'review-required',
message: 'Review the proposed plan.',
payload: {
title: 'Release plan',
changes: ['Add search', 'Add tests'],
},
}),
],
}
},
}

onInterruptBoundary는 config를 변경할 수 없으며 일시 중지만 수행할 수 있습니다. 사용자 답변에 따라 프롬프트, 도구 또는 메시지를 변경하려면 Apply Answers를 참조합니다.

continuation에서 일시 중지 건너뛰기

continuation은 새로운 chat() 호출입니다. 모든 경계 훅이 다시 실행됩니다.

동일한 요청을 반환하면 실행이 다시 일시 중지됩니다. ctx.parentRunId가 설정되어 있으면 emit을 건너뜁니다. 일시 중지가 원래 요청에만 해당할 때 이 건너뛰기를 사용합니다.

onInterruptBoundary(ctx) {
if (ctx.phase !== 'beforeModel') return
if (ctx.parentRunId) return
return {
interrupts: [
reviewPlan.interrupt({
key: 'initial-plan',
reason: 'review-required',
message: 'Review the proposed plan.',
payload: {
title: 'Release plan',
changes: ['Add search'],
},
}),
],
}
}

각 단계에서 읽을 수 있는 내용

모든 훅은 동일한 ChatMiddlewareContext를 받습니다. 변경되는 것은 내용입니다.

ctx에서 유용한 필드:

  • phaseiteration
  • parentRunId (continuation에서 설정됨)
  • messages (읽기 전용 뷰)
  • systemPrompts, toolNames, hasTools, modelOptions
  • accumulatedContent (이 모델 차례의 어시스턴트 텍스트)
  • modelprovider (이 요청에서 고정됨)
  • context (chat({ context })의 값)
  • abort(reason)defer(promise)

ctx.messages를 변경해도 엔진 config는 변경되지 않습니다.

beforeModel

이 반복의 onConfig는 이미 실행되었습니다. 프롬프트, 도구, 메시지 및 modelOptions는 해당 병합 이후의 값입니다.

  • accumulatedContent는 비어 있습니다
  • messages는 현재까지의 대화입니다

모델 호출 비용을 지불하기 전에 데이터가 필요할 때 이 단계를 사용합니다.

afterModel

모델 스트림이 완료되었습니다.

  • accumulatedContent에는 이 차례의 어시스턴트 텍스트가 있습니다
  • messages에는 아직 이 차례가 포함되지 않습니다
  • 제안된 도구 호출은 ctx에 없습니다. 아직 messages에도 없습니다

도구 이름이나 인수가 필요하면 beforeTools까지 기다리거나 modelStream 중에 onChunk를 확인합니다.

beforeTools

엔진이 toolCalls가 포함된 어시스턴트 메시지를 messages에 추가했습니다. 도구는 아직 실행되지 않았습니다.

부수 효과가 발생하기 전에 제안된 호출을 검사하려면 이 단계를 사용합니다.

afterTools

도구가 실행되었습니다. role: 'tool'인 결과 메시지가 이미 messages에 있습니다. onToolPhaseComplete도 이미 실행되었습니다.

다음 모델 차례 전에 결과를 검사하려면 이 단계를 사용합니다.

실제 사용 사례

beforeModel: 먼저 선택 받기

모델이 작성하기 전에 계획, 대상 또는 로캘을 요청합니다.

예:

  • "이 답변에 어떤 브랜드 보이스를 사용해야 합니까?"
  • "어떤 티켓을 처리해야 합니까?"
  • "이 요청이 이 에이전트의 범위에 포함됩니까?"

afterModel: 초안 검토하기

모델이 텍스트를 작성했습니다. 도구가 실행되기 전에 사람이 이를 승인하도록 합니다.

예:

  • sendEmail 전에 이메일 초안을 검토합니다
  • runQuery 전에 SQL 쿼리를 검토합니다
  • 사용자에게 전달되기 전에 지원 답변을 검토합니다

beforeTools: 부수 효과 제어하기

모델이 도구를 요청했습니다. 아무것도 실행되지 않았습니다.

예:

  • 일괄 삭제를 확인합니다
  • 결제를 확인합니다
  • 배포를 확인합니다

이는 tool approval과 유사합니다. 하나의 도구에 대한 예/아니요 질문이 아닐 때 generic interrupt를 사용합니다. 여러 도구를 하나의 배치로 판단해야 할 때도 사용합니다.

afterTools: 결과 감사하기

도구가 이미 실행되었습니다. 모델이 사용하기 전에 사람이 출력을 확인하도록 합니다.

예:

  • 검색 결과 고객 PII가 반환되었습니다. 컨텍스트에 유지해도 되는지 묻습니다
  • 코드 실행 결과 diff가 생성되었습니다. 다음 차례에 적용해도 되는지 묻습니다
  • 조회 결과 신뢰도가 낮은 일치 항목이 반환되었습니다. 어떤 레코드를 유지할지 묻습니다

사용해 보기

React chat 예제에는 네 단계 모두를 위한 플레이그라운드가 있습니다.

  1. examples/ts-react-chat을 시작합니다.
  2. /generic-interrupts를 엽니다.
  3. 단계를 선택하고 두 카드를 모두 해결합니다.

프롬프트에 답변을 적용하거나 실행을 중지할 준비가 되면 Apply Answers로 이동합니다.