> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aireiter.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude 메시지 인터페이스

> - Anthropic Messages API 형식 완전 호환
- 다중 회차 대화 및 시각적 이해 지원
- 스트리밍 및 비스트리밍 두 가지 출력 모드 지원


export const apiKeyUrl = 'https://aireiter.com/keys';

## Authorizations

<ParamField header="x-api-key" type="string" required>
  API 키, 인증에 사용됨 (Anthropic SDK 표준 방식)

  API Key 받기:

  <a href={apiKeyUrl} target="_blank">API Key 관리 페이지</a>에서 API Key를 받으세요

  ```
  x-api-key: YOUR_API_KEY
  ```

  Bearer Token 형식도 지원됩니다:

  ```
  Authorization: Bearer YOUR_API_KEY
  ```
</ParamField>

## Body

<ParamField body="model" type="string" required>
  모델 이름

  * `claude-haiku-4-5-20251001` — 경량화 및 고속, 고빈도 단순 작업에 적합
  * `claude-sonnet-4-5-20250929` — 성능과 비용의 균형, 종합 추천
  * `claude-sonnet-4-6` — Sonnet 신버전, 더 강력한 성능
  * `claude-opus-4-5-20251101` — 플래그십 추론 모델, 복잡한 분석에 적합
  * `claude-opus-4-6` — Opus 신버전, 가장 강력한 능력

  전체 모델 목록은 다음을 참조하세요: `GET /api/v1/models`
</ParamField>

<ParamField body="messages" type="array" required>
  메시지 목록

  메시지 배열, 모델은 이 메시지를 바탕으로 다음 응답을 생성합니다. 각 메시지는 `role` 과 `content` 두 개의 필드를 포함합니다.

  **빠른 입력 (Try it 영역):**

  1. "+ Add an item"를 클릭하여 메시지를 추가합니다
  2. `role` 에 `user` (사용자 메시지) 또는 `assistant` (AI 응답, 다중 대화용)를 입력합니다
  3. `content` 에 하고 싶은 말을 입력합니다

  <Expandable title="상세 필드 설명">
    <ParamField body="role" type="string" required default="user">
      역할 유형

      선택 가능 값: `user` (사용자 메시지), `assistant` (AI 응답, 다중 대화 및 사전 채우기용)

      참고: Claude API의 system 프롬프트는 별도의 `system` 파라미터로 처리하며 messages에 포함되지 않습니다
    </ParamField>

    <ParamField body="content" type="string" required>
      메시지 내용

      메시지의 텍스트 내용을 작성합니다
    </ParamField>
  </Expandable>

  **단일 사용자 메시지 예시:**

  ```json theme={null} theme={null}
  [{"role": "user", "content": "안녕하세요, Claude"}]
  ```

  **다중 대화 예시:**

  ```json theme={null} theme={null}
  [
    {"role": "user",      "content": "안녕하세요"},
    {"role": "assistant", "content": "안녕하세요! 저는 Claude입니다."},
    {"role": "user",      "content": "AI에 대해 설명해줄 수 있나요?"}
  ]
  ```

  **사전 채우기된 조수 응답:**

  ```json theme={null} theme={null}
  [
    {"role": "user",      "content": "태양의 그리스 이름은? (A) Sol (B) Helios (C) Sun"},
    {"role": "assistant", "content": "정답은 ("}
  ]
  ```
</ParamField>

<ParamField body="max_tokens" type="integer" required>
  최대 출력 토큰 수

  모델이 최대 생성할 수 있는 토큰 수를 제어하며, 모델은 상한에 도달하기 전에 자연스럽게 종료할 수 있습니다. 최소값: `1`.

  모델마다 다른 컨텍스트 윈도우 상한이 있으니 모델 문서를 참고하세요.
</ParamField>

<ParamField body="system" type="string | array">
  시스템 프롬프트

  모델의 역할, 지시문 및 배경 정보를 설정합니다.

  **문자열 형식 (권장):**

  ```json theme={null} theme={null}
  {"system": "당신은 전문 Python 프로그래밍 강사이며 모든 질문에 한국어로 답변합니다."}
  ```

  **구조화 형식 (cache\_control 지원):**

  ```json theme={null} theme={null}
  {
    "system": [
      {
        "type": "text",
        "text": "당신은 전문 Python 프로그래밍 강사입니다.",
        "cache_control": {"type": "ephemeral"}
      }
    ]
  }
  ```
</ParamField>

<ParamField body="stream" type="boolean">
  스트리밍 출력 사용 여부

  `true`로 설정하면 SSE(Server-Sent Events)를 통해 실시간 스트리밍으로 응답을 반환합니다. \*\*기본값 `true`\*\*입니다. 비스트리밍 응답을 원할 경우 명시적으로 `"stream": false`를 전송해야 합니다.

  스트리밍 이벤트 순서:
  `ping` → `message_start` → `content_block_start` → `content_block_delta` × N → `content_block_stop` → `message_delta` → `message_stop`
</ParamField>

<ParamField body="temperature" type="number">
  온도 값, 범위 `0–1`

  * 낮은 값 (예: `0.2`): 더 확정적이고 보수적인 출력
  * 높은 값 (예: `0.8`): 더 무작위적이고 창의적인 출력

  기본값 `1.0`. `top_p`와 동시에 사용하지 않는 것이 좋습니다.
</ParamField>

<ParamField body="top_p" type="number">
  핵심 샘플링 파라미터, 범위 `0–1`

  누적 확률이 `top_p`에 도달할 때까지의 토큰 집합에서 샘플링합니다. 기본값 `1.0`.
  `temperature`와 동시에 사용하지 않는 것이 좋습니다.
</ParamField>

<ParamField body="top_k" type="integer">
  Top-K 샘플링

  확률이 가장 높은 K개의 토큰 중에서만 샘플링하여 낮은 확률의 롱테일을 필터링합니다. 고급 사용 사례 튜닝에 적합합니다.
</ParamField>

## Response

<ResponseField name="id" type="string">
  메시지 고유 식별자

  예시: `"msg_01XFDUDYJgAACzvnptvVoYEL"`
</ResponseField>

<ResponseField name="type" type="string">
  객체 유형, 고정값 `"message"`
</ResponseField>

<ResponseField name="role" type="string">
  역할, 고정값 `"assistant"`
</ResponseField>

<ResponseField name="content" type="array">
  콘텐츠 블록 배열

  **텍스트 콘텐츠:**

  ```json theme={null} theme={null}
  [{"type": "text", "text": "안녕하세요! 저는 Claude입니다."}]
  ```

  콘텐츠 유형: `text` (텍스트)
</ResponseField>

<ResponseField name="model" type="string">
  실제 요청을 처리하는 모델 이름
</ResponseField>

<ResponseField name="stop_reason" type="string">
  중단 사유

  * `end_turn` — 자연 종료
  * `max_tokens` — `max_tokens` 제한 도달
  * `stop_sequence` — 사용자 지정 중단 시퀀스 발동
</ResponseField>

<ResponseField name="stop_sequence" type="string | null">
  중단 시퀀스에 의해 중단된 경우 트리거된 시퀀스 내용 반환; 그렇지 않으면 `null`
</ResponseField>

<ResponseField name="usage" type="object">
  토큰 사용 통계

  <Expandable title="필드 설명">
    <ResponseField name="input_tokens" type="integer">
      입력 토큰 수 (시스템 프롬프트 포함)
    </ResponseField>

    <ResponseField name="output_tokens" type="integer">
      출력 토큰 수
    </ResponseField>

    <ResponseField name="cache_creation_input_tokens" type="integer">
      이번에 캐시에 작성된 토큰 수 (프롬프트 캐싱)
    </ResponseField>

    <ResponseField name="cache_read_input_tokens" type="integer">
      이번에 캐시에서 읽은 토큰 수 (캐시 적중 시 값 있음)
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null} theme={null}
  curl https://aireiter.com/api/v1/messages \
    -H "x-api-key: $API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-sonnet-4-5-20250929",
      "max_tokens": 1024,
      "messages": [
        {"role": "user", "content": "안녕하세요, 세계"}
      ]
    }'
  ```

  ```python Python theme={null} theme={null}
  import anthropic

  client = anthropic.Anthropic(
      api_key="YOUR_API_KEY",
      base_url="https://aireiter.com/api"
  )

  message = client.messages.create(
      model="claude-sonnet-4-5-20250929",
      max_tokens=1024,
      messages=[
          {"role": "user", "content": "안녕하세요, 세계"}
      ]
  )

  print(message.content[0].text)
  ```

  ```javascript JavaScript theme={null} theme={null}
  import Anthropic from '@anthropic-ai/sdk';

  const client = new Anthropic({
    apiKey: process.env.API_KEY,
    baseURL: 'https://aireiter.com/api'
  });

  const message = await client.messages.create({
    model: 'claude-sonnet-4-5-20250929',
    max_tokens: 1024,
    messages: [
      { role: 'user', content: '안녕하세요, 세계' }
    ]
  });

  console.log(message.content[0].text);
  ```

  ```go Go theme={null} theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "io/ioutil"
      "net/http"
      "os"
  )

  func main() {
      url := "https://aireiter.com/api/v1/messages"

      payload := map[string]interface{}{
          "model":      "claude-sonnet-4-5-20250929",
          "max_tokens": 1024,
          "messages": []map[string]string{
              {"role": "user", "content": "안녕하세요, 세계"},
          },
      }

      jsonData, _ := json.Marshal(payload)

      req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
      req.Header.Set("x-api-key", os.Getenv("API_KEY"))
      req.Header.Set("anthropic-version", "2023-06-01")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      body, _ := ioutil.ReadAll(resp.Body)
      fmt.Println(string(body))
  }
  ```

  ```java Java theme={null} theme={null}
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.URI;

  public class Main {
      public static void main(String[] args) throws Exception {
          String url = "https://aireiter.com/api/v1/messages";
          String apiKey = System.getenv("API_KEY");

          String payload = """
          {
            "model": "claude-sonnet-4-5-20250929",
            "max_tokens": 1024,
            "messages": [
              {"role": "user", "content": "안녕하세요, 세계"}
            ]
          }
          """;

          HttpClient client = HttpClient.newHttpClient();
          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("x-api-key", apiKey)
              .header("anthropic-version", "2023-06-01")
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(payload))
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());
          System.out.println(response.body());
      }
  }
  ```

  ```php PHP theme={null} theme={null}
  <?php

  $url = "https://aireiter.com/api/v1/messages";
  $apiKey = getenv('API_KEY');

  $payload = [
      "model"      => "claude-sonnet-4-5-20250929",
      "max_tokens" => 1024,
      "messages"   => [
          ["role" => "user", "content" => "안녕하세요, 세계"]
      ]
  ];

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "x-api-key: " . $apiKey,
      "anthropic-version: 2023-06-01",
      "Content-Type: application/json"
  ]);

  $response = curl_exec($ch);
  curl_close($ch);
  echo $response;
  ?>
  ```

  ```ruby Ruby theme={null} theme={null}
  require 'net/http'
  require 'json'
  require 'uri'

  url     = URI("https://aireiter.com/api/v1/messages")
  api_key = ENV['API_KEY']

  payload = {
    model:      "claude-sonnet-4-5-20250929",
    max_tokens: 1024,
    messages:   [{ role: "user", content: "안녕하세요, 세계" }]
  }

  http         = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true

  request                    = Net::HTTP::Post.new(url)
  request["x-api-key"]       = api_key
  request["anthropic-version"] = "2023-06-01"
  request["Content-Type"]    = "application/json"
  request.body               = payload.to_json

  puts http.request(request).body
  ```

  ```swift Swift theme={null} theme={null}
  import Foundation

  let url    = URL(string: "https://aireiter.com/api/v1/messages")!
  let apiKey = ProcessInfo.processInfo.environment["API_KEY"] ?? ""

  let payload: [String: Any] = [
      "model":      "claude-sonnet-4-5-20250929",
      "max_tokens": 1024,
      "messages":   [["role": "user", "content": "안녕하세요, 세계"]]
  ]

  var request = URLRequest(url: url)
  request.httpMethod = "POST"
  request.setValue(apiKey,        forHTTPHeaderField: "x-api-key")
  request.setValue("2023-06-01",  forHTTPHeaderField: "anthropic-version")
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
  request.httpBody = try? JSONSerialization.data(withJSONObject: payload)

  URLSession.shared.dataTask(with: request) { data, _, _ in
      if let data = data {
          print(String(data: data, encoding: .utf8) ?? "")
      }
  }.resume()
  ```

  ```csharp C# theme={null} theme={null}
  using System;
  using System.Net.Http;
  using System.Text;
  using System.Threading.Tasks;

  class Program
  {
      static async Task Main(string[] args)
      {
          var url    = "https://aireiter.com/api/v1/messages";
          var apiKey = Environment.GetEnvironmentVariable("API_KEY");

          var payload = @"{
              ""model"": ""claude-sonnet-4-5-20250929"",
              ""max_tokens"": 1024,
              ""messages"": [
                  {""role"": ""user"", ""content"": ""안녕하세요, 세계""}
              ]
          }";

          using var client = new HttpClient();
          client.DefaultRequestHeaders.Add("x-api-key", apiKey);
          client.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");

          var content  = new StringContent(payload, Encoding.UTF8, "application/json");
          var response = await client.PostAsync(url, content);
          Console.WriteLine(await response.Content.ReadAsStringAsync());
      }
  }
  ```

  ```c C theme={null} theme={null}
  #include <stdio.h>
  #include <curl/curl.h>
  #include <stdlib.h>
  #include <string.h>

  int main(void) {
      CURL *curl;
      const char *api_key = getenv("API_KEY");

      curl_global_init(CURL_GLOBAL_DEFAULT);
      curl = curl_easy_init();

      if (curl) {
          const char *payload =
              "{\"model\":\"claude-sonnet-4-5-20250929\","
              "\"max_tokens\":1024,"
              "\"messages\":[{\"role\":\"user\",\"content\":\"안녕하세요, 세계\"}]}";

          char auth_header[256];
          snprintf(auth_header, sizeof(auth_header), "x-api-key: %s", api_key);

          struct curl_slist *headers = NULL;
          headers = curl_slist_append(headers, auth_header);
          headers = curl_slist_append(headers, "anthropic-version: 2023-06-01");
          headers = curl_slist_append(headers, "Content-Type: application/json");

          curl_easy_setopt(curl, CURLOPT_URL, "https://aireiter.com/api/v1/messages");
          curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
          curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

          curl_easy_perform(curl);
          curl_slist_free_all(headers);
          curl_easy_cleanup(curl);
      }

      curl_global_cleanup();
      return 0;
  }
  ```

  ```dart Dart theme={null} theme={null}
  import 'dart:convert';
  import 'dart:io';
  import 'package:http/http.dart' as http;

  void main() async {
    final url    = Uri.parse('https://aireiter.com/api/v1/messages');
    final apiKey = Platform.environment['API_KEY']!;

    final response = await http.post(
      url,
      headers: {
        'x-api-key':          apiKey,
        'anthropic-version':  '2023-06-01',
        'Content-Type':       'application/json',
      },
      body: jsonEncode({
        'model':      'claude-sonnet-4-5-20250929',
        'max_tokens': 1024,
        'messages':   [{'role': 'user', 'content': '안녕하세요, 세계'}],
      }),
    );

    print(response.body);
  }
  ```

  ```r R theme={null} theme={null}
  library(httr)
  library(jsonlite)

  url     <- "https://aireiter.com/api/v1/messages"
  api_key <- Sys.getenv("API_KEY")

  response <- POST(
    url,
    add_headers(
      `x-api-key`         = api_key,
      `anthropic-version` = "2023-06-01",
      `Content-Type`      = "application/json"
    ),
    body = toJSON(list(
      model      = "claude-sonnet-4-5-20250929",
      max_tokens = 1024,
      messages   = list(list(role = "user", content = "안녕하세요, 세계"))
    ), auto_unbox = TRUE),
    encode = "raw"
  )

  cat(content(response, "text"))
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null} theme={null}
  {
    "id": "msg_01XFDUDYJgAACzvnptvVoYEL",
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "안녕하세요! 저는 Claude입니다. 만나서 반갑습니다."
      }
    ],
    "model": "claude-sonnet-4-5-20250929",
    "task_id": "v1api_01XFDUDYJgAACzvnptvVoYEL",
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "usage": {
      "input_tokens": 12,
      "output_tokens": 18,
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 0
    }
  }
  ```

  ```json 400 theme={null} theme={null}
  {
    "type": "error",
    "error": {
      "type": "invalid_request_error",
      "message": "messages는 필수이며 비어 있으면 안 됩니다"
    }
  }
  ```

  ```json 401 theme={null} theme={null}
  {
    "type": "error",
    "error": {
      "type": "authentication_error",
      "message": "유효하지 않은 API 키"
    }
  }
  ```

  ```json 402 theme={null} theme={null}
  {
    "type": "error",
    "error": {
      "type": "insufficient_credits_error",
      "message": "크레딧이 부족합니다"
    }
  }
  ```

  ```json 404 theme={null} theme={null}
  {
    "type": "error",
    "error": {
      "type": "not_found_error",
      "message": "모델 'xxx'를 찾을 수 없습니다"
    }
  }
  ```

  ```json 500 theme={null} theme={null}
  {
    "type": "error",
    "error": {
      "type": "api_error",
      "message": "모든 제공자가 실패했습니다"
    }
  }
  ```
</ResponseExample>

## 사용 예제

### 기본 대화

```python theme={null} theme={null}
import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_API_KEY",
    base_url="https://aireiter.com/api"
)

message = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "양자 컴퓨팅의 기본 원리를 설명해 주세요"}
    ]
)

print(message.content[0].text)
```

### 시스템 프롬프트 + 다중 대화

```python theme={null} theme={null}
message = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    system="당신은 코드 리뷰와 최적화 제안에 능한 숙련된 Python 개발 전문가입니다.",
    messages=[
        {"role": "user",      "content": "데코레이터란 무엇인가요?"},
        {"role": "assistant", "content": "데코레이터는 Python의 문법 설탕으로, ..."},
        {"role": "user",      "content": "실제 프로젝트 예제를 하나 보여줄 수 있나요?"}
    ]
)
```

### 스트리밍 응답

```python theme={null} theme={null}
with client.messages.stream(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[{"role": "user", "content": "AI에 관한 짧은 글을 써주세요"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

### 시각적 이해

```python theme={null} theme={null}
# URL 이미지
message = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {"type": "url", "url": "https://example.com/chart.png"}
                },
                {"type": "text", "text": "이 차트의 추세를 설명해주세요"}
            ]
        }
    ]
)

# Base64 이미지
import base64

with open("image.jpg", "rb") as f:
    image_data = base64.b64encode(f.read()).decode("utf-8")

message = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",
                        "data": image_data
                    }
                },
                {"type": "text", "text": "이 이미지를 분석해주세요"}
            ]
        }
    ]
)
```

## 스트리밍 응답 이벤트 형식

```text theme={null} theme={null}
event: ping
data: {"type":"ping"}

event: message_start
data: {"type":"message_start","message":{"id":"msg_xxx","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-5-20250929","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":25,"output_tokens":0}}}

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"안녕하세요"}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":18}}

event: message_stop
data: {"type":"message_stop"}
```

## 주의사항

1. **인증 방식**: `x-api-key` 요청 헤더 또는 `Authorization: Bearer` 두 가지 방식을 지원하며, Anthropic 공식 SDK는 기본적으로 전자를 사용합니다.

2. **포인트 부족**: 잔액이 부족할 경우 HTTP `402`를 반환하므로, 충전 후 다시 시도해 주세요.

3. **스트리밍 연결 끊김 재접속**: 클라이언트는 SSE 재접속 메커니즘을 구현해야 하며, 연결이 끊길 경우 이미 수신한 내용을 기반으로 재요청 여부를 판단합니다.

4. **모델 선택 권장 사항**:
   * Haiku — 고빈도 간단 질문 응답, 비용 최저
   * Sonnet — 코드 생성, 문서 처리, 종합 추천
   * Opus — 복잡한 추론, 장문 분석, 최고 성능
