From e6ecaf6fb3b1e9c7d141a111f3c1e8810b46ab6a Mon Sep 17 00:00:00 2001 From: Andrew Barba Date: Mon, 7 Sep 2026 12:20:28 -0400 Subject: [PATCH] docs(eve): tutorial - make sample data the default learning path (#3116) Signed-off-by: Andrew Barba --- docs/tutorial/connect-a-warehouse.mdx | 8 +++---- docs/tutorial/first-agent.mdx | 31 ++++++++++++-------------- docs/tutorial/guard-the-spend.mdx | 6 ++--- docs/tutorial/how-it-runs.mdx | 5 ++--- docs/tutorial/meta.json | 4 ++-- docs/tutorial/query-sample-data.mdx | 26 +++++++++------------ docs/tutorial/remember-definitions.mdx | 10 ++++----- docs/tutorial/run-analysis.mdx | 16 ++++++------- docs/tutorial/ship-it.mdx | 6 ++--- docs/tutorial/team-playbooks.mdx | 20 ++++++++--------- 10 files changed, 62 insertions(+), 70 deletions(-) diff --git a/docs/tutorial/connect-a-warehouse.mdx b/docs/tutorial/connect-a-warehouse.mdx index 4bd540e04..f1637897f 100644 --- a/docs/tutorial/connect-a-warehouse.mdx +++ b/docs/tutorial/connect-a-warehouse.mdx @@ -1,9 +1,9 @@ --- -title: "Connect a Warehouse" -description: "Part 4 of the Build an Agent tutorial. Let each user connect their own warehouse over an OAuth MCP via Vercel Connect." +title: "Connect a Warehouse (Optional)" +description: "Optional follow-up to the Build an Agent tutorial. Let each user connect their own warehouse over an OAuth MCP via Vercel Connect." --- -The sample dataset got the analytics assistant running, but it's a stand-in. Now point the agent at a real warehouse and let each user connect their own by signing in through their browser. That's what a connection is for. It's an MCP server the model reaches through tools, with auth that eve drives for you. +Complete [the tutorial with sample data](./first-agent) first. When you have a warehouse with an MCP server, you can connect it to the analytics assistant and let each user sign in through their browser. That's what a connection is for. It's an MCP server the model reaches through tools, with auth that eve drives for you. [Vercel Connect is generally available](https://vercel.com/changelog/vercel-connect-secure-access-to-external-services-for-your-agents). You can complete the tutorial with the [sample dataset](./query-sample-data); Connect is only needed for the OAuth integration shown on this page. @@ -76,6 +76,6 @@ Right before each request to the MCP server, eve resolves the bearer and sends i Gate the connection behind approval (`approval: once()`) or narrow which tools the model sees (`tools.allow`) when needed. See [MCP connections](../connections/mcp). -→ Next: [Run analysis](./run-analysis) +→ Return to the tutorial: [Run analysis](./run-analysis) Learn more: [MCP connections](../connections/mcp) · [Authentication](../guides/auth-and-route-protection) diff --git a/docs/tutorial/first-agent.mdx b/docs/tutorial/first-agent.mdx index 4f50f8f99..9afbe4a17 100644 --- a/docs/tutorial/first-agent.mdx +++ b/docs/tutorial/first-agent.mdx @@ -9,12 +9,14 @@ For a bespoke learning experience, start with the [eve template gallery](https:/ - “Build an eve incident-response agent that investigates alerts using our observability tools.” - “Build an eve GitHub maintainer that triages issues and summarizes pull requests.” -If you prefer a step-by-step walkthrough, continue with this tutorial. It constructs one app end to end: a data analytics assistant. You ask in natural language, and over the next nine steps it learns to query a warehouse, run analysis in a sandbox, remember your team's metric definitions, and refuse to exceed your query budget without asking. +If you prefer a step-by-step walkthrough, continue with this tutorial. It constructs one app end to end: a data analytics assistant. You ask in natural language, and over eight steps it learns to query sample data, run analysis in a sandbox, remember your team's metric definitions, and refuse to exceed your query budget without asking. + +Step 1 gets it talking. In Step 3 you create a small local dataset that you use throughout the tutorial. You can complete the tutorial without a warehouse or Vercel Connect account. [Connect a warehouse](./connect-a-warehouse) is an optional follow-up once your agent works. ## Prerequisites - Node 24 or newer and npm. -- A model credential. The scaffold's default model goes through the [Vercel AI Gateway](../getting-started), so you need `AI_GATEWAY_API_KEY` (or `VERCEL_OIDC_TOKEN` pulled via `vercel link`). A direct provider model like `anthropic("claude-opus-4-8")` instead needs that provider's AI SDK package and key, here `@ai-sdk/anthropic` and `ANTHROPIC_API_KEY`. +- A working model credential, configured through [Getting Started](../getting-started). Keep the model and credential you already tested there. If you have not run eve before, complete [Getting Started](../getting-started) first. Without a credential, "Run the agent" below fails when the runtime tries to reach the model; the dev TUI's `/model` flow walks you through pasting a key or linking a project. @@ -25,24 +27,19 @@ npx eve@latest init analytics-assistant cd analytics-assistant ``` -The command writes the starter agent with eve's default model and built-in HTTP API -channel (`agent/channels/eve.ts`), installs dependencies, initializes Git, and -starts the development server. Stop the server before continuing with the edits -below. It does not create a Vercel project or deploy. `init` creates the +The command writes the starter agent and built-in HTTP API channel +(`agent/channels/eve.ts`), installs dependencies, initializes Git, and offers +to start the development server. Stop the server if you started it before +continuing with the edits below. It does not create a Vercel project or deploy. `init` creates the `analytics-assistant/` directory, so `cd` into it before running further commands. -## Set the model +## Keep your configured model -`agent/agent.ts` holds the model and config. Use a capable model for analysis work: - -```ts -import { defineAgent } from "eve"; - -export default defineAgent({ - model: "anthropic/claude-opus-4.8", -}); -``` +`agent/agent.ts` holds the model and config. Use the same model and credentials +that worked in Getting Started. The tutorial does not require switching providers. +If you need to configure this new project, use `/model` in the dev TUI before +sending your first question. ## Give it an analyst persona @@ -57,7 +54,7 @@ You are a senior data analyst. You answer questions about the team's data. the data, say so plainly. ``` -Instructions are identity and standing rules. On-demand procedures belong in skills (Step 7), and actions belong in tools (Step 3). See [Instructions](../instructions). +Instructions are identity and standing rules. On-demand procedures belong in skills (Step 6), and actions belong in tools (Step 3). See [Instructions](../instructions). ## Run the agent diff --git a/docs/tutorial/guard-the-spend.mdx b/docs/tutorial/guard-the-spend.mdx index 880c86d1f..2ca629c51 100644 --- a/docs/tutorial/guard-the-spend.mdx +++ b/docs/tutorial/guard-the-spend.mdx @@ -1,15 +1,15 @@ --- title: "Guard the Spend" -description: "Part 8 of the Build an Agent tutorial. Gate expensive queries with cost-based approval. The agent pauses, asks, and resumes." +description: "Part 7 of the Build an Agent tutorial. Gate expensive queries with cost-based approval. The agent pauses, asks, and resumes." --- -A single warehouse query can scan terabytes and run up the bill. So before the analytics assistant fires off an expensive scan, make it stop and check with you. The agent pauses, asks you, and resumes with your answer. That's human-in-the-loop, and you wire it up with one field on the tool. +Use the sample dataset to practice an approval before a tool runs. The example assigns a simulated scan cost to each query; it does not incur warehouse charges. Make the analytics assistant stop and check with you before a query exceeds that threshold. The agent pauses, asks you, and resumes with your answer. That's human-in-the-loop, and you wire it up with one field on the tool. `approval` runs before `execute`. Return `"user-approval"` and the turn parks on an approval request; you answer, and the run picks up from that exact step. The function gets the tool input, so you can make the decision cost-based. ## Estimate, then gate -This step keeps `run_sql` on the Step 3 sample dataset so you can demo the gate locally. With a real warehouse you'd gate the warehouse connection tool from Step 4 the same way, on a dry-run byte estimate instead of the toy heuristic below. +This step keeps `run_sql` on the Step 3 sample dataset so you can demo the gate locally. If you later [connect a warehouse](./connect-a-warehouse), use the provider's dry-run estimate to decide when approval is needed. Add a cheap estimator and gate `run_sql` on it: diff --git a/docs/tutorial/how-it-runs.mdx b/docs/tutorial/how-it-runs.mdx index 1cc9862c1..befde46fe 100644 --- a/docs/tutorial/how-it-runs.mdx +++ b/docs/tutorial/how-it-runs.mdx @@ -15,9 +15,8 @@ Each turn runs as a durable workflow, and eve saves progress at every step. Comp That's why the features in the rest of this tutorial work the way they do: -- The warehouse sign-in in Step 4 parks the turn until you authorize in the browser. A few minutes is fine. -- The metric glossary in Step 6 survives across turns. State is checkpointed at step boundaries, so it sticks. -- The spend approval in Step 8 pauses the turn on your yes/no, then picks up exactly where it left off. +- The metric glossary in Step 5 survives across turns. State is checkpointed at step boundaries, so it sticks. +- The spend approval in Step 7 pauses the turn on your yes/no, then picks up exactly where it left off. You author capabilities, including tools, instructions, channels, and skills. eve drives the model-to-tool loop and decides when a turn continues, waits, or ends. You never write that loop yourself. diff --git a/docs/tutorial/meta.json b/docs/tutorial/meta.json index fec035e61..aa5020830 100644 --- a/docs/tutorial/meta.json +++ b/docs/tutorial/meta.json @@ -5,11 +5,11 @@ "first-agent", "how-it-runs", "query-sample-data", - "connect-a-warehouse", "run-analysis", "remember-definitions", "team-playbooks", "guard-the-spend", - "ship-it" + "ship-it", + "connect-a-warehouse" ] } diff --git a/docs/tutorial/query-sample-data.mdx b/docs/tutorial/query-sample-data.mdx index bd20111e7..0d15850cf 100644 --- a/docs/tutorial/query-sample-data.mdx +++ b/docs/tutorial/query-sample-data.mdx @@ -1,6 +1,6 @@ --- 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." +description: "Part 3 of the Build an Agent tutorial. Add a run_sql tool over a local 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`. @@ -14,27 +14,21 @@ npm install sql.js npm install --save-dev @types/sql.js ``` -`sql.js` loads its WebAssembly binary from the installed package at runtime. Keep the package external so the binary remains next to its JavaScript: +`sql.js` loads its WebAssembly binary from the installed package at runtime. Keep the package external so the binary remains next to its JavaScript. Add this `build` property inside your existing `defineAgent({ ... })` in `agent/agent.ts`, keeping your configured `model` and its imports: -```ts title="agent/agent.ts" {5-7} -import { defineAgent } from "eve"; - -export default defineAgent({ - model: "anthropic/claude-opus-4.8", - build: { - externalDependencies: ["sql.js"], - }, -}); +```ts +build: { + externalDependencies: ["sql.js"], +}, ``` Restart the dev server after changing `externalDependencies`. eve reads this setting when the server starts. ## A tiny sample dataset -Store a small in-memory dataset under `agent/lib/`. Keep it tiny. This is throwaway scaffolding, not the real warehouse (that comes in Step 4). +Create `agent/lib/sample-db.ts` with the dataset below. These four orders and three customers supply the data for every remaining tutorial step. No database server, account, or connection string is needed. The dataset is recreated when the app restarts. ```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 = ` @@ -113,8 +107,10 @@ Restart the dev server with `npm run dev` and ask: 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. +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. The result is Globex with 9,900 cents ($99.00). The amounts are stored in cents, so divide by 100 when reporting dollars. eve drove the whole loop; you supplied the tool. -→ Next: [Connect a warehouse](./connect-a-warehouse) +Keep `run_sql` and the sample database for the rest of the tutorial. When you have your own data service, [Connect a warehouse](./connect-a-warehouse) explains the optional integration. + +→ Next: [Run analysis](./run-analysis) Learn more: [Tools](../tools) diff --git a/docs/tutorial/remember-definitions.mdx b/docs/tutorial/remember-definitions.mdx index 9752ed20a..9f93b258f 100644 --- a/docs/tutorial/remember-definitions.mdx +++ b/docs/tutorial/remember-definitions.mdx @@ -1,6 +1,6 @@ --- title: "Remember Definitions" -description: "Part 6 of the Build an Agent tutorial. Use defineState to remember the team's metric glossary across turns." +description: "Part 5 of the Build an Agent tutorial. Use defineState to remember the team's metric glossary across turns." --- Every team has house definitions for the analytics assistant. "Active" means a purchase in the last 30 days, revenue is net of refunds, a "week" starts Monday. Re-explaining all of that on every turn is a waste. State gives the agent a place to keep them. @@ -55,15 +55,15 @@ export default defineTool({ ## See it persist ```text -> For us, an active customer is one with a purchase in the last 30 days. +> For this dataset, a high-value customer has at least $50 in total orders. Remember that. - → calls define_metric("active customer", "purchase in the last 30 days") + → calls define_metric("high-value customer", "at least $50 in total orders") -> How many active customers do we have? +> How many high-value customers do we have? → recalls the definition, writes the matching SQL, answers ``` -The second turn is a separate turn in the same session, yet the definition is still there. State checkpoints at step boundaries, so it's the same durability from [Step 2](./how-it-runs), now applied to your own data. +The sample data has two high-value customers: Acme ($57) and Globex ($99). This definition works with the same fixed dataset whenever you run the tutorial. The second turn is a separate turn in the same session, yet the definition is still there. State checkpoints at step boundaries, so it's the same durability from [Step 2](./how-it-runs), now applied to your own data. State is scoped to a session and isolated per agent, so a subagent starts with fresh state and never sees the parent's. Need to reset something each turn? Call `update(() => fresh)` in a lifecycle hook. More in [State](../concepts/state). diff --git a/docs/tutorial/run-analysis.mdx b/docs/tutorial/run-analysis.mdx index 734269ea9..7bf833f41 100644 --- a/docs/tutorial/run-analysis.mdx +++ b/docs/tutorial/run-analysis.mdx @@ -1,6 +1,6 @@ --- title: "Run Analysis" -description: "Part 5 of the Build an Agent tutorial. Seed the warehouse schema into the sandbox workspace, then compute and chart beyond SQL." +description: "Part 4 of the Build an Agent tutorial. Seed the sample database schema into the sandbox workspace, then compute and chart beyond SQL." --- SQL tells the analytics assistant the numbers, but a cohort curve, a forecast, or a chart needs real computation. That's what the sandbox is for. It's an isolated bash environment with a `/workspace` filesystem, and every agent gets exactly one. @@ -9,7 +9,7 @@ This takes two pieces. First seed reference files the model can read, then compu ## Seed the schema into the workspace -Mount the warehouse schema into the sandbox so the model isn't guessing at table shapes. Seeding uses the folder sandbox layout, where anything under `agent/sandbox/workspace/` lands in the live `/workspace` cwd at session bootstrap. +Mount the sample database schema into the sandbox so the model isn't guessing at table shapes. Seeding uses the folder sandbox layout, where anything under `agent/sandbox/workspace/` lands in the live `/workspace` cwd at session bootstrap. ```text agent/sandbox/ @@ -21,8 +21,8 @@ agent/sandbox/ ```sql -- agent/sandbox/workspace/schema.sql -- Reference only: table shapes the analyst can read before writing queries. -CREATE TABLE orders (id INT, customer_id INT, amount_cents INT, created_at DATE); -CREATE TABLE customers (id INT, name TEXT, plan TEXT, signed_up_at DATE); +CREATE TABLE orders (id INTEGER, customer_id INTEGER, amount_cents INTEGER, created_at TEXT); +CREATE TABLE customers (id INTEGER, name TEXT, plan TEXT); ``` Top-level workspace entries get advertised to the model automatically, so it knows `schema.sql` is there to read. A `workspace/` folder seeds your files without requiring a sandbox definition. Add one next to install the chart dependencies. @@ -118,17 +118,17 @@ The tool uses the Python environment installed in bootstrap. `matplotlib.use('Ag `sandbox.run()` returns an exit code even when the command fails. Check `exitCode` before returning a chart path. Throwing an error gives the model the failed command's output, including a missing Python executable or matplotlib import, so it can report the failure instead of claiming a chart exists. -Now ask for something past plain SQL. If you skipped Step 4, this still works against the Step 3 sample dataset: +Ask for a chart using the sample data from Step 3: ```text -Plot total order revenue per customer. +Plot daily order revenue in dollars for May 2026. ``` -The model queries for the numbers (the warehouse from Step 4, or the sample dataset if you skipped it), checks `schema.sql` to get the grain right, then calls `chart_series` to render the PNG in `/workspace`. +The model queries `run_sql` for daily totals, divides `amount_cents` by 100, and passes date/value points to `chart_series`. The sample data has four dates with revenue of $42, $15, $99, and $8. The chart uses the same data you queried in Step 3. ## Secrets stay out of the sandbox -The sandbox has no `process.env` and no access to your app's secrets. Your warehouse token lives in the app runtime, and firewall brokering is the only path it takes to the warehouse host. It never enters the sandbox process. +The sandbox has no `process.env` and no access to your app's secrets. The `run_sql` tool and sample database run in your app. You pass only the query results to the chart tool; the sandbox does not need database credentials. The local backend runs the sandbox on your laptop during `eve dev`; on Vercel it runs on Vercel Sandbox. Lifecycle, backends, and network policy are in [Sandbox](../sandbox). diff --git a/docs/tutorial/ship-it.mdx b/docs/tutorial/ship-it.mdx index 604d345d9..4bfa6a143 100644 --- a/docs/tutorial/ship-it.mdx +++ b/docs/tutorial/ship-it.mdx @@ -1,6 +1,6 @@ --- title: "Ship It" -description: "Part 9 of the Build an Agent tutorial. Put a web dashboard on the agent with useEveAgent, replace placeholderAuth, and deploy to Vercel." +description: "Part 8 of the Build an Agent tutorial. Put a web dashboard on the agent with useEveAgent, replace placeholderAuth, and deploy to Vercel." --- The analytics assistant runs in the TUI. Now add a web dashboard and deploy a private, single-user version on Vercel. This example protects the sample app with a username and password. It does not require another authentication service. @@ -125,10 +125,9 @@ The private assistant queries the sample data, runs analysis in a sandbox, chart ## What you learned -Across the nine steps you built and shipped one agent, and along the way you used: +Across the eight steps you built and shipped one agent, and along the way you used: - **Tools** to give the model typed actions (`run_sql`, `chart_series`, `define_metric`). -- **Connections** to reach a warehouse over an OAuth MCP, with per-user tokens eve resolves for you. - **The sandbox** to compute and chart beyond SQL in an isolated `/workspace`. - **State** (`defineState`) to remember the team's glossary across turns. - **Dynamic skills** (`defineDynamic`) to load the right team playbook per caller. @@ -138,6 +137,7 @@ Across the nine steps you built and shipped one agent, and along the way you use ## Next steps +- [Connect a warehouse](./connect-a-warehouse) when you have a data service to use in place of the sample dataset. - [MCP connections](../connections/mcp) for tool allowlists and per-connection approval. - [Sandbox](../sandbox) for backends, lifecycle, and network policy. - [Dynamic capabilities](../guides/dynamic-capabilities) for schema-derived dynamic tools, a read-only analyst subagent, and model-authored report workflows on this same example. diff --git a/docs/tutorial/team-playbooks.mdx b/docs/tutorial/team-playbooks.mdx index 126fa3160..366eec95d 100644 --- a/docs/tutorial/team-playbooks.mdx +++ b/docs/tutorial/team-playbooks.mdx @@ -1,9 +1,9 @@ --- title: "Team Playbooks" -description: "Part 7 of the Build an Agent tutorial. Load the caller's team playbook with a dynamic skill keyed on the principal." +description: "Part 6 of the Build an Agent tutorial. Load the caller's team playbook with a dynamic skill keyed on the principal." --- -The glossary from [Step 6](./remember-definitions) is per-session. But your teams have standing analysis conventions for the analytics assistant (Growth runs cohort retention a particular way, Finance has its own revenue-recognition rules), and those shouldn't bleed across tenants. Load the right team's playbook for whoever is asking. +The glossary from [Step 5](./remember-definitions) is per-session. But your teams have standing analysis conventions for the analytics assistant (Growth groups revenue by date, Finance reconciles customer totals), and those shouldn't bleed across tenants. Load the right team's playbook for whoever is asking. A skill is an on-demand procedure. The model pulls it in with `load_skill` only when a turn needs it. Make it dynamic and the skill gets decided at runtime instead of baked in. A `defineDynamic` resolver reads the session and returns a `defineSkill` (or nothing). Here you key that decision on the caller's identity in `ctx.session.auth`. @@ -18,14 +18,14 @@ const PLAYBOOKS: Record = { growth: { title: "Growth analysis playbook", markdown: - "When analyzing retention, use weekly cohorts anchored on signup week, " + - "report curves not point estimates, and exclude trial accounts.", + "When analyzing sample orders, group revenue by order date, report dollars, " + + "and compare customers by plan.", }, finance: { title: "Finance analysis playbook", markdown: - "Report revenue net of refunds and recognized over the subscription term. " + - "Always reconcile against the close-of-month snapshot.", + "Reconcile total order revenue against the daily and customer totals. " + + "Report dollars and label this as gross order revenue; the dataset has no refunds.", }, }; @@ -52,14 +52,14 @@ export default defineDynamic({ ## See it route -The team comes from authenticated claims, which the auth layer stamps on in [Step 9](./ship-it). Until then `ctx.session.auth.current` has no `team`, so the resolver returns `null` and no playbook loads. To verify routing now, stamp a team in local dev. Add a dev-only entry to `agent/channels/eve.ts` ahead of `localDev()`, and remove it before Step 9 wires real auth: +The team comes from authenticated claims, which the auth layer stamps on in [Step 8](./ship-it). Until then `ctx.session.auth.current` has no `team`, so the resolver returns `null` and no playbook loads. To verify routing now, stamp a team in local dev. Add a dev-only entry to `agent/channels/eve.ts` ahead of `localDev()`, and remove it before Step 8 wires real auth: ```ts title="agent/channels/eve.ts" import { eveChannel } from "eve/channels/eve"; import { localDev, placeholderAuth, vercelOidc, type AuthFn } from "eve/channels/auth"; -// Dev-only: stamp a team so Step 7's playbook resolver has something to read. -// Remove before Step 9. +// Dev-only: stamp a team so Step 6's playbook resolver has something to read. +// Remove before Step 8. const devTeam: AuthFn = () => process.env.NODE_ENV === "production" ? null @@ -75,7 +75,7 @@ export default eveChannel({ }); ``` -Restart with `npm run dev` and ask "what's our 8-week retention?" The model sees the Growth playbook fits, calls `load_skill`, and applies the Growth conventions to that turn (weekly cohorts, no trial accounts). Switch `team` to `"finance"`, restart, and the same question routes to Finance's playbook instead. +Restart with `npm run dev` and ask "Summarize May 2026 revenue using our team's playbook." The model calls `load_skill`, queries the sample data, and applies Growth's daily and plan groupings. Switch `team` to `"finance"`, restart, and start a new session before asking again. The resolver runs at `session.started`, so changing the team does not replace the playbook in an existing session. The same question in the new session uses Finance's reconciliation rules. Because the team comes from authenticated claims, not from the message, one tenant can't borrow another's playbook through the message content.