Exercises — Agentic frameworks (LangChain, LlamaIndex)
This page is a self-test ladder for the parent topic. Work each problem before opening the solution. Levels climb from L1 Recognition (do you know the words?) up to L5 Mastery (can you design and reason quantitatively?).
Before we start, two words recur on every page below.
The figure below is the mental model for the whole page. Describe it in words: it is a single horizontal bar, the full width of which equals one context window of tokens (the x-axis is tokens, counted left to right from to ). The bar is split into four side-by-side coloured blocks whose widths add up exactly to :
- a cyan block on the far left labelled "System prompt = 300 tok",
- an amber block labelled "Memory = 480 tok",
- a wide white block labelled "Retrieved docs (free) = 2816 tok",
- a cyan block on the far right labelled "Reply reserve = 500 tok".
The amber arrow points at the white block with the note "docs get the leftovers". Concretely: . The white "free" width is whatever is left after the three fixed pieces — this is precisely the calculation in Exercise 2.1, and Exercises 2.2, 5.2 are further arithmetic on this same bar.

Level 1 — Recognition
Exercise 1.1 (L1)
Match each LangChain component to its one-line job:
| Component | |
|---|---|
| Chain | |
| Tool | |
| Memory | |
| Retriever |
Jobs: (a) stores conversation state across turns; (b) a deterministic fixed sequence of LM calls; (c) a function the agent may call; (d) an interface to a vector database / search index.
Recall Solution 1.1
- Chain → (b) deterministic fixed sequence. No reasoning about order — the steps are hard-coded.
- Tool → (c) a callable function wrapped with a description + argument schema.
- Memory → (a) the state store persisting across turns.
- Retriever → (d) the interface to a vector DB / search index (see 5.3.02-Vector-databases).
Why these and not each other? A Chain is fixed; an Agent (not listed here) is the thing that chooses. Don't confuse the recipe with the cook.
Exercise 1.2 (L1)
True or false: LlamaIndex is orchestration-centric and LangChain is data-centric.
Recall Solution 1.2
False — it is the reverse.
- LlamaIndex is data-centric: how do I get the right context out of 10,000 pages?
- LangChain is orchestration-centric: how do I chain LM calls, tools and memory?
Mnemonic: Llama = Library (of documents). Chain = Chaining calls.
Exercise 1.3 (L1)
In the ReAct agent loop, name the four repeating text labels the LM and framework pass back and forth.
Recall Solution 1.3
Thought: → Action: → Action Input: → Observation:, looping until Final Answer:.
- Thought/Action/Action Input come from the LM (it writes text).
- Observation is injected by the framework after running the tool.
See 6.2.04-ReAct-(Reasoning-and-Acting) for the origin of this pattern.
Level 2 — Application
Exercise 2.1 (L2)
A ConversationBufferMemory keeps the last 6 messages. Each message averages 80 tokens. Your system prompt is 300 tokens and you reserve 500 tokens for the model's reply. If the context window is tokens, how many tokens are left for retrieved documents?
Recall Solution 2.1
This is the white slice of the s01 bar. Subtract everything fixed from :
Why subtract the reply reservation? The model's output also lives in the window. If you forget it, generation gets truncated mid-sentence.
Exercise 2.2 (L2)
You retrieve the top-k document chunks (each chunk = 400 tokens, as defined above). Using the 2816-token budget from 2.1, what is the largest you can safely stuff into the prompt?
Recall Solution 2.2
We floor (round down) because a partial 8th chunk (the 8th block would need tokens –, past the free) would overflow the window. This is exactly the "stuff" step of a RetrievalQA chain — the free budget sets your . In the s01 bar, this is asking how many 400-token blocks fit in the white slice.
Exercise 2.3 (L2)
Write the description field for a Tool that converts currency. Explain in one sentence why the wording matters.
Recall Solution 2.3
Tool(
name="CurrencyConverter",
func=convert,
description="Convert an amount between currencies. "
"Input: 'AMOUNT FROM_CODE TO_CODE', e.g. '100 USD EUR'."
)Why the wording matters: the LM never sees the Python code — it decides whether and how to call the tool purely from the description string (see 6.2.05-Tool-calling-and-function-calling-APIs). Vague description → the tool is never chosen or is called with malformed input.
Level 3 — Analysis
Exercise 3.1 (L3)
An agent answers "What is the population of Tokyo? Multiply it by 2." Each LM call costs $0.002 and each tool call is free but adds one extra LM call (to read the observation). Trace the loop and compute the total LM-call cost.
Recall Solution 3.1
Trace the ReAct loop (each LM turn = one call):
| Turn | LM produces | Tool run |
|---|---|---|
| 1 | Action: Wikipedia("Tokyo population") | Wikipedia → "14 million" |
| 2 | Action: Calculator("14000000 * 2") | Calculator → "28000000" |
| 3 | Final Answer: "28 million" | — |
That is 3 LM calls:
Why 3 and not 2? After the last tool's observation, the LM must be called again to compose the Final Answer. Each observation costs you one more round trip — this is why chatty agents get expensive.
Exercise 3.2 (L3)
ConversationSummaryMemory replaces old messages with an LM summary. Over a 20-message chat, buffer memory would carry tokens. The summary compresses all-but-the-last-4 messages into a fixed 120-token summary. Compute memory tokens for summary memory, and the token savings.
Recall Solution 3.2
Keep the last 4 verbatim, summarise the other 16:
The tradeoff: the 16 summarised messages lose detail — an exact quote from message 3 may vanish. You trade fidelity for budget. That is why VectorStoreMemory exists: it retrieves the exact old message only when relevant.
Exercise 3.3 (L3)
A naive RAG pipeline returns the top-5 chunks (400 tokens each, as defined above) by cosine similarity. Two of the five chunks are near-duplicates (similarity 0.98 to each other). Explain, referencing the context budget, why this hurts answer quality, and name one fix.
Recall Solution 3.3
Why it hurts: duplicate chunks consume two slots of your finite budget for one idea. With only slots, you have effectively wasted 20% of your context on redundant information, crowding out a different relevant chunk that never made the cut. Fix: apply maximal marginal relevance (MMR) — re-rank so each new chunk must be relevant to the query and dissimilar to already-chosen chunks. This is standard in 5.3.03-Retrieval-Augmented-Generation-(RAG) retrievers.
Level 4 — Synthesis
Exercise 4.1 (L4)
Design decision: You are building an assistant that (a) always does the same 3 steps — embed question, search DB, answer — for FAQ queries, but (b) sometimes needs to decide whether to also call a live weather API. Should you use a Chain, an Agent, or both? Justify architecturally.
Recall Solution 4.1
Both — a Chain wrapped as a Tool inside an Agent.
- The fixed FAQ recipe (embed → search → answer) has a known order ⇒ a
RetrievalQAChain. Deterministic, cheap, no "which step?" LM calls. - The conditional weather branch requires a run-time decision ⇒ that decision belongs to an Agent.
- Architecture: expose the FAQ chain and the weather API each as a Tool; let a ReAct Agent choose between them.
Principle: fixed sub-recipes → Chains; run-time branching → Agent. Push determinism as deep as possible to save LM calls (recall Exercise 3.1: each decision costs a round trip).
Exercise 4.2 (L4)
Given the loop pseudocode below, insert a step counter guard so a stuck agent (one that loops calling tools forever) terminates. State the failure mode it prevents.
while True:
out = llm(prompt + history)
parsed = parse(out)
if isinstance(parsed, AgentFinish):
return parsed.output
result = tools.run(parsed.tool, parsed.tool_input)
history.append((parsed, result))Recall Solution 4.2
MAX_STEPS = 8
steps = 0
while steps < MAX_STEPS:
out = llm(prompt + history)
parsed = parse(out)
if isinstance(parsed, AgentFinish):
return parsed.output
result = tools.run(parsed.tool, parsed.tool_input)
history.append((parsed, result))
steps += 1
return "Stopped: max steps reached." # graceful fallbackFailure mode prevented: an infinite reasoning loop — the LM keeps emitting Action: and never emits Final Answer:, e.g. re-querying the same tool. Each iteration is a paid LM call, so without a cap this is an unbounded bill and a hung request. LangChain's real max_iterations parameter does exactly this.
Exercise 4.3 (L4)
You must answer questions over 10,000 pages where the corpus is far larger than one context window (, i.e. the total document size in tokens hugely exceeds the context window defined in the intro), and the answer often depends on combining facts from several documents. A single vector top-k search misses cross-document reasoning. Sketch a two-stage index design and, treating = number of sub-questions, give the exact LM-call count per query.
Recall Solution 4.3
Symbols: is the set of all documents, its total token size, and is the context window (the fixed token budget from the intro). just means "the library is far too big to paste into one prompt." Let be the number of sub-questions the query is split into.
Two-stage design:
- Router / sub-question stage — 1 LM call: one LM call reads the query and emits all sub-questions at once (a single generation listing them). This is 1 call regardless of .
- Retrieve stage — 0 LM calls: run one vector search per sub-question. Vector searches are pure database lookups, not LM calls, so these searches cost 0 LM calls.
- Synthesise stage — 1 LM call: one final LM call fuses all retrieved chunks into the answer.
Exact cost (this design):
Caveat — when does enter the cost: if instead you answer each sub-question with its own LM call before fusing (a "sub-answer per branch" design), the count becomes LM calls. The cheap design above deliberately keeps it at 2 by decomposing and synthesising in single batched calls. That decomposition step is what buys cross-document reasoning flat top-k cannot give.
Level 5 — Mastery
Exercise 5.1 (L5)
Full cost + budget model. An agent handles a query with:
- system prompt = 250 tokens (present in every LM call),
- user question = 100 tokens (so the initial prompt = 250 + 100 = 350 tokens),
- 3 ReAct iterations, each observation adds 150 tokens to the growing history,
- a final answer call.
Model pricing is $0.01 per 1000 input tokens, output ignored. History accumulates: call sees all prior observations. Compute the total input tokens billed across all LM calls, and the dollar cost.
Recall Solution 5.1
Build the growing prompt. Base prompt each call = 350 tokens; each completed iteration appends 150 tokens of observation that persists into all later calls.
| LM call | Observations already in history | Input tokens |
|---|---|---|
| 1 | 0 | |
| 2 | 1 | |
| 3 | 2 | |
| 4 (final answer) | 3 |
Sum the input tokens across all four calls:
Convert to dollars at $0.01 per 1000 input tokens:
The key insight (super-linear cost): history accumulates, so each extra iteration is billed again on every later call. Total cost grows roughly with the square of the number of steps, not linearly. This is the quantitative reason to cap iterations and to summarise memory.
Exercise 5.2 (L5)
Compare, quantitatively, buffer vs summary memory over a 30-message chat with 80-token messages, where summary memory keeps the last 4 verbatim + a 120-token summary. If the context window is and system+reply overhead is 800 tokens, how many document tokens does each memory strategy leave free? What does this imply?
Recall Solution 5.2
Same s01 bar, two different amber (memory) slices. Free document tokens .
Buffer memory (all 30 messages carried verbatim):
Summary memory (120-token summary + last 4 verbatim):
Why these numbers? Buffer memory pays 80 tokens for every message forever, so a long chat's amber slice grows without bound and squeezes the white document slice. Summary memory caps the old history at a fixed 120 tokens no matter how long the chat runs, so its amber slice stays small.
What this implies: summary memory frees extra document tokens — about 3.2× the buffer's document budget (). On long chats, buffer memory starves retrieval: the documents that actually ground the answer get squeezed out by verbatim chat history. You trade some fidelity of old turns (an exact early quote may be lost in the summary) for far richer grounding context — usually a winning trade for RAG-style assistants.
Exercise 5.3 (L5)
Synthesis + judgment. You must serve 100,000 queries/day. Profiling shows 80% are simple FAQ lookups and 20% need multi-tool agentic reasoning. Simple = 1 LM call ($0.002), agentic = 4 LM calls ($0.002 each). Compute daily cost if you (a) route everything through the agent, vs (b) route FAQs through a plain chain and only the rest through the agent. Report the savings.
Recall Solution 5.3
(a) Everything agentic (4 calls each):
(b) Routed:
- FAQ (80% = 80,000) as a 1-call chain: 80{,}000 \times 1 \times \0.002 = $160$
- Agentic (20% = 20,000) at 4 calls: 20{,}000 \times 4 \times \0.002 = $160$
- Total = \160 + $160 = $320/\text{day}$
The lesson: cheap routing (send the easy 80% down the deterministic path) is where real production savings live — the same "push determinism deep" principle from Exercise 4.1, now with a price tag.
Recall Quick self-check (cloze)
Each observation in a ReAct loop forces one extra ::: LM call, so cost grows super-linearly with steps. The retrieval budget for documents = window − (system + memory + reply reservation) ::: everything fixed is subtracted first. Summary memory trades ::: fidelity of old turns for a smaller token footprint. Fixed recipes should be ::: Chains; run-time branching should be an Agent.
Related: 6.2.01-Definition-and-characteristics-of-AI-agents · 6.2.04-ReAct-(Reasoning-and-Acting) · 6.2.05-Tool-calling-and-function-calling-APIs · 5.3.02-Vector-databases · 5.3.03-Retrieval-Augmented-Generation-(RAG)