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_WINDOW1000000으로 두면 자동 컴팩션이 기본값 대신 전체 1M 윈도우를 써서 긴 세션을 유지해요.

팁: 매 셸마다 export하기보다 ~/.claude/settings.jsonenv 키에 넣으면 모든 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를 보세요.

더 알아보기 (Learn more)