UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

82 lines (63 loc) • 3.19 kB
--- title: "Query Sample Data" description: "Part 3 of the Build an Agent tutorial. Add a run_sql tool over the bundled sample dataset and watch the tool loop." --- The analytics assistant can hold a conversation, but it can't see a single row of data. Give it a tool. A tool is the action primitive. Typed input goes in, your code runs, structured output comes back. The name the model sees is the filename, so `agent/tools/run_sql.ts` becomes the tool `run_sql`. ## A tiny sample dataset To make the first query work without setup, bundle a small in-memory dataset under `agent/lib/`. Keep it tiny. This is throwaway scaffolding, not the real warehouse (that comes in Step 4). ```ts title="agent/lib/sample-db.ts" // A toy SQLite-in-memory stand-in. Swap for your real warehouse in Step 4. import initSqlJs from "sql.js"; const SEED = ` CREATE TABLE orders (id INTEGER, customer_id INTEGER, amount_cents INTEGER, created_at TEXT); INSERT INTO orders VALUES (1, 10, 4200, '2026-05-01'), (2, 10, 1500, '2026-05-03'), (3, 11, 9900, '2026-05-04'), (4, 12, 800, '2026-05-06'); CREATE TABLE customers (id INTEGER, name TEXT, plan TEXT); INSERT INTO customers VALUES (10, 'Acme', 'pro'), (11, 'Globex', 'enterprise'), (12, 'Initech', 'free'); `; let dbPromise: Promise<import("sql.js").Database> | null = null; async function db() { dbPromise ??= initSqlJs().then((SQL) => { const database = new SQL.Database(); database.run(SEED); return database; }); return dbPromise; } export async function runReadOnlySql(sql: string) { const database = await db(); const [result] = database.exec(sql); if (!result) return { columns: [], rows: [] as unknown[][] }; return { columns: result.columns, rows: result.values }; } ``` ## Define the run_sql tool ```ts title="agent/tools/run_sql.ts" import { defineTool } from "eve/tools"; import { z } from "zod"; import { runReadOnlySql } from "../lib/sample-db"; export default defineTool({ description: "Run a read-only SQL query against the analytics tables (orders, customers) " + "and return the columns and rows.", inputSchema: z.object({ sql: z.string().describe("A single read-only SELECT statement."), }), async execute({ sql }) { const { columns, rows } = await runReadOnlySql(sql); // Bound the output so a wide query can't flood the model's context. return { columns, rows: rows.slice(0, 500), truncated: rows.length > 500 }; }, }); ``` Tools run in your app runtime with full `process.env`, not in the sandbox. The `inputSchema` both validates the call and types the `input` you get inside `execute`. For output bounding, `toModelOutput`, and authorization, see [Tools](../tools). ## Watch the tool loop Restart the dev server with `npm run dev` and ask: ```text Which customer has spent the most, and how much? ``` Watch the loop play out in the TUI. The model emits a `run_sql` call, eve runs your `execute`, and the rows come back as a tool result. The model reads them and answers with a real number. eve drove the whole loop. All you supplied was the tool. → Next: [Connect a warehouse](./connect-a-warehouse) Learn more: [Tools](../tools)