AI Engineering Programme Start here Weeks Playground Open Codespace
‹ All weeks

Week 2

Context and retrieval, as the agent's main tool

17 of 34 checks pass ›route startOn GitHub

Do this next

Build this week's exercise, then run the gate.

make check WEEK=2

This week at a glance

BuildMetrics, the heading chunker, the version filter, hybrid search and search_docs. Then the context experiment and six configurations, on a real model.
Filesapp/retrieval/metrics.py, app/retrieval/chunkers.py, app/retrieval/versions.py, app/retrieval/hybrid.py, app/tools/search.py
Measuremake evidence WEEK=2 on a real model, into evidence/week-2.json
Recorddecisions/week-2.md from the evidence, ai-use/week-2.yml, reflections/week-2.md
GuidesWorked example A: retrieval · Where the vectors live · Glossary

checks fail 17 of 34 checks pass

The 17 that do not
  • a version 3 question returns no version 2 materialtest_week2.py
  • bm25 a long document does not win by being longtest_week2.py
  • bm25 rare terms count for moretest_week2.py
  • bm25 repeats count for less each timetest_week2.py
  • bm25 scores only documents that share a query termtest_week2.py
  • evaluate averages per querytest_week2.py
  • heading chunker keeps heading with its bodytest_week2.py
  • hybrid search finds an exact setting nametest_week2.py
  • hybrid search keeps the version filter and ktest_week2.py
  • pages with no version are returned for every versiontest_week2.py
  • precision recall mrr on a toy exampletest_week2.py
  • precision uses k not the number of hitstest_week2.py
  • reciprocal rank fusion merges by rank not scoretest_week2.py
  • search docs is described and needs a versiontest_week2.py
  • search docs refuses malformed calls before searchingtest_week2.py
  • search docs returns only that versions pages with what a citation needstest_week2.py
  • the filter runs before ranking not aftertest_week2.py

Red is the normal state of a week you have not built yet. Each name is a test in the file beside it.

The 17 that do
  • context experiment runs and the wrong version costs accuracytest_week2.py
  • fixed cuts sentences in halftest_week2.py
  • index then search finds the right documenttest_week2.py
  • paragraph chunker keeps a table row wholetest_week2.py
  • reindex replaces rather than duplicatestest_week2.py
  • search respects k and strategytest_week2.py
  • section chunker says where each chunk sitstest_week2.py
  • select and compress drops unrelated passagestest_week2.py
  • select and compress keeps the passage that answerstest_week2.py
  • select and compress respects the budgettest_week2.py
  • select and compress trims inside a passagetest_week2.py
  • sentence chunker never cuts mid sentencetest_week2.py
  • similarity alone returns the wrong versiontest_week2.py
  • the version is read from the filenametest_week2.py
  • unknown search mode is 400test_week2.py
  • unknown strategy is refusedtest_week2.py
  • unknown strategy on index is 400test_week2.py

Hidden evaluation

Not shown on the public page. The programme runs it on each week you merge; your mentor sees the totals on the brief and brings them to you.

Decision and AI use

Written last: make evidence WEEK=2, then decisions/week-2.md and ai-use/week-2.yml.

Measuredthis week

Degrade and repair

modehitsmean tokens intotal $mean ms
top311/305590.022030
stuffed19/301912285.7457049
repaired11/302080.009360

Dothe week

The exercise is in your repository, where you can run it:

code weeks/2/README.md
make next

Read it on GitHub · what the gate verifies · the tests

Whythis week is here

You arrived with

A model behind an interface: it survives the provider misbehaving, never pays twice, records what it cost and swaps provider from a setting. Output against a schema, and the agent's first two tools, get_customer and get_ticket.

This week leaves

Hybrid search, keyword and meaning together, restricted to the customer's version and called by the agent as a tool, with every choice in it made by a number.

Which is why the next week can

Week 3 grounds answers in retrieved text. Grounding a bad retrieval just makes the wrong answer better written.

Readthe concept

The same model gives a different answer when you give it different context. Choosing the context is the job, and retrieval is how you choose it.

Read this once (about 20 minutes). Write the sentence in your own words in reflections/week-2.md, Q1, before you touch code.

Two ideas, one week

Context engineering is deciding what the model sees on each call. Everything you put in the window competes for attention with everything else. More context is not more knowledge; past a point it is more noise, more latency and more money for the same answer, and sometimes a worse one. The named failure modes:

Failure What it looks like
Lost in the middle A fact placed mid-context is missed more often than one at the start or end
Poisoning A wrong or hostile passage in the context steers the answer
Distraction Plausible but irrelevant passages pull the answer off the question
Clash Two passages disagree and the model picks one silently

The disciplines that follow: select (only what this call needs), compress (summarise what persists), isolate (keep tasks in separate contexts), cache (reuse a stable prefix so you do not pay to re-read it), and be explicit about memory: what persists between turns and sessions, and what must not.

flowchart LR
  Q[question] --> S["select
retrieve only what this call needs"] S --> W["context window
an attention budget, not a bucket"] W --> M[model] subgraph failures [what goes wrong when the window is treated as storage] L[lost in the middle] ~~~ P[poisoning] ~~~ D[distraction] ~~~ X[clash] end W -.- failures

Retrieval is how you select. Cut the corpus into chunks, embed them, find the nearest to the question, hand those to the model. Every choice in that sentence is a measurable decision:

The same clause, cut four ways. Only one strategy keeps "3. Fees." with its fee.

flowchart TB
  subgraph fixed [fixed, 500 chars]
    f1["…sixty (60) days written no"] --- f2["tice. 3. Fees. Client will pay Provider 6,500 GBP…"]
  end
  subgraph sentence [sentence, ~400 chars]
    s1["…written notice. 3. Fees."] --- s2["Client will pay Provider 6,500 GBP per month…"]
  end
  subgraph paragraph [paragraph, ~800 chars]
    p1["3. Fees. Client will pay Provider 6,500 GBP per month, invoiced monthly in advance, payable within 14 days of invoice."]
  end
  subgraph heading [heading-aware, per section]
    h1["## Fees
Client will pay Provider 6,500 GBP…"] end

Where the measurement sits: before the model, so a wrong answer can be traced to the step that produced it.

flowchart LR
  Q[question] --> E1[embed]
  D[("chunks + vectors
one index per strategy")] --> C E1 --> C[cosine over every chunk] C --> K[top k hits] K --> MEAS{{"measure here
P@k · R@k · MRR · hit rate"}} K --> M[model, Week 3]

The same model, different context

The cleanest way to see this week's sentence is to hold everything still except the context. scripts/context_experiment.py asks the same twenty questions of the same model with the same prompt, five times each: with no passages, with the wrong version's page, with the right page, with the right page and forty irrelevant ones, and with the right page buried in the middle of eighty. On the right page the answers are mostly right. On the wrong version's page they are confidently wrong, and they cite it. With too much, they cost ten times as much, and on smaller models they start to slip. The model did not change. The answers did. Your decision on how much context to send is read off that table.

Retrieval as a tool

From this week retrieval is not only an endpoint: it is search_docs, the agent's second tool, called with the version get_customer returned. The tool's schema makes the version required, so the model cannot leave it out and fall back into the version trap. That is the pattern every later week repeats: the boundary lives in the schema and the code, not in the prompt.

What this looks like in the service

Idea Where it lives What you do this week
Chunking app/retrieval/chunkers.py Build the heading-aware one; measure it against paragraph and section-aware
Hybrid search app/retrieval/hybrid.py, SEARCH_MODE Build BM25 and reciprocal rank fusion; measure dense against hybrid
Embedding app/retrieval/embed.py Switch EMBED_PROVIDER and see what changes
Store and search app/retrieval/store.py, /index, /search Read it; it is the interface a vector database would sit behind
Metrics app/retrieval/metrics.py Build precision@k, recall@k, MRR, hit rate
Attention budget explore/w2_02_lost_in_the_middle.py Bury one fact, watch cost and accuracy move
The context itself app/retrieval/context.py, scripts/context_experiment.py Same model, five contexts: measure what the context alone does
Retrieval as a tool app/tools/search.py Design search_docs, version required

What you should be able to say by Friday

Read more

Checked September 2026.

Optionalself-test

Answers are kept in this browser only, under hub.quiz in local storage. They are never sent anywhere: not to the repository, not to the progress export, not to your mentor, and they have no effect on your route. Clearing your browser data clears them. The explanations are the point, not the score.

‹ Week 1Week 3 ›