Worked examples — Agentic frameworks (LangChain, LlamaIndex)
This page is a drill. The parent note built the agent loop and the two frameworks. Here we push that loop through every kind of situation it can hit — the clean cases, the broken cases, and the sneaky exam twists — so that when your own agent behaves strangely, you already recognise the shape of the problem.
Before we start, one plain-language recap so no term is used unexplained:
The scenario matrix
Every agent bug or exam question is one of these cells. The worked examples below each carry a tag like (Cell A2) so you can see the coverage is complete.
| # | Cell class | What makes it tricky | Worked example |
|---|---|---|---|
| A1 | Happy path multi-tool | tools called in the right order | Ex 1 |
| A2 | Wrong tool / bad description | LM picks the wrong tool because the description misled it | Ex 2 |
| B1 | Zero tools available | agent must answer from its own knowledge | Ex 3 |
| B2 | Degenerate: empty tool result | tool returns "" or "not found" | Ex 4 |
| C1 | Loop limit / infinite loop | agent never emits Final Answer |
Ex 5 |
| C2 | Token overflow | history + docs exceed context window | Ex 6 |
| D1 | Retrieval sign case: query matches nothing | top- similarity all low | Ex 7 |
| D2 | Retrieval word problem | pick chunks + fit under , real numbers | Ex 8 |
| E1 | Exam twist: LangChain vs LlamaIndex choice | which framework, and why | Ex 9 |
We measure retrieval quality with cosine similarity. Why cosine and not plain distance? Because we care about direction of meaning, not vector length — two documents about "Tokyo" should count as similar even if one is longer.
Before the formula, two pieces of notation must be earned.
(1) The dot product . For two vectors and , the symbol "" (read "dot") means: multiply the matching components and add them up, Why this operation? It is the natural way to ask "do these two arrows point the same way?". If both point right, the products are positive and add up large. If one points left while the other points right, a product goes negative and the total shrinks. So the dot product is a single number that grows when directions agree and drops (even below zero) when they disagree — exactly the "shared meaning" signal we want.
(2) The length . The symbol (two bars around a vector) means the length of the vector — its distance from the origin. It is called the L² norm, computed by Pythagoras: for a 2-D vector , So is just "how long is this arrow?". A unit vector has . With both symbols defined, cosine similarity is:
Example 1 — Happy path, two tools in order (Cell A1)
Forecast: guess the count of LM calls before reading — is it 2, 3, or 4?
- LM call #1 → Wikipedia.
Why this step? The population is a fact the LM does not reliably know, so it must reason then act:
Action: Wikipedia. - Observation injected: "14,000,000". Why? The tool result is pasted back so the LM can continue — this is ReAct in motion.
- LM call #2 → Calculator, input
14000000 * 2. Why? The LM now has the number and picks the arithmetic tool. See 6.2.05-Tool-calling-and-function-calling-APIs. - Observation:
28000000. LM call #3 →Final Answer: 28,000,000. Why a third call? The LM must see the product before it can phrase the answer.
The figure below traces this exact chain — read it left-to-right, top-to-bottom: two violet LM-call boxes alternate with orange Observation boxes, and the magenta arrow into the bottom-right box is the third and final LM call.

Verify: . LM calls = 3 (one per turn: search, compute, finish). Units: people × dimensionless = people. ✓
Example 2 — Wrong tool from a bad description (Cell A2)
Forecast: whose fault is this — the LM or the human?
First, a one-line definition so this drill stands alone: the agent here uses a description-driven ReAct setup — the LM is handed no worked examples, only each tool's name and description, and must choose a tool purely by matching the query against those descriptions. (In LangChain this preset is literally named ZERO_SHOT_REACT_DESCRIPTION: zero-shot = no examples, ReAct = reason-then-act, description = choose by description.)
- The LM reads tool descriptions to choose. Why this step? Since it has no examples, the description text is the only signal for tool choice.
- "Useful for questions" matches a math question no better than a search does, so the LM defaults to the more general-sounding tool. Why? Ambiguity → the model guesses; it picks Wikipedia because "questions" reads like "look things up".
- Fix: description → "Do math. Input: an expression like
47 * 89." Why? Now the tool advertises the shape of input it wants, which the LM can pattern-match.
Verify: correct answer . The bug is human-caused (description), confirmed by the fix restoring correct routing. ✓
Example 3 — Zero tools available (Cell B1)
Forecast: does it crash, loop, or answer?
- The parser looks for an
Action:line. Why? Every loop iteration first asks "did the LM request a tool?" - With no tools, a well-built agent's prompt lists zero actions, so the LM has nothing to call and emits
Final Answerdirectly. Why? It falls back to its own arithmetic ability — no external step needed. - Answer returned in one turn:
4.
Verify: ; LM calls = 1 (no tool round-trip). This is the degenerate loop — the while True body runs once and returns. ✓
Example 4 — Empty and "not found" tool results (Cell B2)
Forecast: should the agent retry, or give up gracefully? Do the two cases differ?
- Case (i) — Observation injected is
""(empty). Why? We always inject something, even emptiness, so the LM knows the tool ran. - The LM sees an empty observation and reasons "no data found". Why? An empty observation is itself information — "this path is dead".
- Case (ii) — Observation injected is
"No results found."(non-empty, but a failure message). Why the distinction? An empty string is silence; a "not found" string is a spoken failure. The parser treats both as valid observations, but the second is riskier: a naive agent may mistake the sentence "No results found." for real content and try to summarise it. - Both cases resolve the same way: the LM must recognise a no-data signal — whether blank or a failure sentence — and emit
Final Answer: I could not find the population of Zzyzx.Why? Honest failure beats a hallucinated number; a good agent prompt explicitly instructs "if the observation is empty or says no results, stop and report you could not find it."
Verify: both cases terminate in 2 LM calls (search → finish); no numeric claim, sanity check is termination, which holds for both empty and "not found" inputs. ✓
Example 5 — Loop limit prevents an infinite loop (Cell C1)
Forecast: 5, 6, or infinite?
- Each iteration = one LM call + one tool call.
Why? That is the body of the
whileloop from the parent's pseudocode. - Only the LM calls count toward our "LM-call tally". Why this clarification? Cost and rate-limits are driven by the language model, which is the expensive component; a tool call (running local Python or a free API) is cheap. So when we count "LM calls" we count only the violet boxes, one per iteration — not the tool executions. Across 5 iterations that is 5 LM calls, whatever the tools cost.
- A counter increments every iteration; at iteration 5 the guard
count >= max_iterationsfires. Why this step? Without a ceiling, a stubborn LM burns tokens and money forever. - The executor returns
"Agent stopped due to iteration limit."Why? Graceful stop beats a runaway process.
The figure below draws the loop as a violet circle the agent spins around; the numbers 1–5 mark each pass, and the orange ABORT box is where the counter guard cuts the circle open.

Verify: with max_iterations = 5, LM calls made (calls 1..5), then it aborts. Guard fires when count == 5. ✓
Example 6 — Token overflow, and how memory rescues it (Cell C2)
Forecast: which memory type survives longer?
Let = the number of completed conversation turns (one user message + one agent reply counts as one turn). We track how many tokens turns consume.
- Buffer memory keeps every turn. Usable budget for turns tokens. Why subtract? The system prompt is fixed overhead we can't touch.
- Turns fit while , i.e. , so turn 15 is the first that overflows. Why the division? Each turn is a fixed 500-token block; we count how many blocks of size 500 fit inside 7200.
- Summary memory replaces all history with 600 tokens + the current 500-token turn. Usage . Why does this help — and stay bounded forever? After each turn the memory re-summarises the previous summary plus the new turn back into one 600-token summary. So the summary is a fixed-size bucket that gets rewritten, not appended to. At turn 100 the tokens in use are still , exactly as at turn 15 — the 600 never grows because summarisation is applied repeatedly, capping the total.
Verify: (turn 14 fits); (turn 15 overflows) → first overflow at turn 15. Summary usage for every turn → no overflow. ✓
Example 7 — Retrieval where the query matches nothing (Cell D1)
Forecast: is any chunk actually relevant?
- Compute each cosine. All four vectors are unit length (, check: ), so cosine = dot product.
Why the shortcut? When both lengths in the denominator are , the denominator is and only the dot product remains.
- Top-1 is with similarity . Why? Largest cosine = smallest angle to the query direction.
- Threshold check. , so we do retrieve . Had all scores been (like alone), we'd retrieve nothing and the agent should answer "no relevant context found". Why apply a threshold at all? Cosine will always hand back some top-1 chunk, even when everything is irrelevant — the ranking never says "nothing here". A minimum-score cutoff is the guardrail that turns "least-bad match" into "actually good enough", so we stuff the prompt only with chunks that genuinely share the query's direction and skip the noise.
The figure plots all four arrows from the origin: the magenta query points straight up, leans close to it (small angle → high cosine), while and lie flat on the horizontal axis (90° away → cosine 0).

Verify: cosines ; max → retrieve . ✓
Example 8 — Retrieval word problem: fit chunks under the window (Cell D2)
Forecast: does the 0.62 chunk survive?
- Budget for chunks tokens. Why? Scaffolding is non-negotiable overhead.
- How many 450-token chunks fit? . Why floor? You cannot include a fraction of a chunk; a half-chunk is useless.
- Apply the 0.50 relevance threshold first. Scores : {0.91, 0.88, 0.85, 0.80, 0.62, 0.60} — that is 6 chunks. The 0.10 chunk is below threshold, so it is noise and discarded. Why threshold before packing? Adding low-similarity chunks just dilutes the prompt with irrelevant text; the 0.50 cutoff (chosen in Ex 7) filters those out.
- Now compare the two limits. Token capacity allows 13 chunks; relevance allows only 6. Since , all 6 relevant chunks fit and tokens are not the limiter. Definition — the binding constraint. When several limits apply at once (here: "fit in tokens" and "be relevant enough"), the binding constraint is the one that runs out first and therefore actually decides the answer. The other limit is slack. Here relevance (6) runs out before tokens (13), so relevance is binding.
- Included chunks: 6; lowest included score = 0.60.
Verify: token-capacity; chunks with score = 6; included ; lowest included score . Binding constraint = relevance, not tokens. ✓
Example 9 — Exam twist: pick the framework and justify (Cell E1)
Forecast: are these the same framework?
- Task (a) is data-centric: the hard part is getting the right chunks out of a huge corpus. Why? Massive corpus + retrieval quality = LlamaIndex's home turf (indices, query engines). → LlamaIndex.
- Task (b) is orchestration-centric: the hard part is sequencing tool calls and holding memory. Why? Multiple tools + conversation state = LangChain's agent + memory abstractions. → LangChain.
- Nuance: in practice you often use LlamaIndex as a retriever tool inside a LangChain agent — they compose, they don't compete.
Recall Self-test
Which framework is "data-centric"? ::: LlamaIndex Which framework's core loop is Reasoning + Acting over tools? ::: LangChain The cosine similarity of two unit vectors equals their … ::: dot product
Coverage check
Every matrix cell A1–E1 now has exactly one worked example, spanning happy paths, wrong-tool and empty/not-found degeneracies, loop and token limits, zero-match and word-problem retrieval, and the framework-choice exam twist. If your agent misbehaves, match its symptom to a cell above.