Anthropic 호환 API
Anthropic 호환 API (Anthropic-Compatible API)
Anthropic Messages API(/v1/messages)를 쓰는 클라이언트라면, Anthropic SDK뿐 아니라 Claude Code 같은 에이전트 CLI까지 그대로 SGLang 서버에 붙일 수 있어요. SGLang이 Anthropic 호환 /v1/messages 엔드포인트를 제공하기 때문이에요. API의 전체 레퍼런스는 Anthropic API Reference에서 볼 수 있어요.
출처: 공식문서
무엇이 준비되어 있나요
이 엔드포인트는 모든 SGLang 서버에 자동으로 등록돼서, 켜기 위한 별도 플래그가 필요 없어요. 같은 모델·채팅 템플릿·추론/도구 호출 파서를 OpenAI 호환 엔드포인트와 공유하며, 스트리밍/비스트리밍 응답, 도구 사용, count_tokens 라우트를 지원해요.
이 튜토리얼이 다루는 내용은 다음과 같아요.
POST /v1/messages(비스트리밍과 스트리밍)POST /v1/messages/count_tokens- Claude Code를 서버에 연결하기 — 프리픽스 캐시 재사용에 필요한
CLAUDE_CODE_ATTRIBUTION_HEADER설정 포함
서버 띄우기
터미널에서 서버를 띄우고 초기화를 기다리면 돼요. Anthropic /v1/messages 엔드포인트는 자동 등록되므로, 일반적인 서버 실행 외에 추가 플래그가 필요 없어요. 아래 예시는 단일 노드 GLM-5.2-FP8 설정이고, 하드웨어·양자화별 검증된 명령은 GLM-5.2 쿡북에서 볼 수 있어요.
sglang serve \
--model-path zai-org/GLM-5.2-FP8 \
--tp 8 \
--speculative-algorithm EAGLE \
--speculative-num-steps 5 \
--speculative-eagle-topk 1 \
--speculative-num-draft-tokens 6 \
--reasoning-parser glm45 \
--tool-call-parser glm47 \
--host 0.0.0.0 \
--port 30000
참고:
- 엔드포인트는 모델에 무관해요.
/v1/messages라우트는 어떤 모델이든 기본으로 켜져 있어요. 여기서 GLM-5.2를 쓴 이유는 그 모델의 추론+도구 사용 출력이 Claude Code 통합에서 빛나기 때문이고, 어떤 모델이든 동작해요.- 모델 이름과
[1m]. SGLang은 요청의model필드를 검증하지 않아서 Claude Code는 아무 이름이나 보낼 수 있어요.[1m]접미사는 클라이언트 쪽 힌트예요. Claude Code는 모델 이름이[1m]으로 끝날 때만 1M-컨텍스트 베타를 켜요 — 없으면 컨텍스트가 제한돼요. 아래ANTHROPIC_DEFAULT_*_MODEL환경변수에도 같은glm-5.2[1m]을 설정하세요.--reasoning-parser/--tool-call-parser는 선택이에요. 모델이 추론 내용을 내보낼 때(GLM-5.2, Qwen3, DeepSeek-R1 ...)나 도구 호출을 구조화된tool_use블록으로 파싱하길 원할 때 추가하세요. 도구 호출 파서가 없으면 도구 스키마는 받아들여지지만 모델의 도구 호출이 원시 텍스트로 돌아와서 Claude Code가 실행할 수 없어요.- 컨텍스트 길이는 기본적으로 모델 자신의 값을 따라가요 (GLM-5.2는 1M).
--context-length은 제한할 때만 넘기세요.
메시지 보내기
비스트리밍
Anthropic Python SDK를 서버에 연결해 써요. OpenAI SDK와 달리 Anthropic SDK는 자기 스스로 /v1/messages를 붙이기 때문에, base_url은 /v1 접미사가 없는 서버 루트예요.
from anthropic import Anthropic
client = Anthropic(
base_url="http://127.0.0.1:30000",
api_key="EMPTY", # SGLang does not require a real key by default
)
message = client.messages.create(
model="zai-org/GLM-5.2-FP8",
max_tokens=512,
messages=[{"role": "user", "content": "List 3 countries and their capitals."}],
)
# A reasoning model may emit a `thinking` block before the `text` block —
# pick the text block rather than assuming content[0].
print(next(b.text for b in message.content if b.type == "text"))
예시 출력:
Here are 3 countries and their capitals:
1. **France** - Paris
2. **Japan** - Tokyo
3. **Brazil** - Brasília
추론 모델은 text 블록 앞에 thinking 블록을 낼 수 있으니, content[0]이라고 단정하지 말고 type == "text"인 블록을 고르는 게 안전해요.
스트리밍
stream=True로 설정하면 Server-Sent Events를 생성되는 대로 받아요.
with client.messages.stream(
model="zai-org/GLM-5.2-FP8",
max_tokens=512,
messages=[{"role": "user", "content": "Say this is a test"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
예시 출력:
This is a test.
시스템 프롬프트
최상위 system 필드는 Anthropic API 형태에 맞춰 문자열 또는 텍스트 블록 리스트로 받아들여져요.
message = client.messages.create(
model="zai-org/GLM-5.2-FP8",
max_tokens=512,
system="You are a helpful assistant that answers concisely.",
messages=[{"role": "user", "content": "What is the capital of France?"}],
)
print(next(b.text for b in message.content if b.type == "text"))
예시 출력:
The capital of France is Paris.
도구 사용 (Tool Use)
도구 정의는 Anthropic의 tools 스키마를 따라가요. 서버를 --tool-call-parser와 함께 띄우면 모델의 도구 호출이 tool_use 콘텐츠 블록으로 돌아와요.
message = client.messages.create(
model="zai-org/GLM-5.2-FP8",
max_tokens=512,
tools=[
{
"name": "get_weather",
"description": "Get the weather for a city",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
)
print(message.stop_reason)
print([b for b in message.content if b.type == "tool_use"])
예시 출력:
tool_use
[ToolUseBlock(type='tool_use', id='toolu_01XXXX', name='get_weather', input={'city': 'Paris'})]
토큰 세기 (Counting Tokens)
POST /v1/messages/count_tokens는 응답을 생성하지 않고 요청의 토큰 길이를 돌려줘요. /v1/messages와 같은 요청 변환을 재사용하므로 시스템 프롬프트, 도구, 멀티 턴 이력까지 모두 반영돼요.
resp = client.messages.count_tokens(
model="zai-org/GLM-5.2-FP8",
messages=[{"role": "user", "content": "Hello, world"}],
)
print(resp.input_tokens)
예시 출력:
15
Claude Code 사용하기
Claude Code를 시작하는 셸에 몇 개의 환경변수를 설정하면 SGLang 서버를 가리킬 수 있어요. 서버가 :30000에서 이미 띄워져 있다면 전체 세트를 export하고 claude를 실행하세요.
export ANTHROPIC_BASE_URL="http://127.0.0.1:30000"
export ANTHROPIC_AUTH_TOKEN="dummy" # required by Claude Code; any non-empty string works
export API_TIMEOUT_MS="3000000" # long timeout — reasoning + 1M-context turns are slow
export CLAUDE_CODE_AUTO_COMPACT_WINDOW="1000000" # let auto-compact use the full 1M window
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # drop autoupdater/telemetry/error-reporting noise
export CLAUDE_CODE_ATTRIBUTION_HEADER=0 # required for prefix-cache reuse — see below
export ANTHROPIC_DEFAULT_HAIKU_MODEL="glm-5.2[1m]" # [1m] suffix enables Claude Code's 1M-context beta
export ANTHROPIC_DEFAULT_SONNET_MODEL="glm-5.2[1m]" # [1m] suffix enables Claude Code's 1M-context beta
export ANTHROPIC_DEFAULT_OPUS_MODEL="glm-5.2[1m]" # [1m] suffix enables Claude Code's 1M-context beta
claude
각 변수가 하는 역할을 짚어볼게요.
ANTHROPIC_BASE_URL— Claude Code를 Anthropic API 대신 당신의 SGLang 서버로 연결해요.ANTHROPIC_AUTH_TOKEN— Claude Code는 비어 있지 않은 인증 토큰을 요구해요. SGLang은--api-key없이 띄우면 어떤 값이든 받아들여요.API_TIMEOUT_MS— 올려두세요. 출력이 긴 추론 모델과 1M-컨텍스트 턴은 기본 타임아웃을 흔히 넘겨요.ANTHROPIC_DEFAULT_{HAIKU,SONNET,OPUS}_MODEL— 각 등급에 대해 Claude Code가 보내는 모델 이름이에요. SGLang은 이 필드를 검증하지 않으니 아무 이름이나 돼요.glm-5.2[1m]을 쓰세요.[1m]접미사가 Claude Code의 1M-컨텍스트 베타를 켜는 클라이언트 쪽 힌트거든요 (없으면 컨텍스트가 제한돼요).CLAUDE_CODE_AUTO_COMPACT_WINDOW—1000000으로 두면 자동 컴팩션이 기본값 대신 전체 1M 윈도우를 써서 긴 세션을 유지해요.
팁: 매 셸마다 export하기보다
~/.claude/settings.json의env키에 넣으면 모든 Claude Code 세션에 적용돼요.{ "env": { "ANTHROPIC_BASE_URL": "http://127.0.0.1:30000", "ANTHROPIC_AUTH_TOKEN": "dummy", "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1", "CLAUDE_CODE_ATTRIBUTION_HEADER": "0", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-5.2[1m]", "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-5.2[1m]", "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.2[1m]" } }
필수: 프리픽스 캐시 재사용을 위한 CLAUDE_CODE_ATTRIBUTION_HEADER=0
참고: Claude Code가 SGLang(또는 어떤 비-Anthropic 게이트웨이)을 통할 때마다 이 값을 설정하세요. 없으면 멀티 턴 대화가 매 턴마다 이력 전체를 다시 프리필해요.
Claude Code는 요청마다 시스템 프롬프트 앞에 attribution 블록을 붙여요. 형태는 x-anthropic-billing-header: cc_version=<ver>.<per-request-hash>; cc_entrypoint=...; cch=<hash>;이에요. 그중 per-request hash는 턴 사이에 처음으로 달라지는 토큰이라, 라디스 프리픽스 캐시가 그 hash 앞의 짧은 프리픽스만 재사용하고 시스템 프롬프트와 대화 이력 전체를 매 턴 다시 프리필하게 돼요.
CLAUDE_CODE_ATTRIBUTION_HEADER=0을 설정하면 attribution 줄 전체가 시스템 프롬프트에서 빠져요. 이건 문서화된 Claude Code 환경변수이고, 그 명시적 목적이 "LLM 게이트웨이를 통할 때 프롬프트 캐시 적중률을 높이는 것"이에요 (Claude Code env-vars 레퍼런스 참고).
참고:
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC는 attribution 블록을 제거하지 않아요 — 그건 autoupdater/telemetry/error 보고만 다뤄요. attribution 헤더는 별도 코드 경로라, 그건CLAUDE_CODE_ATTRIBUTION_HEADER=0을 쓰세요.
문제 해결 (Troubleshooting)
Connection refused / fetch failed — 서버가 떠 있고 ANTHROPIC_BASE_URL의 포트가 --port(기본 30000)와 일치하는지 확인하세요. ANTHROPIC_BASE_URL을 원격 호스트로 설정했다면 연결을 막는 프록시 뒤가 아닌지, 실제로 닿는지 확인하세요.
Model not found / 서버에서 404 — SGLang은 요청의 model 필드를 검증하지 않고 시작 시 로드된 모델을 그대로 서빙하므로, 404는 대개 요청이 /v1/messages 라우트에 아예 도달하지 못했다는 뜻이에요. ANTHROPIC_BASE_URL이 서버를 가리키는지(포트 누락 없이) 그리고 서버가 로딩을 끝냈는지 확인하세요.
도구 호출이 안 되거나 원시 텍스트로 돌아옴 — 모델에 맞는 --tool-call-parser(예: glm47, qwen3)로 서버를 띄우세요. 없으면 tools 필드는 받아들여지지만 모델의 도구 호출이 tool_use 블록 대신 텍스트로 돌아와서 Claude Code가 실행할 수 없어요.
느림 / 매 턴 이력 전체를 다시 프리필 — CLAUDE_CODE_ATTRIBUTION_HEADER=0이 빠져 있어요. 시스템 프롬프트의 Claude Code 요청별 attribution hash가 라디스 프리픽스 캐시 재사용을 무너뜨려요. 위 섹션을 보세요.
컨텍스트가 1M 아래로 제한됨 — Claude Code가 1M-컨텍스트 베타를 켜려면 모델 이름이 [1m]으로 끝나야 해요. ANTHROPIC_DEFAULT_*_MODEL이 [1m] 접미사를 쓰는지, 로드한 모델의 네이티브 컨텍스트가 1M인지(GLM-5.2는 1048576) 확인하세요. --context-length은 늘리는 게 아니라 제한할 때만 쓰세요.
파라미터
/v1/messages 엔드포인트는 표준 Anthropic Messages API 파라미터를 받아요. 전체 목록은 Anthropic Messages API reference를 참고하세요.
추론 모델은 OpenAI 호환 엔드포인트와 같은 --reasoning-parser 메커니즘으로 지원돼요. 요청을 통해 모델의 reasoning kwarg를 넘기면 되는데 (예: DeepSeek-V3 스타일 모델은 thinking, Qwen3 스타일은 enable_thinking), reasoning-parser/채팅 템플릿 매핑은 OpenAI APIs - Completions를 보세요.