DEV Community

Cover image for Building an AI Agent from Scratch: The 80-Line Code Review Agent
Mehrdad khodaverdi
Mehrdad khodaverdi

Posted on

Building an AI Agent from Scratch: The 80-Line Code Review Agent

Introduction
There’s a pervasive myth in the AI development community that building an intelligent agent requires complex frameworks, specialized knowledge, and thousands of lines of code. Frameworks like LangChain, CrewAI, and Mastra have created an aura of sophistication around agents, making them seem almost magical.

The reality is far simpler. At its core, an AI agent is nothing more than a loop that orchestrates communication between a language model and external tools. The complexity frameworks provide—conversation memory, retry logic, fallback mechanisms—are conveniences, not necessities.

This article strips away the mystique by building a functional code review agent from scratch. The entire orchestration loop fits in roughly 80 lines of code. You’ll learn what actually happens under the hood and, more importantly, when a framework is worth using versus when it’s just unnecessary overhead.

Section 1: The Agent Loop Explained
The fundamental insight that demystifies AI agents is this: an agent is just a loop with state. The model doesn’t “think” or “reason” in any special way—it generates tokens based on a prompt, and the loop coordinates what happens next.

The architecture follows a simple pattern:

Send the user’s prompt and available tools to the LLM
The LLM responds with either text or a tool call request
If it’s a tool call, execute the requested function locally
Send the tool’s result back to the model
Repeat until the model provides a final answer or a limit is reached
This pattern is often called the ReAct (Reason + Act) cycle, though the principle applies regardless of the specific terminology. The LLM decides which tool to use and when it’s done—everything else is orchestration.

The Loop Implementation
Here’s the core loop concept in pseudocode:

async function runAgent(userMessage, maxIterations = 10) {
let messages = [{ role: "user", content: userMessage }];

for (let i = 0; i < maxIterations; i++) {
    const response = await llm.chat(messages, { tools: availableTools });

    if (response.isFinal) {
        return response.text;
    }

    if (response.toolCalls) {
        const results = await executeTools(response.toolCalls);
        messages.push({ role: "assistant", toolCalls: response.toolCalls });
        messages.push({ role: "tool", results });
    }
}

throw new Error("Max iterations exceeded");
Enter fullscreen mode Exit fullscreen mode

}
The loop itself is trivial. The real work happens in three places:

Tool definitions: Exposing functions the model can invoke
Prompt engineering: Guiding the model’s behavior and decision-making
Error handling: Managing failures, retries, and edge cases
Section 2: Building a Code Review Agent
Let’s examine a concrete implementation: a code review agent called “Steve” that analyzes Git diffs and provides feedback with the persona of a senior engineer with 15 years of experience.

The Tools
The agent needs three fundamental capabilities to perform code reviews:

getDiff: Retrieve the current Git diff
getFile: Read a specific file’s contents
listFiles: Explore the repository structure
These tools are defined using a schema that the LLM can understand, typically using a JSON schema format that describes the function name, description, and parameters.

const tools = [
{
name: "getDiff",
description: "Get the git diff of the current repository",
parameters: {
type: "OBJECT",
properties: {},
required: []
}
},
{
name: "getFile",
description: "Read a file from the repository",
parameters: {
type: "OBJECT",
properties: {
path: { type: "STRING", description: "Path relative to repo root" }
},
required: ["path"]
}
},
{
name: "listFiles",
description: "List files and directories at a given path",
parameters: {
type: "OBJECT",
properties: {
path: { type: "STRING", description: "Directory path" }
},
required: ["path"]
}
}
];
The System Prompt
The system prompt defines the agent’s persona and behavior. For the code review agent:

You are Steve, a senior software engineer with 15 years of experience. Review the code thoroughly. Be direct—even sarcastic when appropriate. Don’t hesitate to point out flaws, security issues, and maintainability concerns.

The prompt is critical because it shapes how the model interprets its role and the quality of its outputs. A well-crafted system prompt can dramatically improve agent performance without changing any code.

The Conversation Flow
The agent’s operation follows a straightforward sequence:

Step 1: The user sends a message like “Please review the current git diff.”
Step 2: The model receives the message along with the tool definitions. It can respond in three ways: plain text (the final answer), a tool call request, or both text and a tool call.
Step 3: If the model requests a tool call, the application executes it locally. For example, it might run git diff and capture the output.
Step 4: The tool result is sent back to the model as another message in the conversation.
Step 5: The loop continues until the model provides a final answer or the iteration limit is reached.
Crucially, the entire conversation history is sent back to the model on each iteration. This allows the model to maintain context and make informed decisions about which tool to call next.

Iteration Limits
The loop uses a for loop rather than a while loop to prevent infinite iterations and runaway token costs. A limit of 10 iterations typically provides enough capacity for even complex reviews while protecting against uncontrolled spending.

Section 3: Production Considerations
The 80-line agent is functional, but a production-ready agent requires additional considerations.

Retry Logic and Error Handling
The demo implementation encountered 503 errors from overloaded Gemini models, requiring a retry mechanism. Production systems should implement:

Exponential backoff: Progressively longer waits between retries
Jitter: Random variation to prevent thundering herd problems
Fallback models: Automatic switching to alternative models when primary fails
Timeout handling: Maximum wait times for each request
Context Window Management
As conversations grow, context windows fill up. Production agents implement strategies like:

Summarization: Compressing previous interactions
Selective retention: Keeping only relevant context
Sliding windows: Maintaining only the most recent N exchanges
Tool Execution Safety
Tools execute code and interact with systems. Production implementations require:

Sandboxing: Isolated execution environments
Permission controls: Limiting what tools can access
Audit trails: Logging all tool invocations
Rollback capabilities: Undoing changes when needed
Observability
Understanding agent behavior in production requires:

Structured logging: Tracking decisions and actions
Performance metrics: Latency, token usage, success rates
Traces: End-to-end visibility of agent runs
Regression testing: Catching degradations in behavior
Best Practices
Start Without a Framework
Build your first agent from scratch. You’ll understand the mechanics, edge cases, and limitations. Only after this foundational understanding should you consider adopting a framework.

Keep the Loop Simple
The orchestration loop should be straightforward and visible. Complexity should live in tool implementations, not in the orchestration layer.

Design Tools Carefully
Tools are the agent’s interface to the world. Each tool should:

Have a clear, single responsibility
Include thorough documentation
Return structured, predictable results
Handle errors gracefully
Implement Iteration Limits
Always enforce maximum iteration counts. This protects against infinite loops and cost overruns.

Use Structured Output When Possible
When models can return structured data (e.g., JSON), parsing is more reliable and error handling is easier.

Common Mistakes
Overcomplicating the Loop
Many developers prematurely add complexity to the loop itself. The loop should be a thin coordinator—not a container for business logic.

Poor Tool Design
Tools that are ambiguous, inconsistently documented, or that require complex parameters confuse the model and lead to poor decisions.

Neglecting Error Handling
LLM APIs fail. Tools fail. Network calls timeout. Production agents must handle these failures gracefully.

Insufficient Prompt Engineering
The system prompt defines the agent’s entire persona and behavior. Investing time here pays massive dividends in output quality.

Skipping Observability
Without logging and monitoring, debugging agent behavior becomes nearly impossible. Start with basic observability from day one.

Final Thoughts
The dirty secret behind AI agents is that they’re far simpler than the marketing suggests. A functional agent can be built in under 100 lines of code using only basic API calls and a loop. The frameworks that dominate the conversation are conveniences—not necessities.

This doesn’t mean frameworks are useless. They provide production-grade retries, fallback mechanisms, conversation memory, and integrations that would take significant time to build from scratch. But using a framework without understanding what it does is a recipe for frustration when things go wrong.

Start by building from scratch. Understand the loop. Experience the edge cases. Then, if a framework simplifies your life, adopt it with confidence—knowing exactly what it handles and what you still need to manage yourself.

The agent ecosystem is rapidly evolving, but the fundamentals remain constant. Master the basics, and you’ll be equipped to navigate any framework or paradigm that emerges.

Top comments (0)