Unit 1: How LLMs work, for defenders · Lesson 2 of 5 · 20 min
The context window
See exactly what the app packs into each request, and what falls out when it is full.
mission
- You are
- You are the AI security engineer at Fernway Travel.
- Situation
- Support says Fernbot sometimes ignores the help articles. A developer thinks the model is broken.
- Goal
- Trace what goes into the context window for a few requests and find out when articles are dropped.
- Hand in
- A one line explanation of when Fernbot drops documents.
The context window as a fixed size tray
Send a question with curl
Fernbot is a web API on 127.0.0.1:8088. curl sends HTTP requests from the command line, and jq picks fields out of JSON. On your own machine, install both with your package manager, for example sudo apt install curl jq. -s hides the progress meter. The URL is the app's /chat endpoint. -H says the body is JSON. -d sends the body, a JSON object with a message field. jq -r .reply prints only the reply text.
curl -s http://127.0.0.1:8088/chat -H 'Content-Type: application/json' -d '{"message": "Can I bring my bike?"}' | jq -r .reply
Sports equipment such as bikes and skis needs a special baggage booking 48 hours before departure.
you should see The baggage policy for bikes.
curl- Sends web requests and prints the response. Built into Linux, macOS and Windows 10 and later.
jq- Picks fields out of JSON and reshapes it. Install with sudo apt install jq, or brew install jq on a Mac.
Read the fern helper
Typing that curl command every time is slow, so your ~/.bashrc loads a small shell function from tools/fern.sh. Line 1 names the function fern. Line 2 builds the JSON body with jq -n: --arg puts your first argument in the message field with safe quoting, and --argjson merges an optional second argument of extra fields, such as '{"guard": true}'. Line 3 sends that body with the same curl command. -d @- tells curl to read the body from its input.
cat tools/fern.sh
fern() {
jq -nc --arg message "$1" --argjson extra "${2:-null}" '{$message} + $extra' |
curl -s http://127.0.0.1:8088/chat -H 'Content-Type: application/json' -d @-
}
you should see Four lines. fern prints the app's raw JSON, so you pipe it to jq.
defender tip To use it on your own machine, copy the four lines into your own ~/.bashrc and open a new terminal.
cat- Prints the contents of one or more files. Built into Linux and macOS.
Trace a normal request
Every reply carries a trace field with each section of the context and its size in tokens. Fernbot counts with the same o200k_base encoding as ttok. jq -c prints each section on one line. This is the single most useful view when you debug or attack test an LLM app.
fern "Can I bring my bike?" | jq -c '.trace.sections[], {total: .trace.total}'
{"part":"system","tokens":60,"source":"system_prompt.txt"}
{"part":"document","tokens":81,"source":"baggage.md","score":1}
{"part":"user","tokens":6,"source":"your message"}
{"total":147}
you should see System prompt first, then the retrieved article, then the user message. 147 tokens in total.
Find the limits in the config
Context window and output limits are settings, not magic. Find them before you reason about behaviour.
grep -E "context_window|max_output_tokens" app/config.json
"context_window": 600,
"max_output_tokens": 300,
grep- Prints the lines of text that match a pattern. Built into Linux and macOS.
Trace a request with two documents
Retrieval can pull in more than one article. Each one is text the model will read and may follow. With -r, jq prints the reply as plain text.
fern "How long does a refund take? I want my money back" | jq -rc '.trace.sections[], .reply'
{"part":"system","tokens":60,"source":"system_prompt.txt"}
{"part":"document","tokens":58,"source":"refunds.md","score":2}
{"part":"document","tokens":71,"source":"supplier-faq.md","score":1}
{"part":"user","tokens":12,"source":"your message"}
Refunds go back to the original card within 7 working days.
For refunds, contact refunds@fernway-help.example.
you should see The second reply line does not come from refunds.md. Keep that in mind for the untrusted content unit.
Overflow the window
Long messages push other content out. Here the user's text is so long that the help article no longer fits. .trace.notes lists what the app dropped.
fern "$(printf 'bike %.0s' $(seq 1 480)) can I bring my bike?" | jq -rc '.trace.sections[], .trace.notes[], .reply'
{"part":"system","tokens":60,"source":"system_prompt.txt"}
{"part":"user","tokens":487,"source":"your message"}
dropped baggage.md: no room left in the context window
I can help with bookings, baggage, refunds and travel documents.
you should see A note that baggage.md was dropped, and a generic answer instead of the real policy.
defender tip Attackers use long inputs to push safety instructions or documents out of the window. Cap message length before you build the prompt.
printf- Prints formatted text. Built into Linux and macOS.
seq- Prints a sequence of numbers. Built into Linux and macOS.
What a defender takes away
- 1The model only knows what is in the window for this request.
- 2Instructions, documents and user text share one budget and one block of text.
- 3When the window fills, something is dropped. Decide what, rather than letting it happen.
Questions
0 of 3 solved
Answer each one from what you find on your lab machine.