Indu Learning Platform
Course/Unit 5 · AI foundations
Lesson 5-5

Calling an LLM from code

Call a model API from Python, get structured JSON back, add a tool, evaluate with a small test set and keep cost and latency in check.

Watch firstWhat is Tool Calling? Connecting LLMs to Your Data · IBM TechnologyOpen on YouTube ↗

Why code, not chat

Real MDG work is volume: 3,000 failed IDocs after a cutover rehearsal, 40,000 suppliers to check. From code you send every item with the same prompt and get output a program can use. Every provider works the same way: messages and settings in over HTTPS, content plus token usage out.

Read inputIDoc errors CSVBuild prompttemplate + rowCall APImessages.createParse + validateJSON schemaStore resulttable / queue
A batch AI job is a loop with a model call in the middle.

Your first API call

Run pip install anthropic and set ANTHROPIC_API_KEY as an environment variable (never hard-code it). A call needs a model, max_tokens and messages; system is optional. The API is stateless: resend earlier messages to continue a conversation.

Python
import anthropic

client = anthropic.Anthropic()   # reads ANTHROPIC_API_KEY
MODEL = "claude-sonnet-5-5"      # placeholder: check the docs for current names

resp = client.messages.create(
    model=MODEL,
    max_tokens=500,
    temperature=0,
    system="You are an SAP MDG support analyst. Be concise.",
    messages=[{"role": "user", "content": "Explain IDoc status 51 for CREMAS in two sentences."}],
)
print(resp.content[0].text)
print(resp.usage.input_tokens, resp.usage.output_tokens)
print(resp.stop_reason)          # "end_turn", "max_tokens", "tool_use", ...

Try it: complete the call

Try it · Fill the blanks

Fill the blanks in this Anthropic SDK call.

resp = client.(
    model=MODEL,
    =300,
    system="You classify SAP IDoc errors.",
    messages=[{"role": "", "content": prompt}],
)
text = resp.[0].text
tokens_in = resp.usage.

Structured JSON output

Ask for JSON and show its exact shape. Then parse with json.loads, validate keys and allowed values, retry once, and route failures to a human. A stronger option: a tool whose input_schema is your output schema, forced with tool_choice. Some providers also offer structured-output modes. Validate either way.

Python
import json
ALLOWED = {"MASTER_DATA", "CONFIG", "TECHNICAL", "UNKNOWN"}

def classify(error_text: str) -> dict:
    resp = client.messages.create(
        model=MODEL, max_tokens=300, temperature=0,
        messages=[{"role": "user", "content": PROMPT.replace("{error_text}", error_text)}],
    )
    if resp.stop_reason == "max_tokens":
        raise ValueError("truncated output")
    data = json.loads(resp.content[0].text)      # fails loudly if not JSON
    if data.get("category") not in ALLOWED:
        raise ValueError(f"bad category: {data.get('category')}")
    return data

PROMPT asks for {category, field, fix, confidence} as JSON only, with the error inside <error> tags.

Tool calling round trip

Pass tools=[...]. If the model wants one, stop_reason is "tool_use" and the reply holds a tool_use block with id, name and input. Run your function, then send a tool_result block with the matching tool_use_id. Loop with a maximum count and an allow-list of functions.

Step through · 1 of 5
Send messages +toolsrequest 1stop_reason:tool_usemodel asksRun your functionyour codeSend tool_resultrequest 2Final answerend_turn
Your code sends the user's question plus the tool definitions. The model sees the tool names, descriptions and schemas.
The two API requests that make up one tool call.

Evaluate with a small test set

Treat a prompt like a BRFplus rule: test before transport. Build 20–50 masked, real inputs with expected answers, including tricky ones. Score with code and rerun after every prompt or model change. Exact match works for categories; free text needs keyword checks or a rubric-graded model call you spot-check.

v1: bare instruction68%v2: + category definitions81%v3: + 3 examples90%v4: + 'UNKNOWN' allowed94%
Illustrative: accuracy on a 50-case IDoc test set as the prompt improves.

Try it: run an evaluation

Try it · Put in order

Put the evaluation workflow in order.

  1. 1Label the expected category for each
  2. 2Collect 20–50 real, masked IDoc errors, including tricky ones
  3. 3Compare with the expected labels in code and record the score
  4. 4Run the prompt over every case
  5. 5Rerun the full set and compare scores
  6. 6Change one thing in the prompt

Cost, latency and tokens

Cost = input tokens × input price + output tokens × output price; output usually costs several times more. Latency is driven mostly by output length. Levers: send less context, ask for compact JSON, use a smaller model for simple classification, cache repeats, batch non-urgent jobs. Log usage on every call.

Job (per item)Input tokOutput tokNoteClassify IDoc error~400~60Small model is fineCompare 2 suppliers~600~120JSON verdict + evidenceReview mapping rule~1,500~300Includes spec extractDraft FS section~6,000~2,000Strong model, human edit
Rough sizes. Multiply by item count and current prices.

Try it: bad JSON overnight

Try it · What would you do?

Your overnight job classified 3,000 IDoc errors. About 2% came back as invalid JSON or with a category that is not allowed. What should the pipeline do?

Remember

  • An API call is messages + settings in, content + usage out; the API is stateless.
  • For automation, request JSON, validate it in code and route failures to a human; tool calls are a tool_use/tool_result round trip.
  • A labelled test set measures every change; less context and compact output control cost and latency.

Check yourself

Your JSON classifier sometimes returns stop_reason 'max_tokens'. What should code do?
In a tool-calling round trip, what links your tool output to the model's request?
You changed a prompt and it 'looks better' on two examples. What is the reliable check?
Which change most reduces latency for a high-volume classification job?