Agents
Everything so far, sending prompts, writing tool functions, dispatching a tool call, are the individual pieces. An agent is what you get when you wire them into a loop: call the model, execute whatever it asks for, feed the result back, and repeat, until the model decides the task is done. This final project builds that loop and points it at a real bug.
The agent loop
def run_agent(user_task, max_steps=10):
messages = [
{
'role': 'system',
'content': 'You are a coding agent. Use the available tools to inspect and fix the bug, then explain the fix.',
},
{'role': 'user', 'content': user_task},
]
for _ in range(max_steps):
response = client.chat.completions.create(
model='openai/gpt-4o-mini',
messages=messages,
tools=tools,
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content # no more tools requested, this is the agent's final answer
for call in message.tool_calls:
result = dispatch_tool_call({
'name': call.function.name,
'arguments': call.function.arguments,
})
messages.append({
'role': 'tool',
'tool_call_id': call.id,
'content': str(result),
})
return "Agent didn't finish within max_steps."
This needs a real API key and calls the model in a loop, so it's read-only here, this is exactly what you'll run locally for the final project.
Why this is "the feedback loop"
messagesaccumulates the entire conversation, the original task, every one of the model's replies (including tool call requests), and every tool result, appended with role'tool'. Each new call to the model sees all of it, so it remembers what it already tried and what happened.- The loop only stops when
message.tool_callsis empty, meaning the model chose to reply with a plain final answer instead of requesting another tool. Until then, it keeps working: read a file, notice a bug, run code to test a fix, write the corrected file, run it again to confirm. max_stepsis a safety net. Without it, a model stuck in a bad loop (repeatedly trying the same failing fix) would run forever, capping the attempts guarantees the agent eventually stops one way or another.
This is the difference between a chatbot and an agent: a chatbot replies once, an agent keeps working, using tools and its own prior results, until it decides the job is actually finished.
Final project requirements
- Set up a real OpenRouter (or OpenAI) API key and the
openaiPython package locally, this project needs to make real API calls. - Implement
read_file,write_file, andrun_python(from the Functions lesson, or your own versions). - Write a JSON tool schema for each function (Function Calling lesson) and pass all of them via
tools. - Implement the full
run_agent()loop above: call the model, execute any requested tool calls, feed each result back as a'tool'-role message, and repeat until the model gives a final plain-text answer. - Point your agent at a real, small, deliberately-broken Python file (plant a genuine bug, an off-by-one error, a wrong variable name, a missing edge case) and give it a task like "There's a bug in bug.py, find and fix it." Let it read the file, diagnose the problem, and use
run_python/write_fileto actually fix it, then confirm the fix works.
Stretch goals
- Log every tool call (name and arguments) to the console as the agent works, visibility into why it's doing what it's doing is invaluable for debugging your own agent.
- Add a
list_filestool so the agent can explore an entire project directory, not just one file you already told it about. - Handle the case where the model requests multiple tool calls in a single turn (the
for call in message.tool_calls:loop above already supports this, verify it with a task that needs two tools at once). - Add a friendlier message (or a retry with a smaller task) when
max_stepsis hit, instead of just giving up.
Submit a link to your finished project (a repo or gist) below, an instructor will review it before you can mark this lesson complete.