Skip to main content

usage-tracking-ops

Set up and operate usage tracking / cost attribution for an AgentOps Stacks project — tag resources with project/environment/agent, attach a serverless usage policy, retrofit tags onto a project scaffolded without them, tag newly added compute, and query attributed usage in system.billing.usage. Triggers on "track usage", "cost attribution", "chargeback", "tag resources", "usage policy", "how much does my agent cost".

Source facts

Repository
databricks-solutions/agentops-stacks
Last source activity
October 12, 2026 at 18:20
Detected SKILL.md language
English
Stars
7
Forks
7

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
usage-tracking-ops
description
Set up and operate usage tracking / cost attribution for an AgentOps Stacks project — tag resources with project/environment/agent, attach a serverless usage policy, retrofit tags onto a project scaffolded without them, tag newly added compute, and query attributed usage in system.billing.usage. Triggers on "track usage", "cost attribution", "chargeback", "tag resources", "usage policy", "how much does my agent cost".
# usage-tracking-ops — Usage Tracking & Cost Attribution Covers tagging an AgentOps Stacks project so its Databricks usage can be attributed to the project, environment, and agent. Tags are for **tracking and monitoring**, not spend limits. Say "serverless usage policy", never "budget policy". The bundle/API field is still named `budget_policy_id`, and the CLI group is still `databricks account budget-policy`. References: [Use tags to attribute and track usage](https://docs.databricks.com/aws/en/admin/account-settings/usage-detail-tags), [Serverless usage policies](https://docs.databricks.com/aws/en/admin/usage/budget-policies), [AI Search usage policies](https://docs.databricks.com/aws/en/ai-search/budget-policies). ## When to use - "tag my agent's resources for cost attribution" - "how much is this project costing?" / "usage by agent" - "I scaffolded without tags, add them now" - "set up a usage policy" / "attach a usage policy" - "I added a job cluster / SQL warehouse / serving endpoint, how do I tag it?" - deploy errors mentioning `budget_policy_id` ## Tag scheme | Key | Value | Where | |---|---|---| | `project` | bundle name | the usage policy (serverless) and every classic/experiment resource | | `environment` | `${bundle.target}` | the usage policy (serverless) and every classic/experiment resource | | `agent` | agent name | per-agent MLflow experiments | Serverless resources (Apps, Vector Search, Lakebase) get tags only from the usage policy. Policies never tag classic compute. Per-agent app usage comes from `usage_metadata.app_name`. Policy facts to keep in mind: - A user with access to exactly one policy gets it applied to new resources automatically. With several and none chosen, the alphabetically first one is used. Set `budget_policy_id` explicitly. - Granting access doesn't attach a policy to existing resources; redeploy with `budget_policy_id` set. - Tag changes on a policy apply only to usage that starts after the change. - A deleted policy's ID stays on the resource without error, but its usage is no longer tagged. - Use `budget_policy_id`. Some resources also expose `usage_policy_id`, which is Private Preview. ## Key resources in the project | File | Mechanism | |---|---| | `databricks.yml` | `variables.usage_policy_id`. Each app has `budget_policy_id: ${var.usage_policy_id}` | | `resources/lakebase.yml` | `budget_policy_id` only (tags come from the policy) | | `resources/vector_search.yml` | `budget_policy_id` on the VS endpoint (no direct tags). Name is shared across targets, so targets in one workspace share the endpoint and its policy. Index-level policies exist but aren't in the bundle schema; set them in the UI or with `update_index_budget_policy()` | | `resources/data_pipeline.yml` | serverless pipeline: `tags` map plus `budget_policy_id` | | `resources/uc_function_registration.yml` | Runs on serverless: billing tags come from `budget_policy_id`; the job `tags` map labels the job. A commented `job_clusters` example sets `custom_tags` for classic compute. An `existing_cluster_id` cluster must carry its own `custom_tags` | | `resources/experiment.yml` | experiment `tags` list (not billed, but links traces) | | `scripts/create_usage_policy.sh` | Creates one policy per target and wires it in (needs an account-level CLI profile) | | `scripts/enable_usage_policy.py` | Writes policy IDs into `databricks.yml` and uncomments the `budget_policy_id` lines. Refuses if a target lacks an ID | | `docs/usage-tracking.md` | Project-specific guide, including console steps for an admin | Detect whether tagging is enabled: `grep -n "usage_policy_id:" databricks.yml`. If a `budget_policy_id` line starts with `#`, no policy has been attached yet. --- ## Operations ### 1. Set up a serverless usage policy Serverless usage (Apps, Vector Search, serverless jobs/pipelines, Lakebase) only carries custom tags through a policy. **Never create one without the user's explicit confirmation.** Creating a policy needs the **workspace admin** role or the **Serverless usage policy: Manager** permission. Seeing every policy in the account also needs the account-level **Billing admin** role. 1. Ask whether the user is a workspace admin (or has the Manager permission). - **Yes, in the UI** → workspace: username → **Settings → Compute**, next to **Serverless usage policies** click **Manage → Create**. Name it `<project>-<target>`, add tags `project` and `environment`. Repeat for each target, then wire the IDs in (step 2). - **Yes, and they have an account-level CLI profile** (the CLI only has the account API) → run the script: ```bash databricks auth login --host <accounts-console-url> --account-id <account-id> -p my-account ./scripts/create_usage_policy.sh my-account dev staging prod ``` If the project lacks the script (it was scaffolded with tags off), create the policies directly: ```bash databricks account budget-policy create -p my-account --json '{ "policy": {"policy_name": "<project>-<target>", "custom_tags": [{"key": "project", "value": "<project>"}, {"key": "environment", "value": "<target>"}]}}' ``` - **No** → give them the workspace UI steps above to forward to a workspace admin. - **They already have a policy** → the ID is on the policy's page (**Settings → Compute → Serverless usage policies → Manage**), or in `databricks account budget-policy list -p <account-profile>`. The policy **must carry `project` and `environment` tags**. Apps, Vector Search, and Lakebase take their tags only from the policy, so an untagged policy attributes nothing. Check the policy page, or an app's Settings page ("Selected policy has no tags"). 2. Wire the IDs in. `create_usage_policy.sh` does this automatically. Otherwise run: ```bash python3 scripts/enable_usage_policy.py dev=<id> staging=<id> prod=<id> # per target python3 scripts/enable_usage_policy.py --default <id> # one shared policy ``` It sets `targets.<t>.variables.usage_policy_id` (or the default) and uncomments every `# budget_policy_id: ${var.usage_policy_id}` line in `databricks.yml` and `resources/*.yml`. If any target would be left without an ID, it exits **without changing anything**. Pass the missing targets and don't edit the files by hand to get around it. 3. If the project has no `scripts/enable_usage_policy.py` (it was scaffolded before the script existed), make the same edits by hand. Set `usage_policy_id` under every target (or the default) **before** uncommenting the `budget_policy_id` lines, so no target ends up with an empty ID. 4. **Grant access.** On each policy (**Settings → Compute → Serverless usage policies → Manage** → policy → **Permissions** → **Grant access**), grant the **User** role to the developers and to the staging/prod deploy service principals. Otherwise deploy fails. 5. `databricks bundle validate -t <target>`, then deploy. ### 2. Retrofit tags onto a project scaffolded with tags off Add exactly what the template renders with `input_enable_cost_tags=yes`: ```yaml # databricks.yml variables: usage_policy_id: description: Serverless usage policy ID for usage tracking and cost attribution default: "" # set once a policy exists resources: apps: <agent>: # budget_policy_id: ${var.usage_policy_id} # uncomment once the ID is set ``` ```yaml # resources/lakebase.yml (postgres_projects.memory) # budget_policy_id: ${var.usage_policy_id} # resources/uc_function_registration.yml and resources/data_pipeline.yml # budget_policy_id: ${var.usage_policy_id} tags: project: <project> environment: ${bundle.target} # Any job on classic compute: tag the cluster, since serverless policies don't cover it job_clusters: - job_cluster_key: main new_cluster: custom_tags: project: <project> environment: ${bundle.target} # resources/vector_search.yml (vector_search_endpoints.vs_endpoint) # budget_policy_id: ${var.usage_policy_id} # resources/experiment.yml and each per-agent experiment in databricks.yml tags: - key: project value: <project> - key: environment value: ${bundle.target} - key: agent value: <agent> ``` Never leave `budget_policy_id: ${var.usage_policy_id}` live while the variable is empty. Keep it commented until a real ID is set, then wire it with operation 1 step 2. Copy `scripts/enable_usage_policy.py` from a freshly rendered template if the project lacks it. Once tagging is retrofitted, `add_agent.py` and `add_supervisor.py` detect the `usage_policy_id` variable and tag new agents automatically. Then `databricks bundle validate -t dev`. ### 3. Tag newly added resources | Resource | Add | |---|---| | Job cluster (`job_clusters[].new_cluster`) | `custom_tags: {project: <project>, environment: ${bundle.target}}` | | Existing all-purpose cluster (`existing_cluster_id`) | the same `custom_tags` on the cluster itself; the job can't tag it | | Serverless job (no cluster set) | `budget_policy_id: ${var.usage_policy_id}` for billing (don't rely on job tags for serverless), plus job `tags` to label the job | | Serverless pipeline (`serverless: true`) | pipeline `tags` plus `budget_policy_id: ${var.usage_policy_id}`. Job-triggered pipelines don't inherit the job's policy, so set it on the pipeline | | SQL warehouse | `tags: {custom_tags: [{key: project, value: <project>}, {key: environment, value: ${bundle.target}}]}` (not covered by usage policies) | | Model serving endpoint | `tags: [{key: project, value: <project>}, ...]` plus `budget_policy_id: ${var.usage_policy_id}` | | Lakebase project | `budget_policy_id: ${var.usage_policy_id}` only | | Vector Search endpoint / App | `budget_policy_id: ${var.usage_policy_id}` only | Limits: max 20 custom tags per resource. Keys and values may only contain letters, digits, and `+ - = . , _ : @` (no spaces or `/`). ### 4. Query attributed usage ```sql SELECT u.usage_date, u.custom_tags['environment'] AS environment, u.billing_origin_product AS product, u.usage_metadata.app_name AS app_name, SUM(u.usage_quantity) AS dbus, SUM(u.usage_quantity * p.pricing.effective_list.default) AS est_list_cost FROM system.billing.usage u LEFT JOIN system.billing.list_prices p ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_start_time >= p.price_start_time AND (p.price_end_time IS NULL OR u.usage_end_time <= p.price_end_time) WHERE u.custom_tags['project'] = '<project>' AND u.usage_date >= current_date() - INTERVAL 30 DAYS GROUP BY ALL ORDER BY u.usage_date DESC, est_list_cost DESC; ``` Per-agent usage: group by `usage_metadata.app_name` (App compute) and filter traces by the experiment `agent` tag. Estimates use list price. Your negotiated rate may differ. --- ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Deploy: permission denied on `budget_policy_id` | Deploying identity lacks **User** on the policy | Grant User on the policy to the user/SP (step 1.4) | | Deploy: policy not found / invalid ID | Wrong account or a typo | Re-check `usage_policy_id` per target, then `databricks account budget-policy get <id>` | | Usage stopped being tagged, deploys still succeed | The policy was deleted. Resources keep its ID, but it applies no tags | Create a new policy and wire its ID in (step 1) | | Resource got a policy you didn't set | The deployer has access to policies, so one is applied by default (the only one, or the alphabetically first) | Set `budget_policy_id` explicitly | | Serverless pipeline tags lag after a policy change | Pipeline is in development mode | Expected: up to 24 hours to propagate | | App / Lakebase / VS rows have no tags, policy is attached | The policy itself has no tags | Add `project` and `environment` to the policy (Settings → Compute → Serverless usage policies) | | Serverless rows have no `project` tag | `budget_policy_id` still commented, or the policy lacks the tags | Attach the policy (step 1). Tags aren't retroactive | | No rows yet | Billing lag | `system.billing.usage` lags by a few hours | | Tag rejected at deploy | Invalid characters (space, `/`) or more than 20 tags | Fix the key/value. Project and agent names are already safe | | Job usage has no tags | Serverless: `budget_policy_id` still commented. Classic: the cluster lacks `custom_tags` (job tags don't reach an `existing_cluster_id` cluster) | Attach the policy, or add `custom_tags` to the job cluster or existing cluster | | VS usage all under one `environment` | Targets share a workspace, so they share the one VS endpoint | Expected. Give each target its own endpoint name if you need the split | | `environment` wrong for serverless usage | One shared policy across targets | Create one policy per target and override `usage_policy_id` per target |
View on GitHub