What is a Data Analysis Agent with LangChain?
A Data Analysis Agent built with LangChain is an AI-powered system that can autonomously perform data analysis tasks by reasoning through problems, selecting appropriate tools, and executing operations on datasets. Unlike traditional data analysis workflows that require manual coding for each query, a LangChain agent uses a Large Language Model (LLM) as its "brain" to interpret natural language questions, decide which analytical tools to invoke, and synthesize results into meaningful answers.
At its core, the agent follows a ReAct (Reasoning + Acting) pattern — it thinks through what a user is asking, determines which tool (like a Python interpreter, SQL query engine, or chart generator) can help answer the question, calls that tool, observes the result, and then reasons again to determine if it has enough information to respond or if it needs additional tool calls. This iterative loop enables the agent to handle complex, multi-step analytical queries that would normally require writing multiple scripts.
LangChain provides a robust framework for building these agents with components like create_react_agent, Tool wrappers, and built-in integrations with pandas, matplotlib, and various data sources. The agent can work with CSV files, SQL databases, pandas DataFrames, JSON data, and even live APIs — making it incredibly versatile for real-world data analysis workflows.
Why Data Analysis Agents Matter
Data analysis agents represent a paradigm shift in how analysts, developers, and business users interact with data. Here's why they are becoming essential:
- Democratization of data analysis: Non-technical stakeholders can ask complex analytical questions in plain English without knowing Python, SQL, or statistical methods. An agent translates intent into executable operations.
- Rapid iteration and exploration: Instead of writing, debugging, and rewriting analysis scripts, users can have a conversational back-and-forth with their data. This speeds up hypothesis testing dramatically.
- Multi-tool orchestration: Real-world analysis often requires combining multiple techniques — filtering a DataFrame, running a statistical test, and visualizing results. Agents seamlessly chain these operations together.
- Reduced context-switching: Analysts can stay focused on the problem domain rather than constantly looking up pandas API documentation or matplotlib syntax.
- Reproducibility and audit trails: LangChain agents can log every thought, action, and observation, creating a transparent record of how a conclusion was reached.
- Scalability of expertise: A well-built agent can embody the analytical best practices of an experienced data scientist and make that expertise available 24/7 across an organization.
Setting Up Your Environment
Prerequisites and Installation
Before building your agent, you need Python 3.10 or higher installed. Create a new project directory and set up a virtual environment. Install the core dependencies with pip:
# Create and activate a virtual environment
python -m venv langchain_agent_env
source langchain_agent_env/bin/activate # On Windows: langchain_agent_env\Scripts\activate
# Install LangChain and related packages
pip install langchain langchain-openai langchain-experimental
pip install pandas matplotlib seaborn openai
pip install python-dotenv jupyter
# Optional: for SQL database agents
pip install langchain-community sqlalchemy
# Optional: for advanced tooling and tracing
pip install langgraph langsmith
The langchain-experimental package includes the create_react_agent function and various pre-built tool integrations that are essential for building data analysis agents. langchain-openai provides the OpenAI LLM integration, though you can substitute other model providers like Anthropic, Google, or open-source models.
API Keys and Configuration
Create a .env file in your project root to store API keys securely. For this tutorial, we'll use OpenAI's GPT-4o model, but the patterns work with any capable LLM.
# .env file contents
OPENAI_API_KEY=your-openai-api-key-here
# Optional: for LangSmith tracing (highly recommended for debugging agents)
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=your-langsmith-api-key-here
LANGCHAIN_PROJECT=data-analysis-agent-tutorial
Load these environment variables at the start of your script or notebook:
import os
from dotenv import load_dotenv
load_dotenv() # Load variables from .env file
# Verify the key is loaded (prints masked for security)
print(f"OpenAI key loaded: {'✓' if os.getenv('OPENAI_API_KEY') else '✗'}")
print(f"LangSmith tracing: {'✓' if os.getenv('LANGCHAIN_TRACING_V2') else '✗'}")
Core Components of a LangChain Data Analysis Agent
The LLM (Language Model)
The LLM serves as the reasoning engine of the agent. It interprets user queries, decides which tools to invoke, and synthesizes final answers. For data analysis, you want a model with strong coding and mathematical reasoning capabilities. GPT-4o, Claude 3.5 Sonnet, or Gemini 2.0 Flash are excellent choices.
from langchain_openai import ChatOpenAI
# Initialize the LLM with appropriate settings
llm = ChatOpenAI(
model="gpt-4o", # Strong reasoning and code generation
temperature=0, # Zero temperature for deterministic, precise analysis
max_tokens=4000, # Sufficient for complex analytical responses
verbose=True # Log LLM calls (useful for debugging)
)
# Test the LLM independently
response = llm.invoke("Explain the difference between correlation and causation in one paragraph.")
print(response.content)
Setting temperature=0 is critical for data analysis agents — you want the agent to produce consistent, reproducible results rather than creative but potentially incorrect analysis. Higher temperatures can cause the agent to hallucinate pandas operations or produce inconsistent numerical results.
Tools for Data Analysis
Tools are the "hands" of the agent — they allow it to execute actual operations on data. LangChain provides several pre-built tools specifically designed for data analysis, and you can easily create custom ones. Each tool has a name, a description (which helps the LLM decide when to use it), and a callable function.
The most powerful built-in tool for data analysis is the Python REPL tool, which allows the agent to execute arbitrary Python code. When combined with pandas and matplotlib, this gives the agent nearly unlimited analytical capabilities.
from langchain_experimental.tools import PythonREPLTool
from langchain_experimental.utilities import PythonREPL
# Create a Python REPL tool - this allows the agent to execute Python code
# The tool automatically has access to any libraries imported in the global scope
python_repl = PythonREPLTool(
description="Execute Python code to perform data analysis operations. "
"Use this for pandas DataFrame manipulations, statistical calculations, "
"data filtering, aggregations, and creating visualizations. "
"Input should be valid Python code. "
"Always use print() to display results you want to see."
)
# For more control, you can create a custom REPL with pre-imported libraries
from langchain_experimental.utilities import PythonREPL
custom_repl = PythonREPL()
# Pre-execute imports so they're available to the agent
custom_repl.run("import pandas as pd; import numpy as np; import matplotlib.pyplot as plt")
In addition to the Python REPL, you may want specialized tools for specific data sources:
from langchain_community.tools import QuerySQLDataBaseTool
from langchain_community.utilities import SQLDatabase
# Connect to a SQLite database (or any SQLAlchemy-compatible database)
db = SQLDatabase.from_uri("sqlite:///sales_data.db")
sql_tool = QuerySQLDataBaseTool(
db=db,
description="Query the sales database using SQL. "
"Use this to retrieve raw data from tables: customers, orders, products. "
"Input should be a valid SQLite query."
)
# You can also create a tool for loading external data
from langchain.tools import Tool
def load_csv_file(filepath: str) -> str:
"""Load a CSV file and return summary statistics."""
import pandas as pd
df = pd.read_csv(filepath)
return f"Loaded CSV with {len(df)} rows and {len(df.columns)} columns.\n" \
f"Columns: {list(df.columns)}\n" \
f"First 5 rows:\n{df.head().to_string()}\n" \
f"Data types:\n{df.dtypes.to_string()}"
csv_loader_tool = Tool(
name="csv_loader",
func=load_csv_file,
description="Load a CSV file from the local filesystem. "
"Input is the file path. Returns summary information about the dataset."
)
The Agent Executor
The agent executor ties the LLM and tools together and manages the ReAct loop. LangChain offers several agent creation approaches. The modern recommended approach uses create_react_agent from langgraph (LangChain's stateful agent framework), but the classic create_react_agent from langchain is still widely used and simpler for getting started.
Here's the classic approach using the AgentExecutor pattern:
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
# Pull a standard ReAct prompt template from the LangChain hub
# This prompt teaches the agent the ReAct reasoning pattern
prompt = hub.pull("hwchase17/react")
# Alternatively, define a custom prompt optimized for data analysis
from langchain_core.prompts import PromptTemplate
custom_prompt = PromptTemplate.from_template(
"""You are an expert data analyst assistant. Your job is to help users analyze data
by reasoning step-by-step and using the available tools.
Available tools: {tools}
Use the following format:
Question: the input question you must answer
Thought: analyze what the question is asking and plan which tool to use
Action: the tool to use, must be one of [{tool_names}]
Action Input: the input to the tool, as a valid string
Observation: the result of the tool
... (repeat Thought/Action/Action Input/Observation as needed)
Thought: I now have enough information to answer
Final Answer: the final answer to the question, with clear explanations
Important rules for data analysis:
- When performing calculations, double-check your work
- When creating visualizations, save them to files and describe what they show
- When working with DataFrames, always check for null values and data types first
- Explain your reasoning clearly in the Final Answer
- If a tool returns an error, try to diagnose and fix the issue
Begin!
Question: {input}
Thought: {agent_scratchpad}"""
)
# Create the agent
agent = create_react_agent(
llm=llm,
tools=[python_repl, csv_loader_tool], # Add your tools here
prompt=custom_prompt
)
# Wrap in an AgentExecutor for execution control
agent_executor = AgentExecutor(
agent=agent,
tools=[python_repl, csv_loader_tool],
verbose=True, # Shows the ReAct thought process
handle_parsing_errors=True, # Gracefully handle LLM output parsing errors
max_iterations=10, # Prevent infinite loops
max_execution_time=120, # Timeout in seconds for safety
early_stopping_method="generate" # Try to generate answer if stuck
)
The max_iterations and max_execution_time parameters are safety measures. Data analysis can sometimes involve complex chains of operations, so set these values based on your expected query complexity. For production systems, always include these limits.
Building Your First Data Analysis Agent
Defining Custom Tools for Statistical Analysis
Let's create a complete, working data analysis agent with custom tools for common analytical operations. This approach gives you fine-grained control over what the agent can do and how it does it.
import pandas as pd
import numpy as np
from langchain.tools import Tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain_core.prompts import PromptTemplate
# --- Define custom tool functions ---
def calculate_descriptive_stats(dataframe_name: str, column: str) -> str:
"""Calculate descriptive statistics for a column in a stored DataFrame."""
# Access a global dictionary of stored DataFrames (see data_storage_tool below)
import pandas as pd
# This assumes a global `stored_dataframes` dict exists
df = globals().get('stored_dataframes', {}).get(dataframe_name)
if df is None:
return f"Error: DataFrame '{dataframe_name}' not found. Available: {list(globals().get('stored_dataframes', {}).keys())}"
if column not in df.columns:
return f"Error: Column '{column}' not in DataFrame. Available columns: {list(df.columns)}"
stats = df[column].describe().to_string()
skew = df[column].skew()
kurtosis = df[column].kurtosis()
return f"Descriptive statistics for {dataframe_name}.{column}:\n{stats}\nSkewness: {skew:.4f}\nKurtosis: {kurtosis:.4f}"
def calculate_correlation(dataframe_name: str, col1: str, col2: str) -> str:
"""Calculate Pearson correlation between two columns."""
df = globals().get('stored_dataframes', {}).get(dataframe_name)
if df is None:
return f"Error: DataFrame '{dataframe_name}' not found."
corr = df[col1].corr(df[col2])
# Also calculate Spearman rank correlation for robustness
spearman = df[col1].corr(df[col2], method='spearman')
return f"Correlation between {col1} and {col2}:\n" \
f" Pearson r: {corr:.4f}\n" \
f" Spearman ρ: {spearman:.4f}\n" \
f" Interpretation: {'Strong' if abs(corr) > 0.7 else 'Moderate' if abs(corr) > 0.4 else 'Weak'} " \
f"{'positive' if corr > 0 else 'negative'} relationship"
def generate_histogram(dataframe_name: str, column: str, bins: int = 20) -> str:
"""Generate a histogram for a column and save as image."""
import matplotlib.pyplot as plt
import seaborn as sns
df = globals().get('stored_dataframes', {}).get(dataframe_name)
if df is None:
return f"Error: DataFrame '{dataframe_name}' not found."
plt.figure(figsize=(10, 6))
sns.histplot(data=df, x=column, bins=bins, kde=True)
plt.title(f"Distribution of {column} (with KDE)")
plt.xlabel(column)
plt.ylabel("Frequency")
filename = f"histogram_{dataframe_name}_{column}.png"
plt.savefig(filename, dpi=150, bbox_inches='tight')
plt.close()
return f"Histogram saved to '{filename}'. The plot shows the distribution of {column} " \
f"with a kernel density estimate overlay. Check for skewness, multimodality, and outliers."
def store_dataframe(code: str) -> str:
"""Execute Python code to create or modify DataFrames.
Use this to load data, clean it, or create new derived DataFrames.
The code should store results in the global stored_dataframes dict.
Example: stored_dataframes['sales'] = pd.read_csv('sales.csv')"""
# Create a restricted execution environment
import pandas as pd
import numpy as np
# Initialize the global storage if not exists
if 'stored_dataframes' not in globals():
globals()['stored_dataframes'] = {}
try:
exec(code, {'pd': pd, 'np': np, 'stored_dataframes': globals()['stored_dataframes']})
available = list(globals()['stored_dataframes'].keys())
return f"Code executed successfully. Available DataFrames: {available}"
except Exception as e:
return f"Error executing code: {str(e)}"
# --- Create Tool objects ---
stats_tool = Tool(
name="descriptive_statistics",
func=lambda input_str: calculate_descriptive_stats(*input_str.split('|')),
description="Calculate descriptive statistics (mean, std, quartiles, skewness, kurtosis) "
"for a specific column. Input format: 'dataframe_name|column_name'. "
"Example: 'my_data|sales_amount'"
)
correlation_tool = Tool(
name="correlation_analysis",
func=lambda input_str: calculate_correlation(*input_str.split('|')),
description="Calculate Pearson and Spearman correlation between two columns. "
"Input format: 'dataframe_name|column1|column2'. "
"Example: 'my_data|price|quantity_sold'"
)
histogram_tool = Tool(
name="generate_histogram",
func=lambda input_str: generate_histogram(*input_str.split('|')),
description="Generate and save a histogram for a column. "
"Input format: 'dataframe_name|column_name|bins(optional)'. "
"Example: 'my_data|age|30'"
)
data_storage_tool = Tool(
name="execute_data_code",
func=store_dataframe,
description="Execute Python code to load, clean, or transform data. "
"Use this to load CSV files, create DataFrames, filter data, or add computed columns. "
"Always store results in the 'stored_dataframes' dictionary. "
"Example code: stored_dataframes['customers'] = pd.read_csv('customers.csv')"
)
# Combine all tools
tools = [stats_tool, correlation_tool, histogram_tool, data_storage_tool]
Creating and Configuring the Agent
# Initialize the LLM
llm = ChatOpenAI(
model="gpt-4o",
temperature=0,
max_tokens=4000
)
# Create a data-analysis-optimized prompt
agent_prompt = PromptTemplate.from_template(
"""You are a senior data analyst AI assistant with expertise in statistics and data visualization.
You have access to the following analytical tools:
{tools}
Your tools are:
- execute_data_code: Run Python code to load, clean, or transform data. Always use this FIRST to load data.
- descriptive_statistics: Get summary statistics for a column
- correlation_analysis: Calculate correlations between columns
- generate_histogram: Create and save histograms
Follow the ReAct format strictly:
Question: {input}
Thought: Analyze the question and plan your approach. What data do you need? What analyses?
Action: {tool_names} (choose exactly one tool)
Action Input: the input to the tool, following the exact format specified
Observation: result from the tool
... (repeat Thought/Action/Action Input/Observation as needed)
Thought: I now have all the information needed to answer comprehensively.
Final Answer: Provide a clear, structured answer with:
1. Summary of findings
2. Key numbers and statistics
3. Interpretation of what the numbers mean
4. Any caveats or limitations
CRITICAL RULES:
- ALWAYS use execute_data_code FIRST to load data before any analysis
- If a tool returns an error, diagnose the problem and try a corrected input
- When done, provide a complete Final Answer - do not leave analysis incomplete
- Round numerical results to 4 decimal places for readability
Current conversation:
{agent_scratchpad}"""
)
# Create the agent
agent = create_react_agent(
llm=llm,
tools=tools,
prompt=agent_prompt
)
# Create the executor
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=15,
max_execution_time=180,
early_stopping_method="generate"
)
print("✅ Data Analysis Agent initialized successfully!")
print(f"Available tools: {[tool.name for tool in tools]}")
Running Your First Analysis Queries
# Example 1: Load a dataset and perform basic analysis
query1 = """
I have a CSV file called 'sales_data.csv' in my current directory.
Please load it, tell me what columns it has, and then analyze the
distribution of sales amounts. Calculate descriptive statistics
and generate a histogram.
"""
result1 = agent_executor.invoke({"input": query1})
print("\n" + "="*80)
print("FINAL RESULT:")
print(result1['output'])
# Example 2: Multi-step correlation analysis
query2 = """
Using the sales_data DataFrame you loaded, I want to understand
what factors correlate with high sales amounts. Check the correlation
between 'sales_amount' and each of these columns if they exist:
'price', 'discount_percentage', 'customer_age', 'time_on_page_minutes'.
Tell me which factor has the strongest correlation with sales.
"""
result2 = agent_executor.invoke({"input": query2})
print("\n" + "="*80)
print("FINAL RESULT:")
print(result2['output'])
When you run these queries with verbose=True, you'll see the agent's complete thought process in the console — each Thought, Action, and Observation step. This transparency is invaluable for understanding how the agent reasons and for debugging when things go wrong.
Advanced: DataFrame Agent with Pandas Integration
Using the Built-in Pandas DataFrame Agent
LangChain provides a specialized agent type specifically designed for pandas DataFrames. This agent can directly manipulate DataFrames using natural language queries and automatically handles common pandas operations. It's particularly useful when you want the agent to work with a single DataFrame that's already loaded in memory.
import pandas as pd
from langchain_experimental.agents import create_pandas_dataframe_agent
from langchain_openai import ChatOpenAI
# Load your dataset
df = pd.read_csv('sales_data.csv')
# Display basic info for the agent's context
print(f"DataFrame shape: {df.shape}")
print(f"Columns: {list(df.columns)}")
print(f"Data types:\n{df.dtypes}")
# Create a pandas-specific agent
# This agent has built-in knowledge of pandas operations
pandas_agent = create_pandas_dataframe_agent(
llm=ChatOpenAI(model="gpt-4o", temperature=0),
df=df,
verbose=True,
allow_dangerous_code=True, # Required for executing pandas code
max_iterations=15,
agent_executor_kwargs={
"handle_parsing_errors": True,
"max_execution_time": 120
}
)
# Query the agent with natural language
response = pandas_agent.invoke(
"What are the top 5 products by total sales revenue? "
"Create a bar chart showing their revenue and save it as 'top_products.png'. "
"Also tell me what percentage of total revenue these top 5 represent."
)
print(response['output'])
The pandas DataFrame agent is remarkably capable. It can handle complex operations like groupby aggregations, pivot tables, rolling windows, and even statistical tests — all from natural language instructions. However, be cautious with allow_dangerous_code=True in production environments; consider sandboxing the execution environment.
Handling Complex Analytical Queries with Multi-DataFrame Agents
Real-world analysis often involves multiple datasets that need to be joined, compared, or analyzed together. Here's how to build an agent that can manage multiple DataFrames:
import pandas as pd
from langchain_experimental.agents import create_pandas_dataframe_agent
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import Tool
# Load multiple datasets
customers_df = pd.read_csv('customers.csv')
orders_df = pd.read_csv('orders.csv')
products_df = pd.read_csv('products.csv')
# Create individual agents for each DataFrame
customer_agent = create_pandas_dataframe_agent(
llm=ChatOpenAI(model="gpt-4o", temperature=0),
df=customers_df,
verbose=False,
allow_dangerous_code=True,
number_of_head_rows=5 # Include first 5 rows in context for the agent
)
orders_agent = create_pandas_dataframe_agent(
llm=ChatOpenAI(model="gpt-4o", temperature=0),
df=orders_df,
verbose=False,
allow_dangerous_code=True,
number_of_head_rows=5
)
# Wrap each agent as a Tool for the orchestrator agent
def query_customers(query: str) -> str:
"""Query the customers DataFrame."""
result = customer_agent.invoke(query)
return result['output'] if isinstance(result, dict) else str(result)
def query_orders(query: str) -> str:
"""Query the orders DataFrame."""
result = orders_agent.invoke(query)
return result['output'] if isinstance(result, dict) else str(result)
def execute_python(code: str) -> str:
"""Execute Python code that can combine multiple DataFrames.
The DataFrames 'customers_df', 'orders_df', and 'products_df'
are available in the execution environment."""
try:
# Make DataFrames available in the execution context
local_vars = {
'pd': pd,
'customers_df': customers_df,
'orders_df': orders_df,
'products_df': products_df
}
exec(code, local_vars)
# Return any printed output
import io, sys
return "Code executed successfully."
except Exception as e:
return f"Error: {str(e)}"
# Create tools
tools = [
Tool(name="customer_data_query", func=query_customers,
description="Query the customers dataset. Ask questions about customer demographics, "
"locations, segments, etc. Input is a natural language question."),
Tool(name="orders_data_query", func=query_orders,
description="Query the orders dataset. Ask questions about order amounts, dates, "
"statuses, etc. Input is a natural language question."),
Tool(name="python_executor", func=execute_python,
description="Execute Python code to perform cross-dataset analysis, joins, or "
"complex calculations. Access DataFrames: customers_df, orders_df, products_df.")
]
# Create the orchestrator agent
orchestrator_llm = ChatOpenAI(model="gpt-4o", temperature=0)
orchestrator_prompt = PromptTemplate.from_template(
"""You are a data analyst orchestrator. You have access to multiple datasets
and can query them individually or execute Python to combine them.
Available tools: {tools}
Tool names: {tool_names}
Follow the ReAct format to answer the user's question.
When you need to compare data across datasets, use the python_executor tool.
When you need information from a single dataset, use the specific query tool.
Question: {input}
{agent_scratchpad}"""
)
orchestrator_agent = create_react_agent(
llm=orchestrator_llm,
tools=tools,
prompt=orchestrator_prompt
)
multi_df_executor = AgentExecutor(
agent=orchestrator_agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=20
)
# Execute a complex cross-dataset query
complex_query = """
I need to understand our customer ordering patterns. Specifically:
1. What are the top 3 customer segments by total order value?
2. For each of these segments, what is the average order value and
how many orders do they place per month on average?
3. Is there a correlation between customer age (from customers dataset)
and order frequency (from orders dataset)?
Present your findings clearly with numbers and interpretation.
"""
result = multi_df_executor.invoke({"input": complex_query})
print(result['output'])
This multi-DataFrame architecture allows the orchestrator agent to reason about which dataset contains the needed information and either query individual datasets or execute Python to perform cross-dataset joins and analyses. The pattern scales to any number of data sources.
Adding Memory for Conversational Analysis
Implementing Conversation Memory
One of the most powerful features of LangChain agents is the ability to maintain context across multiple turns of conversation. This enables users to have a fluid, iterative analytical dialogue rather than asking isolated one-shot questions. Memory allows the agent to reference previous findings, build on earlier analyses, and avoid redundant operations.
from langchain.memory import ConversationBufferMemory
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain_experimental.tools import PythonREPLTool
from langchain_core.prompts import MessagesPlaceholder
# Initialize memory to store conversation history
memory = ConversationBufferMemory(
memory_key="chat_history", # Key for the prompt template
return_messages=True, # Return messages as LangChain message objects
output_key="output" # Key for storing agent responses
)
# Create a prompt that includes chat history
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
prompt_with_memory = ChatPromptTemplate.from_messages([
("system", """You are an expert data analyst assistant with access to a Python execution tool.
You can perform data analysis, create visualizations, and execute Python code.
Available tools: {tools}
Tool names: {tool_names}
Use the ReAct format:
Thought: reason about what to do next
Action: choose a tool
Action Input: input for the tool
Observation: tool result
... (repeat as needed)
Thought: I can now answer
Final Answer: comprehensive response
Important: When referencing previous analyses from the conversation history,
cite specific numbers and findings. Build upon previous work rather than repeating it."""),
("placeholder", "{chat_history}"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
# Create the conversational agent
llm = ChatOpenAI(model="gpt-4o", temperature=0)
python_tool = PythonREPLTool()
conversational_agent = create_react_agent(
llm=llm,
tools=[python_tool],
prompt=prompt_with_memory
)
conversational_executor = AgentExecutor(
agent=conversational_agent,
tools=[python_tool],
memory=memory,
verbose=True,
handle_parsing_errors=True,
max_iterations=15,
max_execution_time=120
)
Multi-Turn Data Exploration in Practice
# First turn: Load and explore data
turn1 = """
Load the CSV file 'ecommerce_data.csv'. Show me the columns,
data types, and check for any missing values.
Then tell me the overall revenue and number of orders.
"""
result1 = conversational_executor.invoke({"input": turn1})
print("TURN 1 RESULT:", result1['output'])
# Second turn: Build on previous analysis (agent remembers the data is loaded)
turn2 = """
Using the data you already loaded, break down revenue by product category.
Which category generates the most revenue? Create a pie chart showing
the revenue distribution by category and save it as 'revenue_by_category.png'.
"""
result2 = conversational_executor.invoke({"input": turn2})
print("TURN 2 RESULT:", result2['output'])
# Third turn: Drill down further using context from previous turns
turn3 = """
For the top-performing category you identified, I want to understand
customer behavior. What's the average order value for that category?
Is there a day-of-week pattern in orders? Show me the order count
by day of week for just that top category.
"""
result3 = conversational_executor.invoke({"input": turn3})
print("TURN 3 RESULT:", result3['output'])
# Fourth turn: The agent remembers all previous context
turn4 = """
Summarize everything we've learned so far about the business:
- Overall metrics
- Category performance
- Customer behavior patterns for the top category
Provide actionable recommendations based on the data.
"""
result4 = conversational_executor.invoke({"input": turn4})
print("TURN 4 RESULT:", result4['output'])
# You can inspect what the agent remembers
print("\n" + "="*80)
print("MEMORY CONTENTS:")
print(memory.load_memory_variables({})['chat_history'])
The conversational memory transforms the agent from a simple query-response system into a true analytical collaborator. Users can explore data naturally, drilling down, pivoting, and building up complex understanding incrementally — exactly how human analysts work.
Best Practices for Building Data Analysis Agents
Building reliable, production-grade data analysis agents requires attention to several critical areas. Here are the best practices distilled from real-world implementation experience:
-
Use zero temperature for deterministic results:
Always set
temperature=0for analytical LLM calls. Data analysis requires