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 캐시된다.
프롬프트 캐싱 배치 전략
렌더 순서는 tools → system → messages — 안 변하는 걸 앞에 둬야 캐시가 산다(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_start → content_block_delta → content_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_details 는 refusal 일 때만 채워지고 그 외엔 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_progress → ended. 결과 타입: succeeded / errored / canceled / expired(뒤 셋은 과금 안 됨). stream:true 와 max_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)보다 높다. 단조증가로 가정하지 마라.
- 배치 결과 순서: 요청 순서와 무관하게 온다. 인덱스로 매칭하면 데이터가 섞인다.
참고
- api-quickstart — 최소 호출·인증·모델 ID. 이 문서의 전제
- prompt-caching — 캐시 배치·TTL·breakpoint 상세
- structured-output — JSON 스키마 강제 출력 패턴 전반
- set-up-tool-use — tool use 첫 설정 워크스루
- models-overview — 모델별 컨텍스트·가격·thinking 노브
- vercel-ai-sdk · openai-api-parity — 다른 SDK 로 같은 걸 할 때
출처: platform.claude.com 공식 문서(first-party) [HIGH] + 규격서 §7. 휘발성 값은 (verified 2026-07). 원본 → 2026-07-25-anthropic-claude-deep