Lesson 03 Claude Agent SDK Intro
Theme:

Homework — Three SDK Exercises

Lesson 03 · Building Your Own Agents · ~30 min

All three tasks below extend hello_agent.py from the Lab 03 walk-through. Keep the original script handy as a base — each task is a small, focused edit.

Task 1 — Replace the docstring; observe the failure mode

Edit the get_weather docstring to the literal string "Tool." (no other description). Re-run the same question:

@tool
def get_weather(city: str) -> str:
    """Tool."""
    return f"{city}: 4°C, light rain"
python hello_agent.py

✓ Self-check: the agent either asks the user for more information instead of calling the tool, or hallucinates the weather without calling it at all. Restore the original docstring once you’ve seen the behaviour.

Then write a short note (2–3 sentences) in a comment at the top of the file: why does the docstring matter more than the function name?

Task 2 — Use a Pydantic model as a tool argument

Add the following imports and model to hello_agent.py:

from pydantic import BaseModel

class TravelQuery(BaseModel):
    city: str
    days_ahead: int = 0  # 0 = today, 1 = tomorrow, ...

Write a second tool whose argument is the Pydantic model rather than two scalars:

@tool
def get_forecast(query: TravelQuery) -> str:
    """Get a forecast for `query.city` for `query.days_ahead` days from now."""
    return f"{query.city} in {query.days_ahead}d: 7°C, sunny"

Add get_forecast to the agent’s tools=[...] list and ask:

What will the weather in Lviv be in 3 days?

✓ Self-check: the agent calls get_forecast({"city": "Lviv", "days_ahead": 3}) and the SDK validates the dict against the Pydantic model before passing it to your function.

Task 3 — Stream and render token-by-token

Replace the result = agent.run(...) line with a streaming loop:

for event in agent.stream("What's the weather in Kyiv?"):
    if event.type == "text_delta":
        print(event.text, end="", flush=True)
    elif event.type == "tool_use":
        print(f"\n[calling {event.tool.name}({event.tool.input})]")
    elif event.type == "end_turn":
        print()

Run it.

✓ Self-check: you see the model’s text appear token-by-token, with a bracketed log line each time it calls a tool, and a final newline when it’s done.

Note on usage accounting

When streaming you no longer have a direct result.usage object — you have to accumulate it from the events yourself. The message_stop event carries the per-message usage figures; sum those if you want a streaming-mode equivalent of result.usage.total_cost_usd. The Solutions page shows one way to do it.

View solutions →