00 Setup
Get your environment ready — OpenRouter, ClickHouse, and LangFuse connected from the start.
Outcome
A cloned repo with a Python virtualenv installed, a .env populated with credentials
for OpenRouter, ClickHouse Cloud, and LangFuse Cloud, an arena database in ClickHouse
Cloud seeded with synthetic e-commerce data, and the local dashboard running at
http://localhost:5174 — Leaderboard tab empty for now, that's expected until
Module 01.
Why
One agent core, reused by two callers (the benchmark harness and the serving API); it asks a
model via OpenRouter and reads data through ClickHouse's read-only v_* views, and
every run is traced to LangFuse — wired in from this first module.
AgentArena is one NL→SQL agent core (agents/) reused by two callers — the benchmark
harness (eval/harness.py) and the live serving API (serving/api.py) — so the demo
and the benchmark share exactly the same code path: same prompt templates, same model
client, same read-only SQL sandbox. That's what makes the benchmark's numbers
trustworthy predictors of production behavior, rather than a separate "eval harness"
that quietly diverges from what actually ships.
Notice that LangFuse is one of the three accounts you set up in this very first module — before you've picked a model, before you've run a single question. That's deliberate: LangFuse isn't something you bolt on once the chatbot works, it's the tool that runs the contest in Module 01, measures the winner's quality in Module 02, drives the improvement loop in Module 03, and watches production in Module 04 — one project, one set of traces and datasets, start to finish. Every module after this one builds on that one code path and that one LangFuse project, so getting the three accounts and the seeded database right here is what makes the rest of the workshop just work.
Concepts — under the hood
Three pillars, three jobs, wired together from this first module:
- OpenRouter — one OpenAI-compatible API in front of every model family in this
workshop (Anthropic, OpenAI, Google, DeepSeek, Qwen, Z.ai).
Instead of juggling six provider SDKs and six sets of credentials, the agent core
(
agents/) speaks one client to reach all six models inconfig.yaml. That's what makes a fair, apples-to-apples contest in Module 01 possible: every model is onemodel=string away, behind the same endpoint, the same request shape. - ClickHouse — the warehouse, doing two jobs at once. It holds the business data
(the synthetic e-commerce tables you're about to seed) behind the
v_*views, and it holds the results of every benchmark run (eval_runs). The agent is only ever allowed toSELECTfrom thev_*views — never the raw tables, never write — both because that's a stable, documented read-only contract for the agent to reason about, and becauseagents/sqlguard.pyenforces it as a SELECT-only sandbox: it parses the generated SQL and rejects anything that isn't a singleSELECT/WITH…SELECTstatement, and blocks a denylist of write/DDL keywords (INSERT,UPDATE,DELETE,DROP,ALTER,SYSTEM, …) even inside an otherwise-valid statement. - LangFuse — the eval and observability store. It's connected now, before you've picked a model or asked a single question, because it isn't a bolt-on for later — it's the tool that grades the contest in Module 01, lets you drill into quality in Module 02, and watches production in Module 04. One project, one continuous trail of traces and datasets, from the first model choice onward.
📸 Screenshot: the LangFuse Cloud project's Settings → API Keys page, showing where
the public/secret key pair you paste into .env comes from — capture from the live UI.
Pitfall — placeholder OPENROUTER_API_KEY. .env.example ships OPENROUTER_API_KEY=sk-or-...
as a template, not a real key. If you forget to overwrite it, every model call in
Module 01 fails with an OpenRouter auth error, not a ClickHouse or LangFuse error — so
check .env first if that's what you see.
Pitfall — wrong ClickHouse host or region. CLICKHOUSE_CLOUD_HOST must be the exact
host from your service's connection details (region-specific, e.g.
abc123.us-east-1.aws.clickhouse.cloud), not the generic clickhouse.cloud domain.
A mismatched host fails fast with a DNS/connection error during scripts/arena.sh up —
that's the signature to recognize.
Pitfall — ARENA_RO_PASSWORD mismatch. scripts/arena.sh up creates the arena_ro
read-only user using whatever ARENA_RO_PASSWORD is set to at that moment. If you
change the value in .env afterward without re-running setup (or dropping and
recreating the user), the agent's read-only connection starts failing to authenticate
even though .env "looks right."
Goal
Three sets of credentials in .env, a seeded arena database with the v_* views the
agent will query, and the local dashboard reachable in a browser.
Step 1 — Create three accounts
You need API credentials from three services before you touch the terminal:
| Service | What you need | Where to get it | .env var(s) |
|---|---|---|---|
| OpenRouter | An OPENROUTER_API_KEY | openrouter.ai → Keys. OpenRouter fronts every model family used in this workshop (Anthropic, OpenAI, Google, DeepSeek, Qwen, Z.ai) behind one OpenAI-compatible API. | OPENROUTER_API_KEY, OPENROUTER_BASE_URL |
| LangFuse Cloud | A project's public + secret key | cloud.langfuse.com → create a project → Settings → API Keys. | LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_BASE_URL |
| ClickHouse Cloud | Host, admin user, admin password | clickhouse.com/cloud → create a service → connection details. | CLICKHOUSE_CLOUD_HOST, CLICKHOUSE_CLOUD_USER, CLICKHOUSE_CLOUD_PASSWORD |
Keep all three handy — you'll paste them into .env in a moment.
Step 2 — Clone the repo
AgentArena lives inside the ClickHouse_Demos monorepo, under workshops/agent_arena.
Clone the whole repo, then move into that subdirectory — every command from here on
assumes you're standing in it:
git clone https://github.com/ClickHouse/ClickHouse_Demos.git
cd ClickHouse_Demos/workshops/agent_arenaStep 3 — Create a virtualenv and install dependencies
python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txtStep 4 — Configure .env
Copy the example file:
cp .env.example .env.env
Fill in the values from Step 1 — every value below is empty or a placeholder in
.env.example:
# ClickHouse Cloud (data + eval_runs)
export CLICKHOUSE_CLOUD_HOST=xxx.clickhouse.cloud
export CLICKHOUSE_CLOUD_USER=default
export CLICKHOUSE_CLOUD_PASSWORD=
export CLICKHOUSE_CLOUD_DATABASE=arena
export ARENA_RO_PASSWORD=
# OpenRouter (LLM provider)
export OPENROUTER_API_KEY=sk-or-...
export OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
# LangFuse Cloud (eval store + tracing)
export LANGFUSE_BASE_URL=https://us.cloud.langfuse.com
export LANGFUSE_PUBLIC_KEY=pk-lf-...
export LANGFUSE_SECRET_KEY=sk-lf-...What each block is for:
CLICKHOUSE_CLOUD_*— your ClickHouse Cloud service's admin credentials. Setup uses the admin user once, to create thearenadatabase and a dedicated read-only user (arena_ro) that the agent queries through for the rest of the workshop.ARENA_RO_PASSWORD— pick any password; it becomesarena_ro's password when the read-only user is created.OPENROUTER_*— your OpenRouter key and its base URL. Every model inconfig.yamlis addressed through this one endpoint.LANGFUSE_*— your LangFuse Cloud project's host and keys. This is where every trace, score, and dataset in the workshop lives, from the first Arena run in Module 01 through production in Module 04.
Step 5 — Seed ClickHouse with synthetic e-commerce data
This is the ClickHouse-only path — it creates the arena database, the arena_ro
read-only user, generates synthetic e-commerce data (customers, products, orders, order
items, events) directly into ClickHouse, and builds the v_* views the agent queries
against:
source .env && scripts/arena.sh upEvery agent, prompt, and piece of golden SQL in this workshop queries the
v_customers, v_products, v_orders, v_order_items, and v_events views — never
the raw tables.
scripts/arena.sh up also starts the local dashboard API and web UI. When
it finishes, open http://localhost:5174 — the Leaderboard tab will be empty until
you run the contest in Module 01.
What a healthy run looks like. scripts/arena.sh up prints, in order:
ClickHouse: database + read-only user + results tables— thearenadatabase, thearena_roread-only user, and the emptyeval_runstable get created.Seeding ClickHouse directly + views + schema context— oneclickhouse: inserted <N> into <table>line per table (customers,products,orders,order_items,events), thendone, then thev_*views and the schema context the agent reads get built.Starting dashboard API (:8000) + web UI (:5174)— two[ready]lines. If either says[NOT up], the port is probably already taken; check the log path it prints (.run/dashboard-api.logor.run/web.log).
📸 Screenshot: the terminal output of a successful scripts/arena.sh up, showing
the per-table insert counts and both servers reporting [ready] — capture from a live
run.
📸 Screenshot: http://localhost:5174 on first load — the dashboard shell with an
empty Leaderboard tab — capture from the live UI.
You can re-check any of this later with scripts/arena.sh status, which prints
whether the servers are up and the row count of each v_* view.
How to verify you are done
.envhas real values (not placeholders) forCLICKHOUSE_CLOUD_*,OPENROUTER_*, andLANGFUSE_*.scripts/arena.sh upfinished without error.http://localhost:5174loads in a browser, showing a Leaderboard tab (empty is correct for now).
Exercise — break and diagnose a connection
Learn to recognize a broken .env value by its failure signature, on purpose, while
the stakes are zero:
- Open
.envand change one character inARENA_RO_PASSWORD(or temporarily comment it out). - Re-run
source .env && scripts/arena.sh up. It should still get through the ClickHouse admin steps (those use the admin credentials), but watch for where the read-only path — anything that connects asarena_ro— starts complaining. - Read the error message closely: is it an authentication error, a "user does not exist" error, or silence followed by a timeout? Note which one you got.
- Restore the correct
ARENA_RO_PASSWORDand re-runscripts/arena.sh up. Confirm it completes cleanly again.
This is the same diagnostic instinct you'll want later when a teammate's setup "isn't working" — matching the error text to which of the three services is misconfigured, instead of re-checking everything.
Wrap-up
You have a seeded ClickHouse database, credentials for all three services — including LangFuse, connected before any model has been chosen — and the local dashboard running. Everything after this module reuses this same environment and this same LangFuse project; no further setup steps.
End state
Environment ready. Continue to 01 Select the base model to run the contest against this data.