写了个 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 自动化什么?评论区聊聊 💬

相关推荐
亚林瓜子3 小时前
为AWS ElastiCache添加CPU和内存利用率告警
云计算·aws
亚马逊云开发者1 天前
血的教训!OpenClaw 从月烧 200 刀到 30 刀,我都踩了哪些坑?
aws
Moshow郑锴1 天前
2026.3 AWS连环事故原因分析-究竟是裁员导致还是AI失控?
人工智能·云计算·aws·incident
Lim小刘1 天前
告别“裸奔”:OpenClaw 龙虾 Agent 在 AWS 上的企业级安全加固实战
人工智能·安全·aws·openclaw
亚马逊云开发者1 天前
🔐 老板说OpenClaw权限太大了?手把手教你收紧到刚刚好!
aws
亚马逊云开发者2 天前
用了一周 Amazon Q Developer,这几个场景真香
aws
亚马逊云开发者2 天前
AI Agent 安全护栏,配置即用
aws
炸裂狸花猫2 天前
Kubernetes架构演进:Node Pool分层与Pod IP不足的解决方案
云原生·架构·kubernetes·aws·oci·oke