Chapter 9·Part IIIAgentic architecture

What is Agentic AI?

Your first agents: a minimal Agno agent with a single tool, then a data quality agent that scans the defect-seeded motor India claims triangle and reports what it finds.

In the book this is Part III, Agentic AI: From Concept to Architecture.

What you’ll build

  • Autonomy, goal-directed behaviour, and environmental interaction as the three defining properties
  • The five-stage cognitive loop: perception, reasoning, planning, action, memory
  • ReAct, Plan-and-Execute, and Reflexion design patterns
  • Why heterogeneous inputs favour agents and uniform inputs favour deterministic pipelines
  • Agentic failure modes: wrong tool selection, compounding misinterpretation, and loops

01_column_agent.py

live agent on GeminiSource on GitHub

A single Gemini agent reasons about which triangle column to trust.

about 20s · live model callOpen in Colab
# ── Section purpose: a minimal Agno agent that explains a column name ──
# Book reference: Chapter 9, §9.6 "Your First Agent (Code)"
from agno.agent import Agent              # high-level Agent class
import os
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))  # for common/
from common.config import get_model


# Tool the agent can call. Agno turns this Python function into a tool
# definition automatically by reading the type hints and the docstring.
def lookup_column_definitions(column_name: str) -> str:
    """Look up the definition of a claims-triangle column.

    Args:
        column_name (str): The column name to look up.
    """
    definitions = {
        'paid_loss_usd':     'Cumulative paid losses to date, in USD.',
        'reported_loss_usd': 'Cumulative reported losses (paid + case reserve), in USD.',
        'case_reserve_usd':  'Case reserve held on open claims, in USD.',
        'payment_currency':  'ISO currency code of the original payment, free text.',
    }
    return definitions.get(column_name, 'Unknown column.')


# Build the agent. Agno wraps the loop for us; we supply tools and instructions.
column_agent = Agent(
    model=get_model(),
    tools=[lookup_column_definitions],
    instructions='Explain claims-triangle columns clearly and concisely.',
    tool_call_limit=5,                   # cap tool calls per run, like an iteration cap
    markdown=True,
)

# Run the agent on a goal. print_response runs the full loop and prints the answer.
agent_goal = "Explain what the column 'payment_currency' means in the motor triangle."
column_agent.print_response(agent_goal, stream=True)

Runs the unmodified chapter script on the server and streams the agent's tool calls and reasoning here.

02_data_quality_agent.py

live agent on GeminiSource on GitHub

Profiles the claims triangle and flags the seeded data defects.

about 30s · live model callOpen in Colab
# ── Data quality agent: Agno + three actuarial tools ──
# Book reference: Chapter 9, Case Study "Building a Data Quality Agent"
# Repo note: the CSV path points at the repo's data directory; the book
# assumes the file sits alongside the script.
import os

import pandas as pd
from agno.agent import Agent
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))  # for common/
from common.config import get_model

# Load the Meridian Re motor India triangle from the warehouse extract.
DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
triangle_df = pd.read_csv(os.path.join(DATA_DIR, 'meridian_motor_india_triangle.csv'))


# Tool 1: count missing values in each loss column.
def check_missing_values() -> dict:
    """Count missing values in each loss column of the motor India triangle.

    Returns the count of missing entries per loss column.
    """
    loss_columns = ['paid_loss_usd', 'reported_loss_usd', 'case_reserve_usd']
    return {'missing_by_column': triangle_df[loss_columns].isna().sum().to_dict()}


# Tool 2: find rows with negative case reserves.
def check_negative_reserves() -> dict:
    """Find rows in the motor India triangle with negative case reserves.

    Negative case reserves usually indicate a roll-forward error.
    """
    flagged = triangle_df[triangle_df['case_reserve_usd'] < 0]
    return {
        'negative_reserve_count': len(flagged),
        'rows': flagged[['accident_year', 'dev_period_months', 'case_reserve_usd']].to_dict('records'),
    }


# Tool 3: find rows where reported_loss_usd is lower than paid_loss_usd.
def check_development_consistency() -> dict:
    """Find rows where reported_loss_usd is lower than paid_loss_usd.

    Reported < paid usually indicates recoveries mis-coded as negative payments.
    """
    inconsistent = triangle_df[triangle_df['reported_loss_usd'] < triangle_df['paid_loss_usd']]
    return {
        'inconsistency_count': len(inconsistent),
        'rows': inconsistent[['accident_year', 'dev_period_months',
                               'paid_loss_usd', 'reported_loss_usd']].to_dict('records'),
    }


# Build the data quality agent. The three Python functions become tools
# automatically; their docstrings become the tool descriptions.
data_quality_agent = Agent(
    model=get_model(),
    tools=[
        check_missing_values,
        check_negative_reserves,
        check_development_consistency,
    ],
    instructions=(
        'You are a data quality agent for actuarial claims triangles. '
        'Use the three tools available to scan for issues, reason about which '
        'flags are worth raising, and produce a short report with one paragraph '
        'per issue type.'
    ),
    tool_call_limit=10,
    markdown=True,
)

if __name__ == "__main__":
    agent_goal = (
        'Scan the motor India triangle for data quality issues '
        'and produce a short report.'
    )
    data_quality_agent.print_response(agent_goal, stream=True)

Runs the unmodified chapter script on the server and streams the agent's tool calls and reasoning here.