Lab 03 — Get structured JSON out of a model with tool use

Scaffold: 4/5. Same infrastructure as Lab 01. The gap is bigger this time: you wire up a tool schema and parse the result, not just add one argument.

The scenario

A support system needs to turn each incoming free-text message into a structured record it can route: an intent, the product mentioned, and an urgency. Asking the model for JSON in the prompt gets you close, but every so often it wraps the JSON in prose or a code fence and the parser downstream breaks. Tool use fixes that: you declare the exact shape you want, and the runtime returns typed arguments.

The requirement

POST a support message, get back a structured record:

{ "record": { "intent": "billing", "product": "Pro", "urgency": "high" } }

shaped by a schema instead of by pleading in the prompt.

What’s provided

Your task

In src/handler.py:

  1. Pass toolConfig={"tools": [TICKET_TOOL]} to converse(), with a short system prompt telling the model to record the request via the tool.
  2. Find the block in response["output"]["message"]["content"] that has a toolUse key; its ["toolUse"]["input"] is your typed record.
  3. Return it, and return an error if the model answered in prose instead of calling the tool, so you notice the failure rather than shipping it.

Run it

./scripts/deploy.sh
./scripts/test.sh
./scripts/test.sh "Please cancel my subscription, no rush."
./scripts/teardown.sh

What success looks like

test.sh returns a record object whose fields come straight from the schema (intent from its enum, urgency low/medium/high) and then checks them: the script exits non-zero if the function raised, if no record came back, or if a value lands outside its enum. Try a message with no clear product and watch it omit the optional field but still fill the required ones.

The enum steers the model hard, and at temperature: 0 a fourth urgency value is unlikely. It is not impossible. Plain tool use hands back whatever the model put in the toolUse block without validating it against the schema; Bedrock’s strict tool use ("strict": true on the tool definition, where the model supports it) is the opt-in that adds that validation. Without it, the assertion in test.sh is what turns the shape into a guarantee.

If it fails

Reveal the solution

SRC=solution ./scripts/deploy.sh && ./scripts/test.sh

What you just learned

Next

Lab 04 — Stream a response token by token. You move from a single blocking call to ConverseStream, so the answer starts arriving while it is still being generated.