写了个 Skill 让 OpenClaw 自动查 AWS 账单,从此告别手动翻控制台

写了个 Skill 让 OpenClaw 自动查 AWS 账单,从此告别手动翻控制台

起因

事情是这样的。

我最近一直在用 OpenClaw 做工作自动化,用着挺爽。但有个需求它满足不了------我想让它每天自动查一下亚马逊云科技的账单,花超了就提醒我一声。

OpenClaw 自带了天气、网页摘要这些技能,但成本监控这种定制需求?不好意思,没有。

然后我发现 OpenClaw 有个叫 Skill 的扩展机制。简单来说,你写一个 Markdown 文件,告诉 AI 遇到某类问题该怎么做,它就"学会"了这个能力。

是的,就是 Markdown。不是 Python 包,不是 npm 模块,是一个 .md 文件。

当时我觉得有点不可思议,但试了一下发现真的能用。这篇文章就记录一下我从零写一个"AWS 成本监控"Skill 的过程,代码全部可以直接跑。


Skill 是什么

别把 Skill 想得太复杂。

一句话概括:Skill = 一份写给 AI Agent 的 SOP(标准操作流程)。

你在一个叫 SKILL.md 的文件里写清楚三件事:

  1. 什么时候触发(用户说了什么关键词)
  2. 具体做什么(执行哪个脚本、调用哪个 API)
  3. 输出什么(报告格式长什么样)

Agent 收到用户请求后,先扫描所有 Skill 的描述,看哪个匹配。匹配上了,读正文,照着做。就这么简单。

不需要编译,不需要注册,不需要重启。放到目录下,下次对话就生效。


目录结构

ruby 复制代码
~/.openclaw/workspace/skills/aws-cost-monitor/
├── SKILL.md              # 必需:告诉 Agent 干嘛
├── scripts/
│   └── check_cost.py     # 可选:辅助脚本
└── references/
    └── cost-explorer-notes.md  # 可选:参考文档

只有 SKILL.md 是必需的,脚本和参考文档按需添加。

我的习惯是:能让 Agent 直接调工具完成的,就不写脚本。需要确定性输出的(比如 API 调用),才放到 scripts/ 下。


SKILL.md

这是核心,直接贴完整内容:

markdown 复制代码
# AWS Cost Monitor

## Description

亚马逊云科技成本监控技能。自动查询当前账户的云资源费用,
生成成本报告,支持按服务维度拆分,超出预算时发送告警。

## Triggers

- 用户说"查一下我的 AWS 账单"
- 用户说"这个月花了多少钱"
- 用户说"AWS 成本报告"
- 用户说"看看云资源开销"
- 用户说"账单超了没"
- 用户说"查一下这个月的费用"
- 用户说"成本分析"
- 用户说"哪个服务花钱多"

## Steps

1. 读取并执行 scripts/check_cost.py 获取成本数据
2. 解析脚本输出的 JSON 格式数据
3. 按以下格式生成成本报告:

💰 亚马逊云科技成本报告 | YYYY-MM-DD

📅 统计周期:YYYY-MM-01 ~ YYYY-MM-DD 💵 本月累计费用:$XXX.XX

📊 按服务拆分(Top 5):

  1. Amazon EC2 --- $XX.XX
  2. Amazon S3 --- $XX.XX
  3. Amazon Bedrock --- $XX.XX
  4. Amazon RDS --- $XX.XX
  5. Amazon Lambda --- $XX.XX

📈 日均费用: <math xmlns="http://www.w3.org/1998/Math/MathML"> X . X X 📆预估月底总费用: X.XX 📆 预估月底总费用: </math>X.XX📆预估月底总费用:XXX.XX

bash 复制代码
4. 如果预估月底总费用超过 $500(默认阈值),追加告警:

⚠️ 费用告警:预估本月总费用 <math xmlns="http://www.w3.org/1998/Math/MathML"> X X X . X X ,已超过预算阈值 XXX.XX,已超过预算阈值 </math>XXX.XX,已超过预算阈值500!

markdown 复制代码
5. 如果用户指定了预算阈值,用指定值替代默认值
6. 将报告发送给用户

## Notes

- 需要本机已配置 AWS CLI 凭证
- 需要 IAM 权限:ce:GetCostAndUsage
- 费用数据有 24-48 小时延迟
- 默认查询当前月份

写 Triggers 的经验

这里分享一个我踩过的坑。

一开始我只写了"查 AWS 账单"和"成本报告"两个触发条件。结果用户换个说法------"这个月云上花了多少"------Agent 就不认了。

后来我把能想到的口语化表达都加上去了。Triggers 宁可多写几条,也不要少写。 Agent 的语义理解能力不错,但你把路铺好了,它走起来更稳。


写脚本:check_cost.py

这个脚本的活儿很简单:调 Cost Explorer API → 拿数据 → 输出 JSON。

python 复制代码
#!/usr/bin/env python3
"""
AWS Cost Monitor --- 查询亚马逊云科技账户成本数据
依赖:boto3(pip install boto3)
权限:需要 IAM 策略 ce:GetCostAndUsage
"""

import json
import sys
from datetime import datetime, timedelta

import boto3


def get_month_range(year: int = 0, month: int = 0) -> tuple[str, str]:
    """获取指定月份的起止日期。"""
    today = datetime.utcnow()
    if year == 0 or month == 0:
        year = today.year
        month = today.month

    start = f"{year:04d}-{month:02d}-01"

    if month == 12:
        end_date = datetime(year + 1, 1, 1)
    else:
        end_date = datetime(year, month + 1, 1)

    # 当前月用明天作为 End(Cost Explorer 的 End 是排他的)
    if year == today.year and month == today.month:
        end_date = today + timedelta(days=1)

    end = end_date.strftime("%Y-%m-%d")
    return start, end


def query_total_cost(client, start: str, end: str) -> float:
    """查询总费用。"""
    resp = client.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="MONTHLY",
        Metrics=["UnblendedCost"],
    )
    total = 0.0
    for result in resp["ResultsByTime"]:
        amount = result["Total"]["UnblendedCost"]["Amount"]
        total += float(amount)
    return round(total, 2)


def query_cost_by_service(client, start: str, end: str) -> list[dict]:
    """按服务维度查询费用。"""
    resp = client.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="MONTHLY",
        Metrics=["UnblendedCost"],
        GroupBy=[{"Type": "DIMENSION", "Key": "SERVICE"}],
    )
    services = {}
    for result in resp["ResultsByTime"]:
        for group in result["Groups"]:
            name = group["Keys"][0]
            amount = float(group["Metrics"]["UnblendedCost"]["Amount"])
            services[name] = services.get(name, 0.0) + amount

    sorted_services = sorted(
        services.items(), key=lambda x: x[1], reverse=True
    )
    return [
        {"service": name, "amount": round(amount, 2)}
        for name, amount in sorted_services
        if amount > 0.01
    ]


def estimate_monthly_total(total_so_far: float, start: str) -> float:
    """预估月底总费用。"""
    today = datetime.utcnow()
    start_date = datetime.strptime(start, "%Y-%m-%d")
    days_passed = (today - start_date).days
    if days_passed <= 0:
        return total_so_far

    if start_date.month == 12:
        next_month = datetime(start_date.year + 1, 1, 1)
    else:
        next_month = datetime(start_date.year, start_date.month + 1, 1)
    total_days = (next_month - start_date).days

    daily_avg = total_so_far / days_passed
    return round(daily_avg * total_days, 2)


def main():
    """主函数。"""
    year, month = 0, 0
    if len(sys.argv) >= 3:
        year = int(sys.argv[1])
        month = int(sys.argv[2])

    start, end = get_month_range(year, month)
    client = boto3.client("ce", region_name="us-east-1")

    total = query_total_cost(client, start, end)
    services = query_cost_by_service(client, start, end)

    today = datetime.utcnow()
    start_date = datetime.strptime(start, "%Y-%m-%d")
    days_passed = (today - start_date).days
    daily_avg = round(total / max(days_passed, 1), 2)
    estimated_total = estimate_monthly_total(total, start)

    result = {
        "period": {"start": start, "end": end},
        "total_cost": total,
        "daily_average": daily_avg,
        "estimated_monthly_total": estimated_total,
        "days_passed": days_passed,
        "top_services": services[:10],
        "query_time": today.strftime("%Y-%m-%d %H:%M:%S UTC"),
    }
    print(json.dumps(result, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()

代码不长,核心就三个函数:

  • query_total_cost:查总费用
  • query_cost_by_service:按服务拆分
  • estimate_monthly_total:根据日均预估月底开销

安装 & 测试

放文件

bash 复制代码
mkdir -p ~/.openclaw/workspace/skills/aws-cost-monitor/scripts
mkdir -p ~/.openclaw/workspace/skills/aws-cost-monitor/references

# 把三个文件分别放到对应位置,然后:
chmod +x ~/.openclaw/workspace/skills/aws-cost-monitor/scripts/check_cost.py

先手动测一下脚本

bash 复制代码
pip install boto3
python3 ~/.openclaw/workspace/skills/aws-cost-monitor/scripts/check_cost.py

应该能看到一段 JSON 输出,包含费用数据。如果报错,大概率是 AWS 凭证没配(aws configure)或者 IAM 权限不够。

在 OpenClaw 里测

文件放好后不用重启。直接在 Slack 里说:

查一下我的 AWS 账单

Agent 应该能识别到 Skill 并执行。效果类似:

bash 复制代码
💰 亚马逊云科技成本报告 | 2026-03-19

📅 统计周期:2026-03-01 ~ 2026-03-19
💵 本月累计费用:$127.35

📊 按服务拆分(Top 5):
1. Amazon EC2 --- $52.10
2. Amazon Bedrock --- $35.20
3. Amazon S3 --- $18.05
4. Amazon RDS --- $12.80
5. AWS Lambda --- $9.20

📈 日均费用:$6.70
📆 预估月底总费用:$207.70

看到这个输出的时候我挺开心的。从动手到跑通,前后不到一小时。


踩坑记录

分享几个我实际开发中碰到的问题。

坑 1:Cost Explorer 只认 us-east-1

这个坑了我大概二十分钟。脚本里 region_name 写的是 ap-northeast-1(因为我的资源大部分在东京),一直报错。后来查文档才发现 Cost Explorer API 只能通过 us-east-1 调用,和你的资源在哪个 Region 无关。

坑 2:End 日期是排他的

Cost Explorer 的 TimePeriod.End 是排他的,也就是说 End: "2026-03-19" 实际上查的是到 3 月 18 日的数据。要想包含今天,End 得写明天的日期。脚本里的 today + timedelta(days=1) 就是为了解决这个问题。

坑 3:数据延迟 24-48 小时

新账户或者刚开始产生费用的时候,可能查出来全是零。别以为脚本有 bug,等一两天数据就有了。


进阶玩法

定时巡检

在 OpenClaw 的 HEARTBEAT.md 里加一行:

markdown 复制代码
- [ ] 每天早上 9:00(UTC+8),执行 AWS 成本监控,推送报告

Agent 心跳检查时会主动触发。

Skill 嵌套

如果你有个"飞书通知"Skill,可以在成本监控的 Steps 里引用它:

markdown 复制代码
6. 费用超过阈值时,使用"飞书通知"Skill 发送告警到团队群

Agent 会自动串联两个 Skill。

和 Amazon Bedrock 配合

查完账单只是第一步。更进阶的玩法是把成本数据丢给 Amazon Bedrock,让它分析增长趋势、找出异常、给优化建议。在 Steps 里加一步就行:

markdown 复制代码
7. 将成本数据发给 Amazon Bedrock 分析趋势和异常

我现在每天早上看到的不光是一堆数字,还有一段"这个月 EC2 比上月多了 30%,建议检查是否有闲置实例"之类的分析。挺有用的。


发布到 ClawHub

觉得自己写的 Skill 不错?可以发布到 ClawHub,让其他人也用上。

  1. 确保 SKILL.md 描述完整
  2. 脚本里别硬编码敏感信息
  3. 用打包工具生成 .skill 文件
  4. 提交到 ClawHub

社区共建,少造轮子。


最后

整个 Skill 开发体验用两个字形容:丝滑

写 Markdown,放到目录下,对话触发。从有想法到跑通,不到一小时。这种"写文档就是写插件"的感觉,我还是头一次体验到。

如果你也在用亚马逊云科技,每次查账单都要手动登控制台,试试这个方案。或者你有其他重复性的任务想自动化,OpenClaw 的 Skill 机制值得一试。

你打算用 Skill 自动化什么?评论区聊聊 💬

相关推荐
yyuuuzz1 天前
谷歌云使用的几个常见注意事项
运维·服务器·网络·安全·web安全·云计算·aws
zhojiew1 天前
在AWS中国区的EMR集群中实现基于向量语义搜索的HBase运维诊断系统
运维·hbase·aws
yyuuuzz2 天前
独立开发者线上服务运维的几点实践经验
运维·服务器·网络·云计算·aws
zhojiew2 天前
使用DBT(data build tool)集成AWS Athena完成数据处理的实践
云计算·aws
yyuuuzz3 天前
aws的核心概念与常见使用场景
运维·服务器·网络·云计算·aws
zhojiew3 天前
在AWS云上使用EC2 嵌套虚拟化实例部署Cube Sandbox的实践和问题
云计算·aws
yyuuuzz5 天前
国际云服务器的技术特点与使用经验
运维·服务器·网络·数据库·云计算·aws
我是小邵6 天前
从 Supabase 迁移到 AWS 的云架构演进实践
架构·云计算·aws
炸裂狸花猫6 天前
开源身份认证与访问管理平台 - Keycloak(三)公有云Console集成实践(AWS / 阿里云 / OCI)
阿里云·云原生·keycloak·aws·oci·sso
xixixi777776 天前
AI的“账号”与“钱包”:AWS与Circle同日出手,AI正从工具进化
人工智能·安全·ai·大模型·云计算·aws