Table of Contents

How to Create an Agent

An AI agent is a system that autonomously takes actions to achieve goals. Unlike a chatbot that responds to one message at a time, an agent runs a loop – observing its environment, reasoning about the next step, acting, and repeating until the task is complete. This guide covers building agents from minimal loops to production-grade systems.

The Agent Loop

Every agent follows the same fundamental cycle:

  1. Observe – gather information about the current state (user input, tool results, environment data)
  2. Think – use the LLM to reason about what to do next
  3. Act – execute a tool, call an API, or produce a final response
  4. Repeat – feed the action result back as a new observation

The loop terminates when the agent determines the goal is met or a maximum step limit is reached.

This differs from a chatbot in a critical way: the agent decides autonomously how many steps to take and which tools to use, rather than responding to each user message independently. 1)

The ReAct Pattern

ReAct (Reason + Act) is the most widely used agent pattern. It structures the LLM's output into explicit reasoning traces interleaved with actions:

Thought: I need to find the current stock price for AAPL.
Action: search_web({"query": "AAPL stock price today"})
Observation: AAPL is trading at $198.50.
Thought: I now have the price. I can answer the user.
Action: respond({"message": "AAPL is currently trading at $198.50."})

The explicit Thought step improves transparency and helps the model make better tool-selection decisions. 2)

Building a Minimal Agent

A basic agent needs three components: an LLM, a set of tools, and a loop.

Define Tools

Each tool is a function with a JSON Schema description:

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "Search the web for current information",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Search query"}
                },
                "required": ["query"]
            }
        }
    }
]

The Loop

def agent(user_query, tools, max_steps=10):
    messages = [{"role": "user", "content": user_query}]
    for step in range(max_steps):
        response = llm.chat(messages=messages, tools=tools)
        if response.tool_calls:
            for call in response.tool_calls:
                result = execute_tool(call.name, call.arguments)
                messages.append({"role": "tool", "content": result, "tool_call_id": call.id})
        else:
            return response.content  # Final answer
    return "Max steps reached"

This is the complete minimal agent. The LLM decides whether to call a tool or return a final answer on each iteration.

Adding Memory

Conversation Memory

The messages array acts as short-term memory within a single task. For long-running agents, summarize older messages to stay within context limits:

if count_tokens(messages) > MAX_TOKENS:
    summary = llm.summarize(messages[:-5])  # Keep recent 5
    messages = [{"role": "system", "content": summary}] + messages[-5:]

Persistent Memory

For agents that need to remember across sessions, use a vector database:

This gives the agent a growing knowledge base that improves over time. 3)

Agent Frameworks

Framework Best For Architecture Complexity
LangGraph Complex stateful workflows Graph of nodes and edges High (full control)
CrewAI Multi-agent teams Role-based agents with task delegation Medium
AutoGen Conversational multi-agent Message-passing between agents Medium
Smolagents Lightweight single agents Minimal dependencies, fast prototyping Low

LangGraph Example

from langgraph.graph import StateGraph, END

graph = StateGraph(AgentState)
graph.add_node("reason", reasoning_node)
graph.add_node("act", tool_execution_node)
graph.add_edge("act", "reason")
graph.add_conditional_edges(
    "reason",
    should_continue,
    {"continue": "act", "done": END}
)
agent = graph.compile()

LangGraph provides explicit control over the execution flow, including conditional branching, parallel execution, and human-in-the-loop checkpoints. 4)

Common Patterns

Planning Agent

Decompose a complex goal into sub-tasks before executing:

plan = llm("Break this goal into 5 sequential steps: {goal}")
for step in plan:
    result = agent_loop(step, tools)

Reflection

The agent critiques its own output and iterates:

draft = agent_loop(task, tools)
critique = llm(f"Review this output for errors: {draft}")
if needs_improvement(critique):
    final = agent_loop(f"Improve based on feedback: {critique}", tools)

Multi-Agent Systems

Specialized agents collaborate under an orchestrator:

CrewAI and AutoGen provide built-in patterns for multi-agent collaboration. 5)

Evaluation

Test agents rigorously before deployment:

Build a test suite of 100+ representative tasks. Use an evaluator LLM to score outputs automatically, supplemented by human review for edge cases. 6)

Deployment

See Also

References