API
verified · type: how-to · verified: 2026-07 · review: 30d · updated: 2026-07-25 · [api, model]

Anthropic Messages API 심화

최소 호출은 api-quickstart. 이 문서는 그다음 — tool use 루프, 구조화 출력, 캐싱 배치, 스트리밍, 배치 API, 에러 처리를 실제로 짤 때 여는 참고서.


Tool use 루프 — 수동 vs Tool Runner

Claude 가 툴을 부르면 stop_reason: "tool_use"tool_use 블록이 온다. 앱이 실행하고 tool_result 블록을 user 롤 메시지로 되돌려보내면 Claude 가 최종 답을 만든다. 이걸 직접 돌리는 게 수동 루프.

방식 언제
수동 루프 human-in-the-loop 승인, 커스텀 로깅, 조건부 실행이 필요할 때
Tool Runner (베타) 단순 에이전틱 루프 — 실행·에러 래핑·상태 관리를 SDK 가 대신

tool_choice: 기본 {"type":"auto"}, {"type":"any"}(아무 툴이나 강제), {"type":"tool","name":...}(특정 툴), {"type":"none"}. disable_parallel_tool_use: true 로 턴당 1개로 제한.

클라이언트 툴(내가 실행)과 서버 툴(Anthropic 인프라 실행, 결과가 같은 응답에 옴)이 나뉜다. 서버 툴 최신 버전: web_search_20260209, web_fetch_20260209, code_execution_20260521 (verified 2026-07).

# 수동 루프 (Python) — 승인·로깅이 필요할 때
tools = [{"name":"get_weather","description":"현재 날씨","strict":True,
  "input_schema":{"type":"object","properties":{"city":{"type":"string"}},
  "required":["city"],"additionalProperties":False}}]
messages = [{"role":"user","content":"서울 날씨?"}]
while True:
    r = client.messages.create(model="claude-opus-5", max_tokens=1024,
                               tools=tools, messages=messages)
    messages.append({"role":"assistant","content":r.content})
    if r.stop_reason != "tool_use": break
    results = [{"type":"tool_result","tool_use_id":b.id,
                "content":run_tool(b.name, b.input)}      # 내 함수
               for b in r.content if b.type == "tool_use"]
    messages.append({"role":"user","content":results})
// Tool Runner (베타, TS) — 루프를 SDK 가 돌림
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
const getWeather = betaZodTool({ name: "get_weather", description: "현재 날씨",
  inputSchema: z.object({ city: z.string() }),
  run: async ({ city }) => JSON.stringify({ temp: "9C" }) });
const runner = client.beta.messages.toolRunner({
  model: "claude-opus-5", max_tokens: 1024, tools: [getWeather],
  messages: [{ role: "user", content: "서울 날씨?" }] });
for await (const message of runner) console.log(message);

Python 은 @beta_tool + client.beta.messages.tool_runner(...) 로 같은 걸 한다. Tool Runner 는 Claude Agent SDK 와 별개 — Agent SDK 는 Claude Code 를 라이브러리화한 별개 제품이다(agent-frameworks).


구조화 출력 — JSON outputs + strict tool use

두 갈래다. (a) 응답 전체를 스키마에 맞추려면 output_config.format, (b) 툴 호출 인자만 보장하려면 툴 정의에 strict: true. 둘 다 스키마에 additionalProperties: false + required 를 넣어야 한다. 구버전 top-level output_format 은 deprecated (verified 2026-07). 지원: Claude API 에서 Opus 5·Mythos Preview·4.5 이상. Fable 5 의 Claude API 지원 여부는 문서상 불명확 (재확인 필요).

from pydantic import BaseModel
class Invoice(BaseModel):
    total: float
    vendor: str
r = client.messages.parse(model="claude-opus-5", max_tokens=1024,
    output_format=Invoice,                               # Pydantic → JSON 스키마 자동
    messages=[{"role":"user","content":"영수증 파싱: ..."}])
print(r.parsed_output.total)                             # typed 객체

TS 는 output_config: { format: zodOutputFormat(ZodSchema, "name") } (@anthropic-ai/sdk/helpers/zod). 미지원: 재귀 스키마, 외부 $ref(HTTP), 숫자 제약(minimum/maximum), 문자열 제약(minLength 등). 문법(grammar)은 첫 사용 시 지연 후 24h 캐시된다.


프롬프트 캐싱 배치 전략

렌더 순서는 toolssystemmessages안 변하는 걸 앞에 둬야 캐시가 산다(prompt-caching). breakpoint 최대 4개. 쓰기는 5분 TTL ×1.25 / 1시간 TTL ×2, 읽기는 ×0.1. 최소 캐시 토큰이 모델마다 다르고 단조증가가 아니다 (verified 2026-07):

모델 최소 캐시 토큰
Opus 5 / Fable 5 512
Opus 4.8 / Sonnet 5 / Sonnet 4.6 1,024
Opus 4.7 2,048
Opus 4.6 / Haiku 4.5 4,096

응답 usage 의 cache_creation_input_tokens + cache_read_input_tokens + input_tokens 합이 총 입력이다.


스트리밍

"stream": true. 이벤트: message_start → (content_block_startcontent_block_deltacontent_block_stop)message_delta* → message_stop. 중간에 ping, 그리고 200 이후 mid-stream error(예 overloaded_error)가 올 수 있다. delta 타입: text_delta, input_json_delta(.partial_json, block_stop 후 파싱), thinking_delta, signature_delta. thinking.display:"omitted"(기본값) 면 thinking_delta 가 없다. 128K 출력은 반드시 스트리밍 (verified 2026-07).

with client.messages.stream(model="claude-opus-5", max_tokens=64000,
        messages=[{"role":"user","content":"긴 보고서 써줘"}]) as stream:
    for text in stream.text_stream:
        print(text, end="")
    final = stream.get_final_message()

Claude 4.6+ 는 스트림이 끊겼을 때 assistant prefill 로 못 잇는다(prefill 미지원). 부분 응답 뒤에 user 메시지로 "이어서 계속" 을 지시해 재개한다.


stop_reason 전체 표

의미 처리
end_turn 정상 종료 없음
max_tokens 출력 한도 도달 잘림 — 필요 시 이어받기
stop_sequence 정지 시퀀스 히트 없음
tool_use 툴 호출 대기 tool_result 로 회신
pause_turn 서버 툴 루프 10회 한도 assistant 응답 그대로 붙여 재요청. "계속해" user 메시지 추가 금지
refusal 안전상 거부 stop_details 채워짐

stop_detailsrefusal 일 때만 채워지고 그 외엔 null 이다 — 접근 전 반드시 가드하라.


배치 API

50% 할인(Opus 5 = 입력 $2.50 / 출력 $12.50, verified 2026-07). 한도는 100,000 요청 또는 256MB 중 먼저. 대부분 1시간 내, 24시간 내 미완이면 만료(expire). 결과 29일 보관. custom_id 로 키잉하며 순서 보장이 없다 — 결과 매칭은 반드시 custom_id 로. processing_status: in_progressended. 결과 타입: succeeded / errored / canceled / expired(뒤 셋은 과금 안 됨). stream:truemax_tokens:0 은 배치 미지원. output-300k-2026-03-24 베타 헤더로 최대 300K 출력.


에러 코드

HTTP type 처리
400 invalid_request_error 스키마·파라미터 확인
401 authentication_error API 키
402 billing_error 결제
403 permission_error 리소스 권한
404 not_found_error 은퇴 모델 ID 등
409 conflict_error 동시 수정 충돌
413 request_too_large 요청 바이트 초과
429 rate_limit_error 백오프 + retry-after 존중
500 api_error 지수 백오프 재시도
504 timeout_error 스트리밍으로 전환
529 overloaded_error 잠시 후 재시도

SDK 는 typed 예외(anthropic.NotFoundError 등)를 던진다 — 문자열 매칭 말고 구체 클래스부터 catch. 자동 재시도는 기본 2회. 모든 응답의 request-id 헤더를 로그에 남겨라(SDK ._request_id).


함정 (gotcha)

  • prefill 400: 마지막 assistant 턴 prefill 은 4.6+ 에서 400. 출력 형태를 강제하려면 prefill 말고 structured outputs 를 써라.
  • thinking 블록 훼손 400: tool use 시 assistant 턴의 thinking/redacted_thinking 블록을 받은 그대로(빈 것 포함) 다시 넣어야 한다. 편집·재정렬·필터하면 400.
  • stop_details null: refusal 이 아니면 null. 무조건 접근하면 터진다.
  • 캐시 최소 토큰 착각: Haiku 4.5 가 4,096 으로 Opus 5(512)보다 높다. 단조증가로 가정하지 마라.
  • 배치 결과 순서: 요청 순서와 무관하게 온다. 인덱스로 매칭하면 데이터가 섞인다.

참고

출처: platform.claude.com 공식 문서(first-party) [HIGH] + 규격서 §7. 휘발성 값은 (verified 2026-07). 원본 → 2026-07-25-anthropic-claude-deep