laravel/ai의 v1.3.0 구현을 바탕으로, AI SDK의 기본 조작보다 한 걸음 더 나아가 실행 순서와 중단 조건을 정리합니다.
미들웨어가 실행되는 단위
에이전트가 모델의 응답을 받고 도구를 실행한 뒤 다시 모델을 호출하는 경우, 다음 모델 호출은 별도의 스텝이 됩니다. 각 스텝 직전에HasMiddleware::middleware()에서 반환한 미들웨어를 선언 순서대로 통과합니다.
미들웨어의 handle(PendingStep $step, Closure $next)에 전달되는 PendingStep에는 실행이 끝난 스텝의 배열 steps, 그 누적 사용량 usage, 현재의 0부터 시작하는 스텝 번호 number가 포함됩니다. 첫 usage에는 아직 이번 모델 호출의 사용량이 포함되지 않습니다.
$next($step)의 결과에 붙인 then()은 모델이 응답한 후, 도구 호출이 실행되기 전에 동작합니다. 도구 실행 후에만 호출되는 훅이 아닙니다. 또한 이 시점의 usage는 이전에 완료된 스텝의 합계이며, 진행 중인 요청의 토큰 수를 예측하지 않습니다.
스텝별 처리 등록하기
스텝 번호와 그때까지 사용한 입력 토큰 수만 로그에 남기는 예입니다. 프롬프트 본문이나 모델 출력을 무조건 로그에 저장하지 않도록 합니다.HasMiddleware를 구현하고 middleware()에서 인스턴스를 반환합니다.
middleware()의 나열 순서에서는 Stop을 먼저 둡니다. 상한을 넘어 다음 스텝을 시작하지 않는 경우, 뒤에 오는 LogAgentSteps는 그 스텝에서 실행되지 않습니다. 순서를 반대로 하면 로그의 “시작”만 기록되고 “모델이 응답함”은 기록되지 않을 수 있습니다. $next($step)의 결과를 handle()에서 반환하며, then()의 콜백에서는 응답을 반환할 필요가 없습니다.
Stop은 다음 모델 호출을 멈춘다
Stop::when(...)은 조건이 true가 되었을 때, Stop::unless(...)는 조건이 false가 되었을 때 해당 스텝 전에 처리를 종료합니다. 위 예는 완료된 스텝의 누적 입력 토큰이 50,000 이상이 되면 다음 호출을 하지 않습니다. 1회의 모델 호출이 임곗값을 넘기 전에 강제로 멈추는 하드 리밋이 아닙니다.
독자적인 조건으로 중단하고 싶은 미들웨어는
Laravel\Ai\Gateway\StepResult::stop()을 반환할 수도 있습니다. handle()에서는 $next($step)의 결과, StepResponse, 또는 StepResult::stop()을 반환합니다. 관계없는 타입을 반환하면 예외가 발생합니다.
실행 순서 검증하기
모델 응답과 도구 실행을 혼동하지 않도록, 최소한 다음 케이스를 확인합니다.- 1스텝만으로 이루어진 생성에서는 시작 로그와 모델 응답 로그가 한 번씩 출력된다.
- 도구에 이어지는 추가 스텝에서는 번호가 0부터 증가하고, 후속 스텝의
usage에 이전 스텝의 사용량이 포함된다. - 처음부터 중단 조건이 성립하는 경우, 모델 호출과
LogAgentSteps의 로그가 발생하지 않는다. - 도중에 조건에 도달하는 경우, 그때까지의 스텝은 남고 다음 스텝의 모델 호출은 발생하지 않는다.
then()에 부수 효과가 필요한 경우에도, 모델 응답을 얻기 전에 실패하거나 중단된 경로에서는 호출되지 않는다는 전제로 설계하세요.