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.