Guards (G Module)¶
The G module provides a declarative composition surface for safety, validation, and policy guards. Guards compile into ADK before_model_callback and after_model_callback hooks automatically — you declare what to enforce, not where to enforce it.
Quick Start¶
from adk_fluent import Agent, G
agent = (
Agent("safe_agent", "gemini-2.5-flash")
.instruct("Answer user questions helpfully.")
.guard(G.json() | G.length(max=500) | G.pii("redact"))
.build()
)
import { Agent, G } from "adk-fluent-ts";
const agent = new Agent("safe_agent", "gemini-2.5-flash")
.instruct("Answer user questions helpfully.")
.guard(G.json().pipe(G.length({ max: 500 })).pipe(G.pii({ action: "redact" })))
.build();
Note
TypeScript naming
TypeScript uses camelCase: G.rateLimit, G.maxTurns, G.regexDetector. Composition uses .pipe() instead of Python’s | operator. Most factories take an options object: G.length({ max: 500 }), G.pii({ action: "redact" }), G.toxicity({ threshold: 0.8 }).
Design Principle¶
Each guard answers: “What must this agent never do?” If you remove the guard, the agent loses safety. Guards belong in G, not M (resilience) or T (capability).
Guard Types¶
Structural Validation¶
Guard |
Purpose |
Phase |
|---|---|---|
|
Validate output is valid JSON |
post_model |
|
Enforce output length bounds |
post_model |
|
Block or redact pattern matches |
post_model |
|
Validate output against a schema |
post_model |
|
Validate input against a schema |
pre_model |
Content Safety¶
Guard |
Purpose |
Phase |
|---|---|---|
|
Detect and redact/block PII |
post_model |
|
Block toxic content |
post_model |
|
Block specific topics |
post_model |
Cost & Rate Policy¶
Guard |
Purpose |
Phase |
|---|---|---|
|
Enforce token budget |
post_model |
|
Enforce requests per minute |
pre_model |
|
Limit conversation turns |
pre_model |
Grounding¶
Guard |
Purpose |
Phase |
|---|---|---|
|
Verify output is grounded in sources |
post_model |
|
Detect hallucinated content |
post_model |
Conditional¶
Guard |
Purpose |
|---|---|
|
Apply guard only when predicate is true |
Composition¶
Guards compose with the | operator (Python) or .pipe() method (TypeScript):
safety = G.pii("redact") | G.toxicity(threshold=0.8) | G.budget(max_tokens=5000)
agent.guard(safety)
const safety = G.pii({ action: "redact" })
.pipe(G.toxicity({ threshold: 0.8 }))
.pipe(G.budget({ maxTokens: 5000 }));
agent.guard(safety);
Each guard in the chain runs independently. The first violation aborts.
Provider Protocols¶
Guards that perform detection or judgment delegate to pluggable providers.
PIIDetector¶
from adk_fluent._guards import PIIDetector, PIIFinding
class MyDetector:
async def detect(self, text: str) -> list[PIIFinding]:
# Your detection logic
return [PIIFinding(kind="EMAIL", start=0, end=10, confidence=0.95, text="...")]
agent.guard(G.pii("redact", detector=MyDetector()))
import { G, type PIIDetector, type PIIFinding } from "adk-fluent-ts";
const myDetector: PIIDetector = {
async detect(text: string): Promise<PIIFinding[]> {
return [{ kind: "EMAIL", start: 0, end: 10, confidence: 0.95, text: "..." }];
},
};
agent.guard(G.pii({ action: "redact", detector: myDetector }));
Built-in detector factories:
G.regex_detector(patterns)/G.regexDetector(patterns)— Lightweight regex (dev/test)G.dlp(project)/G.dlp({ project, infoTypes, location })— Google Cloud DLP (production)G.multi(*detectors)/G.multi(...detectors)— Union of findings from multiple detectorsG.custom(async_fn)— Wrap any async callable
ContentJudge¶
from adk_fluent._guards import ContentJudge, JudgmentResult
class MyJudge:
async def judge(self, text: str, context: dict | None = None) -> JudgmentResult:
return JudgmentResult(passed=True, score=0.1, reason="Clean")
agent.guard(G.toxicity(threshold=0.8, judge=MyJudge()))
import { G, type ContentJudge, type JudgmentResult } from "adk-fluent-ts";
const myJudge: ContentJudge = {
async judge(text: string, context?: Record<string, unknown>): Promise<JudgmentResult> {
return { passed: true, score: 0.1, reason: "Clean" };
},
};
agent.guard(G.toxicity({ threshold: 0.8, judge: myJudge }));
Built-in judge factories:
G.llm_judge(model)— LLM-as-judge (default)G.custom_judge(async_fn)— Wrap any async callable
Backwards Compatibility¶
The legacy callable guard pattern still works:
def my_guard(callback_context, llm_request):
# Legacy guard function
return None
agent.guard(my_guard) # Registers as both before_model and after_model callback
const myGuard = (ctx: unknown, llmRequest: unknown) => {
// Legacy guard function
return null;
};
agent.guard(myGuard); // Registers as both beforeModel and afterModel callback
How Guards Compile¶
Guards have an internal phase that determines where they enforce:
pre_model guards compile to
before_model_callbackpost_model guards compile to
after_model_callback
You never specify the phase — it’s determined by the guard type. G.input() is pre-model, G.json() is post-model, etc.
IR Integration¶
Guard specs are preserved on the AgentNode IR for diagnostics and contract checking:
ir = agent.to_ir()
print(ir.guard_specs) # Tuple of GGuard instances
See Also¶
Cookbook: Guards — Legacy + G namespace examples
Cookbook: G Module — Comprehensive G namespace examples
Middleware — M module for resilience (retry, circuit breaker)
Callbacks — Low-level before/after hooks