Build Your Own AI Coding Agent

Lesson 4 of 4

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"

  • messages accumulates 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_calls is 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_steps is 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

  1. Set up a real OpenRouter (or OpenAI) API key and the openai Python package locally, this project needs to make real API calls.
  2. Implement read_file, write_file, and run_python (from the Functions lesson, or your own versions).
  3. Write a JSON tool schema for each function (Function Calling lesson) and pass all of them via tools.
  4. 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.
  5. 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_file to 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_files tool 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_steps is 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.

This lesson ends in a project. Build it on your own machine, there's nowhere to submit it here, but the brief above is everything you need.