Course Content
Building Your First Agent with LangChain
Step-by-step: build a web research agent that writes a report
What You’ll Build
By the end of this lesson, you’ll have a working web research agent that can:
- Search the web for current information
- Look up Wikipedia articles for background context
- Combine information from multiple sources into a coherent summary
- Run queries like “Summarize the latest research on transformer efficiency improvements”
This is a real, runnable agent — not a toy demo. The same architecture scales to production use cases.
Setup: Installing Dependencies
Create a virtual environment and install the required packages:
python -m venv agent-env
source agent-env/bin/activate # On Windows: agent-env\Scripts\activate
pip install langchain langchain-community langchain-anthropic
pip install duckduckgo-search wikipediaYou’ll also need an Anthropic API key:
export ANTHROPIC_API_KEY="your-api-key-here"Step 1: Understanding LangChain’s Agent Architecture
LangChain structures agents around three components:
- LLM — the model that does the reasoning (we’ll use Claude)
- Tools — the functions the agent can call
- Agent executor — the loop that runs the observe-think-act cycle
LangChain provides pre-built tools for common tasks and a standardized interface for adding your own. It also handles the message formatting, tool call parsing, and iteration loop — so you don’t have to write that boilerplate yourself.
Step 2: Setting Up the LLM
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(
model="claude-opus-4-5",
temperature=0, # Use 0 for more deterministic reasoning
max_tokens=4096
)Using temperature=0 makes the agent’s reasoning more consistent and reproducible — important for debugging. You can increase it for more creative tasks.
Step 3: Configuring the Tools
LangChain has built-in wrappers for DuckDuckGo search and Wikipedia:
from langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
# Web search tool
search_tool = DuckDuckGoSearchRun(
name="web_search",
description="""Search the web for current information, recent events, and news.
Use for: recent developments, current statistics, news articles.
Input: a search query string."""
)
# Wikipedia tool
wiki_tool = WikipediaQueryRun(
api_wrapper=WikipediaAPIWrapper(top_k_results=2, doc_content_chars_max=3000),
name="wikipedia",
description="""Look up background information on a topic from Wikipedia.
Use for: technical concepts, historical context, definitions, established facts.
Input: the topic to search for."""
)
tools = [search_tool, wiki_tool]The descriptions matter enormously. The agent reads them to decide which tool to use for a given sub-question. “Use for: recent developments” vs “Use for: technical concepts” gives the agent a clear mental model of when to use each tool.
Step 4: Creating the ReAct Agent
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
# Pull the ReAct prompt template from LangChain Hub
# This is the standard Thought/Action/Observation format
prompt = hub.pull("hwchase17/react")
# Create the agent (LLM + tools + prompt)
agent = create_react_agent(
llm=llm,
tools=tools,
prompt=prompt
)
# Wrap in an executor that handles the loop
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # Print the full Thought/Action/Observation trace
max_iterations=10, # Prevent infinite loops
handle_parsing_errors=True # Gracefully handle malformed outputs
)The AgentExecutor is where the magic happens. It:
- Sends the current state to the agent
- Parses the agent’s output for tool calls
- Executes the tools
- Appends results back to context
- Repeats until the agent produces a final answer
Setting verbose=True during development is essential — you’ll see exactly what the agent is thinking and doing.
Step 5: Running Your First Query
result = agent_executor.invoke({
"input": "Summarize the latest research on transformer efficiency improvements"
})
print("\n=== FINAL ANSWER ===")
print(result["output"])With verbose=True, you’ll see the full trace:
> Entering new AgentExecutor chain...
Thought: The user wants a summary of recent research on transformer efficiency improvements.
I should search for the latest research on this topic, then look up background
on what transformer efficiency means in this context.
Action: web_search
Action Input: latest research transformer efficiency improvements 2025 2026
Observation: Recent papers focus on attention mechanism optimization, sparse
transformers, and mixture-of-experts (MoE) architectures...
Thought: I have some recent results. Let me get background on the key techniques
mentioned to give better context.
Action: wikipedia
Action Input: Sparse Transformer attention mechanism
Observation: Sparse Transformers reduce computational complexity from O(n²) to
O(n√n) by limiting attention to a subset of tokens...
Thought: I now have both current research trends and background context.
I can write a comprehensive summary.
Final Answer: Recent research on transformer efficiency has focused on three
main directions...This trace is your best debugging tool. If the agent makes wrong tool choices or misinterprets results, you’ll see exactly where it went wrong.
Step 6: Adding a Custom Tool
Built-in tools cover common cases, but most real-world agents need custom tools. Here’s how to add one — an arXiv paper search tool:
from langchain.tools import tool
import requests
@tool
def search_arxiv_papers(query: str) -> str:
"""Search arXiv for academic papers on a specific topic.
Use when the user needs academic research, papers, or peer-reviewed findings.
Input: a specific search query (e.g. 'flash attention memory efficient transformers').
Returns: list of recent papers with title, authors, and abstract summary."""
# ArXiv API endpoint
url = "http://export.arxiv.org/api/query"
params = {
"search_query": f"all:{query}",
"max_results": 5,
"sortBy": "submittedDate",
"sortOrder": "descending"
}
try:
response = requests.get(url, params=params, timeout=10)
# Simple XML parsing (in production, use feedparser)
results = []
entries = response.text.split("<entry>")[1:]
for entry in entries[:3]:
title = entry.split("<title>")[1].split("</title>")[0].strip()
summary_start = entry.find("<summary>") + 9
summary_end = entry.find("</summary>")
summary = entry[summary_start:summary_end].strip()[:300]
results.append(f"Title: {title}\nSummary: {summary}...")
return "\n\n".join(results) if results else "No papers found for this query."
except requests.RequestException as e:
return f"Failed to search arXiv: {str(e)}"
# Add to the tools list
tools_with_arxiv = [search_tool, wiki_tool, search_arxiv_papers]
# Recreate the agent with the new tool
agent_with_arxiv = create_react_agent(llm=llm, tools=tools_with_arxiv, prompt=prompt)
agent_executor_v2 = AgentExecutor(
agent=agent_with_arxiv,
tools=tools_with_arxiv,
verbose=True,
max_iterations=10,
handle_parsing_errors=True
)The @tool decorator automatically uses the function’s docstring as the tool description. Keep docstrings clear and include “Use when…” guidance.
Step 7: A More Complex Query
Let’s test with something that requires multiple coordinated searches:
result = agent_executor_v2.invoke({
"input": """Provide a technical overview of mixture-of-experts (MoE) architectures
in large language models. Include: what they are, why they're more efficient than
dense transformers, and what recent models use this approach."""
})The agent should:
- Search Wikipedia for background on MoE
- Search the web for recent MoE models (Mistral, Gemini, etc.)
- Search arXiv for recent technical papers
- Synthesize into a structured answer
This multi-source synthesis is exactly where agents shine over a simple RAG pipeline — they adaptively query different sources based on what each sub-question needs.
Controlling Agent Behavior
Limiting iterations: The max_iterations=10 setting prevents the agent from getting stuck in a loop. For research tasks, 10-15 is usually enough. For complex coding tasks, you might need 20-30.
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=15,
max_execution_time=120, # Also add a timeout in seconds
early_stopping_method="generate", # Generate a final answer when limit hit
)Handling parsing errors: Sometimes the agent produces output that doesn’t match the expected Thought/Action format. handle_parsing_errors=True tells the agent to try again with a reminder about the format, rather than crashing.
Controlling verbosity in production:
import logging
# In production, capture the trace to a log file instead of printing
logging.basicConfig(filename="agent_traces.log", level=logging.INFO)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=False, # Don't print to stdout
return_intermediate_steps=True # But capture steps in the return value
)
result = agent_executor.invoke({"input": query})
# Access the trace programmatically
for step in result["intermediate_steps"]:
action, observation = step
logging.info(f"Action: {action.tool}({action.tool_input})")
logging.info(f"Observation: {observation[:200]}")Complete Working Example
Here’s the full script, ready to run:
import os
from langchain_anthropic import ChatAnthropic
from langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
def create_research_agent():
llm = ChatAnthropic(
model="claude-opus-4-5",
temperature=0,
max_tokens=4096
)
tools = [
DuckDuckGoSearchRun(
name="web_search",
description="Search the web for current information and recent news."
),
WikipediaQueryRun(
api_wrapper=WikipediaAPIWrapper(top_k_results=2, doc_content_chars_max=2000),
name="wikipedia",
description="Look up background information and definitions on Wikipedia."
)
]
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
return AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=10,
handle_parsing_errors=True,
return_intermediate_steps=True
)
if __name__ == "__main__":
agent = create_research_agent()
queries = [
"Summarize the latest research on transformer efficiency improvements",
"What is Flash Attention and why is it important for LLM training?",
"Compare the efficiency of GPT-4 vs Mistral 7B for production deployment"
]
for query in queries:
print(f"\n{'='*60}")
print(f"Query: {query}")
print('='*60)
result = agent.invoke({"input": query})
print(f"\nAnswer: {result['output']}")
print(f"Steps taken: {len(result['intermediate_steps'])}")What to Watch For in the Trace
As you run this, watch for these patterns in the verbose output:
- Redundant searches: The agent searches for the same thing twice. Fix: improve the system prompt to say “don’t repeat searches you’ve already done.”
- Wrong tool selection: The agent uses web search for something Wikipedia would answer better. Fix: sharpen the “Use for:” section of your tool descriptions.
- Getting stuck: The agent keeps searching without reaching a conclusion. Fix: lower
max_iterationsand setearly_stopping_method="generate"so it synthesizes what it has.
Summary
- Install:
langchain,langchain-anthropic,langchain-community,duckduckgo-search,wikipedia - Use
ChatAnthropicwithtemperature=0for consistent reasoning - Tool descriptions should specify “Use when…” to guide tool selection
AgentExecutorhandles the full ReAct loop — setverbose=Trueduring development- Add custom tools with the
@tooldecorator; use docstrings as descriptions - Set
max_iterationsandmax_execution_timeto prevent runaway agents - Use
return_intermediate_steps=Truein production to capture traces for debugging
Next up: Agent Evaluation — how to measure whether your agent is actually doing its job correctly.
