主题
按客户策略
通过一个自定义请求标头拆分网关支出上限和速率限制,使每个终端客户在单个 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 与访问控制。
按标头分离一个默认支出上限
默认支出上限会对一个主体维度的每个成员应用相同的上限。按某个标头分离它,会把该上限独立地应用到每个主体和标头值组合上,而无需为每个值都建一个策略。
默认支出分桶遵循以下规则:
- 每个默认值一个标头:只输入标头名称。由请求提供用来标识桶的值。
- 独立的上限:每个主体和标头值组合都会收到所配置的支出上限。
- 回退上限:不带所配置标头的请求会共享其主体对应的一个回退上限。
要按标头分离一个默认支出上限:
- 前往 LLM Gateway 并选择 Cost Controls。
- 点击 Create spend limit。
- 选择 Workspace、User 或 API Key,然后选择默认将该上限应用于该类型每个主体的选项。
- 选择 Separate limits by custom header。
- 输入 Header name,不带其
X-Gateway-前缀。例如,对于X-Gateway-Customer-Id请求标头,输入Customer-Id。 - 设置支出上限,然后点击 Create spend limit。
策略表格会显示用于分离默认上限的标头。你也可以编辑一个现有的默认支出上限,以添加、更改或移除该标头。
当不同的标头值需要不同的上限时,改用显式策略。
添加一个显式标头条件
一个显式的支出或速率限制策略可以匹配一个精确的标头值。使用显式策略为不同的标头值分配不同的上限。
显式标头条件遵循以下规则:
- 每个策略一个条件:一个策略接受一个标头名和一个值。
- 一个主体范围:将该条件与一个组织、工作区、用户或 API 密钥范围结合。主体一侧接受多个值并匹配其中任意一个。
- 缺失的标头不匹配:不带所配置标头的请求不会匹配该策略。
- 每个匹配的策略都会被强制执行:一个同时匹配普通主体策略和带标头条件策略的请求会被计入两者,且任一个都可能阻止它。
- 最多 10 个条件:一个策略总共不超过 10 个主体条件。
- 前往 LLM Gateway。
- 点击 Create policy。
- 选择策略类型和主体范围,然后设置上限。
- 在 Custom header condition (optional) 下,输入 Header name,不带其
X-Gateway-前缀(例如Customer-Id),以及要匹配的 Header value(例如acme)。 - 保存。
在策略创建后,你无法在 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中的同步脚本以相同的两处替换适用于速率限制。支出上限和速率限制是分开的两个族,因此一个终端客户可以在同一个标头值上各持有一个。