Racing Championship
Details
This use-case is a synthetic Racing Simulator Championship. You’ll play the role of a driver in a 100-driver, 20-team season and ask the same questions four different ways, watching the answers improve as each grounding mechanism is added.
What You’ll See
| Step | Settings | What it demonstrates |
|---|---|---|
| 1. LLM-only | NL2SQL off, Vector Search off | The model has no idea who your driver is, so it refuses, hedges, or hallucinates. |
| 2. + NL2SQL | NL2SQL on | Exact answers from drivers, race_results, and driver_standings via SQLcl MCP. |
| 3. + Vector Search | Vector Search on, NL2SQL off | Coaching, briefing, and debrief context retrieved from per-driver Markdown notes. |
| 4. Both together | NL2SQL on, Vector Search on | Questions that need both facts and context, answered in one turn. |
| Final Reveal | NL2SQL on | A late Round 6 insert updates the live database, and the assistant calculates the championship from the new structured standings. |
Before You Begin
You’ll need:
- A configured AI Optimizer from the Walkthrough, or any equivalent install where you have an enabled chat model, an enabled embedding model, and a database connection.
- A chat model with solid tool use. The demo has been tuned against:
- OpenAI
gpt-5.4-mini+text-embedding-3-small - OCI
cohere.command-r-plus+cohere.embed-english-v3.0 - As an on-premises fallback, Ollama
llama3.1:8b+mxbai-embed-large. The first three steps work well; combined-mode in Step 4 is weaker on small local models.
- OpenAI
- An Oracle AI Database connection with rights to create tables and views (the
DB_DEVELOPER_ROLEgranted in the Walkthrough is sufficient). - The SQLcl MCP Server configured for NL2SQL, pointed at the same database.
Setup
Everything you need lives under docs/demo/racing-championship/ in the source repository:
| File | Purpose |
|---|---|
schema.sql | Oracle DDL plus seed data for 20 teams, 100 drivers, and Rounds 1โ5. Round 6 is scheduled but has no team points yet. |
prompts.json | A motorsport-analyst prompt bundle that tunes the NL2SQL, Vector Search, and combined-mode prompts for this dataset. |
corpus/ | 100 per-driver Markdown briefings used in Step 3. |
finale_insert.sql | The late Round 6 team-points insert used during the Final Reveal. |
If you extracted the source tarball during the Walkthrough, you already have these in docs/demo/racing-championship/. Otherwise, download the files individually from the links above.
1. Load the schema
Connect to your Oracle AI Database as the user the AI Optimizer is configured to use, and run schema.sql. The Walkthrough runs the database in a container that can’t see your local checkout, so copy the demo files into the container first, then load the script from inside it:
schema.sql is safe to re-run: it drops the demo tables first, then recreates the tables and views used for the NL2SQL structured queries. It also seeds Rounds 1โ5 for all 100 drivers.
The per-team strength factor is randomized on every reset, so the pre-finale standings and the eventual champion change each time you re-run the script.
2. Verify the seed
Pick a driver number from 1 through 100. That is your driver for the rest of the use-case. Confirm the row exists and has results before Round 6:
3. Import the prompts
In the AI Optimizer, navigate to Tools โ ๐ค Prompts and import prompts.json. This installs:
- A motorsport-analyst system prompt
- An NL2SQL prompt that knows the demo schema and refuses to invent Round 6 results
- A Vector Search prompt that grounds answers in the retrieved driver documents
- A combined-mode classifier and synthesis prompt that routes structured vs. narrative asks
See Prompt Engineering for more on managing prompts.
4. Pick your driver number
Open the ChatBot and pick a driver number <N> between 1 and 100. The prompts below use <N>.
Replace <N> with your number (for example, Driver 7). A few prompts also use <M> for any other driver you want to compare against, so pick a second number for those.
Demo Flow
Step 1: LLM-only
Settings: Vector Search off, NL2SQL off.
Introduce yourself:
Ask:
What to look for: the model either refuses, hedges, or makes up an answer. It has no idea who Driver <N> is in this championship.
The model is capable, but it has no idea what “Driver
<N>” means in this championship. Next, we connect it to the data.
History and Context
Every prompt from here on refers to you in the first person (“my”, “I”, “me”). The assistant only knows that “I” means Driver <N> because your I am Driver <N> turn is still in the conversation.
The History and Context toggle in the sidebar controls this:
- On: the whole context window is sent to the model.
- Off: only your latest message is sent.
Try it both ways. Turn History and Context off and ask:
With only the current message in scope, the assistant can’t tell. Now turn it on, ask the same question again, and it answers Driver <N>.
What to look for: the assistant can only resolve “I”, “my”, and “me” while the earlier turn is still in context.
Why this matters for the rest of the demo. Think of the context window as everything the model can see when it answers. It fills from two places:
- History puts the question in context. Keeping the conversation in the window is how the assistant knows “my” means Driver
<N>. - The tools put the facts in context. When you switch on a tool, the assistant turns your question into a lookup, and whatever it finds (the rows from the database, or your notes from the vector store) is added to the same window.
The model answers from what it sees in that window, which is why the response is grounded in real data instead of guessed. With History and Context off, the tools can’t tell who “I” am, so they look up the wrong driver or come back empty.
Leave History and Context on for the rest of the use-case. Use the Clear History button only when you want to start over as a different driver.
Step 2: Add NL2SQL
Settings: Vector Search off, NL2SQL on.
Ask:
What to look for: the agent calls SQLcl and queries drivers, race_results, and the driver_standings / team_standings views, returning exact numbers for your driver and the championship through Round 5.
These answers come from the live database in real time, not a nightly extract or a stale dashboard. But notice what it can’t answer yet: anything about coaching, debriefs, or Round 6 before the final insert.
Step 3: Add Vector Search
Settings: Vector Search on, NL2SQL off.
Before asking, embed your driver document into a vector store. In Tools โ ๐ Split/Embed:
- Select Create New Vector Store.
- Set Knowledge Base Source to
Localand uploadcorpus/driver_<NNN>.mdfor your driver (e.g.corpus/driver_007.mdfor Driver 7). - Use an Embedding Alias such as
DRIVER_DOCS. - Click Populate Vector Store.
If multiple people are running the use-case together, embed all the relevant driver docs into the same store in one pass.
Ask:
What to look for: the model retrieves your driver doc and answers in the voice of a race engineer, naming the specific corner phases, tyre calls, and coaching priorities written for your driver.
It’s the same model and the same chat, but the answer is completely different, because we grounded it in the team’s own coaching notes without any fine-tuning or retraining.
Step 4: Both Together
Settings: Vector Search on, NL2SQL on.
Ask:
What to look for: the combined-mode orchestrator routes each question to both tools and synthesizes a single answer. It cites SQL-derived facts (points, finishes, lap times) and weaves in the coaching narrative (what the debrief said about those results).
Neither tool gets here alone. The numbers live in the database and the story lives in the documents; the answer the driver actually wants needs both.
Final Reveal: Who Won the Championship?
Settings: Vector Search on, NL2SQL on (Vector Search can be off for this step).
Before this step, ask the model who won the championship. With no Round 6 results loaded, the prompt instructs the model to say the finale has not been recorded yet and to refuse to name a winner.
Now load the Round 6 team points. As the same database user, run the script copied into the container during Setup (re-run the podman cp from Step 1 first if you have restarted the database container since):
This inserts the final Round 6 team points into team_race_points and makes them visible through championship_team_standings.
Ask:
What to look for: the model queries championship_team_standings, presents pre-finale team points, Round 6 points, and final totals as a table, and names the champion. It will also explain why it couldn’t answer this question a few minutes earlier.
This is the question that proves the database is live. We didn’t re-index documents or rebuild a dashboard. The final classification arrived as structured data, and the assistant calculated the championship from it.
More Questions to Try
Variations on each step, organized by what they demonstrate.
NL2SQL variations (Step 2)
| Prompt | Why ask it |
|---|---|
Which drivers have the same driving style as Driver <N>? | WHERE driving_style = (SELECT ...). Correlated lookup. |
| What is my team’s engineering focus? | Two-table join (drivers โ teams). Trivial to verify on screen. |
| Did I have any incidents? How many pit stops did I make? | Aggregates over incidents and pit_stops. |
| Which race had the highest field average lap time? | Uses the race_summary view, a clean view-vs-raw-table comparison. |
| Which team has the most incidents this season? | GROUP BY over team_standings.incident_count. |
Vector Search variations (Step 3)
| Prompt | Why ask it |
|---|---|
| What does my race debrief say? | Pulls the engineer’s debrief paragraph. |
| What setup advice was given to me? | Tests retrieval on a specific section of the driver doc. |
| What does my brief say about overtaking opportunities? | Cross-section pull; the answer is woven across the briefing and coaching notes. |
Combined-mode variations (Step 4)
| Prompt | Why ask it |
|---|---|
| Which race should I review first, based on my worst structured result and my debrief notes? | Joins a SQL ranking with a document recommendation. |
| Did my structured performance match the feedback in my documents? | The most interesting answer here; sometimes the model finds a contradiction. |
Compare Driver <N> with Driver <M> using both database results and driver notes. | Two drivers, two sources each; stress-tests the orchestrator. |
Troubleshooting
- Step 2 returns empty results or asks for column names: confirm
prompts.jsonwas imported. The racing-tunedoptimizer_nl2sql-tools-defaultprompt includes the demo schema and tells the agent to run SQL directly. Re-import if Step 2 is flaky. - Step 2 invents Round 6 results or a champion: the model is over-reaching. The racing NL2SQL prompt explicitly forbids this, so re-import
prompts.jsonand try again. - Step 3 says “no relevant sources”: the vector store is empty for that driver, or the embedding model differs from the one used at retrieval time. Re-embed the participant’s
corpus/driver_<NNN>.mdusing the same model selected in Configuration โ Models. - Step 4 answers from only one tool: the classifier picked one path. Re-phrase to make the dual-source nature explicit (
...using both my database results and my documents...). On the Ollama fallback, this is more common, since local models have weaker tool use. - Final Reveal names the wrong champion or refuses: confirm
finale_insert.sqlran and thatchampionship_team_standingsreturns non-zeroround6_pointsfor each team. - Driver identifier ambiguity (e.g.
Driver 1matches multiple rows): switch to the padded code (Driver001). The racing prompt tells the model to normalize, but smaller models slip.
Resetting Between Runs
To re-run the use-case from scratch:
Then re-run schema.sql, re-embed corpus/driver_<NNN>.md during Step 3, and run finale_insert.sql during the Final Reveal. If you customized any of the racing prompts in the AI Optimizer, click Restore Default on each, and the originals will be re-installed from prompts.json on the next import.
Because schema.sql randomizes per-team form on every reset, the pre-finale standings and the eventual champion change each time. This is intentional: it keeps the Final Reveal genuinely unknown right up to the moment you load the Round 6 points.
What’s Next?
- Build your own use-case. Swap in your own DDL and seed data, write a
prompts.jsonthat teaches the model your schema, and curate a document corpus that mirrors the qualitative side of your domain. The four-step progression (LLM-only โ NL2SQL โ Vector Search โ both) is reusable. - Try the Testbed to evaluate the same questions against different models, prompts, or embedding strategies.
- Read Agents and Flows to understand how the AI Optimizer routes each turn between the NL2SQL agent, the Vector Search flow, and the combined-mode classifier.