타임아웃

타임아웃 (Timeouts)

런 안의 한 단계가 얼마나 오래 걸릴지를 제한하는 것과, 도구 안에서 런을 끝내는 것은 각각 다른 메커니즘으로 해결돼요. 실패 양상도 서로 달라요. 이 페이지가 그 지도를 정리해드릴게요. 이미 날아가고 있는 런을 멈추는 방법(Cancelling a Run)은 따로 있으니, 흐름이 필요할 때 그쪽을 봐주세요.

출처: 공식문서 — Timeouts

한 단계의 소요 시간 제한하기

아래 손잡이(knob)는 각각 다른 단위의 작업을 제한해요. 어느 것도 런 전체의 벽시계(wall-clock) 시간을 제한하진 않아요.

제한하고 싶은 대상 설정 방법 만료 시 일어나는 일
모델 요청 한 번의 시도 — 제공자 SDK 클라이언트의 재시도가 시도마다 다시 켬 ModelSettingstimeout 제공자 클라이언트가 예외를 던지고, FallbackModel 이나 transport 재시도가 처리하지 않으면 런이 실패
함수 도구 호출 하나 Agent(tool_timeout=...), 또는 도구 개별의 timeout=Tool Timeout 참고 모델이 'Timed out after N seconds.' 라는 재시도 프롬프트를 받고, 그 도구의 재시도 예산을 소진. def 도구는 실제로 멈추진 않아요 — 마감은 await 둘레에서 지켜지므로 워커 스레드는 끝까지 돕니다
함수 하나 @hooks.on.* 데코레이터의 timeout= HookTimeoutErrorAgentRunError 의 하나로 런을 중단. def 도구와 마찬가지로 def 훅도 실제로 멈추진 않고 워커 스레드는 끝까지 돕니다
MCP 서버 연결 MCPToolset(init_timeout=...), 기본 5 연결과 initialize 핸드셰이크가 실패
MCP 요청 하나 MCPToolset(read_timeout=...), 기본 300 요청이 실패. 기본 tool_error_behavior='retry' 아래에선 모델이 재시도 가능한 도구 오류로 봄
리얼타임 세션 열기 RealtimeModelSettingshandshake_timeout, 기본 30초 — OpenAI·Azure OpenAI·xAI 세션을 열 때 RealtimeError 발생. 재연결 시엔 ReconnectPolicy 시도 하나를 소비
런이 한 총 작업량 UsageLimits — 요청·도구 호출·토큰·비용 — Usage Limits 참고 UsageLimitExceeded
런 전체의 벽시계 시간 내장된 것 없음 — agent.run()asyncio.timeout(Python 3.11+)이나 anyio.fail_after() 로 감싸거나, 타이머에서 CancellationToken 을 취소 런이 취소

이 중 두 개는 한마디 덧붙일 게 있어요.

  • ModelSettings['timeout'] 은 모델 클래스마다 적용되며 보편적이지 않아요. 이 설정을 자기 제공자 클라이언트에 넘기는 모델 클래스는 ModelSettings.timeout 아래에 나열돼 있고, OpenAI 기반으로 만든 것들은 OpenAIChatModel / OpenAIResponsesModel 에서 포워딩을 물려받아요. 나머지 모델 클래스는 이 설정을 무시하고, 대신 그들이 만들 때 쓴 HTTP 클라이언트의 타임아웃이 적용돼요. Pydantic AI가 그 클라이언트를 직접 만들 땐 600초 전체 타임아웃에 5초 연결 타임아웃이 기본이에요. Google과 Mistral은 추가로 httpx.Timeout 객체를 거부하고 초 단위 숫자만 받아요.

    이 설정을 무시하는 모델 클래스의 요청을 제한하려면 그 제공자가 실제로 받아들이는 위치에 타임아웃을 설정하세요. 대부분 제공자는 여러분의 http_client를 받지만, 몇몇은 안 그래요: XaiProvider 는 클라이언트 수준 timeout(또는 미리 설정된 xai_client)을 받고, BedrockProvideraws_read_timeout·aws_connect_timeout(또는 미리 설정된 bedrock_client)을 받으며, HuggingFaceProviderhttp_client를 아예 거부하고 hf_client를 요구해요.

  • 도구 타임아웃은 FunctionToolset 에서만 강제되고, 각 툴셋은 자기만의 걸 가져요. Agent(tool_timeout=...) 는 에이전트에 직접 등록한 도구들의 기본값을 정해요 — 여러분이 직접 만들어 toolsets=[...] 로 전달한 FunctionToolset 까진 닿지 않아요. 그 툴셋엔 자체 FunctionToolset(timeout=...) 을 주거나, 개별 도구에 timeout= 을 설정하세요. MCP 서버, 외부 툴셋, 또는 커스텀 AbstractToolset 에서 오는 도구는 둘 다 읽지 않아요 — 그건 서버 쪽이나 transport 수준 타임아웃으로 제한하세요.

도구 본문 안에서 직접 마감을 강제한다면, TimeoutError를 잡아 ModelRetryToolFailed 로 다시 던지는 게 낫고, 밖으로 새어 나가게 두면 안 돼요. 맨 TimeoutError에 무슨 일이 벌어지는지는 그 도구가 자기 타임아웃을 갖느냐에 달려 있어요:

  • 도구나 그 툴셋에 timeout이 없을 때. 보통 예외로 취급되어 에이전트 런 밖으로 전파돼요 — 다만 on_tool_execute_error 를 구현하는 capability 가 있으면 그걸 대체 도구 결과나 ModelRetry 로 바꿀 수 있어요.
  • timeout 이 설정돼 있을 때. 호출이 anyio.fail_after(timeout) 안에서 돌아는데, 그쪽도 만료를 TimeoutError 로 알려요. 그래서 여러분이 직접 던진 TimeoutError는 마감이 지난 것과 구분이 안 되고, 같은 'Timed out after N seconds.' 재시도 프롬프트가 돼요 — 실제로 지나지 않은 마감을 보고하게 되는 거죠.

도구 안에서 다시 던지는 쪽이 더 국소적인 선택이고, 훅은 모든 도구에 정책 하나를 적용하는 용도예요.

도구 안에서 런 끝내기

도구가 무엇을 던지느냐가 런이 계속될지, 모델이 무엇을 보게 될지를 결정해요.

던지는 예외 런 계속? 모델이 보는 것
ModelRetry 호출을 고치라고 요청하는 재시도 프롬프트 — 그 도구의 재시도 예산을 소비
ToolFailed 적응해야 할 실패한 도구 결과 — 재시도 예산은 소비하지 않음
ApprovalRequired / CallDeferred DeferredToolRequests 출력으로 런 종료 — 단, HandleDeferredToolCalls 핸들러가 그 호출을 인라인으로 해결하지 않을 때. 아직 없는 경우: Deferred Tools 참고
그 외 어떤 예외 아니요 기본적으로 아무것도 아님 — agent.run() 밖으로 전파. on_tool_execute_error 를 구현한 capability 가 먼저 보고, 대체 도구 결과를 돌려주거나 ModelRetry를 던져 런을 계속하게 할 수 있음

지연(deferred) 행은 리얼타임 세션 안에선 다르게 읽혀요 — 거기엔 멈출 방법이 없거든요. 살아있는 대화는 대역 외(out-of-band) 결과를 기다릴 수 없어요. HandleDeferredToolCalls 핸들러가 여전히 그 호출을 인라인으로 해결할 기회는 받지만, 런이 DeferredToolRequests 출력으로 끝나는 자리에 세션은 대신 모델에게 '그 도구는 세션 중에 완료할 수 없다'는 설명으로 답하고 계속 진행돼요. Deferred and approval-required tools 참고.

도구는 예외를 던지지 않고도 런을 끝낼 수 있어요 — RunContext.cancel() 을 호출하면 되죠. 그러면 런이 RunCancelled 로 끝나고 도구의 반환값은 버려져요. Cancelling the Run from a Tool 참고.

런을 성공적인 출력으로 일찍 끝내는 예외는 없어요. 도구가 값을 가지고 런을 마무리하게 하려면 그 값을 런의 출력으로 만들면 됩니다 — 모델이 호출할 수 있는 출력 도구 를 에이전트에 주거나, 결과를 만들어내는 출력 함수 를 주면 돼요.

더 알아보기 (Learn more)