AI Agents Complete Guide 2026 — Build Autonomous Systems with LangGraph
Advertisement
Introduction
Why This Matters
AI agents are the fastest-growing category of LLM applications in 2026. Unlike chatbots that respond once, agents take sequences of actions — browsing the web, running code, querying databases, sending emails — in a loop until a goal is complete.
The gap between a toy agent that sometimes works and a production agent that handles real users reliably is large. Production agents need robust tool definitions, error recovery, iteration limits, logging, and human-in-the-loop for high-risk actions. Building without these causes runaway agents that consume unbounded API credits and take unintended actions.
This guide covers the full stack: a minimal agent from scratch, stateful multi-step agents with LangGraph, multi-agent collaboration with AutoGen, and the production checklist that separates demos from deployments.
Simple ReAct Agent from Scratch
from openai import OpenAI
import json
client = OpenAI()
def calculator(expression: str) -> str:
try:
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"Error: {e}"
def search_web(query: str) -> str:
return f"Search results for '{query}': [replace with real search API]"
TOOLS = {"calculator": calculator, "search_web": search_web}
TOOL_SCHEMAS = [
{
"type": "function",
"function": {
"name": "calculator",
"description": "Evaluate mathematical expressions",
"parameters": {"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]},
},
},
{
"type": "function",
"function": {
"name": "search_web",
"description": "Search the internet for current information",
"parameters": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]},
},
},
]
def run_agent(task: str, max_iterations: int = 10) -> str:
messages = [
{"role": "system", "content": "You are a helpful AI agent. Use tools to accomplish tasks."},
{"role": "user", "content": task},
]
for i in range(max_iterations):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=TOOL_SCHEMAS,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
if response.choices[0].finish_reason == "stop":
return msg.content
if msg.tool_calls:
for call in msg.tool_calls:
result = TOOLS[call.function.name](**json.loads(call.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
})
return "Max iterations reached"
print(run_agent("What is 15% of 847?"))LangGraph: Stateful Multi-Step Agents
LangGraph is the production standard for complex agent workflows with cycles, branching, and persistent state:
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
step_count: int
llm = ChatOpenAI(model="gpt-4o")
def agent_node(state: AgentState) -> AgentState:
response = llm.invoke(state["messages"])
return {"messages": [response], "step_count": state["step_count"] + 1}
def should_continue(state: AgentState) -> str:
last_message = state["messages"][-1]
if state["step_count"] >= 5:
return "end"
if "FINAL ANSWER:" in last_message.content:
return "end"
return "continue"
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue, {"continue": "agent", "end": END})
app = workflow.compile()
result = app.invoke({
"messages": [HumanMessage(content="Plan a 3-step approach to analyze a CSV sales file")],
"step_count": 0,
})
print(result["messages"][-1].content)Multi-Agent Systems with AutoGen
AutoGen enables multiple specialized agents to collaborate — engineer writes code, critic reviews it:
import autogen
config_list = [{"model": "gpt-4o", "api_key": "your-key"}]
llm_config = {"config_list": config_list, "timeout": 60}
engineer = autogen.AssistantAgent(
name="Engineer",
llm_config=llm_config,
system_message="You are a senior software engineer. Write clean, production-ready Python with type hints and docstrings.",
)
critic = autogen.AssistantAgent(
name="Critic",
llm_config=llm_config,
system_message="You are a code reviewer. Find bugs, security issues, and performance problems. Be specific about line numbers.",
)
user_proxy = autogen.UserProxyAgent(
name="User",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
code_execution_config={"work_dir": "coding", "use_docker": False},
)
user_proxy.initiate_chat(
engineer,
message="Write a Python function that reads a large CSV file efficiently and returns summary statistics",
)Agent Memory Patterns
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
long_term_db = Chroma(persist_directory="./agent_memory", embedding_function=embeddings)
def remember(fact: str):
long_term_db.add_texts([fact])
def recall(query: str, k: int = 3) -> list[str]:
docs = long_term_db.similarity_search(query, k=k)
return [d.page_content for d in docs]
def memory_agent(user_input: str) -> str:
memories = recall(user_input)
context = "\n".join(memories) if memories else "No relevant memories."
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": f"Relevant past context:\n{context}"},
{"role": "user", "content": user_input},
],
)
answer = response.choices[0].message.content
remember(f"User asked: {user_input[:100]}\nAnswer: {answer[:200]}")
return answerCommon Mistakes / Pitfalls
- No iteration limit — agents can loop indefinitely; always set
max_iterationsand enforce it - Tools without error handling — any tool can fail; return descriptive error strings instead of raising exceptions
- No logging — you cannot debug a production agent that has no trace of what tools it called
- Executing untrusted code without sandboxing — always run code tools in Docker containers
- No budget guardrails — set a max API spend limit and halt agents that exceed it
Best Practices
- Log every tool call, argument, and result to a structured audit trail
- Implement a human-in-the-loop gate for high-risk actions (sending emails, making purchases, deleting data)
- Use LangSmith or similar tracing for debugging multi-step agent behavior in development
- Return structured errors from tools, not exceptions — agents handle strings, not Python exceptions
- Set per-agent timeouts and global budget limits before deploying to production
Key Takeaways
- A production AI agent requires tools, memory, planning, error recovery, and observability — not just LLM calls
- The ReAct pattern (Reason + Act interleaved) is the standard architecture for single-agent loops
- LangGraph supports stateful graphs with cycles, branching, and checkpointing for complex workflows
- AutoGen enables multi-agent debate and collaboration where specialized agents check each other
- Max iteration limits are non-negotiable — without them, buggy agents run forever and drain API budgets
- Human-in-the-loop gates are required for any action with real-world side effects (emails, payments, deletions)
- Vector store memory lets agents recall relevant past interactions without overloading the context window
- LangSmith provides distributed tracing for every node in a multi-agent workflow
Advertisement