Skip to content
LangSmith 文档版本 跟随上游 源快照 1ae3d817 更新于 2026-10-18

按客户策略

通过一个自定义请求标头拆分网关支出上限和速率限制,使每个终端客户在单个 API 密钥下获得各自的上限。

NOTE

LLM Gateway 处于 beta 阶段。

一个支出策略可以按自定义请求标头来分离一个默认上限,使每个标头值获得一个独立的上限。支出速率限制策略也可以匹配某一个特定的标头值。使用这些选项,你可以为自己的终端客户、租户或团队设上限,而无需为每个都签发一个单独的 LangSmith API 密钥。

例如,一个按 X-Gateway-Customer-Id 分离的默认工作区支出限制,会在每个工作区内为 acme 和 globex 提供独立的上限。一个带有条件 X-Gateway-Customer-Id: acme 的显式策略,只会限制携带该确切值的请求。

可匹配的标头

网关会对以 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 开头的标头,都会被丢弃。

WARNING

网关会信任传入请求上的 X-Gateway-* 标头。请在你对终端用户完成身份验证之后,在你自己的后端设置该标头,并且不要将网关 API 密钥分发给终端用户。一个同时控制密钥和标头的调用者可以自行选择使用哪个上限。

WARNING

创建和管理策略需要 organization:manage 权限。有关完整的权限细分,参阅追踪、Engine 与访问控制。

按标头分离一个默认支出上限

默认支出上限会对一个主体维度的每个成员应用相同的上限。按某个标头分离它,会把该上限独立地应用到每个主体和标头值组合上,而无需为每个值都建一个策略。

默认支出分桶遵循以下规则:

  • 每个默认值一个标头:只输入标头名称。由请求提供用来标识桶的值。
  • 独立的上限:每个主体和标头值组合都会收到所配置的支出上限。
  • 回退上限:不带所配置标头的请求会共享其主体对应的一个回退上限。

要按标头分离一个默认支出上限:

  1. 前往 LLM Gateway 并选择 Cost Controls。
  2. 点击 Create spend limit。
  3. 选择 Workspace、User 或 API Key,然后选择默认将该上限应用于该类型每个主体的选项。
  4. 选择 Separate limits by custom header。
  5. 输入 Header name,不带其 X-Gateway- 前缀。例如,对于 X-Gateway-Customer-Id 请求标头,输入 Customer-Id。
  6. 设置支出上限,然后点击 Create spend limit。

策略表格会显示用于分离默认上限的标头。你也可以编辑一个现有的默认支出上限,以添加、更改或移除该标头。

当不同的标头值需要不同的上限时,改用显式策略。

添加一个显式标头条件

一个显式的支出或速率限制策略可以匹配一个精确的标头值。使用显式策略为不同的标头值分配不同的上限。

显式标头条件遵循以下规则:

  • 每个策略一个条件:一个策略接受一个标头名和一个值。
  • 一个主体范围:将该条件与一个组织、工作区、用户或 API 密钥范围结合。主体一侧接受多个值并匹配其中任意一个。
  • 缺失的标头不匹配:不带所配置标头的请求不会匹配该策略。
  • 每个匹配的策略都会被强制执行:一个同时匹配普通主体策略和带标头条件策略的请求会被计入两者,且任一个都可能阻止它。
  • 最多 10 个条件:一个策略总共不超过 10 个主体条件。
  1. 前往 LLM Gateway。
  2. 点击 Create policy。
  3. 选择策略类型和主体范围,然后设置上限。
  4. 在 Custom header condition (optional) 下,输入 Header name,不带其 X-Gateway- 前缀(例如 Customer-Id),以及要匹配的 Header value(例如 acme)。
  5. 保存。

在策略创建后,你无法在 UI 中编辑该标头条件。要更改它,请删除该策略并新建一个,或通过 API 更新 subject_matchers。

为每个终端客户限制支出

一个经销商或多租户应用通常会从它自己的后端调用网关,代表许多终端客户使用一个工作区范围的 API 密钥。当每个客户需要不同的上限时,使用一个显式标头条件。如果每个客户使用相同的上限,则改为按标头分离一个默认支出上限。

步骤 1. 在每次调用时发送一个客户标头

在你后端代表某个终端客户发出的每个请求上附加该标头:

bash
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"}]}'
python
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)

NOTE

如果你的 LangSmith 账户在某个区域实例上,请使用对应的区域网关。

步骤 2. 为一个客户创建上限

通过 LangSmith REST API 为每个终端客户创建一个支出策略:

bash
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}
        }'
python
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. 与你的客户列表同步策略

由于每个终端客户都需要它自己的策略,请让策略集合与你的客户列表保持同步。以下脚本会为每个当前客户创建或更新一个上限,然后删除已离去客户的上限:

python
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. 读取每个客户的支出

API 返回的每个支出策略都会报告 current_spend_usd,即该策略活动窗口内累计的支出。用它来向每个终端客户展示其用量,或在他们达到上限前警告他们。当支出查询失败时该字段会被省略,因此请将缺失值视为未知而非零。

列表端点只有在一个主体匹配键与某个值配对时,才会按该键缩小范围,因此请在你自己的代码中列出支出上限并挑选出按客户的那些:

python
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"])

限制每个终端客户的吞吐量

速率限制使用相同的主体匹配器。交换 policy_type 和 config,即可让一个终端客户拥有它自己的请求和 token 额度:

bash
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中的同步脚本以相同的两处替换适用于速率限制。支出上限和速率限制是分开的两个族,因此一个终端客户可以在同一个标头值上各持有一个。

后续步骤


BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。