Leaders Insights
Leaders Insights

Stay at the top of your field, a little every day.

DomainsMarketingDataFinanceAI
ResourcesLearnTestToolsBlogGlossary
© 2026 Leaders Insights — All rights reserved.
Tracks/AI Essentials/Gemini & Google AI/Agents and automation/Guardrails: permissions, review, and cost
3/4+150 XP

Agents and automation

1Agents on Google AI: the agent development kit+1902Automating with apps script and workspace+1803Guardrails: permissions, review, and cost+1504Multi-agent orchestration with the ADK+210

Guardrails: permissions, review, and cost

# Guardrails: permissions, review, and cost

An agent that can read your Drive, send email on your behalf, and call paid APIs is a junior employee with your credentials and no manager. Guardrails are the manager. This lesson is about building the review gates, permission boundaries, and cost controls that let a Gemini-powered automation run without becoming a liability.

The three failure modes

Every unsupervised agent fails in one of three ways:

1. Wrong action, real consequence. It sends a draft to a customer, deletes the wrong rows, or merges a pull request that breaks production.

2. Over-broad access. It was granted "Drive" when it needed one folder, so a prompt injection or a hallucinated tool call reaches everything.

3. Runaway cost. A loop calls Gemini Pro a thousand times, or a grounded search runs on every row of a 50,000-row sheet.

Guardrails mapmapUsing software to automate repetitive marketing tasks and campaigns, enabling personalisation at scale across channels like email, web, and social.View full definition → cleanly to these:

review gates
for consequence,
scopes
for access,
budgets and model routing
for cost. Treat them as three separate controls, because a fix for one does nothing for the others.

Permissions and scopes: grant the minimum

Start by narrowing what the agent *can* touch, before you worry about what it *should* do.

Oauth scopes in apps script and workspace

When you build an automation in Apps Script (the most common way to wire Gemini into Gmail, Sheets, and Docs), the scopes are declared, not assumed. Pin them explicitly in appsscript.json instead of letting the editor auto-grant broad ones:

json
{
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets.currentonly",
    "https://www.googleapis.com/auth/gmail.compose",
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

Note spreadsheets.currentonly (this one sheet, not all sheets) and gmail.compose (create drafts, cannot send). That single difference between gmail.compose and gmail.send is the line between an agent that *proposes* email and one that *sends* it. Default to the narrower scope and force a human to cross the line.

Service accounts and Vertex AI

On the Vertex AI side, an agent (for example one built with the Agent Development Kit) runs under a service account. Apply the same discipline with IAM:

  • Give the service account roles/aiplatform.user, not Owner.
  • If it reads from BigQuery, grant roles/bigquery.dataViewer on the *specific dataset*, not the project.
  • Never give an autonomous agent write access to billing, IAM, or resource deletion.

The principle is identity per agent. One service account per automation means you can read the audit log and answer "what did *this* agent do" without untangling shared credentials.

Gems and extensions

In the consumer Gemini app, the equivalent of scope is which Extensions are enabled (Gmail, Drive, Maps, and so on) and what a Gem is allowed to reference. A Gem cannot grant itself access to a connector you have not turned on. For anything that touches sensitive data, build the automation in Apps Script or Vertex AI where scopes are explicit, not in a Gem where access is broad and account-wide.

What you never automate unsupervised

Some actions should *always* pause for a human, regardless of how confident the model is. Keep this list short and absolute:

  • Sending external communication. Email, Chat messages, anything a customer sees.
  • Irreversible data changes. Deletes, overwrites, schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → changes, payments, refunds.
  • Code that ships. Merging, deploying, or pushing to a protected branch.
  • Spending money above a per-action threshold (a paid APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → call, a cloud resource).
  • Granting access to anyone or anything.

Gemini Code Assist and the Gemini CLI already lean this way: the CLI shows you a proposed shell command or file edit and waits for confirmation before running it. That confirm step is not friction to remove. It is the review gate doing its job. When you build your own agents, copy that pattern instead of bypassing it.

Human review gates

A review gate is a deliberate stop where the agent produces a *proposal*, persists it somewhere visible, and waits for explicit human approval before executing. The agent's job ends at "drafted." A person's job begins at "approve."

The draft-and-wait pattern

The cleanest gate in Workspace is the Gmail draft. The agent writes the email but uses the compose scope, so the message lands in Drafts, not the recipient's inbox. The human reviews and clicks Send. No extra UI required, and the gate is a tool every user already understands.

Here is that pattern end to end: classify an inboundinboundA strategy that attracts prospects organically via valuable content (blog, SEO, social) rather than interrupting them.View full definition → email with Gemini, then create a *draft* reply that a human must approve.

python
import os
from google import genai

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

REVIEW_PROMPT = """You are a support triage assistant.
Read the customer email and return JSON:
{"category": "...", "needs_human": true|false, "draft_reply": "..."}
Set needs_human=true for refunds, legal, or anything you are unsure about.
Never promise refunds or commitments in draft_reply."""

def triage(email_body: str) -> dict:
    resp = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[REVIEW_PROMPT, email_body],
        config={"response_mime_type": "application/json"},
    )
    return resp.parsed

def handle(email_body: str):
    result = triage(email_body)
    # The agent NEVER sends. It only creates a draft for review.
    create_gmail_draft(result["draft_reply"], flagged=result["needs_human"])
    return result

Two guardrails are baked in. The model is told to flag refunds and uncertainty itself (needs_human), and the action ceiling is a draft, not a send. Even if the model misjudges, the worst case is an unreviewed *draft* sitting in a folder, not an unwanted email in a customer's inbox.

Confidence-based routing

Not every item needs the same scrutiny. Route by risk:

  • Auto-execute the low-stakes, fully reversible actions (labeling an email, adding a calendar tag).
  • Queue for review the medium-stakes ones (a draft reply, a proposed spreadsheet update).
  • Escalate the high-stakes ones to a named person with context attached.

Let the model return its own needs_human signal *and* enforce hard rules in code. Never trust the model alone to decide whether something is dangerous, because a prompt injection can flip that flag. The code-level rule ("category == refund always escalates") is the real guardrail; the model's self-assessment is a convenience on top.

Build agents with the Agent Development Kit

Watch on YouTube

Keeping cost in check

Cost runs away quietly. The agent works, the output looks fine, and the bill arrives at month end. Build the controls before you need them.

Route to the cheaper model by default

The single biggest lever is model choice. Gemini Flash costs a fraction of Gemini Pro per tokentokenA token is the basic unit of text that language models process, often a word fragment, whole word, or punctuation mark rather than a single character.View full definition → and is fast enough for classification, extraction, routing, and summarization. Reserve Pro for genuinely hard reasoning. A good default: every step uses Flash, and you *promote* a step to Pro only when you can name why.

python
def pick_model(task_complexity: str) -> str:
    # Default cheap. Escalate only for hard reasoning.
    return "gemini-2.5-pro" if task_complexity == "hard" else "gemini-2.5-flash"

Trim what you send and store what you can reuse

  • Context size is cost. Long context is a feature, not an instruction to stuff the whole Drive into every call. Send the relevant chunk, not the corpus.
  • Cache repeated prefixes. If every call shares a large fixed system prompt or document, use context caching so you are not billed full price for the same tokenstokensA token is the basic unit of text that language models process, often a word fragment, whole word, or punctuation mark rather than a single character.View full definition → repeatedly.
  • Ground deliberately. Grounding with Google Search adds value and cost. Turn it on for steps that need fresh facts, off for steps that do not.

Cap the loop

Agents loop. A reasoning loop with no ceiling can call the model dozens of times on one task. Always set a hard iteration cap and a per-run tokentokenA token is the basic unit of text that language models process, often a word fragment, whole word, or punctuation mark rather than a single character.View full definition → budget, and fail closed when you hit it:

python
MAX_STEPS = 6

def run_agent(task, step=0):
    if step >= MAX_STEPS:
        return escalate(task, reason="step_limit_reached")
    # ... one reasoning/tool step ...

Watch the bill at the platform level

On Vertex AI and Google Cloud, set a budget and budget alerts so you get notified (or trigger an automated cap) before spend crosses a threshold. A billing alert is your last line of defense when an in-code guardrail is missed. For APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition →-key usage from AI Studio, monitor usage in the console and rotate keys that leak. Budgets do not stop spend by themselves, so pair them with the in-code caps above.

Knowledge check

1. According to the lesson, why should review gates, scopes, and budgets be treated as three separate controls?

2. What is the key practical difference between the 'gmail.compose' and 'gmail.send' scopes, and why does the lesson recommend defaulting to 'compose'?

3. The lesson describes an unsupervised agent as 'a junior employee with your credentials and no manager.' What is the intended point of this analogy?

MULTIPLE CHOICE

4. Select ALL of the following that are examples of the 'over-broad access' failure mode or the correct way to prevent it.

Select all the correct answers.

MULTIPLE CHOICE

5. Select ALL statements that reflect the principle of granting minimum permissions as described in the lesson.

Select all the correct answers.

Putting it together: a review-gated weekly report

Here is how the three controls combine in one realistic automation. A weekly automation reads a sales Sheet, drafts a summary email to leadership, and waits for approval.

  • Scope: Apps Script with spreadsheets.currentonly and gmail.compose. It can read this one sheet and create a draft. It cannot send, and it cannot touch other files.
  • Cost: Flash for the summary (Pro is overkill for "summarize this table"). One call per week, with the sheet trimmed to the last quarter, not all history.
  • Review gate: The output is a Gmail draft addressed to leadership. A person reads it Monday morning and sends, or edits, or discards.

No part of this can email anyone, spend meaningfully, or change data without a human. That is the goal: an automation where the *worst realistic failure* is a draft someone deletes. Design every agent so its failure mode is harmless by construction, not by hoping the model behaves.

Logging: the guardrail you only need once

Log every agent action: the input, the model and tokenstokensA token is the basic unit of text that language models process, often a word fragment, whole word, or punctuation mark rather than a single character.View full definition → used, the proposed action, and whether a human approved it. When something goes wrong (and eventually something will), the log is how you find out what happened and prove what did not. In Apps Script, write to a dedicated log sheet or console.log to Cloud Logging. On Vertex AI, use ADK callbacks to capture each tool call. An action your system cannot explain is an action you cannot trust.

Key Takeaways

  • Scope before behavior. Grant the narrowest OAuth scope or IAM role that works (gmail.compose not gmail.send, one dataset not the project), and run one identity per agent so the audit log is readable.
  • Make the failure mode harmless. Build agents so the worst realistic outcome is a *draft* a human deletes. Never automate sends, deletes, payments, deploys, or access grants unsupervised.
  • Enforce gates in code, not in the prompt. Let the model flag risk as a convenience, but put the real "always escalate" rules in your control flow, where prompt injection cannot flip them.
  • Default to Flash, cap the loop. Use Gemini Flash unless a step needs Pro, cache repeated context, trim what you send, and set a hard iteration limit so no run can spiral.
  • Back it with platform budgets and logs. Cloud billing alerts catch what your code misses, and a full action log is what lets you trust (or revoke) the automation later.

Previous

Automating with apps script and workspace

Next

Multi-agent orchestration with the ADK