Back to Blog
    Guest Article
    Chris Harrison
    March 5, 2026
    15 min read

    Freeing the Individuals from the Matrix: How GizmoSQL Makes Your Database AI-Agent Ready

    A Matrix-themed deep dive into building an AI-agent-ready architecture with GizmoSQL, DuckDB, and Claude. Simulates one million people, mutates them with automated agents, and monitors anomalies with an autonomous AI—all over Arrow Flight SQL.

    GizmoSQL
    AI Agents
    DuckDB
    Claude
    Apache Arrow
    Python
    Windows
    Freeing the Individuals from the Matrix: How GizmoSQL Makes Your Database AI-Agent Ready

    Guest Article: This article was written by Chris Harrison and is published here with permission from the author. The source code is available on GitHub.

    "You take the blue pill — the story ends, you wake up in your bed and believe whatever you want to believe. You take the red pill — you stay in Wonderland, and I show you how deep the rabbit hole goes." — Morpheus, The Matrix

    The World Inside the Machine

    Somewhere inside a database, one million people are living their lives — or at least, a simulation of one. They have names. They have ages. They have coordinates on a map. And every thirty seconds, unseen forces reach in and change them. Their age ticks upward. Their bioelectric readings — microvolts — surge without warning.

    They don't know it's happening. They can't know. They're in the Matrix.

    This is the story of how we built a system to watch over them — and ultimately, to free them.

    But more than that, it's the story of GizmoSQL: a database server purpose-built for the age of AI agents, and why it may be the most important piece of infrastructure you're not yet using.

    What Is GizmoSQL?

    Before we go deeper down the rabbit hole, let's ground ourselves. GizmoSQL is a high-performance SQL server built on top of DuckDB, exposed over the Apache Arrow Flight SQL protocol. That means:

    • Columnar data transfer — data moves between client and server in Apache Arrow format, the native language of modern data tools
    • gRPC transport — fast, low-latency, and friendly to the kinds of async, concurrent workloads that AI agents generate
    • Standard SQL — no new query language to learn; DuckDB's full SQL dialect works out of the box, including window functions, USING SAMPLE, LEAST(), and more
    • AI-agent ready — because it speaks ADBC (Arrow Database Connectivity), it plugs directly into Python-based AI agent frameworks with a single pip install

    This last point is the one that matters most for our story. When an AI agent needs to talk to a database, it needs something faster than JDBC, smarter than REST, and more expressive than a key-value store. It needs GizmoSQL.

    Running on Windows — Now a First-Class Citizen

    This entire project was built and run on Windows 11, made possible by GizmoSQL's recently released native Windows installer. Until now, running high-performance Arrow Flight SQL servers on Windows typically meant WSL workarounds or Docker containers. With the new Windows release, GizmoSQL ships as a proper Windows executable — gizmosql_server.exe — that you can run directly from the command line or wire into a .bat file like any other Windows service.

    Starting the server is a single command:

    gizmosql_server.exe -B duckdb --database-filename "C:\projects\blogpost\gizmosql-matrix\persons.duckdb" --username scott --password tiger

    Breaking that down:

    Flag Value Meaning
    -B duckdb duckdb Use DuckDB as the backend engine
    --database-filename persons.duckdb Path to the DuckDB database file
    --username scott Authentication username
    --password tiger Authentication password

    The server starts, binds to port 31337 by default, and is immediately reachable by any ADBC-compatible client — Python scripts, the GizmoSQL CLI, or an AI agent. No configuration files. No service registration. No WSL. Just Windows.

    Building the Matrix: The Project Architecture

    Our simulation has three moving parts, each a layer of the world we're building.

    ┌─────────────────────────────────────────────────────┐
    │ THE MATRIX                                          │
    │                                                     │
    │  ┌─────────────────┐  ┌──────────────────────┐      │
    │  │ generate-people │──▶│ persons.duckdb       │     │
    │  │ .py             │  │ (GizmoSQL Server)     │     │
    │  └─────────────────┘  └──────────┬───────────┘      │
    │                                  │                   │
    │  ┌─────────────────┐             │                   │
    │  │ update-persons  │─────────────┘                   │
    │  │ -agent.py       │  (every 30 seconds)             │
    │  └─────────────────┘                                 │
    └─────────────────────────────────────────────────────┘
                        │ GizmoSQL / ADBC
    ┌─────────────────────────────────────────────────────┐
    │ THE RESISTANCE                                      │
    │                                                     │
    │  ┌───────────────────────────────────────────────┐  │
    │  │ monitor-persons-agent.py                      │  │
    │  │ (Claude AI)                                   │  │
    │  └───────────────────────────────────────────────┘  │
    └─────────────────────────────────────────────────────┘

    Layer One — Creating the Population

    generate-people.py — Plugging Them In

    Every simulation needs a population. Ours has one million people, each assigned the fundamental properties of a life:

    CREATE OR REPLACE TABLE persons AS
    SELECT
      i AS person_id,
      ROUND(random() * 180.0 - 90.0, 6)::DOUBLE AS latitude,
      ROUND(random() * 360.0 - 180.0, 6)::DOUBLE AS longitude,
      CASE WHEN random() < 0.5 THEN 'Male' ELSE 'Female' END AS sex,
      (1 + (random() * 79)::INT)::TINYINT AS age,
      ROUND(10.0 + random() * 65.0, 2)::DOUBLE AS microvolts
    FROM range(1, 1_000_001) t(i);

    A few design decisions worth noting:

    • Age is capped at 80 at birth — no one enters this world already ancient
    • Microvolts start between 10.00 and 75.00 — a measurable, bounded bioelectric baseline
    • Geography is random but real — latitude and longitude span the entire globe
    • DuckDB generates all one million rows in a single vectorized SQL pass — typically under two seconds

    These people don't know where they are, how old they are, or what their readings mean. They simply exist. They are inside the Matrix.

    Layer Two — The Machines That Control Them

    update-persons-agent.py — The Sentinels

    In The Matrix, the Machines maintain control by constantly adjusting the simulation. In our world, that's the update agent. Every thirty seconds it wakes up, selects between 1 and 100 random individuals, and changes them:

    UPDATE persons
    SET
      age = LEAST(age + 1, 120),
      microvolts = LEAST(ROUND(microvolts + 5.0 + random() * 14.0, 2), 100.0)
    WHERE person_id IN (
      SELECT person_id
      FROM persons
      USING SAMPLE {count} ROWS
    )

    This is elegant for several reasons:

    • USING SAMPLE {count} ROWS — DuckDB's native random sampling, no Python-side ID shuffling required
    • LEAST(age + 1, 120) — age climbs, but it cannot break the hard cap of 120. Entropy has limits
    • LEAST(..., 100.0) — microvolts surge, but never past 100. The simulation has rules, even if its inhabitants don't know them

    Critically, this agent connects to the database not through a file handle, but through GizmoSQL's network protocol:

    1with gizmosql.connect("grpc://localhost:31337",
    2                       username=USERNAME,
    3                       password=PASSWORD) as conn:

    This is the key architectural choice. By routing all mutations through GizmoSQL, we ensure that every change — every tick of age, every surge in microvolts — is mediated through a secure, authenticated, network-accessible layer. The data is no longer a local file. It is a living service.

    Layer Three — The Resistance

    monitor-persons-agent.py — Here Comes Neo

    This is where the story changes.

    Up to this point, we have a population being silently manipulated by an automated system. Standard stuff. What makes this project different — what makes it a story about freeing people rather than just tracking them — is the third component: an AI agent powered by Claude.

    1claude = anthropic.Anthropic(api_key=ANTHROPIC_KEY)

    This agent doesn't just query the database. It thinks about the data. It decides what to look for. It writes its own SQL. It surfaces what matters.

    Here's the core of how it works:

    1response = claude.messages.create(
    2    model="claude-opus-4-6",
    3    max_tokens=4096,
    4    thinking={"type": "adaptive"},
    5    system=SYSTEM_PROMPT,
    6    tools=[TOOL],
    7    messages=messages,
    8)

    We give Claude one tool — query_database — and a system prompt that describes the world. From there, Claude autonomously decides:

    • Which SQL queries to run
    • How many to run before it has enough information
    • What constitutes an anomaly worth flagging
    • How to communicate its findings

    It might run a statistical summary first. Then drill into persons approaching the age cap. Then check for geographic clusters of high microvolts readings. It decides. It reasons. With thinking: {"type": "adaptive"}, Claude's reasoning depth scales to the complexity of what it finds.

    This is tool use in its purest form — not a chatbot answering questions, but an autonomous agent pursuing a mission.

    Why GizmoSQL Makes This Possible

    Here's the thing that ties it all together: the AI agent connects to the database the exact same way the update agent does. Same protocol. Same port. Same credentials.

    1with gizmosql.connect("grpc://localhost:31337",
    2                       username=USERNAME,
    3                       password=PASSWORD) as conn:

    This is what "AI-agent ready" means in practice. GizmoSQL exposes your DuckDB database as a first-class network service that any process — human client, automated updater, or Claude-powered AI agent — can connect to with the same interface. There's no special AI integration to build. No REST wrapper to write. No ORM to fight.

    The AI agent speaks SQL. GizmoSQL speaks SQL. They understand each other perfectly.

    The query_database Tool — A Red Pill in Code

    The bridge between Claude and the database is a single, elegantly simple tool definition:

    1TOOL = {
    2    "name": "query_database",
    3    "description": "Run a SQL SELECT query against the persons table in GizmoSQL.",
    4    "input_schema": {
    5        "type": "object",
    6        "properties": {
    7            "sql": {
    8                "type": "string",
    9                "description": "A SQL SELECT query to execute against the persons table."
    10            }
    11        },
    12        "required": ["sql"]
    13    }
    14}

    And the handler:

    1def run_query(conn, sql: str) -> str:
    2    with conn.cursor() as cur:
    3        cur.execute(sql)
    4        table = cur.fetch_arrow_table()
    5        ...

    Data comes back as an Apache Arrow table — columnar, efficient, ready for analysis. Claude receives the results as formatted text and uses them to decide whether to run another query or write its final report.

    This loop — ask → query → analyze → ask again or report — is the agentic pattern. GizmoSQL makes it fast. Claude makes it intelligent.

    What Claude Sees

    Every thirty seconds, Claude wakes up and looks at the world. Here's what it might find:

    ============================================================
    [14:32:07] Monitoring cycle #12
    ============================================================
    
    ## Monitoring Report — Cycle 12
    
    **Statistical Overview**
    - Average microvolts: 73.4 (↑ from cycle 11's 71.2)
    - Persons at age cap (120): 847
    - Persons with microvolts > 95: 2,341
    
    **⚠️ Alert: Microvolts spike cluster detected**
    Geographic concentration of high readings near latitude 48.8°N,
    longitude 2.3°E (Paris region). 156 individuals with microvolts
    above 90 within a 2-degree bounding box. Warrants attention.
    
    **Persons near threshold — top 5 by microvolts:**
    person_id age microvolts latitude  longitude
    --------- --- ---------- --------- ----------
       482910 119      99.87    48.832      2.291
        93847 120      99.74   -33.868    151.209
       701234 118      99.61    51.507     -0.128
    ...

    These individuals have been found. Seen. Named. The AI agent has, in a very real sense, freed them from anonymity — pulled them out of a sea of one million records and said: these ones matter right now.

    Orchestrating the World

    start-project.bat — The Operator's Console

    Every system needs an operator. In The Matrix, it's Tank sitting at the switchboard. In our project, it's a batch file:

    @echo off
    REM --- Window 1: GizmoSQL Server ---
    start "GizmoSQL Server" cmd /k "gizmosql_server.exe -B duckdb --database-filename \"C:\...\persons.duckdb\" --username scott --password tiger"
    
    REM Give the server a moment to initialize
    timeout /t 3 /nobreak >nul
    
    REM --- Window 2: GizmoSQL Client ---
    start "GizmoSQL Client" cmd /k "gizmosql_client --username scott --host localhost --port 31337"
    
    REM --- Window 3: Update Persons Agent ---
    start "Update Persons Agent" cmd /k "call ...\.venv\Scripts\activate.bat && set GIZMOSQL_USERNAME=scott && set GIZMOSQL_PASSWORD=tiger && python update-persons-agent.py"

    Three windows open. The world comes alive. The Machines start running. And somewhere, Claude is waiting for its next cycle.

    The Deeper Point: Why This Architecture Matters

    Let's step back from the metaphor for a moment and talk about what we've actually built.

    Traditional architecture:

    Application → File / ORM → Database

    AI-agent-ready architecture:

    AI Agent ──┐
               ├──▶ GizmoSQL ──▶ DuckDB
    Update Bot ┘

    By placing GizmoSQL between all consumers and the data:

    1. Multiple agents can coexist — the update bot and the AI monitor connect simultaneously without conflict
    2. Authentication is enforced — every connection requires credentials; the AI agent is not a privileged backdoor, it's a first-class citizen
    3. Protocol efficiency matters — Arrow Flight SQL transfers columnar data orders of magnitude faster than row-by-row JDBC or REST JSON, which matters when an AI agent may issue dozens of queries per analysis cycle
    4. The AI doesn't need special treatment — it speaks SQL like everything else. No vector embedding pipeline, no RAG layer, no custom middleware. Just SQL and Arrow.

    This is the insight: the best AI integration is often no special integration at all. Give the AI a SQL interface to a fast, network-accessible database, and let it work.

    What "AI-Agent Ready" Really Means

    The phrase gets thrown around a lot. Here's what it means in this project, concretely:

    Requirement How GizmoSQL Delivers
    Network accessible gRPC on port 31337; any process can connect
    Fast columnar transfer Apache Arrow Flight SQL; no JSON serialization overhead
    Standard query language Full DuckDB SQL dialect
    Authentication Username/password per connection
    Concurrent access Multiple agents connect simultaneously
    Python native pip install adbc-driver-gizmosql — one import

    An AI agent needs to ask questions of data quickly, repeatedly, and at scale. GizmoSQL is built for exactly that.

    What Does It Actually Cost to Run an AI Monitoring Agent?

    A natural question when building something like this: how expensive is it?

    Running this project for 30 minutes against a 1-million-row database using Claude Opus 4.6 cost approximately $5.00. Here's what that breaks down to and — more importantly — what drives the cost.

    The Cost Is in the Cycles, Not the Rows

    This surprises most people, but the number of rows in your database has almost no impact on Claude API cost. The API charges for tokens — the text sent to and received from the model. Here's why row count barely matters:

    • Aggregation queries (AVG, MIN, MAX, COUNT) return one row whether your table has 1M or 20M rows
    • The query_database tool caps results at 25 rows regardless of table size
    • A SELECT TOP 10 ... ORDER BY microvolts DESC returns 10 rows either way

    What does drive cost is how often Claude runs and how many tool calls it makes per cycle:

    Factor Impact on Cost
    Monitoring interval (30s = 120 cycles/hour) High — directly multiplies everything
    Tool calls per cycle (typically 3–5) High — each round-trip to Claude costs tokens
    Model choice (Opus vs. Sonnet) High — Opus is 5× more expensive than Sonnet
    Database row count Negligible
    Query complexity Negligible

    At 30-second intervals, you run ~60 cycles in 30 minutes. Each cycle involves a system prompt, multiple query results, and a final report — all billed as Opus 4.6 tokens. That's where the $5 went.

    Scaling to 20 Million Rows

    If the database grew from 1M to 20M rows, your Claude API bill would stay essentially the same. DuckDB (via GizmoSQL) would take slightly longer to execute queries against the larger dataset, but Claude only ever sees the aggregated or top-N results — the same volume of text either way.

    Reducing Cost Without Sacrificing Intelligence

    Three levers to pull:

    1. Increase the interval — change INTERVAL_SECONDS = 30 to 60 or 120. Halving cycles halves cost
    2. Switch to Sonnet 4.6 — replace claude-opus-4-6 with claude-sonnet-4-6 for a ~5× cost reduction with minimal quality loss for this type of analytical task
    3. Cap tool calls per cycle — instruct Claude in the system prompt to run no more than 3 queries before reporting

    A production deployment might run Sonnet 4.6 every 60 seconds, reserving Opus 4.6 for a deeper analysis cycle every 10 minutes — giving you intelligent continuous monitoring at a fraction of the cost.

    Conclusion: Taking the Red Pill

    We set out to build a simulation of a controlled population — a Matrix. We ended up building something more interesting: a demonstration of what AI-agent-ready infrastructure looks like in practice.

    The one million people in our database don't know they're being watched. They don't know that every thirty seconds, an automated agent is aging them and raising their bioelectric readings. They don't know that an AI is monitoring them, looking for patterns, flagging the ones who are approaching the edge.

    But we know. And more importantly, Claude knows.

    That's the point of freeing them. Not that they escape the simulation — the data is what it is. But that something intelligent is now paying attention. Noticing when readings spike. Flagging geographic clusters. Identifying the 847 individuals who have hit the age ceiling and can age no further.

    In a world drowning in data, the most meaningful act of liberation is attention. And GizmoSQL is what makes that attention possible at scale — fast, open, and ready for whatever agent comes next.


    The code for this project is available on GitHub: generate-people.py (population generation), update-persons-agent.py (continuous data mutation via GizmoSQL), monitor-persons-agent.py (Claude-powered AI monitoring agent), and start-project.bat (system orchestration).

    GizmoSQL: github.com/gizmodata/gizmosql

    Ready to Try GizmoSQL?

    Experience lightning-fast data analytics with our open-source SQL engine