← Back to all products

Data Analyst Agent Kit

$147

A natural-language-to-SQL analyst agent with an AST-based read-only guard, row and cost caps, Vega-Lite chart output, a DuckDB demo warehouse, and execution-accuracy evals.

3 filespythonv1.0.0
MarkdownPythonJSONYAMLTOMLLLM

Product Preview

Try the interactive reader and demo tools below, or get the full product with all content unlocked.

Interactive Reader (Free Preview) Download Free Sample

File Structure 3 files

data-analyst-agent-kit/ ├── free_sample ├── interactive └── preview

Documentation Preview README excerpt

Data Analyst Agent Kit

A natural-language-to-SQL analyst agent that is safe to point at a real warehouse: the LLM only proposes SQL;

a sqlglot AST guard decides what runs. Read-only enforcement, row cap, cost cap, Vega-Lite chart specs, a seeded

DuckDB demo warehouse, and execution-accuracy evals that compare result sets against golden queries.

It vendors the agent_forge core runtime (src/agent_forge, from Agent Forge Core 1.0.0), so the kit is

standalone. Everything runs offline against the deterministic MockLLM: tests need no keys and no network.

Quickstart


python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest                                   # 29 tests, offline
python examples/quickstart.py            # ask -> guarded SQL -> rows -> Vega-Lite spec
python examples/run_evals.py             # execution accuracy vs evals/golden.yaml
python -m data_analyst.seed demo.duckdb  # write the demo warehouse to a file (seed 42)

Real model: implement the 1-method LLM protocol or use HTTPChatLLM (see examples/real_llm.py).

Real warehouse: pass any duckdb connection to Warehouse(con, GuardConfig(...)).

The guard (`data_analyst/guard.py`)

Exactly one statement, root must be SELECT/UNION (CTEs fine). Rejected anywhere in the AST: INSERT/UPDATE/DELETE/

MERGE/CREATE/DROP/ALTER/COPY/ATTACH/PRAGMA/SET..., data-modifying CTEs, SELECT INTO, file/network table

functions (read_csv, 'file.parquet'...), tables outside the allowlist. The row cap wraps the query in an outer

LIMIT, so a user LIMIT 999999 cannot raise it. The cost cap uses a deterministic proxy (rows scanned, times

table size for any join without ON/USING). See guide/GUARD.md.

Layout

src/data_analyst (seed, guard, warehouse, agent, charts, evals, mock) - src/agent_forge (vendored runtime) -

tests/ - evals/ - guide/ (ARCHITECTURE, GUARD, EVALS) - examples/.

What it is not

Not a security boundary on its own: also connect with a read-only database role/file. The cost proxy is an

estimate, not the DuckDB planner. The mock LLM only knows the demo questions. The vendored runtime's API/HITL

features are included but not required by the analyst.

License: MIT with a no-resale-as-template clause (see LICENSE).

Code Sample .py preview

src/agent_forge/api.py """FastAPI service: per-tenant API keys, blocking + SSE runs, human approval. POST /v1/runs run to completion (or until approval needed) POST /v1/runs/stream same, as Server-Sent Events GET /v1/runs/{id} status / final answer / pending approval POST /v1/runs/{id}/approval approve or reject a paused tool call POST /v1/admin/tenants create tenant + first API key (admin token) GET /healthz liveness (no auth) Auth: ``X-API-Key: afk_...`` (or ``Authorization: Bearer afk_...``). Keys are stored only as HMAC hashes. Runs are tenant-scoped: another tenant's run id returns 404, never 403 (no existence oracle). """ from __future__ import annotations import hmac import json from typing import Any, Iterator from fastapi import Depends, FastAPI, Header, HTTPException, Request from fastapi.responses import JSONResponse, StreamingResponse from pydantic import BaseModel, Field from .config import Settings from .errors import (AgentForgeError, ApprovalNotPending, AuthError, BudgetExceeded, GuardrailViolation, NotFound, PromptInjectionBlocked, ToolNotAllowed) from .runtime import AgentRuntime from .store import Tenant _STATUS = {AuthError: 401, NotFound: 404, ApprovalNotPending: 409, BudgetExceeded: 402, PromptInjectionBlocked: 422, ToolNotAllowed: 403, GuardrailViolation: 422} class RunRequest(BaseModel): task: str = Field(min_length=1, max_length=20000) class ApprovalRequest(BaseModel): approved: bool note: str = Field(default="", max_length=2000) class TenantRequest(BaseModel): id: str = Field(pattern=r"^[a-z0-9][a-z0-9_-]{1,62}$") name: str = "" allowed_tools: list[str] = [] approval_tools: list[str] = [] token_budget: int = Field(default=0, ge=0) usd_budget: float = Field(default=0.0, ge=0) # ... 88 more lines ...
Buy Now — $147 Back to Products