跳到主要内容
版本:Dev

成本归属标签

成本归属标签让组织按自己的维度归属用量和成本,例如部门、事业部、子公司、项目或成本中心。组织先声明标签键,再在调用方 API Key、团队和成员上设置 key=value,或者从调用方的 JWT 读取取值。每个请求按其运行时解析出的标签值归属。之后可以在 成本分析 页面按这些值拆分支出,用标签预算为其封顶,并让限流策略按标签匹配和拆分流量。

标签在 AISIX Enterprise 和 AISIX Cloud 中的行为一致。

前提条件​

开始之前,请准备以下内容:

  • 声明标签键需要组织的 owner 或 admin 角色,或拥有 tag_keys 写权限的自定义角色。在某个资源上设置标签,需要该资源的写权限。
  • 每个需要归属的环境都使用会在用量上记录标签的网关。1.6.x 及更早版本的网关不会记录,参见网关版本要求。
  • 使用 Admin API 示例时,需要控制面地址和具有写权限的 Admin Token:
# AISIX_CP 包含 /api,末尾不带斜杠。
# 本地 AISIX Enterprise 快速入门使用 http://localhost:8080/api。
export AISIX_CP="YOUR_AISIX_CONTROL_PLANE_API_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export TEAM_ID="YOUR_TEAM_ID"
export ENV_ID="YOUR_ENVIRONMENT_ID"

声明标签键​

标签键必须先在组织中声明,资源才能带上它。预先声明可以统一用词,避免 department 同时被写成 dept 或 Department。

  1. 在组织导航中打开 标签键,选择 新建标签键。
  2. 输入 键,例如 department。
  3. 可以填写 显示名称 和 描述。
  4. 选择 声明标签键。

通过 Admin API 声明标签键:

curl -sS -X POST "${AISIX_CP}/tag_keys" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"key": "department",
"display_name": "Department",
"description": "Cost center the usage is charged to."
}'

标签键遵循以下规则:

  • 以小写字母开头,只包含小写字母、数字和下划线,最多 63 个字符:^[a-z][a-z0-9_]{0,62}$。
  • 声明后不能修改键本身。显示名称和描述可以随时编辑。
  • 每个组织最多声明 32 个键。超出时返回 400,错误码为 TAG_KEY_LIMIT;声明已存在的键返回 409。
  • 所有成员都可以读取已声明的键,写入需要 tag_keys 写权限。

删除标签键​

只要有预算、限流策略或信任服务提供方的 tag_claims 在使用某个标签键,删除就会被拒绝,返回 409,错误码为 TAG_KEY_IN_USE。错误信息会列出它们,请先移除。

否则,删除会把该键从所有带有它的调用方 API Key、团队和成员上移除,之后的请求不再归属到它。已记录的用量保留当时的标签,因此已删除键的值仍会出现在已存的历史数据中。只要所选时间范围内的用量带有该键,成本分析 就会继续提供它,并标记为 已删除。

在 API Key、团队和成员上设置标签​

可以在三类资源上设置标签。每个资源只带自己的标签,形式为标签键到标签值的对象:

资源控制台Admin API
调用方 API Key环境 API 密钥 下 Key 的创建和编辑表单中的 成本归属标签 部分POST /environments/{environment_id}/api_keys 和 PATCH /environments/{environment_id}/api_keys/{api_key_id}
团队团队 下团队的创建和编辑表单POST /teams 和 PATCH /teams/{team_id}
成员成员 中成员行的 标签 面板,以及 AISIX Enterprise 中的 创建用户 表单POST /members 和 PATCH /members/{user_id}

控制台提供已声明的键供选择,并提示组织中该键已在使用的值。同样的建议值也可以通过 GET /tag_keys/{key}/values 获取,它列出最多 500 个去重值:调用方 API Key、团队和成员当前设置的值,以及最近 30 天用量归属到的值,从 JWT Claim 读取的值就出现在后者中。来自用量的值只填满配置值留下的名额,并且只有同时拥有 usage 读权限的调用方才能看到。组织很大时这部分查询可能超时,响应中就只有配置值。此时仍可手动输入值。

标签值遵循以下规则:

  • 值是 1 到 128 个字符的自由文本。
  • 值不能包含换行等控制字符,也不能以空白字符(包括不换行空格和全角空格)开头或结尾。控制面会以 400 拒绝这样的值。
  • 资源 tags 中的每个键都必须已在组织中声明。

在 Admin API 中,PATCH 会整体替换 tags 对象,请发送要保留的完整集合。发送 null 或 {} 清空所有标签,不传 tags 则保持不变。例如,以下请求为团队设置两个标签:

curl -sS -X PATCH "${AISIX_CP}/teams/${TEAM_ID}" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"tags": {
"department": "rnd",
"subsidiary": "emea"
}
}'

成员的标签属于其在本组织中的成员资格,而不属于其账号。admin 可以修改成员的标签;修改成员角色仍然需要 owner。通过邀请、单点登录或 SCIM 目录同步加入的成员一开始没有标签。由目录同步管理的成员,其标签也可以在控制台和 Admin API 中编辑。目录同步既不读取也不写入标签,因此标签不会从身份提供方同步。如果要在请求时从身份提供方取值,参见从 JWT Claim 读取标签。

请求如何解析标签​

请求通过身份认证时,网关根据认证所用的调用方 API Key 解析其标签;对于 JWT 调用方,还会读取已验证的 Token。对每个标签键,取最近的值:

  1. 对于 JWT 调用方,取信任服务提供方的 tag_claims 从 Token 读取的值。参见从 JWT Claim 读取标签。
  2. 否则,取调用方 API Key 自己的值。
  3. 否则,取该 Key 绑定的团队上设置的值。
  4. 否则,取拥有该 Key 的成员上设置的值。

只有 Key 自己的团队绑定和成员绑定会参与解析,成员所属的其他团队不提供任何标签。每个请求的每个标签键只有一个值,不同的键可以组合,所以一个请求可以从 Key 取 department、从团队取 business_group、从成员取 subsidiary。请求不能自带标签,网关从不从请求头或请求体中读取标签。

例如,组织声明了 department、business_group 和 subsidiary,并做了如下设置:

资源标签
团队 platformdepartment=infra、business_group=cloud
成员 Alicedepartment=rnd、subsidiary=emea
Key K1,绑定到 platform,所有者为 Alicedepartment=ml
Key K2,绑定到 platform,所有者为 Alice无
Key K3,所有者为 Alice,没有团队无

请求解析出的标签如下:

Keydepartmentbusiness_groupsubsidiary
K1ml,来自 Keycloud,来自团队emea,来自成员
K2infra,来自团队cloud,来自团队emea,来自成员
K3rnd,来自成员未设置emea,来自成员

从 JWT Claim 读取标签​

对于使用 JWT 认证的调用方,信任服务提供方可以从已验证的 Token 中得到标签。它的 tag_claims 字段把标签键映射到 Claim 路径,网关在每个请求上读取这些 Claim。用量分析、导出、标签预算和标签限流都使用读取结果,因此无需逐个给调用方 API Key 打标签,也无需与身份提供方保持同步。

读取到的值只作用于当前请求,不会写回调用方 API Key、团队或成员,因此这些资源的页面不会显示它。

Claim 必须在到达网关的 JWT 里。只有身份提供方的 userinfo 端点才返回的字段读取不到,需要在身份提供方上配置,把它放进 Token。对于使用交互式登录的 MCP 客户端,Claim 必须在 ID Token 里。

配置标签 Claim​

先声明每个标签键,再在环境的 OIDC 身份提供方 视图中创建或编辑服务提供方。在 来自令牌声明的标签(可选) 下选择 添加标签,选择已声明的键并填写 Claim 路径。列表中服务提供方所在的行会显示当前的映射。

通过 Admin API 设置同样的映射时,在创建或更新服务提供方时使用 tag_claims:

export OIDC_PROVIDER_ID="YOUR_TRUST_PROVIDER_ID"

curl -sS -X PATCH "${AISIX_CP}/environments/${ENV_ID}/oidc_providers/${OIDC_PROVIDER_ID}" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"tag_claims": {
"department": "department",
"subsidiary": "org.subsidiary"
}
}'

PATCH 会替换整个 tag_claims 对象。发送 null 或 {} 可移除所有映射,省略该字段则保持不变。

以下情况控制面返回 400,错误码为 INVALID_REQUEST:键未在组织中声明或不符合标签键格式、条目超过 32 条、Claim 路径为空。被映射引用的键要先移除映射才能删除。

Claim 如何变成标签值​

网关先把 Claim 路径当作一个完整的顶层 Claim 名读取,所以 https://example.com/department 这样的名字可以使用。Token 中没有这个名字的 Claim 时,再按点号逐层访问嵌套对象,所以 org.subsidiary 读取 org 内的 subsidiary。

然后按下表转换 Claim 的值:

Claim 值标签值
"rnd"rnd,原样使用
10421042,数字转为文本
["rnd", "ml", "rnd"]ml,rnd,元素去重后按文本排序,再用逗号拼接
["rnd"]rnd,元素本身
true、null、{} 或 []缺失
["rnd", true],即数组中含有字符串和数字以外的元素缺失

结果必须符合与其他标签值相同的规则:1 到 128 个字符,不含控制字符,首尾没有空白。对于数组,每个元素和拼接结果都要满足这条规则。不符合规则的值按缺失处理,不会被截断或去除空白。

一个数组只产生一个标签值,而不是多个。groups Claim 为 ["ml", "rnd"] 的用户带有的值是 ml,rnd,所以 groups=rnd 上的预算不会计入该用户的请求。元素内的逗号不做转义,所以 ["a,b"] 和 ["a", "b"] 产生相同的值。

回落和默认值​

标签 Claim 按标签键逐个解析,优先于调用方 API Key、团队和成员上的值。Claim 缺失时,只有该键回落到后面几层。

要让缺少该 Claim 的请求仍然可见,可以在调用方 API Key 上设置默认值,例如 department=unassigned。这些请求会在成本分析中归到这个值下,也可以为它设置预算。如果某个标签键在所有来源上都没有值,请求就不受该键上的预算或限流策略约束。

取值变化时​

身份提供方上的值变化后,要等签发新 Token 才生效。已记录的用量保留其运行时带有的值,因此人员调动部门不会重新归属历史用量。

通过 MCP 交互式登录登录的用户使用登录时记录的 Claim,刷新会话也不会更新,只有重新登录才会得到新值。服务提供方的 tag_claims 本身的变更,在这两种情况下都对之后的请求生效。

选择低基数的 Claim​

建议映射取值不多的 Claim,例如部门或成本中心。每个不同的值都是成本分析中的一个分组,也是按该标签分组的限流策略中的一个计数器。

多人共用一把调用方 API Key 时,避免映射到每人一个值的字段,例如邮箱。用量按 Key 和标签集合汇总,这样会明显增加汇总数据量。这个值还会进入用量记录和导出,请注意隐私影响。

共用 Key 时的行为​

同一把调用方 API Key 上,不同请求从 Claim 读取到的标签可以不同,这会带来两个可见的影响:

  • 同一把 Key 上,标签预算可能拒绝一个 Token 而放行另一个。aisix_budget_* 指标按 Key 打标签,所以反映的是最近一次请求的判定。
  • 已完成 batch 的用量带有观察到任务完成的那个调用方的标签,而不是提交 batch 的调用方的标签。

排查标签 Claim​

要查看 Claim 产生了什么值,可以在 成本分析 中按该标签键分组,或者在请求日志中展开请求所在的行,查看 标签。如果请求显示的是来自 Key、团队或成员的值,或者落在 未设置 中,说明没有从 Claim 得到值。

具体原因只出现在网关的 debug 日志中。通过 log_level 或 RUST_LOG 为 aisix::auth 目标开启 debug 日志,例如 info,aisix::auth=debug,然后重启网关。此后每个请求上每个被丢弃的 Claim 都会写一行 claim yields no tag value,带有 provider、tag_key 和 reason。这行日志不包含取值本身。

reason含义
claim_absentToken 中该路径上没有 Claim。
value_type值是布尔、null 或对象。
empty_array值是空数组。
array_element_type数组元素不是字符串或数字。
length值、数组元素或拼接结果不是 1 到 128 个字符。
control_character值、数组元素或拼接结果含有控制字符。
surrounding_whitespace值、数组元素或拼接结果的首尾有空白。

归属在请求运行时确定​

网关会把解析出的标签和 Key 的团队记录在该请求的每条用量事件上,包括重试、故障转移以及网关自己的拒绝。控制面随事件一起存储它们,并按记录的内容归属成本。

因此,标签的变更只影响之后的请求。修改 Key、团队或成员的标签,修改信任服务提供方的 tag_claims 或它读取的 Claim 值,把 Key 绑定到其他团队,或删除标签键,都不会重新归属已经记录的用量。在设置标签之前记录的用量,以及不记录标签的网关上报的用量,在该标签上都没有值,显示为 未设置。

按标签分析成本​

组织级的 成本分析 页面一次按一个维度,对组织内所有环境的用量分组。页面分为两部分:筛选 选定要分析的用量,统计 对其进行拆分。建议按这个顺序操作:先筛选用量,再对剩下的用量分组,然后下钻。例如,先筛选 business_group=cloud,再按 department 标签分组,查看该业务组的花费在各部门之间如何分布。

筛选 部分在前:

  • 时间范围:最近 24 小时、7 天、30 天,或自定义范围。页面按整点小时读取,开始时间向下取整,结束时间向上取整。
  • 筛选条件:环境、团队、成员、模型,以及任意多个标签 key=value 筛选。不同标签键的筛选必须同时满足,同一个键的多个值满足其一即可。从 API Key 行下钻时,会把该 Key 加为筛选条件。每个生效的筛选条件都以标签块的形式显示在控件下方,清除筛选 可一次清除全部筛选。

统计 部分随后对筛选后的用量进行拆分:

  • 分组方式:标签键、团队、成员、API Key、模型、环境、日期或目标模型。参见对比模型的目标模型。按日期分组时显示按天的柱状图,其他分组显示横向条形图。
  • 排序依据:成本、总 Token、输入 Token、输出 Token、请求数或成功请求数,分组按从大到小排列。
  • 刷新:重新加载数据,同时重新加载标签键以及团队、成员和环境的名称。

选择某一行即可下钻:该行的分组会成为筛选条件,页面随后选择筛选尚未固定的下一个分组方式。表格上方的面包屑可以回到之前的任意一步。未设置 行汇总在该分组上没有值的用量,不能下钻;日期行也不能下钻。表格最多列出 50 个分组,其余的汇总在 其他 N 个分组 行中,因此列出的分组加上该行等于合计。

按团队或标签键分组时,预算(本期) 列显示对应团队预算或标签预算的本期已花费和额度。按 API Key 分组时,每个 Key 行都可以跳转到该 Key 所在环境的请求日志,并已按该 Key 筛选。

分组方式、标签筛选和下钻中提供的标签键,是组织已声明的键,加上所选时间范围内用量带有、但当前已不再声明的键。后者是在这些用量记录之后被删除的键,会标记为 已删除,仍然可以用于分组和筛选。取值建议来自调用方 API Key、团队和成员当前带有的值以及最近的用量,已删除的键没有取值建议:请手动输入值,或从分组行下钻。如果一个标签键都没有,分组方式和筛选的下拉框会显示提示,并链接到标签键设置页,标签筛选在声明标签键之前不可用。

团队、成员、API Key 或环境在其用量记录之后被删除,仍会单独成行。分组行、图表标签、面包屑和筛选标签块会显示其 ID 的前 8 个字符,并带有 已删除 徽标;已不在组织中的成员显示 已移出。选择徽标可以查看完整 ID 并复制。名称不可用 徽标表示控制台无法确认该资源是否仍然存在,例如团队列表加载失败,并不代表该资源已被删除。

控制面升级后,在升级前记录的用量补入期间,页面显示与 用量 视图相同的回填提示,回填完成前数据并不完整。成本使用组织币种。

要查看某一行的费用是怎么算出来的,请展开它的计费明细。

所有有用量读取权限的成员都可以打开成本分析。自动化可以通过 GET /usage_analysis 读取相同的数据,并通过 GET /usage_analysis/tag_keys 读取某个时间范围内用量带有的标签键(包括已删除的键),参见 AISIX Enterprise / Cloud Admin API 参考。

GET /usage_analysis 不限于表格中显示的分组。它最多列出 limit 个分组(默认 50,最大 500),offset 按排序跳过相应数量的分组,因此自动化可以逐页读取全部分组。参见翻页读取分组。

对比模型的目标模型​

模型行按调用方指定的名称分组。要查看模型组的流量在其背后每个模型上的情况,请在模型行上选择 查看目标模型,或在 分组方式 中选择 目标模型。每一行显示目标模型的 Provider Key、计费金额 金额和 平台成本。平台成本是用量按目标模型自身价格计算的成本,不含模型组价格和低峰折扣。没有价格的部分会以未定价尝试次数显示在金额旁边。

这些计数是每个目标模型上的尝试次数,包含失败的尝试和回退,因此各目标模型相加可能超过该模型的请求数。直接调用某个模型的用量记在该模型自己的行,包括由网关缓存直接返回的响应。调用模型组却没有到达任何目标模型的用量(例如由网关缓存直接返回的响应,或在选中目标模型之前就失败的请求)落在 未设置 行。目标模型行和平台成本只覆盖引入它们的控制面版本之后记录的用量,所以更早的时间范围在这里会少于模型那一行。目标模型无法按团队、成员、API Key 或标签筛选,这些筛选生效时 查看目标模型 和该分组不可用。

自动化可以通过 GET /usage_analysis?group_by=target_model 读取相同的数据,model=<name> 可限定到一个模型组。它的权限与其他用量读取相同。与 team_id、member_id、api_key_id 或 tag 同用会返回 400。每个分组包含:

字段含义
key目标模型 ID。调用模型组却没有到达任何目标模型的用量(例如缓存命中)为 null。
name目标模型当前的名称,模型不再存在后为 null。
environment_id目标模型所属的环境。模型不再存在后不返回。
provider、provider_key_name目标模型的服务提供方密钥的厂商 ID 和显示名称,模型或密钥不再存在后为 null。
cost计费金额,币种为组织币种。响应还会以已废弃的名字 cost_usd 返回它。
prompt_tokens、completion_tokens、total_tokens这些尝试的 Token 数量。
request_count该目标模型上的尝试次数,每次失败的尝试和回退都算。
success_count该目标模型上成功的尝试次数。
upstream_cost平台成本之和,所有尝试都未定价时为 null。
unpriced_request_count该目标模型上 upstream_cost 为 null 的尝试次数。没有到达任何目标模型的尝试不计入。

remainder 和 total 也带 upstream_cost 和 unpriced_request_count。

翻页读取分组​

GET /usage_analysis 和 GET /usage_analysis/breakdown 用同样的方式翻页:

  • limit 设置一次响应列出多少个分组,默认 50,最大 500。offset 按排序跳过相应数量的分组,再开始列出,默认为 0。
  • total.group_count 是所有页加起来的分组总数。
  • remainder 汇总所有不在本页的分组,包括本页之前和之后的分组,因此任何一页上列出的分组加上 remainder 都等于 total。
  • 排序值相同的分组按 key 排序,计费明细的计价条件再按其余各列排序,用量来源排在最后,上游上报的在前、网关估算的在后,因此只要两次请求之间用量没有变化,相邻的页既不会重复也不会遗漏分组。

翻页期间,当前小时的用量仍在不断汇入,分组可能因此在页之间移动。要得到一致的结果,请读取结束于已经过去的整点的时间范围。

每一页都会在整个时间范围上重新计算汇总,因此请逐页依次请求,不要并行请求。并行翻页会使汇总占用的数据库内存成倍增加,而控制面不限制同时进行的请求数。

每个汇总用量的查询有 30 秒的时间限制。查询超时的请求返回 503,错误码为 QUERY_TIMEOUT,且不返回任何分组。请缩小时间范围或收紧筛选条件后重新请求。

控制台本身不翻页:表格和计费明细只显示第一页,其他 N 个分组 行仍然只是一个合计。

读取计费明细​

成本分析里的一笔费用,不一定是 Token 数量乘以价目上的某一个单价。输入 Token 分档会让长请求按另一档单价计费,服务档位价格会替换上游以更快档位处理的请求的单价,模型组的低峰折扣还会减掉一部分费用。计费明细展示一行费用是怎么算出来的:适用了哪些计价条件、每种条件下各计费项计了多少、平均单价是多少。

展开明细​

成本分析结果表的每一行(包括 未设置 和 合计)在名称前都有一个展开按钮。选择 展开计费明细,该行下方会显示这一行用量的计费明细,时间范围和筛选条件与页面一致;选择 收起计费明细 即可收起。其他 N 个分组 行没有计费明细;要查看其中某个分组的明细,请收窄筛选条件,直到该分组单独列出。重新加载数据会收起所有已展开的明细。

明细按计价条件分块列出,费用高的在前。一种计价条件是下面几列取值的一个组合:

列含义
模型调用方指定的模型名称。对于模型组,这里是模型组本身,而不是实际处理请求的模型。
实际调用模型该次尝试实际分派到的模型:直接调用某个模型时就是该模型,调用模型组时是组内的某个目标模型。没有分派到任何模型时显示 —,例如调用模型组的请求直接从网关响应缓存取得了响应。直接调用某个模型时的缓存命中仍显示该模型。
价格阶梯实际为该请求某个计费项定价的输入 Token 分档,例如 第 2 档(无上限)。没有任何计费项用到分档单价时(例如统一定价)显示 —。决策端点上的请求和按音频时长计费的请求,只要有计费项按分档单价计费,也会列在该档下,尽管它们在请求日志中的 价格阶梯 为空。
服务档位上游上报的实际处理档位;上游没有上报时显示 默认。
时段请求在其模型组低峰折扣的时间内完成时为 低峰,否则为 正常。
价格按模型组价格计费时为 模型组价格。按实际调用模型自己的价格计费时为 实际调用模型的价格:直接调用的模型、没有设置组价的模型组,以及组价不覆盖的请求,即决策端点上的请求和实际调用模型按音频时长计费的请求。
用量来源用量由上游上报时为 上游上报。上游缺失请求的至少一项 Token 计数或将其报为零、由网关估算时为 网关估算。同一种计价条件两种用量都有时,会按来源分别列出两次。

在每种计价条件下,每个 计费项 各占一行,列出 用量、平均单价 和 金额。适用了低峰折扣的条件最后有一行 低峰折扣,显示折扣减掉的金额。没有产生费用的条件(例如缓存命中,或调用了没有价格的模型)只显示一行 不计费。

计费项Admin API 中的 component用量单价
输入prompt未从缓存读取的输入 Token提示词
缓存读取cache_read从提示词缓存读取的输入 Token缓存读取(已缓存输入),留空时用 提示词
缓存写入(5 分钟)cache_write有效期 5 分钟的提示词缓存写入,以及上游未按有效期拆分的写入缓存写入(Anthropic 缓存创建),留空时用 提示词
缓存写入(1 小时)cache_write_1h有效期 1 小时的提示词缓存写入1 小时缓存写入的费率
输出completion推理以外的输出 Token补全
推理reasoning推理 Token推理,留空时用 补全
决策接口输入decisions_prompt决策端点上未从缓存读取的输入 Token决策接口定价 中的 提示词
决策接口缓存读取decisions_cache_read决策端点上从提示词缓存读取的输入 Token决策接口定价 中的 缓存读取(已缓存输入)
决策接口缓存写入decisions_cache_write决策端点上的提示词缓存写入决策接口定价 中的 缓存写入(Anthropic 缓存创建)
决策接口输出decisions_completion决策端点上推理以外的输出 Token决策接口定价 中的 补全
决策接口推理decisions_reasoning决策端点上的推理 Token决策接口定价 中的 推理
音频时长duration音频秒数音频每分钟

这些单价来自为该请求计费的那个价格,并且已经应用了它的输入 Token 分档和服务档位价格。计费项以实际计费所用的单价命名,单价留空时的回退不改变计费项:按 提示词 单价计费的缓存读取仍记为 缓存读取。

决策端点上的请求,凡是按价格中的决策端点单价计费的部分,都记在对应的 决策接口 计费项下:

  • 某部分设置了决策端点单价时,记在该单价的计费项下。
  • 某部分的决策端点单价留空时,按单价的回退规则取价,并记在实际用到的那个单价的计费项下。例如只设置了决策端点的 提示词 单价、基础的 缓存读取 单价也留空时,缓存读取按决策端点的 提示词 单价计费,记在 决策接口缓存读取 下;如果设置了基础的 缓存读取 单价,则按它计费,记在 缓存读取 下。
  • 1 小时缓存写入没有自己的决策端点单价。设置了决策端点缓存写入单价时记在 决策接口缓存写入 下;否则价格设置了 1 小时缓存写入单价时记在 缓存写入(1 小时) 下;再否则与该请求的其他缓存写入记在同一个计费项下。
  • 价格没有设置任何决策端点单价时,决策端点上的请求与普通请求一样记在基础计费项下。

明细上方的摘要给出各计费项占金额的比例,例如 输入 62.5% · 输出 37.5%,有低峰折扣时再显示低峰折扣共节省了多少。导出 CSV 会下载这份明细:每种计价条件的每个计费项一行,包含平均单价;之后是低峰折扣(金额为负数)、无明细的用量和合计各一行。用量来源对应 usage_estimated 列,网关估算的用量为 true。

明细中有网关估算的用量时,合计 一行会注明其费用,例如 含网关估算用量 $1.20(3 次请求)。请求数为零时不显示请求数,这发生在只有重试或 fallback 的尝试是估算用量的情况下。

数字之间的关系​

  • 计费项的 金额 是乘以低峰系数之前的金额。一种计价条件的费用等于各计费项金额之和减去 低峰折扣,明细的 合计 等于该行的费用。
  • 平均单价 是金额除以用量,按每百万 Token 显示,音频时长 按每分钟显示。所选时间范围内价格没有改过时,它等于价目上的单价;时间范围内改过价时,它是新旧单价按用量加权的平均值。计价条件的各列和计费项涵盖了所有决定单价的因素,因此每一行只对应一套单价,唯一的例外是所选时间范围内改过价。
  • 输入 Token 分档按请求的输入 Token(含缓存读取和缓存写入)为每个请求选定一档,该请求的所有计费项都按这一档的单价计费。分档不是分段累进的,一个请求不会跨两档计费。
  • 服务档位价格中某一项列出了上游上报的档位时,该项会替换价格的基础单价和输入 Token 分档。无论有没有匹配的项,服务档位 都显示上报的档位;没有任何一项列出该档位的请求按基础单价计费。
  • 只要网关填补了请求的任一 Token 计数,整个请求都算作估算,而不只是被填补的那几项。它的金额是这些计数按正常单价计算的结果,估算不改变任何单价。
  • 每个请求只在第一次尝试的用量来源下计一次。重试或 fallback 的尝试把金额计入其自身用量来源对应的计价条件,但不增加请求数,因此 fallback 被估算的请求可能在 网关估算 下产生费用,却没有对应的请求数。
  • 低峰折扣只作用于调用了配置低峰折扣的模型组的请求。它乘在请求的整笔费用上,所以低峰折扣一行等于金额之和乘以(1 − 系数)。

没有明细的用量​

控制面在为请求计费时记录计费明细,不会重新计算以前的用量。新增计费明细的控制面版本之前记录的用量没有明细;滚动升级期间,由仍在旧版本上的控制面实例记录的用量也没有明细。

所选时间范围内有这类用量时,明细上方会提示从什么时间开始提供明细、有多少金额没有明细,表中还有一行 无明细,金额就是这部分,因此明细的 合计 仍与该行一致。

单次请求的计费明细​

在请求日志中展开一行,计费明细 会逐行列出每个计费项,例如 输入:1,320 Token × $0.15 / 百万 Token = $0.000198;适用了低峰折扣时还有一行 低峰折扣。计费明细上线前记录的行,以及没有可计费内容的行(例如缓存命中,或调用了没有价格的模型),不显示这一项。

用量事件中对应的字段是 cost_components(由 component、quantity、amount 组成的列表)和 off_peak_discount,各金额之和减去 off_peak_discount 就是 cost。计费明细上线前记录的事件中这两个字段为 null,导出文件也带这两个字段。

通过 Admin API 读取明细​

GET /usage_analysis/breakdown 返回成本分析中显示的计费明细。它的 from、to 和筛选参数与 GET /usage_analysis 相同,所需权限也与其他用量读取相同。另有两个参数可以把它收窄到任意分组方式下的某一个分组:

  • target_model_id 只看分派到某个模型的用量。与 group_by=target_model 一样,它和 unset=target_model 都不能与 team_id、member_id、api_key_id 或 tag 同用,否则返回 400。
  • unset 只看某个维度上没有取值的用量,维度名称与 group_by 相同:tag:<key>、team、member、api_key、model 或 target_model。可重复传入多个维度。

如果要按某个分组方式拆开整份明细,而不是一次看一个分组,请传 group_by,参见按分组方式拆分明细。

日期分组请把该 UTC 日的起止时间作为 from 和 to。下面的请求查看 chat 模型组一周的计费明细:

curl -sS "${AISIX_CP}/usage_analysis/breakdown?from=2026-11-01T00:00:00Z&to=2026-11-08T00:00:00Z&model=chat" \
-H "Authorization: Bearer ${AISIX_TOKEN}"
{
"group_by": null,
"groups": [
{
"model": "chat",
"target_model": "31f302a2-d055-4f9f-a662-1dc4b84de9f7",
"target_model_name": "gpt-4o",
"price_tier": { "index": 2, "max_input_tokens": null },
"service_tier": "priority",
"off_peak": true,
"group_price": true,
"usage_estimated": false,
"request_count": 120,
"components": [
{ "component": "prompt", "quantity": 3600000, "amount": 18 },
{ "component": "completion", "quantity": 240000, "amount": 4.8 }
],
"off_peak_discount": 11.4,
"cost": 11.4
}
],
"remainder": { "group_count": 0, "request_count": 0, "components": [], "off_peak_discount": 0, "cost": 0 },
"total": {
"request_count": 120,
"components": [
{ "component": "prompt", "quantity": 3600000, "amount": 18 },
{ "component": "completion", "quantity": 240000, "amount": 4.8 }
],
"off_peak_discount": 11.4,
"cost": 11.4,
"group_count": 1,
"estimated_request_count": 0,
"estimated_cost": 0
},
"covered_from": "2026-11-01T08:00:00Z",
"uncovered_cost": 3.2
}

groups 中的每一项是一种计价条件,各键与控制台中的列对应。group_price 对应 价格 列:按模型组价格计费时为 true,按实际调用模型自己的价格计费时为 false。usage_estimated 对应 用量来源 列:网关估算的用量为 true,上游上报的用量为 false。groups 最多包含 limit 种条件(默认 50,最大 500),从排序中第 offset 种之后开始列出,remainder 汇总所有未列出的条件,参见翻页读取分组。计价条件按费用从高到低排列。request_count 按每个请求第一次尝试的计价条件只计一次,而各计费项和费用累加了所有尝试。total.estimated_request_count 和 total.estimated_cost 是所有 usage_estimated 为 true 的计价条件的 request_count 与 cost 之和,覆盖所有页,因此每一页都相同。covered_from 是时间范围内第一个有明细的小时,uncovered_cost 是没有明细的费用,因此 total.cost 加上 uncovered_cost 等于同一时间范围和筛选条件下 GET /usage_analysis 返回的 total.cost。完整字段参见 AISIX Enterprise / Cloud Admin API 参考。

按分组方式拆分明细​

group_by 会把每种计价条件再按成本分析的某个分组方式拆开:tag:<key>、team、member、api_key、environment 或 day。每种计价条件本来就包含模型和实际调用模型,所以 group_by=model 和 group_by=target_model 返回 400。此时每个分组还会带上:

字段含义
key该分组的取值,与同一分组方式下 GET /usage_analysis 分组的 key 相同:标签值,团队、成员、调用方 API Key 或环境的 ID,或 UTC 日期。在该分组方式上没有取值的用量为 null。
name团队、成员、调用方 API Key 或环境当前的显示名称。标签和日期分组为 null,资源不再存在后也为 null。
environment_id仅 group_by=api_key 时返回:该调用方 API Key 所属的环境。Key 不再存在后不返回。

响应中的 group_by 回显所用的分组方式,没有传入时为 null。

使用 group_by=member 加翻页,一轮就能读取每个成员在每个模型、实际调用模型和价格阶梯上的请求数、各计费项用量和费用。下面的循环每次读取 500 个分组,读完过去一周的数据,每个分组输出一行:

offset=0
while :; do
page=$(curl -sS "${AISIX_CP}/usage_analysis/breakdown?from=2026-11-01T00:00:00Z&to=2026-11-08T00:00:00Z&group_by=member&limit=500&offset=${offset}" \
-H "Authorization: Bearer ${AISIX_TOKEN}")
echo "${page}" | jq -c '.groups[] | {member: .key, name, model, target_model_name, price_tier, usage_estimated, request_count, components, cost}'
offset=$((offset + 500))
[ "${offset}" -lt "$(echo "${page}" | jq '.total.group_count')" ] || break
done

一个成员的用量可能分布在多个分组中,每种计费所依据的计价条件各一个。没有记录成员的用量,其 key 为 null。

按标签设置预算​

标签预算为组织内所有环境中归属到某个标签值的支出封顶。在预算视图中选择 标签 范围,再选择标签键并填写值。在 Admin API 中,将 scope 设为 tag,scope_ref 设为 <key>=<value>,例如 department=rnd。键必须已声明,值为第一个 = 之后的全部内容。limit_cents 以组织币种的最小单位计。

curl -sS -X POST "${AISIX_CP}/budgets" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"display_name": "R&D monthly",
"scope": "tag",
"scope_ref": "department=rnd",
"limit_cents": 50000,
"period": "month",
"hard_stop": true
}'

标签预算的周期、到达限额即阻断(hard stop)和告警规则与其他预算相同,每个标签值只能有一个预算。支出按每个请求用量上记录的标签计入。达到限额的阻断型标签预算会以 429 拒绝之后解析出同一标签值的请求,错误码为 budget_exceeded,其中 scope 为 tag,scope_ref 为该 key=value。

只要组织任一环境中注册的网关运行的是不记录标签的版本(即 1.6.x 及更早版本),创建标签预算就会被拒绝,返回 422,错误码为 DP_INCOMPATIBLE,因为这类网关的用量不会计入该预算。

按标签限流​

条件式限流策略在条件维度和 group_by 中都接受 tag:<key>,键必须已声明。例如,以下策略为每个部门分别设置每分钟 600 个请求的限额:

curl -sS -X POST "${AISIX_CP}/environments/${ENV_ID}/rate_limits" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "per-department-rpm",
"group_by": ["tag:department"],
"limits": { "rpm": 600 }
}'

解析出 department=rnd 的两个调用方 API Key 共用一个计数器,解析出 department=sales 的 Key 有自己的计数器。请求在该标签上没有值时,既不命中针对它的条件,也不命中取反后的条件;按该标签分组的策略也不约束它。

只要该环境中有注册的网关运行 1.6.x 及更早版本,保存使用 tag: 的策略就会被拒绝,返回 422,错误码为 DP_INCOMPATIBLE,因为这些版本无法加载它。

在用量事件和请求日志中读取标签​

每条用量事件都带有 tags(请求解析出的标签)和 team_id(调用方 API Key 绑定的团队),为空时省略。可观测性导出器会把它们发送到外部系统,请求日志在行详情和导出中显示它们。各导出器中的字段名见将事件归因到团队和标签。标签不会成为 Prometheus 指标标签。

网关版本要求​

标签由网关解析并记录。1.6.x 及更早版本的网关会忽略它们:

  • 它能加载带标签的调用方 API Key、团队和成员,但会忽略 tags 字段。控制台会为这些网关显示部分兼容提示。
  • 它的用量不带标签,也不带 team_id,因此在成本分析中显示为 未设置。
  • 它会丢弃使用 tag: 的限流策略。

从 JWT Claim 读取标签需要 1.9.0 或更高版本的网关。更早版本的网关能加载信任服务提供方,但会忽略 tag_claims,因此只按 Key、团队和成员归属请求。环境中注册了这类网关时,带 tag_claims 的服务提供方仍可保存成功:控制台会显示警告,Admin API 响应的 warnings 中会有一条错误码为 field_ignored、字段为 tag_claims 的记录。请先升级控制面,再升级网关。

如上所述,只要注册了这类网关,控制面就会拒绝保存标签预算和标签限流策略。这项检查只能看到保存预算或策略时已经注册的网关。

已知缺口

如果有网关回滚到 1.6.x,或者在标签限流策略或标签预算保存之后又有 1.6.x 网关加入,那么标签限流在这台网关上不生效,它的用量也不会计入标签预算。控制台的 rejected-resources 报告会显示这一情况。将该网关升级即可恢复限流和计数。

下一步​

使用预算和预算告警与通知为标签支出封顶并进行监控。要将带标签的用量与环境级合计对照,参见用量上报。