gcloud), доступ к биллинг-аккаунтам AWS/Azure/GCP, S3-бакет или GCS-бакет для экспортов, проект BigQuery, любой HTTP-клиент к LLM API (endpoint и имя модели — сверьтесь с документацией вашего провайдера), Python 3.xИдея схемы: нативные инструменты каждого облака собирают факты о расходах → складываем их в одно хранилище → LLM раз в сутки читает выгрузку и объясняет аномалии человеческим языком → алерт уходит в чат. ИИ здесь не заменяет биллинг, а интерпретирует его.
| Облако | Бюджеты | Аномалии | Детальная выгрузка |
|---|---|---|---|
| AWS | AWS Budgets | Cost Anomaly Detection | Cost and Usage Report (CUR) → S3 |
| Azure | Azure Budgets (Cost Management) | Cost Anomaly Alerts в Cost Management | Exports в Storage Account |
| GCP | Cloud Billing Budgets | оповещения бюджета по порогам | BigQuery Billing Export |

budgets:*, ce:*, s3:*, cur:*.az login, роль Contributor (или custom-роль) на подписке.gcloud auth login, роль Billing Account Administrator на биллинг-аккаунте.LLM_API_URL, LLM_API_KEY, LLM_MODEL. Точные значения URL и имени модели сверьте с документацией провайдера — здесь они не фиксируются.Что сделать: проверить, что CLI видят нужные аккаунты.
Зачем: 80% ошибок настройки — работа не в том аккаунте/подписке, где лежат расходы.
Как проверить:
aws sts get-caller-identity
az account show --query "{name:name, id:id}" -o table
gcloud config list --format="value(core.account,core.project)"
Пример: aws sts get-caller-identity печатает "Account": "111122223333" — этот номер понадобится в --account-id. Если номер не тот, что в консоли Billing, переключите профиль: export AWS_PROFILE=<профиль>.
Чек-лист перед стартом:

Что сделать: создать по одному бюджету с порогами и включить детальные выгрузки.
Зачем: LLM не умеет опрашивать биллинг «напрямую» — ему нужны файлы с данными. Нативные бюджеты дают быстрый алерт, выгрузки — материал для анализа.
Как проверить: после каждого блока — команда list/describe возвращает созданный объект.
AWS: бюджет с двумя порогами (50% и 100% от суммы, которую вы задаёте сами).
cat > budget.json <<'JSON'
{
"BudgetName": "monthly-guard",
"BudgetLimit": {"Amount": "500", "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}
JSON
cat > notifications.json <<'JSON'
[
{"Notification": {"NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 50, "ThresholdType": "PERCENTAGE"},
"Subscribers": [{"SubscriptionType": "EMAIL", "Address": "<ваш-email>"}]},
{"Notification": {"NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 100, "ThresholdType": "PERCENTAGE"},
"Subscribers": [{"SubscriptionType": "EMAIL", "Address": "<ваш-email>"}]}
]
JSON
aws budgets create-budget --account-id 111122223333 \
--budget file://budget.json \
--notifications-with-subscribers file://notifications.json
Проверка: aws budgets describe-budgets --account-id 111122223333 — в выводе "BudgetName": "monthly-guard". Сумма 500 — пример, подставьте свой месячный лимит.
Детальная выгрузка AWS (CUR) в S3:
aws cur put-report-definition --report-definition '{
"ReportName": "cur-daily",
"TimeUnit": "DAILY",
"Format": "Parquet",
"Compression": "Parquet",
"S3Bucket": "<ваш-бакет>",
"S3Prefix": "cur",
"S3Region": "eu-central-1",
"AdditionalArtifacts": ["ATHENA"],
"RefreshClosedReports": true,
"ReportVersioning": "OVERWRITE_REPORT"
}'
Проверка: aws cur describe-report-definitions — статус отчёта. Первая выгрузка появляется не мгновенно; точный SLA и текст bucket policy для бакета сверьте с документацией AWS.
Azure: бюджет на подписке.
az consumption budget create \
--budget-name monthly-guard \
--amount 500 \
--time-grain Monthly \
--start-date 2025-01-01 --end-date 2026-01-01 \
--category Cost
Проверка: az consumption budget list -o table — строка monthly-guard. Набор флагов в разных версиях Azure CLI отличается — при ошибке разбора аргументов сверьтесь с az consumption budget create -h.
GCP: бюджет с порогами и публикацией в Pub/Sub (топик нужен, чтобы алерт можно было обработать кодом, а не только письмом).
gcloud pubsub topics create budget-alerts
gcloud billing budgets create \
--billing-account=<BILLING_ACCOUNT_ID> \
--display-name=monthly-guard \
--budget-amount=500USD \
--threshold-rule=percent=0.5 \
--threshold-rule=percent=1.0 \
--pubsub-topic=projects/<ваш-проект>/topics/budget-alerts
Проверка: gcloud billing budgets list --billing-account=<BILLING_ACCOUNT_ID> — бюджет в списке. Почему это работает: бюджеты — это правила на стороне биллинга, они срабатывают независимо от вашего кода, поэтому не «замолчат», если упадёт скрипт.
Что сделать: включить экспорт биллинга GCP в BigQuery, а сводку по трём облакам раз в сутки отправлять в LLM.
Зачем: BigQuery — самый простой «общий стол» для агрегатов; из AWS и Azure забираем уже готовые итоги по сервисам и складываем их туда же или в локальный JSON.
Как проверить: таблица в BigQuery существует и по ней проходит запрос за вчера.
В консоли GCP: Billing → Billing export → BigQuery export → выбрать датасет. После включения появится таблица вида <проект>.<датасет>.gcp_billing_export_v1_<BILLING_ACCOUNT_ID>.
bq query --use_legacy_sql=false '
SELECT service.description AS service, ROUND(SUM(cost), 2) AS cost_usd
FROM `<проект>.<датасет>.gcp_billing_export_v1_<BILLING_ACCOUNT_ID>`
WHERE invoice.month = FORMAT_DATE("%Y%m", CURRENT_DATE())
GROUP BY service
ORDER BY cost_usd DESC
LIMIT 10'
Пример вывода: строки Compute Engine | 214.37, Cloud Storage | 31.02 — это топ сервисов за текущий месяц.
Теперь сборщик сводки. Он берёт AWS-итоги через Cost Explorer, Azure — через CLI, GCP — через bq, склеивает в один JSON и отправляет в LLM:
import json, os, subprocess, urllib.request
def aws_totals():
out = subprocess.check_output([
"aws", "ce", "get-cost-and-usage",
"--time-period", "Start=2025-01-01,End=2025-02-01",
"--granularity", "MONTHLY",
"--metrics", "UnblendedCost",
"--group-by", "Type=DIMENSION,Key=SERVICE"], text=True)
return json.loads(out)
def azure_totals():
out = subprocess.check_output([
"az", "consumption", "usage", "list", "--top", "50", "-o", "json"], text=True)
return json.loads(out)
payload = {"aws": aws_totals(), "azure": azure_totals()}
prompt = ("Ты аналитик облачных расходов. Найди в данных три самых дорогих сервиса, "
"укажи долю каждого в процентах и одну гипотезу причины роста. "
"Если данных для вывода не хватает — так и напиши. Данные: "
+ json.dumps(payload)[:20000])
req = urllib.request.Request(
os.environ["LLM_API_URL"],
data=json.dumps({"model": os.environ["LLM_MODEL"],
"messages": [{"role": "user", "content": prompt}]}).encode(),
headers={"Content-Type": "application/json",
"Authorization": "Bearer " + os.environ["LLM_API_KEY"]})
print(json.loads(urllib.request.urlopen(req).read())["choices"][0]["message"]["content"])
Формат тела запроса у разных провайдеров отличается — поля messages/choices сверьте с документацией. Обрезка [:20000] символов нужна, чтобы не упереться в лимит контекста и не платить за мегабайты JSON.
Почему это работает: LLM не считает деньги — она получает уже посчитанные суммы и ищет в них закономерности. Если суммы неверны, ответ тоже будет неверным, поэтому проверка из Шага 3 обязательна.

Что сделать: прогнать сборщик и убедиться, что алерт доходит до чата.
Зачем: «настроено» и «работает» — разные вещи; нужен наблюдаемый результат.
Как проверить:
python collect.py > report.json 2>err.log
head -c 400 report.json
Успех выглядит так: в report.json три строки с суммами по облакам и абзац текста от модели, где названы самые дорогие сервисы. err.log пуст.
Дальше проверяем путь алерта. Простейший способ доставки в Telegram — бот, тот же приём, что в гайде как настроить мониторинг VPS через Telegram-бота с Mini App.
curl -s -X POST "https://api.telegram.org/bot<ТОКЕН>/sendMessage" \
-d chat_id=<CHAT_ID> \
--data-urlencode text="$(python -c 'import json;print(json.load(open("report.json"))["summary"])')"
Проверка: сообщение появилось в чате в течение нескольких секунд. Если появилось — цепочка «выгрузка → сводка → LLM → чат» замкнута.
Финальный результат: раз в сутки приходит сообщение с тремя самыми дорогими сервисами и гипотезой роста, а при превышении 50% и 100% бюджета срабатывает нативный алерт облака. Расписание ставим через cron:
0 7 * * * cd /opt/costbot && /usr/bin/python3 collect.py >> run.log 2>&1
Проблема. Бюджет AWS не создаётся
An error occurred (AccessDeniedException) when calling the CreateBudget operation: User: arn:aws:iam::111122223333:user/bot is not authorized to perform: budgets:CreateBudgetaws iam attach-user-policy --user-name bot \
--policy-arn arn:aws:iam::aws:policy/AWSBudgetsActionsWithAWSResourceControlAccess
Точный список действий, если нужна своя политика, сверьте с документацией AWS.
Проблема. Azure отклоняет создание бюджета
(InsufficientPermissions) The client '<guid>' with object id '<guid>' does not have authorization to perform action 'Microsoft.Consumption/budgets/write' over scope '/subscriptions/<id>'az role assignment create --assignee <object-id> \
--role "Cost Management Contributor" \
--scope /subscriptions/<SUBSCRIPTION_ID>
Проблема. GCP не принимает бюджет с Pub/Sub
ERROR: (gcloud.billing.budgets.create) PERMISSION_DENIED: The caller does not have permissiongcloud auth list
gcloud billing accounts list
Нативные бюджеты и выгрузки трёх облаков дают проверяемые цифры, а LLM превращает их в короткое объяснение раз в сутки. Дальше стоит связать ИИ-разбор с лимитами на сам API и добавить в отчёт разбивку по тегам проектов, чтобы аномалия сразу указывала на команду.
Чем AWS Budgets отличается от Cost Anomaly Detection?
Budgets сравнивает фактические расходы с заданным вами порогом, а Cost Anomaly Detection ищет отклонения от обычного поведения без ручного порога. Обычно включают оба: бюджет ловит общий перерасход, детектор — локальный всплеск по сервису. Точные настройки детектора сверьте с документацией AWS.
Нужен ли отдельный платный сервис для ИИ-мониторинга облака?
Нет. Схема из гайда собирается из нативных бюджетов, экспортов и обычного вызова LLM API. Платите только за токены и за хранение выгрузок.
Можно ли получать алерты в Telegram, а не на email?
Да. Нативные уведомления оставляем как страховку, а текст от модели отправляем ботом через sendMessage. Готовый рецепт бота — в связанном гайде по мониторингу VPS.
Как часто обновляются данные о расходах?
Зависит от облака и типа выгрузки: экспорт GCP в BigQuery и CUR в AWS обновляются не мгновенно, у Azure Exports тоже есть задержка. Для суточного отчёта это не критично, но точный интервал сверьте с документацией каждого провайдера.