Cada día un poco más mientras trabaja con Langgraph.
Seamos realistas: dado que Langchain es uno de los primeros marcos para manejar la integración con LLMS, despegó antes y se convirtió en una especie de ir a Opción cuando se trata de construir agentes listos para la producción, les guste o no.
El hermano menor de Langchain es Langgraph. Este marco utiliza una notación gráfica con nodos y bordes para construir las aplicaciones, haciéndolas altamente personalizables y muy robustas. Eso es lo que estoy disfrutando tanto.
Al principio, algunas anotaciones me parecieron extrañas (¡tal vez solo soy yo!). Pero seguí cavando y aprendiendo más. Y creo firmemente que aprendemos mejor mientras estamos implementando cosas, porque es cuando aparecen los problemas reales. Entonces, después de algunas líneas de código y algunas horas de depuración de código, esa arquitectura gráfica comenzó a tener mucho más sentido para mí, y comencé a disfrutar creando cosas con Langgraph.
De todos modos, si no tienes ninguna introducción al marco, te recomiendo que mires esta publicación [1].
Ahora, aprendamos más sobre el proyecto de este artículo.
El proyecto
En este proyecto, vamos a construir un agente de varios pasos:
- Se necesita en un tipo de modelo de aprendizaje automático: clasificación o regresión.
- Y también ingresaremos las métricas de nuestro modelo, como precisión, RMSE, matriz de confusión, ROC, etc. Cuanto más proporcionamos al agente, mejor será la respuesta.
El agente, equipado con Google Gemini 2.0 Flash:
- Lee la entrada
- Evaluar la métrica del modelo ingresada por el usuario
- Devuelva una lista procesable de sugerencias para ajustar el modelo y mejorar su rendimiento.
Esta es la estructura de la carpeta del proyecto:
ml-model-tuning/
├── langgraph_agent/
│ ├── graph.py #LangGraph logic
│ ├── nodes.py #LLMs and tools
├── main.py # Streamlit interface to run the agent
├── requirements.txt
El agente está en vivo y implementado en esta aplicación web.
Conjunto de datos
El conjunto de datos que se utilizará es un conjunto de datos de juguete muy simple llamado Consejos, desde el paquete Seaborn y de código abierto bajo la licencia BSD 3. Decidí usar un conjunto de datos simple como este porque tiene características categóricas y numéricas, siendo adecuada para ambos tipos de creación de modelos. Además, el comienzo del artículo es el agente, por lo que es donde queremos gastar más atención.
Para cargar los datos, use el siguiente código.
import seaborn as sns
# Data
df = sns.load_dataset('tips')
A continuación, construiremos los nodos.
Nodos
Los nodos de un objeto Langgraph son las funciones de Python. Pueden ser herramientas que el agente usará o una instancia de un LLM. Construimos cada nodo como una función separada.
Pero primero, tenemos que cargar los módulos.
import os
from textwrap import dedent
from dotenv import load_dotenv
load_dotenv()
import streamlit as st
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
Nuestro primer nodo es el que obtiene el tipo de modelo. Simplemente obtiene la entrada del usuario sobre si el modelo a mejorar es una regresión o una clasificación.
def get_model_type(state):
"""Check if the user is implementing a classification or regression model. """
# Define the model type
modeltype = st.text_input("Please let me know the type of model you are working on and hit Enter:",
placeholder="(C)lassification or (R)egression",
help="C for Classification or R for Regression")
# Check if the model type is valid
if modeltype.lower() not in ["c", "r", "classification", "regression"]:
st.info("Please enter a valid model type: C for (C)lassification or R for (R)egression.")
st.stop()
if modeltype.lower() in ["c", "classification"]:
modeltype = "classification"
elif modeltype.lower() in ["r", "regression"]:
modeltype = "regression"
return {"model_type": modeltype.lower()} # "classification" or "regression"
Los otros dos nodos de este gráfico son casi los mismos, pero difieren en la solicitud del sistema. Uno está optimizado para la evaluación de los modelos de regresión, mientras que el otro está especializado en la clasificación. Pegaré solo uno de ellos aquí. Sin embargo, el código completo está disponible en GitHub. Vea todo el código de los nodos aquí.
def llm_node_regression(state):
"""
Processes the user query and search results using the LLM and returns an answer.
"""
llm = ChatGoogleGenerativeAI(
model="gemini-2.5-flash",
api_key=os.environ.get("GEMINI_API_KEY"),
temperature=0.5,
max_tokens=None,
timeout=None,
max_retries=2
)
# Create a prompt
messages = ChatPromptTemplate.from_messages([
("system", dedent("""\
You are a seasoned data scientist, specialized in regression models.
You have a deep understanding of regression models and their applications.
You will get the user's result for a regression model and your task is to build a summary of how to improve the model.
Use the context to answer the question.
Give me actionable suggestions in the form of bullet points.
Be concise and avoid unnecessary details.
If the question is not about regression, say 'Please input regression model metrics.'.
\
""")),
MessagesPlaceholder(variable_name="messages"),
("user", state["metrics_to_tune"])
])
# Create a chain
chain = messages | llm
response = chain.invoke(state)
return {"final_answer": [response]}
Excelente. Ahora es el momento de unir estos nodos construyendo los bordes para conectarlos. En otras palabras, construyendo el flujo de la información de la entrada del usuario a la salida final.
Gráfico
El archivo graph.py se utilizará para generar el objeto Langgraph. Primero, necesitamos importar los módulos.
from langgraph.graph import StateGraph, END
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
from langgraph_agent.nodes import llm_node_classification, llm_node_regression, get_model_type
El siguiente paso es crear el estado del gráfico. Género estatal administra el estado del agente durante todo el flujo de trabajo. Él Realiza un seguimiento de la información El agente se ha reunido y procesado. No es más que una clase con los nombres de las variables y su tipo escrito en el estilo del diccionario.
# Create a state graph
class AgentState(TypedDict):
"""
Represents the state of our graph.
Attributes:
messages: A list of messages in the conversation, including user input and agent outputs.
model_type: The type of model being used, either "classification" or "regression".
question: The initial question from the user.
final_answer: The final answer provided by the agent.
"""
messages: Annotated[AnyMessage, add_messages] # accumulate messages
model_type: str
metrics_to_tune: str
final_answer: str
Para construir el gráfico, utilizaremos una función que:
- Agrega cada nodo con una tupla
("name", node_function_name) - Define el punto de partida en el get_model_type nodo.
.set_entry_point("get_model_type") - Luego, hay un borde condicional, que decide ir al nodo apropiado dependiendo de la respuesta de la
get_model_typenodo. - Finalmente, conecte los nodos LLM al
ENDestado. - Compile el gráfico para prepararlo para su uso.
def build_graph():
# Build the LangGraph flow
builder = StateGraph(AgentState)
# Add nodes
builder.add_node("get_model_type", get_model_type)
builder.add_node("classification", llm_node_classification)
builder.add_node("regression", llm_node_regression)
# Define edges and flow
builder.set_entry_point("get_model_type")
builder.add_conditional_edges(
"get_model_type",
lambda state: state["model_type"],
{
"classification": "classification",
"regression": "regression"
}
)
builder.add_edge("classification", END)
builder.add_edge("regression", END)
# Compile the graph
return builder.compile()
Si desea ver el gráfico, puede usar este pequeño fragmento.
# Create the graph image and save png
from IPython.display import display, Image
graph = build_graph()
display(Image(graph.get_graph().draw_mermaid_png(output_file_path="graph.png")))
Es un agente simple, pero funciona muy bien. Llegaremos a eso pronto. Pero primero debemos construir la pieza frontal.
Construyendo la interfaz de usuario
La interfaz de usuario es una aplicación de transmisión. He elegido esta opción debido a las características fáciles de prototipos y implementación.
Cargamos las bibliotecas necesarias una vez más.
import os
from langgraph_agent.graph import AgentState, build_graph
from textwrap import dedent
import streamlit as st
Configuración del diseño de la página (título, icono, barra lateral, etc.).
## Config page
st.set_page_config(page_title="ML Model Tuning Assistant",
page_icon='🤖',
layout="wide",
initial_sidebar_state="expanded")
Creación de la barra lateral que contiene el campo para agregar una tecla API de Google Gemini y la reinicio botón.
## SIDEBAR | Add a place to enter the API key
with st.sidebar:
api_key = st.text_input("GOOGLE_API_KEY", type="password")
# Save the API key to the environment variable
if api_key:
os.environ["GEMINI_API_KEY"] = api_key
# Clear
if st.button('Clear'):
st.rerun()
Ahora, agregamos el título y las instrucciones de la página para usar el agente. Todo esto es un código simple que usa principalmente la función st.write().
## Title and Instructions
if not api_key:
st.warning("Please enter your OpenAI API key in the sidebar.")
st.title('ML Model Tuning Assistant | 🤖')
st.caption('This AI Agent is will help you tuning your machine learning model.')
st.write(':red[**1**] | 👨💻 Add the metrics of your ML model to be tuned in the text box. The more metrics you add, the better.')
st.write(':red[**2**] | ℹ️ Inform the AI Agent what type of model you are working on.')
st.write(':red[**3**] | 🤖 The AI Agent will respond with suggestions on how to improve your model.')
st.divider()
# Get the user input
text = st.text_area('**👨💻 Add here the metrics of your ML model to be tuned:**')
st.divider()
Y, por último, el código a:
- Ejecutar el
build_graph()función y crear el agente. - Crear el estado inicial del agente, con un vacío
messages. - Invoca al agente.
- Imprima los resultados en la pantalla.
## Run the graph
# Spinner
with st.spinner("Gathering Tuning Suggestions...", show_time=True):
from langgraph_agent.graph import build_graph
agent = build_graph()
# Create the initial state for the agent, with blank messages and the user input
prompt = {
"messages": [],
"metrics_to_tune": text
}
# Invoke the agent
result = agent.invoke(prompt)
# Print the agent's response
st.write('**🤖 Agent Response:**')
st.write(result['final_answer'][0].content)
Todo creado. ¡Es hora de poner a este agente de IA a trabajar!
Por lo tanto, construiremos algunos modelos y le pediremos al agente sugerencias de ajuste.
Ejecutando el agente
Bueno, como este es un agente que nos ayuda con las sugerencias de ajuste del modelo, debemos tener un modelo que sintonizar.
Modelo de regresión
Probaremos primero el modelo de regresión. Podemos construir rápidamente un modelo simple.
# Imports
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.pipeline import Pipeline
from feature_engine.encoding import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import root_mean_squared_error
## Baseline Model
# Data
df = sns.load_dataset('tips')
# Train Test Split
X = df.drop('tip', axis=1)
y = df['tip']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Categorical
cat_vars = df.select_dtypes(include=['object']).columns
# Pipeline
pipe = Pipeline([
('encoder', OneHotEncoder(variables=['sex', 'smoker', 'day', 'time'],
drop_last=True)),
('model', LinearRegression())
])
# Fit
pipe.fit(X_train, y_train)
Ahora, tenemos que recopilar datos de métricas para presentar a nuestro agente de IA para obtener sugerencias de ajuste. Cuantos más datos, mejor. Mientras estoy trabajando con un modelo de regresión, elegí presentar la siguiente información:
- Nombres de características
- Descripción estadística del conjunto de datos
- R²
- Error cuadrado medio de raíz (RMSE)
- Intercepción de regresión y coeficientes
- Vif
total_bill tip sex smoker day time size
count 244.000000 244.000000 244 244 244 244 244.000000
unique NaN NaN 2 2 4 2 NaN
top NaN NaN Male No Sat Dinner NaN
freq NaN NaN 157 151 87 176 NaN
mean 19.785943 2.998279 NaN NaN NaN NaN 2.569672
std 8.902412 1.383638 NaN NaN NaN NaN 0.951100
min 3.070000 1.000000 NaN NaN NaN NaN 1.000000
25% 13.347500 2.000000 NaN NaN NaN NaN 2.000000
50% 17.795000 2.900000 NaN NaN NaN NaN 2.000000
75% 24.127500 3.562500 NaN NaN NaN NaN 3.000000
max 50.810000 10.000000 NaN NaN NaN NaN 6.000000
---
Model: Linear Regression
Score: 0.44
RMSE: 0.84
Intercept: 0.45
Coefficients:
feature coefficient
0 total_bill 0.094700
1 size 0.233484
2 sex_Male 0.028819
3 smoker_No 0.192353
4 day_Sat -0.006064
5 day_Fri 0.179721
6 day_Sun 0.128928
7 time_Dinner -0.094957
VIF:
total_bill 2.226294
tip 1.879238
size 1.590524
Ahora ejecutaré el agente.
Aquí está la respuesta del agente:
🤖 Respuesta del agente:
Aquí hay sugerencias procesables para mejorar su modelo de regresión:
- Explore las relaciones no lineales: Considere agregar características polinomiales para Total_Bill y tamaño, o términos de interacción entre características (por ejemplo, tamaño total_bill *), ya que el modelo lineal actual podría ser demasiado simplista para los patrones de datos subyacentes.
- Evaluar modelos de regresión alternativa: Dado el cuadrado R de 0.44, pruebe otros modelos como regresor bosque aleatorio, regresor de impulso de gradiente o regresor de vectores de soporte, que puede capturar relaciones más complejas y no lineales.
- Dirección de distribución de datos y valores atípicos: Investigue y maneje los valores atípicos en Total_Bill y la punta de la variable objetivo. Considere aplicar transformaciones (por ejemplo, transformación de registro) a características sesgadas para cumplir mejor los supuestos de linealidad y mejorar el rendimiento del modelo.
- Analizar la importancia estadística de la función: Obtenga valores p para cada coeficiente para identificar características que pueden no ser estadísticamente significativas. Eliminar o reevaluar tales características puede simplificar el modelo y potencialmente mejorar la generalización.
Hay un par de sugerencias aquí. Ahora podemos elegir lo que aceptaremos o no. Esto es lo que probé (código en GitHub):
- Entrené a un regresor de bosque aleatorio, pero el resultado no fue bueno con el modelo fuera de la caja, dejando caer el R² a 0.25 y el RMSE a 0.97. Así que descarté esa opción.
- Entonces, si mantengo la regresión lineal, otra sugerencia es usar transformaciones de registro y tratar los valores atípicos. Lo intenté y el resultado es mejor. El modelo va a una R² de 0.55 y RMSE de 0.23. Una mejora significativa.
Modelo de clasificación
Seguí el mismo ejercicio aquí, pero ahora trabajando en un modelo de clasificación, usando el mismo conjunto de datos e intentando predecir si el cliente del restaurante es un fumador o no.
- Entrenado un modelo de clasificación
- Obtuve las métricas iniciales:
Score = 0.69;RMSE = 0.55 - Corrió el agente de IA para sugerencias
- Aplicó algunas sugerencias de ajuste:
class_weight='balanced'yBayesSearchCV. - Tengo las métricas sintonizadas:
Score = 0.71;RMSE = 0.52
Observe cómo la precisión versus el recuerdo también está más equilibrada.
Nuestro trabajo está completo. El agente está funcionando según lo diseñado.
Antes de que te vayas
Hemos llegado al final de este proyecto. En general, estoy satisfecho con el resultado. Este proyecto es bastante simple y rápido de construir, ¡y sin embargo ofrece mucho valor!
Tuning Models no es un de talla única acción. Hay muchas opciones para probar. Por lo tanto, tener la ayuda de un agente de IA para darnos algunas ideas para probar es muy valioso y facilita nuestro trabajo sin reemplazarnos.
¡Prueba la aplicación por ti mismo y avísame si te ayudó a obtener una métrica de rendimiento mejorada!
https://ml tuning-assistant.streamlit.app
Repositorio de Github
https://github.com/gurezende/ml-tuning-assistant
Encuéntrame en línea
Referencias
[1. Building Your First AI Agent with LangGraph] https://medium.com/code-applied/building-your-first-ai-agent-with-langgraph-599a7bcf01cd?sk=a22e309c1e6e3602ae37ef28835ee8433
[2. Using Gemini with LangGraph] https://python.langchain.com/docs/integrations/chat/google_generative_ai/
[3. LangGraph Docs] https://langchain-ai.github.io/langgraph/tutorials/get-started/1-build-basic-chatbot/
[4. Streamlit Docs] https://docs.streamlit.io/
[5. Get a Gemini API Key] https://tinyurl.com/gemini-api- key
[6. GitHub Repository ML Tuning Agent] https://github.com/gurezende/ml-tuning-assistant
[7. Guide to Hyperparameter Tuning with Bayesian Search] https://medium.com/code-applied/dont-guess-get-the-best-a-smart-guide-to-hyperparameter-tuning-with-bayesian-search-123e4e98e845?sk=ff4c378d816bca0c82988f0e8e1d2cdff
[8. Deployed App] https://ml-tuning-assistant.streamlit.app/