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 tigerBreaking 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 requiredLEAST(age + 1, 120)— age climbs, but it cannot break the hard cap of 120. Entropy has limitsLEAST(..., 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 → DatabaseAI-agent-ready architecture:
AI Agent ──┐
├──▶ GizmoSQL ──▶ DuckDB
Update Bot ┘By placing GizmoSQL between all consumers and the data:
- Multiple agents can coexist — the update bot and the AI monitor connect simultaneously without conflict
- Authentication is enforced — every connection requires credentials; the AI agent is not a privileged backdoor, it's a first-class citizen
- 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
- 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_databasetool caps results at 25 rows regardless of table size - A
SELECT TOP 10 ... ORDER BY microvolts DESCreturns 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:
- Increase the interval — change
INTERVAL_SECONDS = 30to60or120. Halving cycles halves cost - Switch to Sonnet 4.6 — replace
claude-opus-4-6withclaude-sonnet-4-6for a ~5× cost reduction with minimal quality loss for this type of analytical task - 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