고객별 정책

고객별 정책 (Per-customer policies)

커스텀 요청 헤더로 게이트웨이 지출 상한과 속도 제한을 분리해 각 최종 고객이 단일 API 키 아래에서 자체 한도를 갖게 해요.

참고: LLM Gateway는 베타 단계예요.

지출 정책은 기본 한도를 커스텀 요청 헤더로 분리해서 각 헤더 값이 독립적인 한도를 갖게 할 수 있어요. 지출 및 속도 제한 정책은 하나의 특정 헤더 값만 일치시킬 수도 있어요. 이 옵션들을 사용해 각각에 별도 LangSmith API 키를 발급하지 않고도 자체 최종 고객, 테넌트 또는 팀을 상한으로 묶을 수 있어요.

예를 들어 X-Gateway-Customer-Id로 분리된 기본 워크스페이스 지출 한도는 각 워크스페이스 내에서 acmeglobex에 독립적인 한도를 부여해요. X-Gateway-Customer-Id: acme 조건이 있는 명시적 정책은 정확히 그 값을 지니는 요청만 제한해요.

매칭 가능한 헤더 (Matchable headers)

게이트웨이는 X-Gateway- 접두사로 시작하는 요청 헤더와 X-Gateway-Metadata JSON 헤더 내부의 키로 일치시켜요. 다른 요청 헤더는 매칭할 수 없어요.

헤더 이름은 매칭 전에 정규화돼요: X-Gateway- 접두사가 제거되고, 나머지는 소문자로 바뀌며, a-z, 0-9, _ 밖의 모든 문자는 _로 교체돼요. X-Gateway-Customer-Id, x-gateway-customer_id, X-Gateway-CUSTOMER.ID 헤더는 모두 매처 키 customer_id로 해석돼요. 헤더 값은 와일드카드나 패턴 매칭 없이 정확하고 대소문자를 구분하는 문자열로 비교돼요.

게이트웨이는 호출자 아이덴티티를 스스로 기록하고 클라이언트가 이를 덮어쓰려는 시도를 무시해요. organization_id, workspace_id, workspace_handle, user_id, user_email, api_key_id, api_key_short, auth_mode, user_agent, applied_policy_ids 또는 applied_policy_names로 해석되는 헤더와 정규화된 이름이 gateway로 시작하는 모든 헤더는 버려져요.

경고: 게이트웨이는 수신 요청의 X-Gateway-* 헤더를 신뢰해요. 최종 사용자를 인증한 후 자체 백엔드에서 헤더를 설정하고, 게이트웨이 API 키를 최종 사용자에게 배포하지 마세요. 키와 헤더를 모두 통제하는 호출자는 어떤 한도를 사용할지 선택할 수 있어요.

경고: 정책 생성·관리에는 organization:manage 권한이 필요해요. 전체 권한 분류는 추적, Engine, 접근 제어를 참고하세요.

기본 지출 한도를 헤더로 분리하기 (Separate a default spend limit by header)

기본 지출 한도는 대상 차원의 모든 구성원에게 동일한 상한을 적용해요. 헤더로 분리하면 그 상한을 모든 대상과 헤더 값 쌍에 각각 독립적으로 적용하며, 각 값에 대한 정책이 필요하지 않아요.

기본 지출 버케팅은 다음 규칙을 따릅니다:

  • 기본당 헤더 하나 (One header per default): 헤더 이름만 입력하세요. 요청이 버킷을 식별하는 값을 제공해요.
  • 독립적인 한도 (Independent limits): 각 대상과 헤더 값 쌍은 구성된 지출 한도를 받아요.
  • 폴백 한도 (Fallback limit): 구성된 헤더가 없는 요청은 대상에 대한 폴백 한도를 공유해요.

기본 지출 한도를 헤더로 분리하려면:

  1. LLM Gateway로 이동해 Cost Controls를 선택하세요.
  2. Create spend limit을 클릭하세요.
  3. Workspace, User 또는 API Key를 선택한 다음 해당 유형의 모든 대상에 기본적으로 한도를 적용하는 옵션을 선택하세요.
  4. Separate limits by custom header를 선택하세요.
  5. X-Gateway- 접두사 없이 Header name을 입력하세요. 예를 들어 X-Gateway-Customer-Id 요청 헤더에는 Customer-Id를 입력하세요.
  6. 지출 한도를 설정하고 Create spend limit을 클릭하세요.

정책 테이블은 기본 한도를 분리하는 데 사용된 헤더를 표시해요. 기존 기본 지출 한도를 편집해 헤더를 추가·변경·제거할 수도 있어요.

다른 헤더 값에 다른 한도가 필요할 때는 대신 명시적 정책을 사용하세요.

명시적 헤더 조건 추가하기 (Add an explicit header condition)

명시적 지출 또는 속도 제한 정책은 하나의 정확한 헤더 값만 일치시킬 수 있어요. 다른 헤더 값에 다른 한도를 배정하려면 명시적 정책을 사용하세요.

명시적 헤더 조건은 다음 규칙을 따릅니다:

  • 정책당 조건 하나 (One condition per policy): 정책은 헤더 이름 하나와 값 하나를 받아요.
  • 대상 범위 하나 (One subject scope): 조건을 조직, 워크스페이스, 사용자 또는 API 키 범위와 결합해요. 대상 쪽은 여러 값을 받고 그중 아무 것과나 일치해요.
  • 헤더 누락은 일치하지 않음 (Missing headers do not match): 구성된 헤더가 없는 요청은 정책과 일치하지 않아요.
  • 모든 일치 정책이 집행됨 (Every matching policy is enforced): 일반 대상 정책과 헤더 조건이 있는 정책 모두에 일치하는 요청은 둘 다에 집계되며, 둘 중 하나가 차단할 수 있어요.
  • 최대 10개 조건 (At most 10 conditions): 정책은 총 10개 이하의 대상 조건을 지녀요.
  1. LLM Gateway로 이동하세요.
  2. Create policy를 클릭하세요.
  3. 정책 유형과 대상 범위를 선택하고 한도를 설정하세요.
  4. Custom header condition (optional) 아래에서 X-Gateway- 접두사 없이 Header name(예: Customer-Id)과 일치시킬 Header value(예: acme)를 입력하세요.
  5. 저장하세요.

정책 생성 후 UI에서 헤더 조건을 편집할 수 없어요. 변경하려면 정책을 삭제하고 새로 만들거나, API를 통해 subject_matchers를 업데이트하세요.

최종 고객별 지출 상한 (Cap spend per end customer)

리셀러 또는 멀티테넌트 애플리케이션은 보통 자체 백엔드에서 게이트웨이를 호출하며, 많은 최종 고객을 대신해 하나의 워크스페이스 범위 API 키를 사용해요. 각 고객이 다른 상한이 필요하면 명시적 헤더 조건을 사용하세요. 모든 고객이 같은 상한을 사용하면 하나의 기본 지출 한도를 헤더로 분리하세요.

1단계: 모든 호출에 고객 헤더 보내기 (Send a customer header on every call)

백엔드가 최종 고객을 대신해 만드는 각 요청에 헤더를 첨부하세요:

curl https://gateway.smith.langchain.com/openai/v1/chat/completions \
    -H "Authorization: Bearer $LANGSMITH_API_KEY" \
    -H "Content-Type: application/json" \
    -H "X-Gateway-Customer-Id: acme" \
    -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'
import os

from openai import OpenAI

client = OpenAI(
    base_url=os.environ["OPENAI_BASE_URL"],
    api_key=os.environ["LANGSMITH_API_KEY"],
)
customer_id = "acme"
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "ping"}],
    extra_headers={"X-Gateway-Customer-Id": customer_id},
)
print(response.choices[0].message.content)

참고: LangSmith 계정이 리전 인스턴스에 있다면 해당 리전 게이트웨이를 사용하세요.

2단계: 한 고객에 대한 상한 만들기 (Create a cap for one customer)

LangSmith REST API를 통해 최종 고객당 하나의 지출 정책을 만드세요:

curl -X POST "https://api.smith.langchain.com/v1/platform/gateway-policies" \
    -H "X-Api-Key: $LANGSMITH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "name": "customer-acme-monthly-cap",
          "policy_type": "spend_cap",
          "action": "block",
          "subject_matchers": [
            {"key": "workspace_id", "value": "0b1c2d3e-4f56-7890-abcd-ef1234567890"},
            {"key": "customer_id", "value": "acme"}
          ],
          "config": {"window": "monthly", "limit_usd": 250}
        }'
import os

import httpx

response = httpx.post(
    "https://api.smith.langchain.com/v1/platform/gateway-policies",
    headers={"X-Api-Key": os.environ["LANGSMITH_API_KEY"]},
    json={
        "name": "customer-acme-monthly-cap",
        "policy_type": "spend_cap",
        "action": "block",
        "subject_matchers": [
            {"key": "workspace_id", "value": "0b1c2d3e-4f56-7890-abcd-ef1234567890"},
            {"key": "customer_id", "value": "acme"},
        ],
        "config": {"window": "monthly", "limit_usd": 250},
    },
    timeout=30.0,
)
response.raise_for_status()

매처 키는 정규화된 이름 customer_id이지 헤더 이름 X-Gateway-Customer-Id가 아니에요. 정책은 API 키를 소유한 조직에 속해요.

subject_matchers가 이미 존재하는 정책을 게시하면 중복을 추가하는 대신 그 정책을 업데이트하므로 이 호출은 반복해도 안전해요.

3단계: 고객 목록과 정책 동기화하기 (Sync policies with your customer list)

각 최종 고객이 자체 정책을 필요로 하므로 정책 세트를 고객 목록과 보조를 맞추세요. 다음 스크립트는 모든 현재 고객에 대한 상한을 만들거나 업데이트한 다음, 사라진 고객의 상한을 삭제해요:

import os

import httpx

API_URL = "https://api.smith.langchain.com/v1/platform/gateway-policies"
WORKSPACE_ID = os.environ["LANGSMITH_WORKSPACE_ID"]

# Your source of truth: end customer identifier mapped to a monthly cap in USD.
CUSTOMER_CAPS = {"acme": 250.0, "globex": 1000.0, "initech": 50.0}


def matchers_for(customer: str) -> list[dict[str, str]]:
    return [
        {"key": "workspace_id", "value": WORKSPACE_ID},
        {"key": "customer_id", "value": customer},
    ]


def existing_caps(client: httpx.Client) -> dict[str, dict]:
    """Return the current per-customer spend caps, keyed by customer identifier."""
    response = client.get(API_URL, params={"policy_type": "spend_cap"})
    response.raise_for_status()
    return {
        matcher["value"]: policy
        for policy in response.json()
        for matcher in policy["subject_matchers"]
        if matcher["key"] == "customer_id"
    }


def sync() -> None:
    headers = {"X-Api-Key": os.environ["LANGSMITH_API_KEY"]}
    with httpx.Client(headers=headers, timeout=30.0) as client:
        existing = existing_caps(client)

        # Posting an existing matcher set updates that policy, so this both
        # creates caps for new customers and corrects caps that changed.
        for customer, limit_usd in CUSTOMER_CAPS.items():
            client.post(
                API_URL,
                json={
                    "name": f"customer-{customer}-monthly-cap",
                    "policy_type": "spend_cap",
                    "action": "block",
                    "subject_matchers": matchers_for(customer),
                    "config": {"window": "monthly", "limit_usd": limit_usd},
                },
            ).raise_for_status()

        # Deletes any per-customer cap missing from CUSTOMER_CAPS, including
        # caps created outside this script.
        for customer, policy in existing.items():
            if customer not in CUSTOMER_CAPS:
                client.delete(f"{API_URL}/{policy['id']}").raise_for_status()


if __name__ == "__main__":
    sync()

고객이 가입하거나 이탈하거나 다른 플랜으로 이동할 때마다 스크립트를 실행하세요.

4단계: 고객별 지출 읽기 (Read spend per customer)

API가 반환하는 각 지출 정책은 정책의 활성 창에 누적된 지출인 current_spend_usd를 보고해요. 이를 사용해 각 최종 고객에게 사용량을 보여주거나 상한 도달 전에 경고할 수 있어요. 지출 조회가 실패하면 필드가 생략되므로, 누락된 값을 0이 아니라 알 수 없음으로 취급하세요.

목록 엔드포인트는 대상 매처 키가 값과 쌍을 이룰 때만 그 키로 좁혀지므로, 자체 코드에서 지출 상한을 나열하고 고객별 항목을 선택하세요:

import os

import httpx

response = httpx.get(
    "https://api.smith.langchain.com/v1/platform/gateway-policies",
    headers={"X-Api-Key": os.environ["LANGSMITH_API_KEY"]},
    params={"policy_type": "spend_cap"},
    timeout=30.0,
)
response.raise_for_status()

for policy in response.json():
    customer = next(
        (m["value"] for m in policy["subject_matchers"] if m["key"] == "customer_id"),
        None,
    )
    if customer is None:
        continue  # A cap on the workspace itself, not on one end customer.
    # current_spend_usd is absent when the spend lookup fails.
    print(customer, policy.get("current_spend_usd"), policy["config"]["limit_usd"])

최종 고객별 처리량 제한 (Limit throughput per end customer)

속도 제한은 동일한 대상 매처를 사용해요. policy_typeconfig를 바꿔 최종 고객에게 자체 요청·토큰 허용량을 부여하세요:

curl -X POST "https://api.smith.langchain.com/v1/platform/gateway-policies" \
    -H "X-Api-Key: $LANGSMITH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "name": "customer-acme-rate-limit",
          "policy_type": "rate_limit",
          "action": "block",
          "subject_matchers": [
            {"key": "workspace_id", "value": "0b1c2d3e-4f56-7890-abcd-ef1234567890"},
            {"key": "customer_id", "value": "acme"}
          ],
          "config": {
            "version": 1,
            "limits": [
              {"metric": "requests", "window": "minute", "value": 100},
              {"metric": "tokens", "window": "hour", "value": 1000000}
            ]
          }
        }'

3단계의 동기화 스크립트는 같은 두 치환으로 속도 제한에도 적용돼요. 지출 상한과 속도 제한은 별도 패밀리이므로 최종 고객이 같은 헤더 값에 각각 하나씩 지닐 수 있어요.

다음 단계 (Next steps)

출처: 문서

더 알아보기 (Learn more)