Stop Building Chatbots. Start Building Employees.

The era of “talking to AI” is ending. The era of AI doing work has begun.

For the last two years, we’ve been obsessed with RAG (Retrieval Augmented Generation)—essentially teaching a chatbot to read a PDF and answer questions. But in late 2025, the industry standard has shifted. We are no longer building chatbots that wait for a prompt; we are building Agents that receive a goal, plan a workflow, execute tasks, and correct their own errors.

Today, we are going to build your first “AI Intern”: an autonomous research agent that can take a simple topic (e.g., “The future of solid-state batteries”), browse the live web, analyze multiple sources, and generate a comprehensive briefing document—all without you typing a second prompt.


The Tech Stack

To build this, we need a framework that handles state (memory of what has been done) and cycles (looping back to fix errors or search again).

  • Language: Python
  • Orchestrator: LangGraph (The industry standard for stateful, multi-actor applications).
  • LLM: OpenAI GPT-4o (or Anthropic Claude 3.5 Sonnet).
  • Tools: Tavily API (A search engine built specifically for AI agents).

The Core Concept: Chains vs. Graphs

Traditional AI chains (like LangChain) are linear: Step A -> Step B -> Step C. Agents, however, are cyclical. They need to loop.

  1. The Agent Node: Decides what to do next.
  2. The Tool Node: Executes an action (e.g., searches Google).
  3. The Edge: Connects them. If the Agent says “I need more info,” it loops to the Tool Node. If it says “I’m done,” it ends.

Step 1: Setting the State

In LangGraph, the “State” is the brain of the operation. It tracks the conversation history and the results of any tool calls.

Python

from typing import Annotated, TypedDict
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages

# The "Brain" of our agent
class AgentState(TypedDict):
    # 'messages' tracks the conversation history.
    # 'add_messages' ensures new messages are appended, not overwritten.
    messages: Annotated[list, add_messages]

Step 2: Giving the Agent Tools

An agent without tools is just a philosopher. We need to give it eyes (Search). We’ll use Tavily, which returns clean text results optimized for LLMs, avoiding the mess of HTML parsing.

Python

from langchain_community.tools.tavily_search import TavilySearchResults

# Initialize the search tool (requires TAVILY_API_KEY)
tool = TavilySearchResults(max_results=3)
tools = [tool]

Step 3: Building the Nodes

We need two primary nodes:

  1. The Reasoner (LLM): Looks at the state and decides “Call a tool” or “Give final answer.”
  2. The Doer (Tool Node): Actually runs the search when requested.

Python

from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode

# Bind the tools to the LLM so it knows they exist
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools)

# Node 1: The Agent (Decision Maker)
def agent_node(state: AgentState):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}

# Node 2: The Tool Executor
tool_node = ToolNode(tools)

Step 4: Wiring the Graph

This is where the magic happens. We define the logic flow.

  • Start at the Agent.
  • Check: Did the Agent ask to use a tool?
    • Yes: Go to Tool Node, then loop back to Agent.
    • No: End the process.

Python

from langgraph.graph import END, START

workflow = StateGraph(AgentState)

# Add the nodes
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)

# Define the entry point
workflow.add_edge(START, "agent")

# Define the conditional logic (The "Manager")
def should_continue(state: AgentState):
    last_message = state["messages"][-1]
    # If the LLM made a tool call, go to tools
    if last_message.tool_calls:
        return "tools"
    # Otherwise, stop
    return END

workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent") # Always loop back to agent after using a tool

# Compile the graph into a runnable app
app = workflow.compile()

Step 5: Running Your AI Intern

Now we simply invoke the graph with a user prompt.

Python

initial_state = {"messages": [("user", "Research the current state of Quantum Computing in 2025 and write a brief summary.")]}

# Stream the output to watch the agent "think"
for event in app.stream(initial_state):
    for value in event.values():
        print("Agent Step:", value["messages"][-1].content)

What happens when you run this?

  1. Agent: “I need to search for ‘State of Quantum Computing 2025’.” (Decides to call tool)
  2. Tool: Returns search results from the web.
  3. Agent: “I have the info. Now I will summarize…” (Writes the final report)
  4. End.

Why This Goes Viral

This tutorial works because it breaks the illusion of “magic.” It shows that an “autonomous agent” isn’t sentient—it’s just a loop. A loop that checks: Do I have the answer? No? Search. Do I have it now? Yes? Answer.

The Future: Multi-Agent Systems

Once you master this single agent, the next step is Multi-Agent Orchestration, where a “Manager Agent” supervises a “Researcher Agent” and a “Writer Agent.” But that is a topic for another day.

Clone the repo, get your API keys, and start building employees, not chatbots.