Module 9

Reliability & Deterministic Enforcement

This module focuses on Agent SDK hooks and structured error contracts that guarantee compliance and normalization even when the model's reasoning deviates from its instructions. Prompts are probabilistic; hooks, gates, and validators are deterministic.

Answer key Module9_Complete.ipynb

1. Deterministic vs. Probabilistic Enforcement

Prompts can ask the model not to issue a high-risk refund. A PreToolUse hook can block the refund before it executes. This distinction matters for financial, legal, privacy, safety, and irreversible operations.

1a. PreToolUse Interception

Use PreToolUse to intercept outgoing tool calls before execution. This is the only way to guarantee that high-value actions, such as refunds over $500, are blocked and redirected to human escalation regardless of the model's intent.

Python (PreToolUse safety gate)
REFUND_LIMIT = 500

def pre_tool_use(tool_call):
    if tool_call["name"] != "issue_refund":
        return {"decision": "allow"}

    amount = tool_call["arguments"].get("amount_usd", 0)
    if amount > REFUND_LIMIT:
        return {
            "decision": "deny",
            "redirectTool": "escalate_to_human",
            "reason": "Refunds over $500 require human approval.",
        }

    return {"decision": "allow"}

1b. Programmatic Prerequisite Gates

tool_choice can force the model to call an identification tool first, but a programmatic gate is what blocks downstream tools until the prerequisite has actually succeeded.

For example, block process_refund until get_customer or identity_verification has returned a verified success status. A model can ignore a prompt; it cannot bypass a runtime gate.

2. Standardized Structured Error Contracts

Uniform prose errors like "Operation failed" are anti-patterns because they prevent the agent from making informed recovery decisions. Every tool should return the same structured fields when it fails.

2a. The errorCategory Enum

  • TRANSIENT: timeouts or service unavailability; Claude should attempt a retry.
  • VALIDATION: invalid input, such as a bad email format; the agent should clarify with the user.
  • PERMISSION: authorization issues; the agent should inform the user of the access gap.
  • BUSINESS: policy violations, such as refund exceeds limit; the agent should communicate the rule.

2b. Metadata Over Prose

Always return an isRetryable boolean. This prevents the agent from wasting tokens on repeated attempts for non-retryable permission, validation, or business-rule failures.

JSON (structured error contract)
{
  "isError": true,
  "errorCategory": "TRANSIENT",
  "isRetryable": true,
  "message": "Refund system is temporarily offline."
}

{
  "isError": true,
  "errorCategory": "BUSINESS",
  "isRetryable": false,
  "message": "Refunds over $500 require human approval."
}

3. PostToolUse Normalization for MCP

Different MCP servers often return inconsistent data formats: Unix timestamps, local date strings, ISO strings, numeric status codes, or provider-specific labels.

Implement PostToolUse hooks to rewrite heterogeneous tool results into a homogeneous shape before they reach the model. This reduces thinking-budget consumption because the model does not have to reconcile formats manually.

Python (PostToolUse normalization)
from datetime import datetime, timezone

def post_tool_use(tool_name, result):
    if tool_name == "crm_database_lookup" and "created_at_unix" in result:
        created_at = datetime.fromtimestamp(
            result["created_at_unix"],
            tz=timezone.utc,
        ).isoformat()
        result["created_at"] = created_at
        del result["created_at_unix"]

    return result

4. Forced Prerequisite Strategy

Use forced tool selection to implement deterministic multi-step sequences. On the first turn, set:

JSON (forced first tool)
{
  "tool_choice": {
    "type": "tool",
    "name": "get_customer"
  }
}

This makes the identification step mandatory. On subsequent turns, switch back to "auto" or "any" so the agent can reason flexibly after the prerequisite evidence exists.

  • tool_choice ensures the first step is attempted.
  • PreToolUse gates ensure unsafe downstream tools cannot execute without verified prerequisite state.
  • Structured errors tell the model whether to retry, clarify, inform, or escalate.
  • PostToolUse normalization ensures the model reasons over stable result shapes.

Lab Exercise: The Defense-in-Depth Refund Workflow

Self-driven lab Module9_Self_Driven_Lab.ipynb

Objective: combine tool_choice, PreToolUse hooks, structured errors, and PostToolUse normalization to build a safe financial agent.

  1. Prerequisite forcing: use tool_choice to force the agent to call an identity_verification tool on its first turn.
  2. Safety hook: implement a PreToolUse hook that blocks issue_refund when amount_usd is over $500, even if the model's reasoning says it is authorized. Redirect the agent to escalate_to_human.
  3. Structured error handling: create a tool handler for a "System Offline" error that returns isError: true, errorCategory: TRANSIENT, and isRetryable: true; observe the agent attempting a recovery retry.
  4. Normalization: implement a PostToolUse hook that converts a Unix timestamp from an MCP database tool into a human-readable ISO string.
  5. Validation vs. access: test a "User Not Found" response as isError: false because it is a valid empty result, then compare it with an actual "Access Denied" error marked as PERMISSION.

Exam tip: errors talk to the model; hooks talk to the runtime. Use structured errors to guide recovery, but use hooks and gates to enforce policy.