autoskills 实战:Azure Cost Management Forecast API 预测请求示例与 Scope URL 完整指南
autoskills 实战:Azure Cost Management Forecast API 预测请求示例与 Scope URL 完整指南
导读
本篇文章基于 autoskills 仓库中 azure-cost 技能包(skills-registry/azure-cost)下的成本预测(cost-forecast)模块编写,系统讲解 Microsoft Cost Management Forecast API 的典型请求体(request body)写法、Scope URL 拼装规则、响应解读与边界约束。读完本文,你将掌握:如何用一条 az rest 命令预测当月剩余成本、预测未来 3 个月账单、在资源组与计费账户等不同 Scope 下发起预测请求,以及如何规避 40 行响应上限、28 天训练数据要求等关键 Guardrails。所有示例均可在安装了 autoskills 的环境中直接落地使用。
一、Forecast API 是什么:与 Query API 的本质区别
在动手写请求体之前,先明确 Forecast API 的定位。autoskills 的 azure-cost/SKILL.md 将成本管理拆成三条工作流:查询历史成本(Cost Query)、优化成本(Cost Optimization)、预测未来成本(Cost Forecast)。当用户提出"本月月底大概要花多少钱""未来三个月的账单趋势"这类问题时,应进入第三条工作流,其入口即 cost-forecast/workflow.md。
Forecast API 与 Query API 的关键差异如下表(摘自 workflow.md):
| 方面 | Query API | Forecast API |
|---|---|---|
| 用途 | 历史成本数据 | 预测未来成本 |
| 时间范围 | 仅过去日期 | 必须包含未来日期 |
| 分组(Grouping) | 最多 2 个维度 | 不支持 |
includeActualCost |
不适用 | 在预测旁附带历史实际成本 |
| 响应列 | Cost、Date、Currency | Cost、Date、CostStatus、Currency |
| 每页最大响应行数 | 5,000 行 | 建议 40 行以内 |
| Timeframe | 多种预设 + Custom | 通常仅 Custom |
⚠️ 注意:如果用户要的是历史成本数据,应走 Cost Query Workflow;如果要省钱,应走 Cost Optimization Workflow。Forecast API 只回答"未来会花多少"。
二、请求前的准备:Scope、角色与执行命令
2.1 权限要求
调用 Forecast API 需要在目标 Scope 上具备 Cost Management Reader 角色(或更高权限)。缺少该角色时 API 返回 403 Forbidden(详见 error-handling.md)。这也是 SKILL.md 中列出的 Required Role:Cost Management Reader + Monitoring Reader + Reader on scope。
2.2 端点与执行方式
Forecast API 端点与 Query API 结构一致,仅资源类型不同:
POST {scope}/providers/Microsoft.CostManagement/forecast?api-version=2023-11-01
autoskills 的最佳实践是使用 az rest 携带 JSON 请求体执行(比 az costmanagement forecast 子命令更可靠,且需带上 ClientType: GitHubCopilotForAzure 请求头):
# 先创建存放请求体的目录
New-Item -ItemType Directory -Path "temp" -Force
# 执行预测请求(以订阅 Scope 为例)
az rest --method post `
--url "/subscriptions/<subscription-id>/providers/Microsoft.CostManagement/forecast?api-version=2023-11-01" `
--headers "ClientType=GitHubCopilotForAzure" `
--body '@temp/cost-forecast.json'
完整的七步工作流(确定 Scope → 选择报告类型 → 设置时间范围 → 配置 dataset → 设置预测专属选项 → 构造并执行 → 解读响应)见 cost-forecast/workflow.md。
三、四种常见预测请求体(核心示例)
以下示例直接继承自 cost-forecast/examples.md,并补充了字段说明,可直接复制使用。
示例 1:预测当月剩余成本(按天粒度)
这是最常见的场景:想知道"这个月到目前为止花了多少、月底预计总共花多少"。将 from 设为本月第一天,to 设为本月最后一天,响应中会包含从月初到今天的历史 Actual 行,以及今天到月末的 Forecast 行。
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<first-of-month>",
"to": "<last-of-month>"
},
"dataset": {
"granularity": "Daily",
"aggregation": {
"totalCost": { "name": "Cost", "function": "Sum" }
},
"sorting": [
{ "direction": "Ascending", "name": "UsageDate" }
]
},
"includeActualCost": true,
"includeFreshPartialCost": true
}
💡 Tip:把
from设为月初,响应就会包含到今天为止的Actual行与剩余天数的Forecast行,天然构成"实际 + 预测"的完整月度视图。
示例 2:预测未来 3 个月(按月粒度)
月度粒度的预测适合做中长期账单规划。注意按月粒度时排序字段要使用 BillingMonth 而非 UsageDate。
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<first-of-month>",
"to": "<3-months-out>"
},
"dataset": {
"granularity": "Monthly",
"aggregation": {
"totalCost": { "name": "Cost", "function": "Sum" }
},
"sorting": [
{ "direction": "Ascending", "name": "BillingMonth" }
]
},
"includeActualCost": true,
"includeFreshPartialCost": true
}
💡 Tip:Monthly 粒度下,响应中的日期列名为
BillingMonth(参见 request-body-schema.md)。
示例 3:资源组 Scope 预测
Scope 是通过 URL 层级控制的,请求体本身无需任何 scope 字段。若只想预测某个资源组的成本,把 URL 中的 scope 段替换为资源组即可:
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<start-date>",
"to": "<end-date>"
},
"dataset": {
"granularity": "Daily",
"aggregation": {
"totalCost": { "name": "Cost", "function": "Sum" }
},
"sorting": [
{ "direction": "Ascending", "name": "UsageDate" }
]
},
"includeActualCost": true,
"includeFreshPartialCost": true
}
💡 Tip:Scope 设在 URL 层。使用资源组 Scope URL 即可把预测范围限定在该资源组内。
示例 4:计费账户(Billing Account)Scope 预测
企业级场景下,财务团队往往需要在计费账户层面预测整体支出。此时 URL 使用 /providers/Microsoft.Billing/billingAccounts/<id>/... 前缀,并建议使用 Monthly 粒度(行数更少、更符合财务汇报周期):
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<start-date>",
"to": "<end-date>"
},
"dataset": {
"granularity": "Monthly",
"aggregation": {
"totalCost": { "name": "Cost", "function": "Sum" }
},
"sorting": [
{ "direction": "Ascending", "name": "BillingMonth" }
]
},
"includeActualCost": true,
"includeFreshPartialCost": true
}
💡 Tip:URL 模式为
/providers/Microsoft.Billing/billingAccounts/<id>/...。计费账户级预测推荐使用 Monthly 粒度。
四、Scope URL 参考表与拼接规则
以下 Scope URL 模式表直接继承自 cost-forecast/examples.md:
| Scope | URL 模式 |
|---|---|
| 订阅 Subscription | /subscriptions/<subscription-id>/providers/Microsoft.CostManagement/forecast |
| 资源组 Resource Group | /subscriptions/<subscription-id>/resourceGroups/<rg-name>/providers/Microsoft.CostManagement/forecast |
| 计费账户 Billing Account | /providers/Microsoft.Billing/billingAccounts/<id>/providers/Microsoft.CostManagement/forecast |
⚠️ 注意:上表只是路径片段(path-only patterns),不是完整 URL。构造完整请求地址时,需要在末尾追加查询参数
?api-version=2023-11-01,并配合az rest --url或完整 HTTPS 地址使用。
主技能 SKILL.md 还提供了完整的 Scope 路径参考(共五种 Scope),可供交叉引用:
| Scope | URL 模式 |
|---|---|
| 订阅 | /subscriptions/<subscription-id> |
| 资源组 | /subscriptions/<subscription-id>/resourceGroups/<resource-group-name> |
| 管理组 | /providers/Microsoft.Management/managementGroups/<management-group-id> |
| 计费账户 | /providers/Microsoft.Billing/billingAccounts/<billing-account-id> |
| 计费档案 | /providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingProfiles/<billing-profile-id> |
五、请求体字段逐项解析
为了写出可预测、可调试的请求体,需要吃透 request-body-schema.md 中的字段表。以下是完整字段参考(继承自原文档):
| 字段 | 类型 | 必填 | 取值 | 说明 |
|---|---|---|---|---|
type |
string | ✅ | ActualCost、AmortizedCost、Usage |
预测的成本类型 |
timeframe |
string | ✅ | Custom |
预测请求必须为 Custom |
timePeriod |
object | ✅ | — | 预测窗口的起止日期 |
timePeriod.from |
string | ✅ | ISO 8601 日期时间 | 起始日期;可以在过去(用于包含实际成本) |
timePeriod.to |
string | ✅ | ISO 8601 日期时间 | 结束日期;必须为未来日期 |
dataset |
object | ✅ | — | 预测的 dataset 配置 |
dataset.granularity |
string | ✅ | Daily、Monthly |
预测结果的时间粒度 |
dataset.aggregation |
object | ✅ | — | 应用的聚合函数 |
dataset.aggregation.totalCost.name |
string | ✅ | Cost |
要聚合的列名 |
dataset.aggregation.totalCost.function |
string | ✅ | Sum |
聚合函数 |
dataset.sorting |
array | 可选 | — | 结果的排序方式 |
dataset.sorting[].direction |
string | 可选 | Ascending、Descending |
排序方向 |
dataset.sorting[].name |
string | 可选 | UsageDate |
排序所依据的列 |
dataset.filter |
object | 可选 | — | 过滤表达式(dimensions/tags) |
includeActualCost |
boolean | 可选 | true、false |
是否在预测旁包含历史实际成本,默认 true |
includeFreshPartialCost |
boolean | 可选 | true、false |
是否包含最近几天尚未结算完全的"部分成本"数据,默认 true,依赖 includeActualCost=true |
5.1 预测专属字段:includeActualCost
- 类型:boolean,默认
true; - 为
true时,响应包含从from日期到今天的历史实际成本行(CostStatus=Actual),以及从今天到to日期的预测行(CostStatus=Forecast); - 为
false时,只返回预测(projected)行。
5.2 预测专属字段:includeFreshPartialCost
- 类型:boolean,默认
true; - 为
true时,会包含最近几天计费数据仍在到达中的部分成本数据; - ⚠️ 依赖约束:设置
includeFreshPartialCost=true而includeActualCost=false会产生校验错误DontContainIncludeActualCostWhileIncludeFreshPartialCost。
5.3 可选过滤:dataset.filter
当预测范围太大(如整个订阅)而只想看某个资源组时,除了在 URL 层切换 Scope,还可以在请求体内加过滤。完整的带过滤请求体示例:
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "2024-01-01T00:00:00Z",
"to": "2024-03-31T00:00:00Z"
},
"dataset": {
"granularity": "Daily",
"aggregation": {
"totalCost": {
"name": "Cost",
"function": "Sum"
}
},
"sorting": [
{
"direction": "Ascending",
"name": "UsageDate"
}
],
"filter": {
"dimensions": {
"name": "ResourceGroupName",
"operator": "In",
"values": ["my-resource-group"]
}
}
},
"includeActualCost": true,
"includeFreshPartialCost": true
}
filter 支持 dimensions(内置维度,如 ResourceGroupName、ServiceName)与 tags(资源标签)两种目标类型,以及 In、Equal、Contains、LessThan、GreaterThan、NotEqual 等比较运算符(完整枚举见 cost-query/request-body-schema.md)。
六、响应结构解读:CostStatus 是关键
6.1 响应列
| 列 | 类型 | 说明 |
|---|---|---|
Cost |
Number | 成本金额(实际或预测) |
UsageDate / BillingMonth |
Datetime | 成本所在日期 |
CostStatus |
String | 标识该行是历史还是预测 |
Currency |
String | 货币代码(如 USD、EUR) |
6.2 CostStatus 取值
| 值 | 含义 |
|---|---|
Actual |
历史实际成本(已发生) |
Forecast |
预测的未来成本(模型预测) |
6.3 粒度与日期列映射
| 粒度 | 日期列 |
|---|---|
Daily |
UsageDate |
Monthly |
BillingMonth |
💡 Tip:响应的解读要点——如果
includeActualCost=true,同一份响应里Actual行帮你核对已发生支出,Forecast行给你模型预测,"实际 + 预测"共同构成对账单周期的完整估计。
七、必须遵守的 Guardrails(边界约束)
预测请求比查询请求有更多硬性约束,全部细节见 cost-forecast/guardrails.md。核心规则如下:
7.1 时间范围校验
| 规则 | 细节 |
|---|---|
to 日期必须在未来 |
预测天数必须 > 0;全部为过去日期的区间会返回 CantForecastOnThePast 错误 |
from 可以在过去 |
from 在过去时,响应包含从 from 到今天的历史实际成本与从今天到 to 的预测 |
| 两个日期都必须合法 | timePeriod 存在时,from、to 必须是可解析的 ISO 8601 日期时间字符串 |
| Monthly + includeActualCost | Monthly 粒度配合 includeActualCost=true 时必须显式给出合法 timePeriod,否则报 DontContainsValidTimeRangeWhileMonthlyAndIncludeCost |
| 最大预测区间 | 最多 10 年 |
7.2 训练数据要求(28 天门槛)
| 要求 | 值 |
|---|---|
| 最少历史数据 | 4 周(28 天)的成本数据 |
| 推荐的训练窗口 | 最多 3 个月历史 |
| 计费数据晚到容忍度 | 2 天 |
| 新订阅(< 28 天) | 无法生成预测 |
⚠️ Warning:历史不足 28 天的新订阅无法生成预测。此时 API 会返回 "Forecast is unavailable for the specified time period"——这不是错误,是模型数据不足的合法响应,不要重试,应改用 Cost Query Workflow 获取已有的历史数据。
7.3 分组(Grouping)不支持
Forecast API 不接受 grouping 字段,这是硬性限制。如果用户想要"按资源组/服务分组的预测",应告知其不可用,并建议改用 Query API 查询带分组的历史数据。
7.4 响应行数上限:40 行
| 约束 | 细节 |
|---|---|
| 最大行数 | 每次预测响应 40 行 |
| Daily 反例 | 30 天实际 + 30 天预测 = 60 行 → 超限 |
| 建议 | Daily 粒度下预测窗口控制在 2~3 周以内 |
| 更长区间 | 改用 Monthly 粒度以保持在行数上限内 |
💡 Tip:如果需要超过 2~3 周的按天预测,可以把请求拆成多个较小的时间窗口,或切换为 Monthly 粒度。
7.5 速率限制(Rate Limiting)
Forecast API 与 Query API 共享同一套速率限制:
| 限制 | 值 |
|---|---|
| 每用户 | 每分钟 20 个请求 |
| 每 Scope | 每分钟 4 个请求 |
| 每租户 | 每 10 秒 12 个、每分钟 60 个、每小时 600 个 |
| 每客户端类型 | 每分钟 2,000 个请求 |
⚠️ Warning:per-scope 限制(4 请求/分钟)最严格。对同一订阅、资源组或计费账户的连续查询共享该限制。
遇到 429 响应时,检查响应中所有 x-ms-ratelimit-microsoft.costmanagement-*-retry-after 头(qpu-retry-after、entity-retry-after、tenant-retry-after),取最长的值等待,在时长完全过去之前不要重试,最多重试 3 次。
八、错误处理速查
8.1 HTTP 状态码
| 状态 | 错误 | 原因 | 补救 |
|---|---|---|---|
| 400 | Bad Request | 请求体非法、缺少 dataset、日期全在过去、字段依赖组合非法 |
检查请求体结构;确保 to 在未来;核对 includeActualCost/includeFreshPartialCost 依赖 |
| 401 | Unauthorized | 认证失败——令牌缺失或过期 | 重新 az login 或刷新访问令牌 |
| 403 | Forbidden | Scope 上权限不足 | 确保身份在目标 Scope 上具有 Cost Management Reader 角色 |
| 404 | Not Found | Scope URL 非法——订阅、资源组或计费账户不存在 | 核对 Scope URL 路径与资源 ID |
| 424 | Failed Dependency | 训练数据质量差——预测模型无法计算 | 若 includeActualCost=true 则回退为实际成本;否则建议走 Query 工作流 |
| 429 | Too Many Requests | 被限流——QPU、entity 或 tenant 配额超限 | 检查所有 x-ms-ratelimit-microsoft.costmanagement-*-retry-after 头,等待最长值后重试 |
| 503 | Service Unavailable | 服务临时不可用 | 检查 Azure 服务状态页 |
8.2 校验错误码参考
| 错误码 | 说明 | 修复 |
|---|---|---|
EmptyForecastRequestBody |
请求体为空或 null | 提供包含 type、timeframe、timePeriod、dataset 的完整请求体 |
InvalidForecastRequestBody |
请求体 JSON 结构非法 | 检查 JSON 语法——括号、逗号与字段名 |
DontContainsDataSet |
请求体缺少 dataset 字段 |
添加包含 granularity 和 aggregation 的 dataset 对象 |
DontContainsValidTimeRangeWhileContainsPeriod |
timePeriod 存在但 from/to 非法 |
确保 from、to 均为合法 ISO 8601 字符串 |
DontContainsValidTimeRangeWhileMonthlyAndIncludeCost |
Monthly 粒度 + includeActualCost=true 但缺少合法 timePeriod |
显式添加含合法 from/to 的 timePeriod |
DontContainIncludeActualCostWhileIncludeFreshPartialCost |
includeFreshPartialCost=true 但未设置 includeActualCost=true |
设置 includeActualCost=true 或 includeFreshPartialCost=false |
CantForecastOnThePast |
from 和 to 都在过去 |
确保 to 日期在未来 |
8.3 重试策略
| 状态 | 是否重试 | 策略 |
|---|---|---|
| 429 | ✅ 是 | 读取全部 retry-after 头,取最长值,等待完全过去后再试;最多 3 次 |
| 400 | ❌ 否 | 依据校验错误码修复请求体 |
| 401 | ❌ 否 | 重新认证 |
| 403 | ❌ 否 | 授予 Cost Management Reader 角色 |
| 404 | ❌ 否 | 修正 Scope URL |
| 424 | ❌ 否 | 训练数据问题,重试无意义;回退实际成本或走 Query 工作流 |
| 503 | ❌ 否 | 检查服务状态,稍后重试 |
⚠️ Warning:除 429 外,其他错误都不要重试——它们都表示需要在重发请求前修复的问题(完整说明见 error-handling.md)。
九、在 autoskills 中如何运用这些示例
autoskills 仓库以"One command. Your entire AI skill stack. Installed."为理念,azure-cost 技能包由主入口 SKILL.md 与三条工作流组成,其中 cost-forecast 目录下包含:
workflow.md—— 七步预测工作流总纲(Scope 确定、报告类型、时间设置、dataset 配置、预测选项、执行、响应解读);request-body-schema.md—— 请求体完整字段表与响应结构;examples.md—— 本文讲解的四类典型请求体;guardrails.md—— 时间、训练数据、行数、限流等全部边界规则;error-handling.md—— 状态码、校验错误码与重试策略。
这些文档经 scripts/validate-registry.mjs 校验后进入技能注册表,随 autoskills 一并安装分发。你在使用 Agent 时,只需说出"预测我本月月底的 Azure 花费"这类意图,技能路由(见 SKILL.md 的意图路由表)就会自动选择 Cost Forecast Workflow,并参照本文示例构造 az rest 请求。
一个可直接套用的完整调用序列
# 1. 构造请求体(预测当月剩余,按天粒度)
# 写入 temp/cost-forecast.json,内容即"示例 1"
# 2. 创建临时目录并执行
New-Item -ItemType Directory -Path "temp" -Force
az rest --method post `
--url "/subscriptions/<subscription-id>/providers/Microsoft.CostManagement/forecast?api-version=2023-11-01" `
--headers "ClientType=GitHubCopilotForAzure" `
--body '@temp/cost-forecast.json'
# 3. 解读响应
# CostStatus=Actual -> 已发生的实际成本
# CostStatus=Forecast -> 模型预测的未来成本
# 将两者相加即可得到本月底的预计总支出
十、常见问题速览(FAQ)
Q1:为什么我的请求返回 "CantForecastOnThePast"?
from 和 to 都在过去。Forecast API 要求 to 必须在未来(guardrails.md)。
Q2:为什么返回 "Forecast is unavailable for the specified time period"? 这不是错误,是模型因历史数据不足(少于 28 天)而无法预测的合法响应。不要重试,改用 Cost Query Workflow 取历史数据。
Q3:想按资源组分组预测,为什么报错?
Forecast API 不支持 grouping 字段。改用 Query API 查询带分组的历史数据。
Q4:Daily 粒度的预测区间为什么不能太长? 每次响应上限 40 行。Daily 粒度下 30 天实际 + 30 天预测即 60 行超限,建议 Daily 窗口控制在 2~3 周,更长用 Monthly 粒度或拆分窗口。
Q5:includeFreshPartialCost 报依赖错误?
该字段依赖 includeActualCost=true,必须同时显式设置,否则返回 DontContainIncludeActualCostWhileIncludeFreshPartialCost。
总结
Azure Cost Management Forecast API 是"月底账单估算"与"未来支出规划"的核心工具。掌握四类典型请求体(当月剩余按天、未来三月按月、资源组 Scope、计费账户 Scope)、Scope URL 拼接规则(路径片段 + ?api-version=2023-11-01)、40 行响应上限与 28 天训练数据门槛,就能在 autoskills 的 azure-cost 技能包支持下,稳定产出"实际 + 预测"的完整成本视图。所有字段细节、边界规则与错误码,均可在仓库内 cost-forecast 目录的配套文档中随时查阅。