L2 · 13 · Level 2 Labs
Twelve hands-on labs, one or more per Level 2 page. Each has a goal, steps and a clear success criterion. Together they build the skills and the pieces you need for the capstone. Expect 10–15 hours in total.
Setup
- Scala via Coursier (
cs setup):scala-cli,sbt, JDK 21. - Python 3.12+ with
uv:uv add httpx pydantic pytest hypothesis. - A local model runner (page 11): Ollama with a small chat model and
nomic-embed-text, or LM Studio. - Optional: an approved hosted model via your company gateway (page 03).
- A git repo
ai-labs-l2/. Commit after every lab.
| # | Lab | Page | Time |
|---|---|---|---|
| 1 | Ticket triage chain with golden tests | 01 | 60 min |
| 2 | Schema-enforced extraction with repair loop | 02 | 60 min |
| 3 | Harden the LLM client | 03 | 45 min |
| 4 | Add tools to the Store Assistant | 04 | 60 min |
| 5 | Measure semantic search (hit@3) | 05 | 45 min |
| 6 | Policy RAG with abstention | 06 | 60 min |
| 7 | Spec → plan → tests → agent | 07 | 90 min |
| 8 | A skill that passes its trigger set | 08 | 60 min |
| 9 | Metals MCP + one external server | 09 | 45 min |
| 10 | Kill the mutants | 10 | 60 min |
| 11 | Local model bake-off | 11 | 45 min |
| 12 | Text-to-SQL with guard and evaluation | 12 | 60 min |
Lab 1 · Ticket triage chain with golden tests
- Write 30 realistic support tickets (pickup, returns, payment, product, other), with 5 deliberately ambiguous.
- Build the chain from page 01: classify (label only) → count in code → summarise the top issue.
- Store the classify prompt in
prompts/classify.v1.md. Writegolden.jsonlwith expected labels. - Add self-consistency (5 votes). Route agreement below 0.6 to "needs human".
Success: golden accuracy ≥ 90% on the clear tickets, and the ambiguous ones are flagged for a human.
Lab 2 · Schema-enforced extraction with repair loop
- Use
ReturnExtraction.scalaorreturn_extraction.py(page 02) with a real model andresponse_format. - Write 10 messy customer emails: missing order ID, two items, an unknown product, a non-English sentence.
- Log how many needed a retry and which validation errors occurred.
Success: no invented order IDs (null when missing), unknown SKUs rejected, and every result either valid or escalated.
Lab 3 · Harden the LLM client
- Run
LlmClient.scala(page 03) against a local model. - Stop the server mid-run, then use a wrong model name, then set
max_tokensto 5. Observe the retries, the non-retried 404, and thefinish_reason=lengthwarning. - Add a concurrency limit (at most 4 in-flight calls) and a total-deadline parameter.
Success: each failure type produces the correct behaviour and a clear log line, with no secrets logged.
Lab 4 · Add tools to the Store Assistant
- Extend
StoreAssistant.scala(page 04) withget_product_info(sku)andpropose_transfer(sku, from, to, qty). The latter only returns a proposal. - Validate arguments in code: known SKUs and stores, quantity 1–10.
- Run
--livewith a local model. Ask a multi-step question that needs two tools.
Success: correct tool sequence, invalid arguments return errors the model explains, and no transfer is "performed".
Lab 5 · Measure semantic search
- Grow the catalogue in
ProductSearch.scalato 30 products. - Write 20 queries with the expected SKU. Compute hit@1 and hit@3 for the toy embedder, a real embedder, and hybrid (RRF).
Success: a small results table in your notes showing which approach wins and why.
Lab 6 · Policy RAG with abstention
- Write 3 short policy documents (returns, pickup, price matching) with headings.
- Use
PolicyRag.scalawith real embeddings and a real model. Check citations in code. - Golden set: 15 answerable and 5 unanswerable questions. Measure hit@3, citation validity and abstention.
Success: all 5 unanswerable questions abstain, and no answer cites a non-existent source.
Lab 7 · Spec → plan → tests → agent
- Write a spec (page 07 template) for a small feature in a sample Scala service, e.g. "orders held over 7 days are cancelled".
- Get a plan, edit it, write or approve tests first, and commit them.
- Let the agent implement with "don't modify tests". Do the break-it-three-ways check.
Success: all tests green, test files unchanged in the implementation commit, and all 3 deliberate bugs caught.
Lab 8 · A skill that passes its trigger set
- Build
scala-munit-suite(page 08) withcheck_suite.py. - Write 12 trigger prompts (8 should trigger, 4 near-misses). Run each in a fresh session.
- Iterate on the description until at least 11/12 behave correctly.
Lab 9 · Metals MCP + one external server
- Enable Metals MCP. Ask the agent to inspect a type and compile a module through the tools.
- Add one external server (e.g. Playwright MCP, pinned version) via
.vscode/mcp.json, with any secret supplied throughinputs. - Run the trust checklist for that server and note the result.
Lab 10 · Kill the mutants
- Run the ScalaCheck suite from page 10. Change rounding to
DOWN. Does a property fail? If not, add one that does. - Run Stryker4s (Scala) or mutmut (Python) on a small module, or do a manual mutation test with your agent.
- Ask AI to write tests that kill the survivors, deriving expectations from the spec.
Success: mutation score improves, and you can explain what each new test protects.
Lab 11 · Local model bake-off
- Install two local models of different sizes (page 11).
- Run
bench.py, then run labs 1, 2 and 4 against each. - Record speed, pass rates and tool-calling reliability.
Lab 12 · Text-to-SQL with guard and evaluation
- Extend
sql_eval.py(page 12) with 10 golden questions on the sample schema. - Generate SQL with a model using the strong prompt (schema + semantics + assumptions).
- Run every candidate through
SqlGuardlogic (port it to Python or call the Scala version), then evaluate.
Success: execution accuracy reported, and every failure explained (wrong filter, join, definition, or a guard rejection).
When you finish, go to the capstone. Most of its pieces come from these labs.