CCAR-P 개념 카드

■ stop_reason 으로 분기

refusal 포함1.2 본편

표시가 없는 문장은 공식 문서에서 확인한 내용이에요. 다른 표시는 읽는 법

왜 배우나 — 시험이 요구하는 것

시험 목표 1.2 입력부터 피드백까지 구조 설계 · D1 설계 17%
Design end-to-end architectures (input → processing → output → feedback loops)

이 개념으로 푸는 판단 (할 수 있어야 하는 것)

  • 1.2② 앱은 다음 단계를 무엇으로 아나? — 처리 루프를 무엇으로 제어하나 (stop_reason)
  • 5.2② HTTP 200 인데 content 가 비었다 — 어떻게 처리하나? — stop_reason 으로 분기하고 거절을 처리하기
  • 4.6③ 5xx 오류율은 0 인데 "답이 비었다" 는 신고가 는다 — 거절(refusal)을 따로 계측하기
  • 7.3③ API 오류가 쏟아질 때 무엇부터 보나? — 오류 종류별 대응 (재시도할 것 / 한도를 볼 것 / 오류가 아닌 것)

한눈에

모든 응답에는 stop_reason 이 있다. 이 값은 Claude 가 왜 멈췄는지 알려 준다. 앱은 HTTP 상태가 아니라 이 값으로 다음 걸음을 정한다

  • 그대로 쓴다
  • 이어 간다
  • 재시도한다
  • 다른 모델로 폴백한다

stop_reason 은 성공 응답 본문의 일부다. 오류(4xx·5xx)는 요청 처리 실패로 따로 온다

비유 컨테이너 종료 코드(0 정상 · 137 OOM)와 같다. "끝났다" 만 보고 코드를 안 보면 OOM 을 놓친다

HTTP 상태만 보면 거절도 200 이라 성공처럼 보이고, stop_reason 을 봐야 그대로 씀·이어감·재시도·다른 모델로 폴백 중 다음 걸음을 정한다는 대비 그림.
그림으로 보기 (AI 생성 · Codex · 누르면 크게)

ELI5

택배 기사가 문 앞에 쪽지를 붙이고 가는 것과 같아요.

  • "배달 끝" 쪽지면 상자를 열면 돼요.
  • "도장이 필요해요" 쪽지면 도장을 찍어 돌려줘야 다음 배달이 와요.
  • "이 물건은 못 싣습니다" 쪽지가 붙어도 초인종은 똑같이 울려요.
  • 그래서 초인종 소리만 듣지 말고 쪽지를 꼭 읽어야 해요.

개념 설명

영상 — 200 이 왔는데, 성공일까? (약 43초 · 소리 없음 · 자막). 응답 5건·거절 2건·재시도 3번은 그림용 가정 작은 화면에서는 전체화면(⛶)으로 보세요.

원리

왜 따로 있나

  • 오류(4xx·5xx)는 요청 처리가 실패한 것이다. stop_reason 은 처리는 성공했고, 생성이 왜 멈췄는지를 알려 준다. 둘은 다른 칸이다. 그래서 HTTP 상태만 보면 stop_reason 이 말하는 것을 전부 놓친다.
  • 앱이 할 다음 걸음은 네 갈래다.
    • 그대로 쓴다
    • 이어 간다(tool_use · pause_turn · max_tokens)
    • 잘렸다고 표시한다
    • 다른 모델로 재시도한다(refusal)

이어가는 세 값의 차이

  • tool_use : 내 도구를 기다린다. 그래서 내가 tool_result 를 보낸다. 그 메시지에는 tool_result 만 넣는다(뒤에 텍스트를 붙이면 턴이 끝난다).
  • pause_turn : 서버 도구(웹 검색 등) 루프가 반복 한도(기본 10회)에 걸렸다. 받은 assistant 콘텐츠를 그대로 돌려보낸다. 내 도구가 기다리는 중이면 절대 pause_turn 이 아니라 tool_use 가 온다.
  • max_tokens : 내가 준 출력 한도에 걸렸다. 한도를 올리거나 이어서 생성한다. model_context_window_exceeded 는 창 자체를 채운 것이다. 잘린 것으로 처리한다.

거절이 까다로운 이유

  • 안전 분류기(Fable 5.1 · Fable 5 · Opus 5.5 · Opus 5)가 막으면 HTTP 200, stop_reason: "refusal", 빈 content 가 온다. 오류가 아니라서 오류율·5xx 감시가 못 본다.
  • 같은 모델은 대개 또 거절한다. 다른 모델로 보내면 대개 답이 나온다. 그래서 처방은 폴백이다.
  • stop_details.category 는 분야 이름일 뿐이다. null 이 정상 값일 수 있다. 그래서 분기 기준은 stop_reason 그 자체다.
  • 폴백은 "요청의 속성" 이다. 폴백을 안 단 경로(재시도 핸들러 · 백그라운드 워커 · 서브에이전트 호출)에는 폴백이 적용되지 않는다.

핵심 질문 두 개

  • 앱은 다음 걸음을 무엇으로 정하나? HTTP 상태인가, stop_reason 인가?
  • 거절이 나면 누가 보고, 어디로 다시 보내나?

규칙과 판단

stop_reason 값 7개마다 할 일을 짝지은 표. end_turn 은 그대로 씀, tool_use 는 실행하고 결과 돌려줌, refusal 은 다른 모델로 재시도.
그림으로 보기 (AI 생성 · Codex · 누르면 크게)

갈림길

① 값별 할 일 [1.2②]

stop_reason 이 무엇인가?HTTP 상태 말고
end_turn

그대로 쓴다

자연 종료다

tool_use

실행하고 결과를 돌려준다

tool_use 블록마다 tool_result 를 1개 보낸다

pause_turn

그대로 다시 보낸다

서버 도구 루프가 반복 한도(기본 10회)에 걸린 것이다. 받은 assistant 콘텐츠를 그대로 보내 이어 간다

max_tokens

한도를 올리거나 이어서 생성한다

max_tokens 한도에 도달한 것이다. 잘린 답에는 불완전하다는 안내를 붙인다

model_context_window_exceeded

잘린 것으로 처리한다

모델 컨텍스트 창을 채운 것이다. 불완전하다는 안내를 붙이거나 이어서 생성한다

stop_sequence

어느 것인지 읽는다

stop_sequence 필드를 읽는다

refusal

↓ 아래 「거절 처리」 갈림길로 이어짐

다음 물음 — 무엇으로 알아채나?

③④⑤ 거절 처리 [5.2②] [4.6③]

무엇으로 알아채나?200 이라 오류가 아니다
HTTP 상태 · 5xx

안 보인다

오류율·5xx 기반 감시에는 안 보인다

category · content

이것으로 분기하지 않는다

category·explanation 은 null 일 수 있다

stop_reason = "refusal"
어디로 재시도하나?
같은 모델

대개 또 거절한다

재시도는 다른 모델로 한다

다른 모델
어디서 돌리나?
Claude API · 가장 간단

서버 측 fallbacks

한 번 왕복으로 끝난다. 베타다

SDK 사용 · 모든 플랫폼

SDK 미들웨어

클라이언트에 한 번 설정한다

배치

모아서 다시 제출한다

배치에는 서버 측 폴백이 없다

⑤ 폴백 구멍 [5.2②] [7.3③]

폴백을 켰는데도 빈 답이 오나?
429 · 과부하 · 5xx

안전 분류기 거절만 폴백한다

429·과부하·서버 오류는 그대로 돌아온다

폴백 모델이 바쁘다

앞 거절을 돌려준다

폴백 모델이 rate limit(속도 제한)·과부하 상태면 폴백하지 않는다. 앞 거절을 돌려준다(recommended_model 힌트)

서브에이전트 호출

따로 설정한다

fallbacks 는 도구 실행 안의 모델 호출로 번지지 않는다

재시도 핸들러 · 백그라운드 워커

모든 요청 경로에 단다

전역 플래그가 아니라 요청마다 단다

흐름

그림 속 이름·금액·시간·토큰 수는 예시예요. 공식 한도·동작은 카드 본문 값이 기준.

장면 글로 보기

칸 : 앱 → 주 모델 → 폴백 모델 → 도구

규칙 (앱) : HTTP 상태가 아니라 stop_reason 으로 다음 걸음을 정한다

질문 : 앱은 무엇을 보고 다음 걸음을 정할까?

① tool_use 루프 ✓

  1. 시작 : 앱 — 말 : “주문 #1042 어디쯤 왔어요?”
  2. 요청 : 앱 → 주 모델 — 쪽지 : user: 주문 #1042 어디쯤? / tools: [get_order]
  3. 멈춤 : 주 모델 → 앱 : tool_use — 쪽지 : stop_reason: "tool_use" / get_order(1042)
  4. 실행 : 앱 → 도구 : 도구를 실행한다 — 쪽지 : get_order(1042)
  5. 결과 : 도구 → 앱 — 쪽지 : 상태: "배송 중"
  6. 이어가기 : 앱 → 주 모델 : tool_result 만 담은 user 메시지 — 쪽지 : tool_result: / 상태: "배송 중"
  7. 답 : 주 모델 → 앱 : end_turn 이면 끝난다 — 쪽지 : stop_reason: "end_turn" / "지금 배송 중이에요"
  8. 끝 : tool_use 면 실행하고 결과를 넣어 다시 보낸다. end_turn 이면 끝난다

② 거절하면 다른 모델로 ✓

  1. 시작 : 앱 — 말 : “(고객 문의 한 건)”
  2. 요청 : 앱 → 주 모델 — 쪽지 : model: Opus 5.5 / messages: 고객 문의
  3. 멈춤 : 주 모델 → 앱 : HTTP 200 + refusal — 쪽지 : HTTP 200 / stop_reason: "refusal" / content: []
  4. 재시도 : 앱 → 폴백 모델 : 같은 요청을 다른 모델로 보낸다 — 쪽지 : 같은 messages / → 폴백 모델
  5. 답 : 폴백 모델 → 앱 — 쪽지 : stop_reason: "end_turn" / content: 답 본문
  6. 끝 : 폴백 뒤 대화는 폴백 모델에서 이어 간다

③ 200 이니 성공 ≠

  1. 시작 : 앱 — 말 : “(고객 문의 한 건)”
  2. 건너뜀 : 폴백 모델
  3. 요청 : 앱 → 주 모델 — 쪽지 : model: Opus 5.5 / messages: 고객 문의
  4. 멈춤 : 주 모델 → 앱 : HTTP 200 + refusal — 쪽지 : HTTP 200 / stop_reason: "refusal" / content: []
  5. 표시 : 앱 : 빈 답을 성공으로 보여 준다 — 쪽지 : 화면: 빈 답 / 5xx 알림: 0건
  6. 끝 : 오류율·5xx 감시에는 안 보인다

나머지 규칙

  1. tool_use 이어가기 : 다음 user 메시지에는 tool_use 블록마다 tool_result 를 1개씩 넣는다. 그 밖에는 아무것도 넣지 않는다. 같은 tools 배열을 유지한다 [1.2②]

    tool_result 뒤에 텍스트를 붙이면 assistant 턴이 끝난다(기다리던 서버 도구가 있으면 400). 더 할 말은 턴이 끝난 뒤 별도 user 메시지로 보낸다

    pause_turn 은 클라이언트 tool_use 가 기다리는 응답에는 절대 오지 않는다. 그때는 tool_use 가 온다

    에이전트 루프에서는 tool_use 면 실행하고 결과를 넣어 다시 보낸다. end_turn 이면 끝난다 (Skilljar)

  2. 배치 : 거절 항목은 result.type: "succeeded" 와 stop_reason: "refusal" 로 온다. 배치에는 서버 측 폴백이 없다(fallbacks 를 넣으면 항목별 오류가 난다). 그래서 거절 항목만 모아 폴백 모델로 새 배치를 내거나 직접 요청한다. 자세한 것은 ◆ Batch API vs 동기 호출 에 있다

  3. 과금 : 출력 전 거절은 bio·frontier_llm·reasoning_extraction 만 청구한다. 나머지 분류와 null 은 무료다 (2026-09-24 청구 재개 — 시험 뒤 변경). 스트림 중간 거절은 입력과 이미 나온 출력을 청구한다. 거절도 rate limit 에는 센다

규칙 원문 ①③④⑤ (갈림길로 그린 규칙)
  1. 값 7개와 할 일 [1.2②]

    값뜻할 일
    end_turn자연 종료그대로 쓴다
    max_tokensmax_tokens 한도 도달한도를 올리거나 이어서 생성한다
    stop_sequence내 stop_sequences 중 하나가 나왔다stop_sequence 로 어느 것인지 본다
    tool_use도구 호출실행하고 결과를 돌려준다
    pause_turn서버 도구 루프 반복 한도(기본 10회)받은 assistant 콘텐츠를 그대로 다시 보내 이어 간다
    refusal거절stop_details 를 읽고 폴백 모델로 재시도한다
    model_context_window_exceeded모델 컨텍스트 창을 채웠다잘린 것으로 처리한다

    잘린 답(max_tokens · 창 초과)에는 불완전하다는 안내를 붙이거나 이어서 생성한다

    스트리밍이면 stop_reason 은 message_start 에서는 null 이다. message_delta 에서 온다

  2. 거절은 HTTP 200 과 stop_reason: "refusal" 로 온다. content 는 비어 있다. 안전 분류기가 있는 모델은 Fable 5.1 · Fable 5 · Opus 5.5 · Opus 5 다 [5.2②]

    분기는 stop_reason(또는 stop_details.type)으로 한다. content 나 category·explanation 으로 분기하지 않는다. 둘 다 null 일 수 있다. null 자체가 정상 값이다. 나중에 채워질 자리표시가 아니다. explanation 은 보여 주기만 하고 파싱하지 않는다

    재시도는 다른 모델로 한다. 같은 모델에 다시 보내면 대개 또 거절한다

    스트림 중간에 거절하면 부분 출력은 불완전하다. 그래서 버린다. 폴백 뒤 멀티턴은 폴백 모델에서 이어 간다

  3. 거절은 따로 계측 : 거절은 오류율·5xx 기반 감시에는 안 보인다. 그래서 거절마다 이벤트를 1개 남긴다. 폴백으로 처리된 응답마다 이벤트를 1개 남긴다. 두 수의 차이에 알림을 건다 [4.6③]

    Claude Code 는 거절에 api_error 를 띄우지 않는다. 대신 claude_code.api_refusal 이벤트를 남긴다. 자세한 것은 ■ observability 세 신호 에 있다

  4. 재시도 세 방법 [5.2②] [7.3③]

    상황방법
    Claude API, 가장 간단서버 측 fallbacks(베타)다. 한 요청에 한 응답이다
    모든 플랫폼, Anthropic SDKSDK 미들웨어다. 클라이언트에 한 번 설정한다
    raw HTTP · 직접 짠 재시도직접 재시도하고 fallback credit 을 쓴다(프롬프트 캐시 값을 두 번 내지 않는다)

    "default" 모드는 거절 분류별 권장 폴백 모델로 재시도한다. 권장이 없는 분류는 거절을 그대로 돌려준다 (2026-07-24 추가 — 시험 뒤 변경일 수 있다)

    폴백은 안전 분류기 거절에만 일어난다. 429·과부하·서버 오류는 그대로 돌아온다. 자세한 것은 ■ rate limit 에 있다

    폴백 모델이 rate limit·과부하 상태면 폴백하지 않는다. 앞 거절을 돌려준다(recommended_model 힌트)

    구멍은 네 가지로 막는다

    • 모든 요청 경로(재시도 핸들러 · 오류 복구 분기 · 백그라운드 워커)에 폴백을 단다
    • 서브에이전트 호출에는 따로 단다
    • 전역 플래그가 아니라 요청의 속성으로 단다
    • 재시도 예산은 요청 단위로 잡는다
더 깊이 : 판단 규칙 · 상황 변수 (설명)

판단 규칙

조건답근거
응답을 받았다HTTP 상태가 아니라 stop_reason 으로 분기한다handling-stop-reasons
tool_use 다도구를 실행한다. tool_use 블록마다 tool_result 를 1개 보낸다. 그 메시지에는 tool_result 만 넣고, 같은 tools 를 쓴다handling-stop-reasons
pause_turn 이다받은 assistant 콘텐츠를 그대로 다시 보내 이어 간다handling-stop-reasons
max_tokens · model_context_window_exceeded 다잘린 것으로 본다. 안내를 붙이거나 이어서 생성한다. max_tokens 는 한도를 올려도 된다handling-stop-reasons
refusal 이다stop_reason 으로 감지한다. 다른 모델로 재시도한다refusals-and-fallback
Claude API 에서 거절을 가장 간단히 재시도하려 한다서버 측 fallbacks(베타)를 쓴다refusals-and-fallback
여러 플랫폼에서 Anthropic SDK 로 거절을 재시도하려 한다SDK 미들웨어를 쓴다refusals-and-fallback
raw HTTP 로 거절을 재시도하려 한다직접 재시도하고 fallback credit 을 쓴다refusals-and-fallback
배치 항목이 거절됐다거절 항목을 모아 폴백 모델로 새 배치를 내거나 직접 요청한다 (배치에는 서버 측 폴백이 없다)refusals-and-fallback
거절을 감시한다거절 이벤트와 폴백 처리 이벤트를 따로 센다. 그 차이에 알림을 건다refusals-and-fallback
스트리밍에서 stop_reason 을 읽는다message_delta 이벤트에서 읽는다 (message_start 에서는 null)handling-stop-reasons

상황 변수

변수값답 쪽등급근거
감시 기준HTTP 오류율 · 5xx 만 본다거절을 못 본다. 그래서 stop_reason 분포를 따로 센다refusals-and-fallback
감시 도구Claude Code OTel 이다거절은 api_error 가 아니라 claude_code.api_refusal 이벤트로 온다monitoring-usage
실행 모델Fable 5.1 · Fable 5 · Opus 5.5 · Opus 5안전 분류기 거절이 올 수 있다. 그래서 폴백이 필수다refusals-and-fallback
실행 모델위 네 모델 밖이다두 문서는 네 모델만 적는다미해결미해결 1
실행 방식동기 · Claude API서버 측 폴백이 가능하다refusals-and-fallback
실행 방식Message Batches서버 측 폴백이 없다. 거절 항목만 다시 제출한다refusals-and-fallback
호출 구조에이전트 + 서브에이전트서브 호출에 따로 폴백을 단다. 재시도 예산은 요청 단위로 잡는다refusals-and-fallback
폴백 모델 상태rate limit(속도 제한) · 과부하폴백을 시도하지 않는다. 앞 거절과 recommended_model 을 돌려준다refusals-and-fallback
실패 종류429 · 529 · 5xx폴백 대상이 아니다. 재시도·백오프 쪽이다(■ rate limit)refusals-and-fallback · errors(529 = 과부하)
거절 시점스트림 중간부분 출력은 버린다. 입력과 나온 출력을 청구한다refusals-and-fallback
거절 분류bio · frontier_llm · reasoning_extraction출력 전 거절도 청구한다refusals-and-fallback (2026-09-24 재개)

연습 문제

고객지원 채팅 앱이 Anthropic SDK 로 Opus 5.5 를 부르고, Claude API 말고 다른 클라우드 플랫폼에도 같은 앱을 올린다모든 플랫폼. 어떤 문의에 HTTP 200 인데 stop_reason 이 "refusal" 이고 content 가 빈 응답refusal이 왔다. 담당자는 "같은 요청을 같은 모델로 한 번 더 보내자"고 한다. 어떻게 처리해야 하나?

시험 대비

함정

  • "HTTP 상태 코드로 실패·거절을 잡는다" 는 함정이다. 거절은 HTTP 200 이다
  • "5xx 알림 임계값을 낮춘다" 는 함정이다. 거절은 5xx 가 아니다. 따로 센다
  • "stop_details.category 로 거절을 분기한다" 는 함정이다. category 는 null 일 수 있다. stop_reason 으로 분기한다
  • "같은 요청을 같은 모델로 재시도한다" 는 함정이다. 대개 또 거절한다. 다른 모델로 보낸다
  • "refusal 이면 끝이니 빈 답을 그대로 사용자에게 보낸다" 는 함정이다. 다른 모델로 보내면 대개 답을 얻는다
  • stop_reason 을 5개로만 알고 '컨텍스트 창이 가득 차 멈춤' 보기를 버리는 것은 함정이다. 값은 7개다. model_context_window_exceeded 가 있다
  • "pause_turn 은 오류이니 새 요청으로 보낸다" 는 함정이다. 받은 assistant 콘텐츠를 그대로 보내 이어 간다
  • "폴백을 켜면 429·5xx 도 다른 모델로 간다" 는 함정이다. 폴백은 안전 분류기 거절에만 일어난다
  • tool_result 와 같은 메시지에 추가 지시 텍스트를 붙이는 것은 함정이다. 턴이 끝난다. 추가 지시는 별도 user 메시지로 보낸다

신호

"Every Messages API response includes a stop_reason field that tells you why Claude stopped generating."

"Run the tool and return the result."

"Safety classifiers return this stop reason as a normal HTTP 200 response, not an error."

"Re-sending a refused request to the same model usually earns another refusal."

"A refusal is an HTTP 200, so monitoring built on error rates or 5xx responses never sees it."

"Only a safety classifier decline triggers the fallback."

"Check for stop_reason equal to "refusal" directly."

보통은 거절을 다른 모델로 폴백하지만, 폴백 모델이 rate limit 에 걸리면 앞 거절이 그대로 오고, 서브에이전트 호출에는 fallbacks 를 따로 설정해야 한다.
그림으로 보기 (AI 생성 · Codex · 누르면 크게) — 자세한 조건은 아래 '더 깊이'에 있어요
더 깊이 : 뒤집히는 때 · 오답 재료 (설명)

뒤집히는 때

1. "출력 전 거절은 공짜"가 분류 따라 뒤집힘

  • 조건 : 2만 5천 토큰 요청이 Fable 5.1 에서 출력 전에 거절됐다.
  • 계산 : 분류가 cyber 면 $0 이다. 분류가 bio 면 25,000 × $10 / 1M = $0.25 다. 폴백(Opus 4.8)은 따로 25,000 × $5 / 1M = $0.125 에 출력 값이 더해진다.
  • 전제 : 캐시가 없다. 가격은 2026-09-28 조회값이다(ko-pricing). 폴백 쪽 출력 토큰은 뺀다.
  • 등급 : 계산 (청구 분류는 확인). 2026-09-24 전에는 출력 전 거절이 모두 무료였다(릴리스 노트, 2026-06-02). 그래서 시험(2026-07 기준)은 "출력 전 거절은 과금되지 않는다" 로 물을 가능성이 있다.
  • 근거 : refusals-and-fallback · 릴리스 노트

2. 폴백을 켰는데도 거절이 그대로 옴

  • 조건 : fallbacks 를 설정했다. 부하가 몰려 폴백 모델이 rate limit 에 걸렸다.
  • 판단 : 폴백 시도를 건너뛰고 앞 거절을 돌려준다. stop_details.recommended_model 이 직접 재시도할 모델을 힌트로 준다(보장은 아니다). "폴백 켰으니 거절 0" 가정이 깨진다. 그래서 폴백 모델의 rate limit 을 예상 거절량에 맞춰 잡는다.
  • 근거 : refusals-and-fallback

3. 서브에이전트에서만 빈 답

  • 조건 : 메인 호출에는 fallbacks 가 있다. 도구 실행 안에서 부르는 서브에이전트 모델 호출에는 없다.
  • 판단 : fallbacks 는 도구 실행 안의 모델 호출로 번지지 않는다. 그래서 그 호출에 따로 설정한다. 한 턴에 거절이 여러 번(에이전트 + 서브에이전트) 날 수 있다. 그래서 재시도 예산도 요청 단위로 잡는다.
  • 근거 : refusals-and-fallback

4. max_tokens 로 멈춤 — 한도만 올리면 되나

  • 조건 : 분류 답이 중간에 끊겼다. stop_reason 은 max_tokens 다.
  • 판단 : 한도 올리기와 이어서 생성하기가 둘 다 공식 처방이다. 입력 크기를 몰라 최대치를 원하면, 컨텍스트 창 끝까지 쓰게 두고 model_context_window_exceeded 로 받는 길도 있다. 비스트리밍 10분 제한과 스트리밍 전환은 이 카드 밖이다(4.5 쪽).
  • 등급 : (처방) · 업계 일반 (어느 쪽을 먼저 고를지)
  • 근거 : handling-stop-reasons

오답 재료

오답왜 끌리나왜 틀리나근거
"HTTP 상태 코드로 실패·거절을 잡는다"보통 API 는 실패를 상태 코드로 알린다거절은 HTTP 200 이다. stop_reason 은 성공 응답 본문의 일부다handling-stop-reasons
"5xx 알림 임계값을 낮춘다"빈 답이 장애로 보인다거절은 5xx 가 아니다. 그래서 임계값을 바꿔도 안 잡힌다refusals-and-fallback
"같은 요청을 같은 모델로 재시도"일시 오류처럼 보인다대개 또 거절한다. 다른 모델로 보낸다refusals-and-fallback
"refusal = 안전 거절로 끝, 빈 답을 사용자에게"안전 판단이니 존중해야 할 것 같다다른 모델로 보내면 대개 답을 얻는다. 공식 처방은 폴백이다refusals-and-fallback
"stop_details.category 를 읽어 분기"분야별 처리가 세밀해 보인다category·explanation 은 null 일 수 있다. stop_reason 으로 분기한다refusals-and-fallback
'컨텍스트 창이 가득 차 멈춤' 보기를 "그런 값은 없다" 고 버린다stop_reason 을 5개로만 외웠다값은 7개다. model_context_window_exceeded 가 있다handling-stop-reasons
"pause_turn 은 오류이니 처음부터 새 요청"'멈춤' 이라는 이름 때문이다받은 assistant 콘텐츠를 그대로 보내 이어 간다handling-stop-reasons
"폴백을 켜면 429·서버 오류도 다른 모델이 받아 준다"폴백이 장애 조치로 보인다안전 분류기 거절만 폴백한다. 나머지는 그대로 돌아온다refusals-and-fallback
"tool_result 와 같은 메시지에 '이제 요약해' 텍스트를 붙인다"한 번에 보내면 왕복이 줄어 보인다턴이 끝난다(기다리던 서버 도구가 있으면 400). 턴이 끝난 뒤 별도 user 메시지로 보낸다handling-stop-reasons
"assistant 문장('완료했습니다')으로 루프 종료 판정"사람이 읽기에는 분명하다루프는 stop_reason 으로 판정한다. end_turn 이면 끝난다Skilljar p101 486254

참고

더 깊이 : CCA-F · 출처 · 상태 · 미해결 (참고)

CCA-F

겹침 (에이전트 루프에서 tool_use 면 한 번 더 돌고 end_turn 이면 끝난다. 완료를 assistant 문장으로 판정하지 않는다). 여기서는 7값 전부의 처리와 거절(HTTP 200)을 설계·감시·폴백 경로 관점에서 묻는다

출처

상태

  • 시험 뒤 변경 2곳(⑤ `"default"` 모드 · ⑦ 거절 청구 재개)

미해결

  1. 안전 분류기 거절 문서 두 개는 Fable 5.1 · Fable 5 · Opus 5.5 · Opus 5 만 적는다. 그 밖 모델(Sonnet 등)이 refusal 을 내는지는 이 파일들에 없다. 릴리스 노트이 가리키는 streaming refusals 문서는 받지 않았다.
  2. fallbacks 의 "default" 모드는 2026-07-24 에 추가됐다(릴리스 노트). 시험 가이드 기준(2026-07)에 들어갔는지 모른다. 그래서 시험은 "다른 모델로 재시도" 원칙만 물을 가능성이 있다(추론).
  3. 목록 5.2② 는 "refusal 이면 stop_details(category·explanation)를 읽고" 였다. 공식은 분기를 stop_reason(또는 stop_details.type)으로 하라고 한다. category·explanation 은 null 일 수 있다(refusals-and-fallback). 카드에는 고친 쪽만 적는다.
  4. 목록 1.2② 의 "Part 17 팁이 오답으로 짚은 모양" 과 5.2② 의 "영상 목록 5개" 는 영상 쪽 기록이다. 그래서 원문 대조는 안 했다(영상 파일은 안 봤다).
  5. handling-stop-reasons 의 코드 예시(항상 확인 · pause_turn 재시도 · 이어서 생성)는 받은 파일에서 코드 블록이 빠져 있다( 뒤가 빈 절이다).