Introduction
have all the time walked side-by-side, holding fingers.
I bear in mind listening to “Study Statistics to know what’s behind the algorithms” after I began learning Information Science. Whereas all of that was fascinating to me, it was additionally actually overwhelming.
The actual fact is that there are too many statistical ideas, assessments, and distributions to maintain monitor of. When you don’t know what I’m speaking about, simply go to the Scipy.stats web page, and you’ll perceive.
If you’re sufficiently old within the Information Science area, you most likely bookmarked (and even printed) a kind of statistical check cheatsheets. They had been fashionable for some time. However now, the Massive Language Fashions have gotten sort of a “second mind” for us, serving to us to shortly seek the advice of just about any info we li,ke with the additional good thing about getting it summarized and tailored to our wants.
With that in thoughts, my considering was that choosing the proper statistical check might be complicated as a result of it is determined by variable varieties, assumptions, and many others.
So, I believed I may get an assistant to assist with that. Then, my venture took type.
- I used LangGraph to construct a multi-step agent
- The front-end was constructed with Streamlit
- The Agent can shortly seek the advice of Scipy Stats documentation and retrieve the correct code for each particular scenario.
- Then, it provides us a pattern Python code
- It’s deployed in Streamlit Apps, in case you need to strive it.
- App Hyperlink: https://ai-statistical-advisor.streamlit.app/
Superb!
Let’s dive in and discover ways to construct this agent.
LangGraph
LangGraph is a library that helps construct advanced, multi-step purposes with massive language fashions (LLMs) by representing them as a graph. This graph structure permits the builders to create circumstances, loops, which make it helpful for creating refined brokers and chatbots that may resolve what to do subsequent based mostly on the outcomes of a earlier step
It primarily turns a inflexible sequence of actions into a versatile, dynamic decision-making course of. In LangGraph, every node is a perform or instrument.
Subsequent, let’s be taught extra in regards to the agent we’re going to create on this publish.
Statistical Advisor Agent
This agent is a Statistical Advisor. So, the primary concept is that:
- The bot receives a statistics-related query, resembling “Tips on how to examine the technique of two teams“.
- It checks the query and determines if it must seek the advice of Scipy’s documentation or simply give a direct reply.
- If wanted, the agent makes use of a RAG instrument on embedded SciPy documentation
- Returns a solution.
- If relevant, it returns a pattern Python code on tips on how to carry out the statistical check.
Let’s shortly take a look at the Graph generated by LangGraph to point out this agent.
Nice. Now, let’s lower to the chase and begin coding!
Code
To make issues simpler, I’ll break the event down into modules. First, let’s set up the packages we are going to want.
pip set up chromadb langchain-chroma langchain-community langchain-openai
langchain langgraph openai streamlit
Chunk and Embed
Subsequent, we are going to create the script to take our documentation and create chunks of textual content, in addition to embed these chunks. We try this to make it simpler for vector databases like ChromaDB
to look and retrieve info.
So, I created this perform embed_docs()
that you would be able to see within the GitHub repository linked here.
- The perform takes Scipy’s documentation (which is open supply beneath BSD license)
- Splits it into chinks of 500 tokens and overlap of fifty tokens.
- Makes the embedding (remodel textual content into numerical values for optimized vector db search) utilizing
OpenAIEmbedding
- Saves the embeddings in an occasion of
ChromaDB
Now the information is prepared as a data base for a Retrieval-Augmented Technology (RAG). However it wants a retriever that may search and discover the information. That’s what the retriever
does.
Retriever
The get_doc_answer()
perform will:
- Load the ChromaDB occasion beforehand created.
- Create an occasion of
OpenAI GPT 4o
- Create a
retriever
object - Glue the whole lot collectively in a
retrieval_chain
that will get a query from the person, sends it to the LLM - The mannequin makes use of the
retriever
to entry the ChromaDB occasion, get related information about statistical assessments, and return the reply to the person.
Now we have now the RAG accomplished with the paperwork embedded and the retriever prepared. Let’s transfer on to the Agent nodes.
Agent Nodes
LangGraph has this fascinating structure that considers every node as a perform. Subsequently, now we should create the capabilities to deal with every a part of the agent.
We’ll comply with the stream and begin with the classify_intent
node. Since some nodes have to work together with an LLM, we have to generate a shopper.
from rag.retriever import get_doc_answer
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
# Occasion of OpenAI
shopper = OpenAI()
As soon as we begin the agent, it is going to obtain a question from the person. So, this node will examine the query and resolve if the following node can be a easy response or if it wants to look Scipy’s documentation.
def classify_intent(state):
"""Examine if the person query wants a doc search or might be answered immediately."""
query = state["question"]
response = shopper.chat.completions.create(
mannequin="gpt-4o",
messages=[
{"role": "system", "content": "You are an assistant that decides if a question about statistical tests needs document lookup or not. If it is about definitions or choosing the right test, return 'search'. Otherwise return 'simple'."},
{"role": "user", "content": f"Question: {question}"}
]
)
determination = response.selections[0].message.content material.strip().decrease()
return {"intent": determination} # "search" or "easy"
If a query about statistical ideas or assessments is requested, then the retrieve_info()
node is activated. It performs the RAG within the documentation.
def retrieve_info(state):
"""Use the RAG instrument to reply from embedded docs."""
query = state["question"]
reply = get_doc_answer(query=query)
return {"rag_answer": reply}
As soon as the correct chunk of textual content is retrieved from ChromaDB, the agent goes to the following node to generate a solution.
def reply(state):
"""Construct the ultimate reply."""
if state.get("rag_answer"):
return {"final_answer": state["rag_answer"]}
else:
return {"final_answer": "I am unsure tips on how to assist with that but."}
Lastly, the final node is to generate a code, if that’s relevant. Which means, if there may be a solution the place the check might be executed utilizing Scipy, there can be a pattern code.
def generate_code(state):
"""Generate Python code to carry out the beneficial statistical check."""
query = state["question"]
suggested_test = state.get("rag_answer") or "a statistical check"
immediate = f"""
You're a Python tutor.
Based mostly on the next person query, generate a brief Python code snippet utilizing scipy.stats that performs the suitable statistical check.
Consumer query:
{query}
Reply given:
{suggested_test}
Solely output code. Do not embody explanations.
"""
response = shopper.chat.completions.create(
mannequin="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
return {"code_snippet": response.selections[0].message.content material.strip()}
Discover one thing essential right here: all capabilities in our nodes all the time have state
as an argument as a result of the state is the only supply of reality for your entire workflow. Every perform, or “node,” within the graph reads from and writes to this central state object.
For instance:
- The
classify_intent
perform reads the query from the state and provides an intent key. - The
retrieve_info
perform can learn the identical query and add a rag_answer, which the reply perform lastly reads to assemble the final_answer. This shared state dictionary is how the totally different steps within the agent’s reasoning and action-taking course of keep linked.
Subsequent, let’s put the whole lot collectively and construct our graph!
Constructing the Graph
The graph is the agent itself. So, what we’re doing right here is principally telling LangGraph what the nodes are that we have now and the way they join to one another, so the framework could make the data run in keeping with that stream.
Let’s import the modules.
from langgraph.graph import StateGraph, END
from typing_extensions import TypedDict
from langgraph_agent.nodes import classify_intent, retrieve_info, reply, generate_code
Outline our state schema. Keep in mind that dictionary that the agent makes use of to attach the steps of the method? That’s it.
# Outline the state schema (only a dictionary for now)
class TypedDictState(TypedDict):
query: str
intent: str
rag_answer: str
code_snippet: str
final_answer: str
Right here, we are going to create a perform that builds the graph.
- To inform LangGraph what the steps (capabilities) within the course of are, we use
add_node
- As soon as we have now listed all of the capabilities, we begin creating the perimeters, that are the connections between the nodes.
- We begin the method with
set_entry_point
. That is the primary perform for use. - We use
add_edge
to attach one node to a different, utilizing the primary argument because the perform from which the data comes, and the second argument is the place it goes. - If we have now a situation to comply with, we use
add_conditional_edges
- We use
END
to complete the graph andcompile
to construct it.
def build_graph():
# Construct the LangGraph stream
builder = StateGraph(TypedDictState)
# Add nodes
builder.add_node("classify_intent", classify_intent)
builder.add_node("retrieve_info", retrieve_info)
builder.add_node("reply", reply)
builder.add_node("generate_code", generate_code)
# Outline stream
builder.set_entry_point("classify_intent")
builder.add_conditional_edges(
"classify_intent",
lambda state: state["intent"],
{
"search": "retrieve_info",
"easy": "reply"
}
)
builder.add_edge("retrieve_info", "reply")
builder.add_edge("reply", "generate_code")
builder.add_edge("generate_code", END)
return builder.compile()
With our graph builder perform prepared, all we have now to do now could be create a stupendous front-end the place we are able to work together with this agent.
Let’s try this now.
Streamlit Entrance-Finish
The front-end is the ultimate piece of the puzzle, the place we create a Consumer Interface that enables us to simply enter a query in a correct textual content field and see the reply correctly formatted.
I selected Streamlit as a result of it is vitally simple to prototype and deploy. Let’s start with the imports.
import os
import time
import streamlit as st
Then, we configure the web page’s look.
# Config web page
st.set_page_config(page_title="Stats Advisor Agent",
page_icon='🤖',
format="extensive",
initial_sidebar_state="expanded")
Create a sidebar, the place the person can enter their OpenAI API key, together with a “Clear” session button.
# Add a spot to enter the API key
with st.sidebar:
api_key = st.text_input("OPENAI_API_KEY", sort="password")
# Save the API key to the atmosphere variable
if api_key:
os.environ["OPENAI_API_KEY"] = api_key
# Clear
if st.button('Clear'):
st.rerun()
Subsequent, we arrange the web page title and directions and add a textual content field for the person to enter a query.
# Title and Directions
if not api_key:
st.warning("Please enter your OpenAI API key within the sidebar.")
st.title('Statistical Advisor Agent | 🤖')
st.caption('This AI Agent is educated to reply questions on statistical assessments from the [Scipy](https://docs.scipy.org/doc/scipy/reference/stats.html) package deal.')
st.caption('Ask questions like: "What's the finest statistical check to match two means".')
st.divider()
# Consumer query
query = st.text_input(label="Ask me one thing:",
placeholder= "e.g. What's the finest check to match 3 teams means?")
Lastly, we are able to run the graph builder and show the reply on display screen.
# Run the graph
if st.button('Search'):
# Progress bar
progress_bar = st.progress(0)
with st.spinner("Considering..", show_time=True):
from langgraph_agent.graph import build_graph
progress_bar.progress(10)
# Construct the graph
graph = build_graph()
consequence = graph.invoke({"query": query})
# Progress bar
progress_bar.progress(50)
# Print the consequence
st.subheader("📖 Reply:")
# Progress bar
progress_bar.progress(100)
st.write(consequence["final_answer"])
if "code_snippet" in consequence:
st.subheader("💻 Steered Python Code:")
st.write(consequence["code_snippet"])
Let’s see the consequence now.

Wow, the result’s spectacular!
- I requested: What’s the finest check to match two teams means?
- Reply: To match the technique of two teams, essentially the most acceptable check is usually the unbiased two-sample t-test if the teams are unbiased and the information is often distributed. If the information shouldn’t be usually distributed, a non-parametric check just like the Mann-Whitney U check is likely to be extra appropriate. If the teams are paired or associated, a paired pattern t-test can be acceptable.
Mission completed for what we proposed to create.
Attempt It Your self
Do you need to give this Agent a Attempt?
Go forward and check the deployed model now!
https://ai-statistical-advisor.streamlit.app
Earlier than You Go
This can be a lengthy publish, I do know. However I hope it was price to learn it to the tip. We discovered so much about LangGraph. It makes us assume otherwise about creating AI brokers.
The framework forces us to consider each step of the data, from the second a query is prompted to the LLM till the reply that can be displayed. Questions like these begin to pop in your thoughts in the course of the improvement course of:
- What occurs after the person asks the query?
- Does the agent have to confirm one thing earlier than transferring on?
- Are there circumstances to think about in the course of the interplay?
This structure turns into a bonus as a result of it makes the entire course of cleaner and scalable, since including a brand new characteristic might be so simple as including a brand new perform (node).
Then again, LangGraph shouldn’t be as user-friendly as frameworks like Agno or CrewAI, which encapsulate many of those abstractions in easier strategies, making the method a lot simpler to be taught and develop, but additionally much less versatile.
Ultimately, it’s all a matter of what downside is being solved and the way versatile you want it to be.
GitHub Repository
https://github.com/gurezende/AI-Statistical-Advisor
About Me
When you favored this content material and need to be taught extra about my work, right here is my web site, the place you may as well discover all my contacts.
[1. LangGraph Docs] https://langchain-ai.github.io/langgraph/concepts/why-langgraph/
[2. Scipy Stats] https://docs.scipy.org/doc/scipy/reference/stats.html
[3. Streamlit Docs] https://docs.streamlit.io/
[4. Statistical Advisor App] https://ai-statistical-advisor.streamlit.app/