Welcome. This is a setup exercise. The goal is not to build something clever yet, but to confirm your environment works and to walk through the basic pieces of an agent built with deepagents:
- A model (we use OpenRouter via
langchain-openrouter) - A tool the agent can call (Tavily web search)
- A backend for the agent filesystem (so it can write
answer.md) - A system prompt that tells the agent who it is and what to do
- Tracing with Langfuse so you can see what the agent did
By the end you should have a small research agent that answers a question by searching the web and saving a markdown report in artifacts/.
Follow the Getting Started section in the top-level README: install dependencies with uv sync and create a .env file with your OpenRouter, Tavily, and Langfuse keys.
The Python harness in main.py is already wired up (model, tool, backend, and Langfuse tracing). Your job is to write the system prompt:
- Open
prompts/research_agent.md.j2and replace the placeholder with a real prompt. The file has a short checklist in a Jinja comment (role, tools, output) to guide you. - Run the agent (see below), read the report at
artifacts/answer.md, and open the run in Langfuse.
There is no single right prompt. Try something, run it, and iterate.
From the repository root:
uv run python -m exercises.block1.mainYou should see the agent think, call internet_search a few times, and print a final message. A file answer.md should appear in exercises/block1/artifacts/.
- Open the final report at
exercises/block1/artifacts/answer.md. - Open the run in Langfuse and click through the trace. You should see the tool calls, their arguments, and the model responses for each step.
Once the basic run works, pick one or more of these (in any order):
Open your run in Langfuse and find each part of the harness in the trace:
- The system prompt the agent received
- Each
**internet_search** tool call (arguments and response) - The model intermediate reasoning between tool calls
- The final
**write_file** (or equivalent) that createdanswer.md
Write down which span corresponds to what.
Pick one variant:
- Add a tool: Implement
fetch_url(url: str)that returns page text (for example viatavily_client.extract(url)), register it increate_deep_agent, and update your prompt so the agent usesfetch_urlfor deep reads afterinternet_search. - Modify a tool: Change
internet_search, for example always passtopic="news", or truncate each result to N characters, and observe how the agent adapts.
Wrap internet_search so every second call raises an error, then watch what the agent does in Langfuse (retry, different query, give up?).
_call_count = 0
def internet_search(query: str, ...):
global _call_count
_call_count += 1
if _call_count % 2 == 0:
raise RuntimeError("Simulated transient error from internet_search")
return tavily_client.search(query, ...)Write a short note on what you observed.
A reference implementation (including a filled-in prompt) is in solutions/block1/. Try not to peek until you have given the prompt an honest attempt.