Case Study

ARIA

Analytical Reporting and Insights Agent, a FinOps AI assistant that answers “what did we spend on GCP last quarter?” in plain English. It holds no data of its own; it orchestrates two upstream platforms through 30 tools, and it is built so that the only path to a number runs through a tool result.

30 Read-Only Tools
3 Independent Guard Layers
10 Tool Rounds, Maximum
0 API Keys Stored

Why A Chatbot Was The Risk

I had built the dashboards. People still asked me the questions.

2Apps that already held every number a stakeholder could want
1Fabricated figure in an executive deck is enough to discredit every number after it, including the correct ones
3Independent guards it took to make an invented number a path the system does not have

Finding a number meant knowing which of the two apps to open, which tab to click and how to filter it. So the questions kept landing in my inbox: what is a business unit tracking at this fiscal year, which subscriptions are over budget, how much are we spending on AI.

A chatbot was the obvious interface and the obvious risk. A model that confidently produces a plausible dollar figure is worse than no tool at all. The engineering question was never whether it can answer. It was whether it can be built so that making one up is not an available move.

One Question, Followed

A representative question, “which subscriptions are over budget this fiscal year?”, from keystroke to audit row.

  1. The Question Arrives

    The request passes an input check first: messages are capped at 2,000 characters and each person is limited to 20 questions per 60 seconds, keyed to their SSO identity rather than an IP address. The question needs no date, because the agent is fiscal-year aware by default (July through June).

  2. The Model Picks Its Tools

    Claude Sonnet, called through Vertex AI, reads a roughly 280-line system prompt that is cached between requests. Extended thinking (a 10,000 token budget) makes it reason about which tool fits before it requests one. For this question, that is the over-budget tool.

  3. The Tools Run Together

    Every tool the model requests in a round executes concurrently, which is safe because each one is read-only and independent. Business unit names are resolved to IDs through a cached lookup, and pagination runs up to 20 pages of 500 records without the model seeing it. The UI shows a live pill such as “Fetching: get_budget_variance,” so the wait looks like work. If one lookup is needed to ask the next, the loop runs again, up to 10 rounds.

  4. It Reads Totals, Not Rows

    Each financial tool returns a summary object computed on the server: actual total, budget total, forecast, delta, and the COGS split. The prompt tells the model to read summary.actual_total and never to add up rows itself.

  5. The Answer Gets Checked

    Before anything reaches the user, the agent scans the response for currency patterns. If it contains dollar figures and every tool failed (or none ran), the response is discarded and replaced with the actual tool errors, not a vague apology.

  6. Depth Follows The Words

    A single-metric question gets one sentence and one drill-down link into the source app at the exact filtered view. Three follow-up suggestions appear, each one level deeper. A comparison would get a table instead, and only the words “monthly” or “breakdown” would unlock the full report.

  7. The Turn Is Logged

    The audit log records the user identity, question, response, tools invoked, token counts (including cache reads) and response time. Nothing about the exchange depends on anyone remembering it.

The Three-Layer Guard

A prompt instruction is a request, not a control. If the FinOps API is down and the model still produces a plausible figure, no “please don’t hallucinate” saves you.

LayerWhere It LivesWhat It DoesWhat It Cannot Do Alone
PromptSystem promptEight data-integrity rules that override every other instruction: every number traces to a tool result, empty results are stated as empty, uncertainty is disclosedEnforce anything; the model can still ignore it
CodeAgent loop, after the final responseScans for dollar figures and discards the answer if every tool errored or none ranJudge a figure when a tool did succeed
ToolEvery failure payloadReturns an instruction field: “STOP. Tell the user this data source is unavailable. Do NOT fabricate numbers.” The model reads the guardrail inside its own contextAct when no tool fails

Thirty Tools, All Read-Only

Every tool is a GET. ARIA cannot change a budget, a resource or a record.

GroupToolsCoversReads From
Financial12Spend, budget variance, scorecards, vendors, COGS, over-budget, AI and DR spendFinOps Reporting App
Infrastructure18GKE workloads and clusters, GCE instances and recommendations, GCS buckets, AI model spendGCP Spotlight

Tiered Answers

The first version answered everything with a wall of analysis. Executives stopped reading at line two.

TierTriggered ByResponse Shape
Tier 1 (default)A single-metric lookupOne sentence and one drill-down link
Tier 2“compare,” “which,” or three or more itemsSummary sentence and a table (5 columns, 10 rows at most)
Tier 3“monthly,” “trend,” “breakdown”Full breakdown with analyst commentary

The tier is set by the user’s words, not by how much data came back.

A tool that returns 40 rows for a one-entity question means the agent aggregates to a single total and stays at Tier 1. The volume of available data is not a reason to spend the reader’s attention.

Spending Where It Pays

Two model features that push the bill in opposite directions, paired on purpose.

10,000Token budget for extended thinking, spent on the part that varies
~280Lines of system prompt, cached between requests because it never changes
10Tool rounds at most: a confused agent burns budget, not the afternoon

Zero Stored Keys

ARIA calls Claude through Vertex AI instead of the direct API. That one choice removed a whole category of operational risk.

ControlSetting
Model AccessWorkload Identity: the pod authenticates as a GCP service account, so no key exists to rotate, leak or commit
PermissionsA custom IAM role granting only aiplatform.endpoints.predict; the agent can call the model and nothing else in the project
Data ResidencyInference runs inside the organization’s own GCP project and region, which made it approvable for corporate financial data
Local DevelopmentThe same code path works with application default credentials, so dev and prod share one identity model
Data AccessRead-only by construction; upstream calls use cluster-internal addresses and public access sits behind SSO on the corporate network
AttributionEvery question is recorded against the SSO identity that asked it, with the tools used to answer

A Thin Agent Over Two Platforms

ARIA owns no cost data. It owns the reasoning, the guardrails and the audit trail.

Interfaces

React chat UI (full page and embedded widget in both source apps) and a Python CLI with interactive and one-shot modes.

FastAPI Backend

Server-sent events for streaming, a 25-second heartbeat, and routes for chat, audit, feedback, suggestions and export. SQLite in WAL mode.

Agent Loop

Claude Sonnet on Vertex AI with extended thinking, a cached system prompt and up to 10 tool rounds.

Two Platforms

FinOps Reporting App (12 tools) and GCP Spotlight (18 tools), over the Ternary API, GCP APIs and BigQuery billing export.

ARIA can never contradict the dashboards it links to.

The upstream apps already handle collection, alias mapping, COGS classification and cost calculation. Querying their databases directly would have duplicated that logic in a third place and guaranteed the copies would eventually disagree, so ARIA goes through their APIs.
Python 3.11 FastAPI httpx React 18 TypeScript Vite Tailwind CSS Click Rich SQLite Vertex AI Docker Kubernetes ArgoCD

After The Answer

Everything asked is logged, exportable and fed back into what the tool suggests next.

What Each Choice Cost

A decision log: what I chose, and the price I accepted for it.

DecisionChosenPrice Accepted
Guard failure modeFail closed on any dollar figure with no successful tool behind itA legitimate answer can be blocked; the user retries
Model hostingVertex AI with Workload IdentityModel identifiers lag the direct API
DatabaseSQLite in WAL modeOne replica; the database changes first at scale
LoginNone; SSO at the platform edgeIdentity comes from proxy headers and feeds the audit log only
ScopeDecline anything off-topicToo strict refuses real FinOps questions; too loose lets scope creep back
ConcurrencyAll tools in a round run at onceValid only while every tool stays read-only

What I Would Change

With financial data, an assistant’s credibility is set by its worst answer, not its average one.

Most of the engineering went into the paths where something has gone wrong: a source is down, a tool returns nothing, a question falls outside scope. Getting those right is what let people rely on the answers when everything worked. The interesting work was never the conversation. It was the constraints around it.

ARIA is the conversational layer over two platforms I built; both are case studies of their own:

 FinOps Reporting Platform  GCP Spotlight
 Back to Projects