Skip to content

Agent Frameworks ​

Comparing tools for building production LLM agents


Learning Objectives ​

By the end of this module, you will be able to:

  • Compare major agent frameworks (LangChain, LlamaIndex, AutoGen, CrewAI)
  • Select the right framework for different use cases and requirements
  • Implement agents using each framework's idioms and patterns
  • Evaluate framework trade-offs: flexibility vs simplicity, features vs overhead
  • Migrate between frameworks when requirements change

Framework Landscape ​

Market Overview

The agent framework landscape is rapidly evolving. LangChain dominates in general-purpose agents, LlamaIndex excels at RAG, AutoGen leads multi-agent systems, and CrewAI provides role-based orchestration.


LangChain ​

Overview ​

LangChain is the most widely adopted framework for building LLM applications, offering extensive tooling for chains, agents, and RAG systems.

Key Concepts ​

LangChain Agent Implementation ​

python
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.tools import tool
from langchain.memory import ConversationBufferMemory

# Define tools
@tool
def search_database(query: str) -> str:
    """Search the product database for items matching the query."""
    # Simulated database search
    return f"Found 5 products matching '{query}': Product A, B, C, D, E"

@tool
def get_weather(location: str) -> str:
    """Get current weather for a location."""
    return f"Weather in {location}: 22°C, sunny"

@tool
def calculate(expression: str) -> str:
    """Evaluate a mathematical expression."""
    try:
        return str(eval(expression))
    except:
        return "Error evaluating expression"

# Create prompt template
prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a helpful assistant with access to tools.
Use tools when needed to answer user questions accurately.
Always explain your reasoning."""),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

# Initialize components
llm = ChatOpenAI(model="gpt-4", temperature=0)
tools = [search_database, get_weather, calculate]
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# Create agent
agent = create_openai_tools_agent(llm, tools, prompt)

# Create executor
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=5
)

# Run agent
response = agent_executor.invoke({
    "input": "What's the weather in Tokyo and find me some headphones"
})
print(response["output"])

LangGraph for Complex Workflows ​

python
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
from operator import add

class AgentState(TypedDict):
    messages: Annotated[list, add]
    current_step: str
    results: dict

def research_node(state: AgentState) -> AgentState:
    """Node that performs research."""
    # Research logic here
    return {
        "messages": [{"role": "assistant", "content": "Research complete"}],
        "results": {"research": "findings..."}
    }

def analyze_node(state: AgentState) -> AgentState:
    """Node that analyzes research."""
    return {
        "messages": [{"role": "assistant", "content": "Analysis complete"}],
        "results": {"analysis": "insights..."}
    }

def should_continue(state: AgentState) -> str:
    """Determine next step."""
    if state.get("results", {}).get("analysis"):
        return END
    return "analyze"

# Build graph
workflow = StateGraph(AgentState)

workflow.add_node("research", research_node)
workflow.add_node("analyze", analyze_node)

workflow.set_entry_point("research")
workflow.add_conditional_edges(
    "research",
    should_continue,
    {"analyze": "analyze", END: END}
)
workflow.add_edge("analyze", END)

# Compile and run
app = workflow.compile()
result = app.invoke({"messages": [], "current_step": "start", "results": {}})

LlamaIndex ​

Overview ​

LlamaIndex (formerly GPT Index) specializes in connecting LLMs with external data through sophisticated indexing and retrieval mechanisms.

Architecture ​

LlamaIndex Agent Implementation ​

python
from llama_index.core import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    Settings
)
from llama_index.core.agent import ReActAgent
from llama_index.core.tools import QueryEngineTool, ToolMetadata, FunctionTool
from llama_index.llms.openai import OpenAI

# Configure LLM
Settings.llm = OpenAI(model="gpt-4", temperature=0)

# Load and index documents
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)

# Create query engine
query_engine = index.as_query_engine(similarity_top_k=3)

# Define tools
query_tool = QueryEngineTool(
    query_engine=query_engine,
    metadata=ToolMetadata(
        name="document_search",
        description="Search through company documents for information. Use this for questions about policies, procedures, or internal information."
    )
)

def calculate(expression: str) -> str:
    """Evaluate a math expression."""
    try:
        return str(eval(expression))
    except:
        return "Error"

calc_tool = FunctionTool.from_defaults(
    fn=calculate,
    name="calculator",
    description="Perform mathematical calculations"
)

# Create ReAct agent
agent = ReActAgent.from_tools(
    [query_tool, calc_tool],
    llm=Settings.llm,
    verbose=True,
    max_iterations=10
)

# Query the agent
response = agent.chat("What is our vacation policy and how many days would that be for 3 years of service?")
print(response)

LlamaIndex with Multiple Indices ​

python
from llama_index.core.tools import QueryEngineTool
from llama_index.core.selectors import LLMSingleSelector
from llama_index.core.query_engine import RouterQueryEngine

# Create multiple indices for different data sources
hr_index = VectorStoreIndex.from_documents(hr_documents)
finance_index = VectorStoreIndex.from_documents(finance_documents)
tech_index = VectorStoreIndex.from_documents(tech_documents)

# Create query engines
hr_engine = hr_index.as_query_engine()
finance_engine = finance_index.as_query_engine()
tech_engine = tech_index.as_query_engine()

# Define as tools with descriptions
query_engine_tools = [
    QueryEngineTool.from_defaults(
        query_engine=hr_engine,
        name="hr_policies",
        description="Contains HR policies, benefits, vacation, and employee guidelines"
    ),
    QueryEngineTool.from_defaults(
        query_engine=finance_engine,
        name="finance_docs",
        description="Contains financial reports, budgets, and expense policies"
    ),
    QueryEngineTool.from_defaults(
        query_engine=tech_engine,
        name="tech_docs",
        description="Contains technical documentation, API docs, and architecture guides"
    ),
]

# Create router that selects appropriate engine
router_engine = RouterQueryEngine(
    selector=LLMSingleSelector.from_defaults(),
    query_engine_tools=query_engine_tools,
    verbose=True
)

# Query automatically routes to correct index
response = router_engine.query("What is the API rate limit for the search endpoint?")

AutoGen ​

Overview ​

AutoGen by Microsoft Research focuses on multi-agent conversations with built-in support for human-in-the-loop and code execution.

Conversation Architecture ​

AutoGen Implementation ​

python
import autogen
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager

# Configuration
config_list = [
    {
        "model": "gpt-4",
        "api_key": "your-api-key"
    }
]

llm_config = {
    "config_list": config_list,
    "temperature": 0,
    "timeout": 120,
}

# Create specialized agents
planner = AssistantAgent(
    name="Planner",
    system_message="""You are a planning agent. Your role is to:
1. Break down complex tasks into steps
2. Assign steps to appropriate agents
3. Track progress and adjust plans as needed

Always start by outlining a clear plan.""",
    llm_config=llm_config,
)

coder = AssistantAgent(
    name="Coder",
    system_message="""You are an expert Python programmer.
Write clean, efficient, and well-documented code.
Always include error handling and type hints.
Test your code mentally before providing it.""",
    llm_config=llm_config,
)

reviewer = AssistantAgent(
    name="Reviewer",
    system_message="""You are a code reviewer. Review code for:
- Correctness and edge cases
- Performance and efficiency
- Security vulnerabilities
- Code style and readability
Provide specific, actionable feedback.""",
    llm_config=llm_config,
)

# User proxy that can execute code
user_proxy = UserProxyAgent(
    name="User",
    human_input_mode="TERMINATE",
    max_consecutive_auto_reply=10,
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={
        "work_dir": "workspace",
        "use_docker": False,
    },
)

# Create group chat
group_chat = GroupChat(
    agents=[user_proxy, planner, coder, reviewer],
    messages=[],
    max_round=20,
    speaker_selection_method="auto",
)

manager = GroupChatManager(
    groupchat=group_chat,
    llm_config=llm_config
)

# Start conversation
user_proxy.initiate_chat(
    manager,
    message="""Create a Python function that:
1. Fetches data from a REST API
2. Parses the JSON response
3. Saves results to a CSV file
Include error handling and tests."""
)

AutoGen with Custom Tools ​

python
from autogen import register_function

# Define a tool function
def web_search(query: str) -> str:
    """Search the web for information."""
    # Implementation here
    return f"Search results for: {query}"

def database_query(sql: str) -> str:
    """Execute a SQL query safely."""
    # Implementation with safety checks
    return f"Query results: ..."

# Create assistant with tools
assistant = AssistantAgent(
    name="Assistant",
    llm_config=llm_config,
)

# Register tools
register_function(
    web_search,
    caller=assistant,
    executor=user_proxy,
    name="web_search",
    description="Search the web for current information"
)

register_function(
    database_query,
    caller=assistant,
    executor=user_proxy,
    name="database_query",
    description="Query the database for structured data"
)

CrewAI ​

Overview ​

CrewAI provides a role-based approach to multi-agent systems, modeling agents as team members with specific roles and goals.

Crew Structure ​

CrewAI Implementation ​

python
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool, WebsiteSearchTool

# Define tools
search_tool = SerperDevTool()
web_tool = WebsiteSearchTool()

# Create agents with roles
researcher = Agent(
    role="Senior Research Analyst",
    goal="Uncover cutting-edge developments in AI and data science",
    backstory="""You work at a leading tech think tank.
Your expertise lies in identifying emerging trends and technologies.
You have a knack for dissecting complex data and presenting
actionable insights.""",
    verbose=True,
    allow_delegation=False,
    tools=[search_tool, web_tool]
)

writer = Agent(
    role="Tech Content Strategist",
    goal="Craft compelling content on tech advancements",
    backstory="""You are a renowned content strategist, known for
your insightful and engaging articles. You transform complex
concepts into compelling narratives.""",
    verbose=True,
    allow_delegation=True
)

editor = Agent(
    role="Senior Editor",
    goal="Ensure content quality and accuracy",
    backstory="""You are a meticulous editor with years of experience
in tech publishing. You have an eye for detail and ensure
all content meets the highest standards.""",
    verbose=True,
    allow_delegation=False
)

# Define tasks
research_task = Task(
    description="""Conduct comprehensive research on the latest
    advancements in AI agents and autonomous systems in 2024.
    Identify key players, breakthrough technologies, and potential
    industry impacts. Your final answer should be a detailed
    report with sources.""",
    expected_output="Detailed research report with citations",
    agent=researcher
)

writing_task = Task(
    description="""Using the research provided, write an engaging
    blog post about AI agents. The post should be informative yet
    accessible, targeting tech-savvy professionals. Include
    practical examples and future implications.""",
    expected_output="Blog post of 1000-1500 words",
    agent=writer
)

editing_task = Task(
    description="""Review the blog post for accuracy, clarity,
    and engagement. Ensure technical accuracy while maintaining
    readability. Provide the final polished version.""",
    expected_output="Final edited blog post ready for publication",
    agent=editor
)

# Create and run crew
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.sequential,
    verbose=2
)

result = crew.kickoff()
print(result)

CrewAI with Custom Process ​

python
from crewai import Crew, Process

# Hierarchical process with manager
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.hierarchical,
    manager_llm="gpt-4",
    verbose=2
)

# The manager agent will:
# 1. Analyze the tasks
# 2. Delegate to appropriate agents
# 3. Validate outputs
# 4. Coordinate handoffs

Framework Comparison ​

Feature Matrix ​

FeatureLangChainLlamaIndexAutoGenCrewAI
Primary FocusGeneral agentsRAG/DataMulti-agentRole-based teams
Learning CurveMediumMediumLowLow
FlexibilityVery HighHighMediumMedium
RAG SupportGoodExcellentBasicBasic
Multi-agentVia LangGraphLimitedExcellentExcellent
Code ExecutionVia toolsLimitedBuilt-inVia tools
Human-in-loopManualManualBuilt-inBuilt-in
MemoryComprehensiveGoodConversationTask-based
StreamingYesYesLimitedLimited
Production ReadyYesYesMaturingMaturing

Decision Matrix ​


Interview Q&A ​

Q1: When would you choose LangChain vs LlamaIndex? ​

Strong Answer:

"The choice depends on the primary use case:

Choose LangChain when:

  • Building general-purpose agents with diverse tool use
  • Need maximum flexibility and customization
  • Complex chain/workflow composition is required
  • Want a large ecosystem of integrations
  • Building agents that do more than just RAG
python
# LangChain excels at complex chains
chain = (
    prompt
    | llm
    | parser
    | tool_executor
    | summarizer
)

Choose LlamaIndex when:

  • Primary focus is RAG over structured/unstructured data
  • Working with multiple data sources needing different indices
  • Need sophisticated retrieval strategies (tree, keyword, hybrid)
  • Want built-in data connectors (PDF, web, databases)
  • Simpler API for common RAG patterns
python
# LlamaIndex excels at data-centric agents
index = VectorStoreIndex.from_documents(docs)
agent = index.as_query_engine()  # One line!

Hybrid Approach: Many production systems use both - LlamaIndex for document retrieval as a tool within a LangChain agent:

python
from langchain.tools import Tool

llama_tool = Tool(
    name='document_search',
    func=llama_index.as_query_engine().query,
    description='Search company documents'
)
```"

---

### Q2: How do you handle the trade-off between framework abstraction and control?

**Strong Answer:**

"This is a fundamental tension in framework selection. My approach:

**1. Start with High Abstraction, Drop Down as Needed**
```python
# Level 1: Framework abstractions
agent = create_openai_tools_agent(llm, tools, prompt)

# Level 2: Custom chains when needed
chain = RunnableSequence([
    custom_step_1,
    custom_step_2,
    agent
])

# Level 3: Raw API calls for full control
response = openai.chat.completions.create(...)

2. Identify Control Points

  • Where does the framework make decisions?
  • Which decisions do I need to override?
  • What's the escape hatch?

3. Framework Evaluation Criteria

CriterionHigh AbstractionLow Level
Speed to MVPFastSlow
DebuggingHarderEasier
CustomizationLimitedFull
MaintenanceFramework managesYou manage

4. Practical Pattern

python
class HybridAgent:
    def __init__(self):
        self.framework_agent = create_agent()  # For common cases
        self.custom_handler = CustomHandler()  # For edge cases

    def run(self, query):
        if self._needs_custom_handling(query):
            return self.custom_handler.run(query)
        return self.framework_agent.run(query)

Key insight: Most production systems end up using frameworks for 80% of cases and custom code for the remaining 20% where control matters most."


Q3: What are the main challenges when migrating between agent frameworks? ​

Strong Answer:

"Migration challenges fall into several categories:

1. Data Model Differences

  • Memory serialization formats differ
  • Tool/function definitions have different schemas
  • Message history structures vary
python
# LangChain message
{'role': 'user', 'content': '...'}

# AutoGen message
{'content': '...', 'role': 'user', 'name': 'User'}

# Migration requires transformation layer
def migrate_messages(langchain_msgs):
    return [transform(m) for m in langchain_msgs]

2. Abstraction Level Mismatch

  • LangChain: Very composable, many small pieces
  • CrewAI: Coarse-grained roles and tasks
  • Migration may require restructuring the entire agent

3. Tool/Function Compatibility

python
# LangChain tool
@tool
def search(query: str) -> str:
    ...

# AutoGen function
def search(query: str) -> str:
    ...
register_function(search, ...)

# Adapter pattern helps
class ToolAdapter:
    @staticmethod
    def langchain_to_autogen(lc_tool):
        ...

4. Memory and State

  • Each framework has different memory abstractions
  • Need to export/import state carefully
  • Consider a framework-agnostic persistence layer

5. Migration Strategy

  1. Abstract common interfaces first
  2. Build adapters for tools and memory
  3. Migrate incrementally (one capability at a time)
  4. Maintain parallel systems during transition
  5. Extensive testing of edge cases"

Summary ​

FrameworkBest ForStrengthsLimitations
LangChainGeneral agents, complex workflowsFlexibility, ecosystem, composabilitySteep learning curve, complexity
LlamaIndexRAG, data-intensive appsIndexing, retrieval, data connectorsLess flexible for non-RAG agents
AutoGenMulti-agent conversationsBuilt-in code execution, human-in-loopLess mature, fewer integrations
CrewAIRole-based teamsIntuitive mental model, easy setupLess flexible, newer framework

Sources ​