Pi IoT Academy
Phase 6 · Ask Claude

Phase 06

Ask Claude.

Numbers on a screen tell you the temperature is 24 °C. They don’t tell you what that means. In this phase you’ll add an “Explain” button to every sensor card. Tap it and Claude Haiku writes a friendly two-sentence explanation in plain English — live, in under four seconds.

~45–60 min Difficulty: 2 / 3 You'll need an API key AI lesson

How the AI service works

Read this first.

You’re going to use a Large Language Model (LLM) called Claude, made by a company called Anthropic. An LLM is a program that’s read most of the internet and can write text in response to a prompt. We’re going to use it as a friendly explainer.

Here’s the path a button-tap takes:

flow
browser tap
   ↓ POST /api/ai/explain { sensor_id }
API service                   (looks up the latest reading)
   ↓ POST /ai/explain { sensor_id, reading }
AI service                    (renders the prompt template)
   ↓ HTTPS
Anthropic Claude Hauiku       (returns a short explanation)
   ↓ stores ai_prompts, ai_responses rows
   ↓ returns text + tokens + latency
browser shows it
Why a separate service?
The Anthropic API key only lives in one container. The main API doesn’t even know how to call Claude — it just forwards the request.
Why Haiku, not Opus?
Haiku is the smallest and fastest Claude. For 2–3 sentence explanations it’s plenty — and roughly 15× cheaper per call than the big models.
Why a template?
Every call uses the same instructions (“You are a friendly science teacher explaining to Year 7 students…”) with just the reading values swapped in. Versioned, reusable, easy to tweak.
Why log prompts & responses?
Cost tracking. If something goes wrong you can replay it. And it’s honest — teachers can see exactly what was asked and what came back.
No API key? Still works.

If you don’t have an Anthropic API key, the AI service uses a stub — it returns a canned message about the reading instead of calling Claude. The whole pipeline still runs, prompts and responses still get logged. You can do the entire phase that way and add a real key later.

Pull and rebuild

~5 minutes · on the Pi

The AI service has new code (Anthropic SDK, prompt template, persistence) and the API has a new dependency (httpx for forwarding). Rebuild both:

terminal · on the Pi
cd /opt/iot-platform
git pull
docker compose build api ai frontend
docker compose up -d

Watch the AI service come up:

terminal · on the Pi
docker compose logs -f ai

You should see a line that says loaded template explain-sensor-v1. If ANTHROPIC_API_KEY is empty in .env, you’ll also see a warning about using the offline stub. Both are fine for this step.

Checkpoint

Try the offline stub

~5 minutes

Make sure the test publisher is running on your laptop (Phase 3 still applies). Open the dashboard and tap a sensor card to open the chart modal. At the bottom you’ll see a new AI explanation section with an “Explain this reading” button.

If ANTHROPIC_API_KEY is empty, you’ll also see a yellow OFFLINE tag next to the section heading. That’s the stub mode.

Tap the button. Within a second you should see a canned message like:

stub response
AI explanations are disabled — no Anthropic API key configured.
Sample reading: temperature: 24.3 celsius (from microbit-test-01).

Below the message you’ll see echo-stub, a token count of 0 → 0, and a tiny latency. That’s the stub’s fingerprint — no real model was called.

Why bother with a stub at all?

Three reasons. Cost — you don’t pay to test the UI. Privacy — demo videos don’t leak data to a third party. And reliability — if Claude is down, the dashboard still works, it just shows a less interesting message.

Checkpoint

Get an API key

~10 minutes · with a teacher
Your teacher will provide

The API key. You don’t need your own — the school account holds it, and you’ll be given a key that’s only for the IoT lab. Never put a personal API key on a shared Pi.

An API key is a long string of letters and numbers, like a very serious password. It identifies your school’s account when the AI service calls Claude. Examples:

example shape
sk-ant-api03-aBcDeF...long...string
Treat it like a password

Don’t commit it to git. Don’t paste it in chat. Don’t screenshot it. The .env file is gitignored on purpose — the key only lives there and inside the running container.

Checkpoint

Switch to real Claude

~5 minutes · on the Pi

SSH into the Pi and edit .env:

terminal · on the Pi
cd /opt/iot-platform
nano .env

Find the line ANTHROPIC_API_KEY= and paste your key after the equals sign. Save with Ctrl+O, Enter, then Ctrl+X.

Restart only the AI service so it picks up the new key:

terminal · on the Pi
docker compose up -d ai

Check the logs — the warning about “using offline stub” should be gone. You should instead see Anthropic ready (claude-haiku-4-5-20251001).

Back to the dashboard. Open a sensor card’s modal again. The yellow OFFLINE tag should now be gone. Tap Explain this reading.

This time the response should be something like:

Claude response
The temperature from microbit-test-01 is 22.4 °C, which is a comfortable
classroom temperature. Nothing unusual — typical of a heated room on a
spring day.

Look at the small grey line below the text: claude-haiku-4-5-20251001 · 178 → 41 tok · 612 ms. That’s the model name, the input/output token count, and how long Claude took to respond. Your numbers will differ but the shape will match.

Tap it again

Each tap is a fresh call — you’ll get slightly different wording each time. That’s normal: LLMs are probabilistic. The meaning stays the same.

Checkpoint

Look at the prompts

~5 minutes

Every “Explain” tap writes two database rows: one in ai_prompts (what we asked) and one in ai_responses (what Claude said). Open sqlite3 on the Pi and have a look:

terminal · on the Pi
sqlite3 data/sqlite/platform.db <<'SQL'
.headers on
.mode column
.width 12 18 60

SELECT id, template_id, substr(rendered_text, 1, 60) AS rendered
FROM ai_prompts
ORDER BY created_at DESC
LIMIT 3;

SELECT substr(prompt_id, 1, 8) AS prompt,
       provider,
       model,
       input_tokens, output_tokens, latency_ms,
       status
FROM ai_responses
ORDER BY created_at DESC
LIMIT 3;
SQL

You’ll see the actual prompt that went to Claude (with your sensor’s values plugged in) and the metadata about each call. This is gold for debugging: if a response looks weird, you can read the exact prompt that produced it.

Where the system prompt lives

Only the user prompt is stored as rendered_text. The system prompt (the “You are a friendly science teacher…” part) is identical for every call — it’s fixed by the template at boot. Look at services/ai/app/prompts/explain-sensor-v1.md to see it.

Checkpoint

Watch token usage

~5 minutes

Claude charges per token (roughly: a word or a piece of one). The Settings page shows a rolling 7-day total so you can keep an eye on cost.

Go to Settings in the sidebar. The new AI usage section shows four cards: Calls, Successes, Input tokens, Output tokens. Tap a few sensor cards and tap “Explain” a few times, then refresh the Settings page (with the page reload icon, or by re-entering Settings). The numbers should grow.

You can also pull the same data straight from the API:

terminal · anywhere
curl -s "http://pi-iot-XX.local/api/ai/usage?days=7" | python3 -m json.tool
Rough numbers for Haiku 4.5

Each Explain call costs ~180 input tokens and ~40 output tokens. At the time of writing, Haiku is ~£1 per million input tokens and ~£4 per million output tokens. So 1000 explain calls ≈ £0.34. Pocket money for a whole school term.

Checkpoint

Final verification

5 minutes

Tick everything below before declaring Phase 6 done.

Phase 6 sign-off

Phase 6 complete?

You’ve put a real Large Language Model in the loop — structured, tracked, and reasonably cheap. Last phase: tie it all together with hardware. Real Micro:bit, real ESP32, real classroom sensors.

Phase 7: Your first module  →

(The button unlocks when every box above is ticked.)