Un bucle de herramienta de LangChain elaborado a mano para comparar métricas de existencias en tiempo real
Construya paso a paso un pequeño asistente de comparación de acciones de LangChain y aprenda el ciclo de crear, vincular, invocar y ejecutar que permite a un LLM solicitar datos en tiempo real en lugar de adivinar.
Un modelo de lenguaje solo conoce lo que está en sus datos de entrenamiento, por lo que pedirle el precio de acción de hoy le genera una suposición segura en lugar de un dato real. Esto representa un problema grave para cualquier asunto financiero. La función de llamada a herramientas lo soluciona al permitir que el modelo solicite a tu código que obtenga datos en tiempo real y luego analice los resultados. Esta guía crea un asistente compacto que compara dos acciones por su precio y relación precio/beneficio, mostrando claramente cada etapa del flujo de trabajo de llamadas a herramientas de LangChain: crear, vincular, invocar y ejecutar.
Por qué un modelo sin herramientas solo hace suposiciones
Imagina a un empleado de tienda que conoce de memoria todos los productos y políticas, pero no tiene acceso al sistema de inventario. Si le preguntas si hay una chaqueta en tamaño mediano, te dará una respuesta segura basada en la información de la semana pasada. Eso es un LLM por sí solo: capaz de expresarse, pero ciego ante el presente.
Déle al asistente un escáner de códigos de barras para que verifique la información en lugar de adivinarla. En LangChain, el escáner es una herramienta: una función ordinaria que el modelo puede solicitar para ejecutar, como una búsqueda de precios, una consulta a una base de datos o una llamada a API. La llamada a herramientas es el mecanismo que permite al modelo decidir cuándo usar el escáner, qué elemento escanear y cómo utilizar los resultados obtenidos.
El bucle de cuatro etapas
La llamada a herramientas (a menudo denominada llamada a función) sigue siempre el mismo ciclo:
- Cree la herramienta. Escriba una función en Python con un nombre descriptivo, parámetros tipados y una documentación que explique su funcionamiento.
- Víncüela al modelo. Llame a
.bind_tools()en el modelo de chat para que sepa qué herramientas existen y qué argumentos requieren.
get_stock_price con ticker='MSFT'” en lugar de una respuesta final.La característica importante es que el modelo, y no tu código, decide cuándo se necesita una herramienta. Una pregunta como “¿Cuánto es 2+2?” recibe respuesta directamente, mientras que “Comparar MSFT y AAPL” puede generar dos llamadas a herramientas en una sola respuesta. El modelo solo solicita las llamadas; ejecutarlas sigue siendo tu tarea. Para saber cómo los frameworks automatizan este ciclo, consulta qué es lo que LangChain realmente automatiza una vez que has creado un ciclo de agente.
Paso 1: Instalar dependencias en la primera ejecución
El script comienza verificando sus paquetes e instalando aquellos que falten, de modo que alguien pueda ejecutarlo sin necesidad de realizar un paso separado con pip install. Convierte cada nombre de paquete en su nombre de importación sustituyendo los guiones por guiones bajos (langchain-groq se convierte en langchain_groq) y, de no ser posible, recurre a llamar a pip a través del intérprete actual:
import sys
import subprocess
required_packages = ["langchain-groq", "langchain-core", "yfinance"]
for package in required_packages:
try:
__import__(package.replace("-", "_"))
except ImportError:
print(f"📦 Package '{package}' not found. Installing now...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
Eso es adecuado para demostraciones, pero los proyectos reales deben especificar las dependencias en requirements.txt o pyproject.toml; las instalaciones en tiempo de ejecución hacen que los builds no sean reproducibles.
Paso 2: Proporcionar la clave API
El modelo funciona en Groq, por lo que el cliente de Groq necesita una clave API en el entorno. La demostración la asigna directamente como marcador de posición:
import os
os.environ["GROQ_API_KEY"] = "Your-API"
Las claves reales deben encontrarse en un archivo .env excluido del control de versiones o en un gestor de secretos, nunca en código comprometido.
Paso 3: Definir la herramienta para datos bursátiles
La herramienta utiliza yfinance para buscar un símbolo bursátil, obtener el precio de cierre más reciente de un día de los registros históricos, y leer la capitalización de mercado y el ratio P/E de las informaciones del símbolo:
from langchain_core.tools import tool
import yfinance as yf
@tool
def get_stock_price(ticker: str) -> str:
"""Fetches the current stock price and key statistics for a given ticker symbol."""
try:
stock = yf.Ticker(ticker)
todays_data = stock.history(period='1d')
if todays_data.empty:
return f"Could not find data for ticker {ticker}."
price = todays_data['Close'].iloc[-1]
info = stock.info
market_cap = info.get('marketCap', 'N/A')
pe_ratio = info.get('trailingPE', 'N/A')
return f"{ticker} Current Price: ${price:.2f}, Market Cap: {market_cap}, P/E Ratio: {pe_ratio}"
except Exception as e:
return f"Error fetching data for {ticker}: {str(e)}"
Tres detalles hacen que esta función sea una buena herramienta:
- El decorador
@toolconvierte la función en una herramienta de LangChain y deriva un esquema de entrada a partir de sus indicaciones de tipo. - La documentación no es un simple decorador. El modelo la lee junto con el nombre para decidir cuándo aplicar la herramienta, por lo que debe indicar claramente qué devuelve.
- Los errores se devuelven como cadenas descriptivas en lugar de generar excepciones. Un símbolo desconocido o una solicitud fallida producen un mensaje sobre el cual el modelo puede razonar (“no se pudieron encontrar datos”), en lugar de una excepción que interrumpe todo el proceso.
Tenga en cuenta que yfinance es un wrapper no oficial de Yahoo Finance: las cotizaciones pueden estar desactualizadas y faltar campos, por lo que se utilizan los valores por defecto 'N/A'.
Paso 4: Crear el modelo y vincular la herramienta
A continuación, se crea el modelo de chat y se le adjunta la lista de herramientas:
from langchain_groq import ChatGroq
llm = ChatGroq(
model="openai/gpt-oss-120b",
temperature=0
)
tools = [get_stock_price]
llm_with_tools = llm.bind_tools(tools)
bind_tools() envía los nombres de las herramientas, sus descripciones y los esquemas de argumentos al modelo con cada solicitud, para que este sepa qué puede solicitar. Tenga en cuenta que también se mantiene el llm no vinculado; se reutilizará más tarde para el resumen final. El nombre del modelo refleja lo que Groq ofrecía en el momento de escribir este texto, así que verifique la lista actual de modelos del proveedor antes de ejecutarlo.
Al establecer temperature=0, la salida se mantiene enfocada y consistente, aunque esto reduce la aleatoriedad en lugar de garantizar respuestas idénticas.
Paso 5: Construir una cadena de prompts
Una plantilla de prompt proporciona una instrucción del sistema y ubica la pregunta del usuario dentro del mensaje dirigido al humano. Luego, el operador de tubería combina el prompt con el modelo consciente de las herramientas en una sola unidad ejecutable, similar a un pipeline de Unix:
from langchain_core.prompts import ChatPromptTemplate
from IPython.display import display, Markdown
prompt = ChatPromptTemplate.from_messages([
("system", "You are an expert financial analyst. Use the tools provided to pull real-time data before comparing or concluding."),
("human", "{input}")
])
chain = prompt | llm_with_tools
El mensaje del sistema realiza el trabajo real aquí: indica al modelo que obtenga datos mediante las herramientas antes de realizar cualquier comparación. Sin él, el modelo podría responder con datos de entrenamiento obsoletos.
Paso 6: Ejecutar el bucle y manejar múltiples llamadas a herramientas
El bloque principal une todo. Invoca la cadena, verifica si la respuesta contiene tool_calls, ejecuta cada búsqueda solicitada, recopila las salidas y, finalmente, pide al modelo simple que escriba una comparación a partir de los datos recopilados. Si no se solicitaron herramientas, muestra la respuesta directa del modelo:
if __name__ == "__main__":
query = "Compare the current stock price and P/E ratio of Microsoft (MSFT) AND Apple (AAPL). Which one looks cheaper based on P/E?"
print(f"🚀 Invoking Financial Pipeline with query: '{query}'\n")
# 1. Ask the model what tools it wants to use
ai_message = chain.invoke({"input": query})
# 2. Check if the model requested tool use
if ai_message.tool_calls:
print(f"🛠️ Model requesting {len(ai_message.tool_calls)} real-time tool lookups...\n")
tool_outputs = []
# 3. Execute ALL generated tool calls
for tool_call in ai_message.tool_calls:
if tool_call["name"] == "get_stock_price":
ticker_symbol = tool_call["args"]["ticker"]
print(f" -> Executing tool lookup for: {ticker_symbol}")
result = get_stock_price.invoke(tool_call["args"])
print(f" [Tool Output] {result}")
tool_outputs.append(result)
# 4. Supply the full collective data back to the LLM
summary_prompt = f"""
User Query: {query}
Real-time Data Harvested: {'; '.join(tool_outputs)}
Synthesize a final response evaluating which asset looks cheaper.
"""
final_answer = llm.invoke(summary_prompt)
print("\n--- Final Analysis Output ---")
display(Markdown(final_answer.content))
else:
print("\n--- Final Analysis Output ---")
print(ai_message.content)
En el caso de preguntas relacionadas tanto con Microsoft como con Apple, el modelo suele devolver dos llamadas a herramientas en una sola respuesta, una por cada símbolo bursátil. El bucle las ejecuta todas antes de continuar, por lo que en el paso final se obtienen ambos conjuntos de números al mismo tiempo. El modelo solicita las llamadas de forma paralela, pero este código las ejecuta una tras otra; con APIs lentas sería posible realizarlas de manera concurrente.
Hay dos mejoras importantes que vale la pena conocer una vez que esto funcione. Primero, la verificación de nombres dentro del bucle es la forma en que se distribuyen las tareas entre varias herramientas; un diccionario que asocia los nombres de las herramientas con sus objetos respectivos funciona mejor que una cadena de instrucciones if. Segundo, esta versión devuelve los resultados al crear un prompt de texto nuevo. El enfoque más habitual en LangChain consiste en añadir cada resultado como un ToolMessage que incluye el tool_call_id correspondiente a la conversación, e invocar nuevamente el modelo asociado a esa herramienta, lo cual permite conservar todo el intercambio y facilita que el modelo solicite llamadas adicionales si los primeros resultados no son suficientes.
La expansión del asistente se realiza de forma incremental: una herramienta como get_financial_news o calculate_valuation se integra mediante la misma llamada a bind_tools(), y el modelo elige entre las herramientas según sus descripciones.
Puntos clave
- Una herramienta es una función con un nombre claro, indicaciones de tipo y una documentación; el decorador
@toolse encarga del resto. .bind_tools()conecta sus funciones al modelo describiéndolas en cada solicitud.- El modelo solo solicita llamadas a herramientas. Su código las ejecuta y devuelve los resultados, y ese control es útil, no una limitación.
- Devuelva los errores de las herramientas como cadenas legibles para que una sola búsqueda fallida no interrumpa el proceso.
- Comience con una herramienta y un bucle, luego agregue herramientas, transmisión de resultados basada en mensajes y concurrencia según sea necesario.
El mismo patrón se aplica dondequiera que el modelo necesite información actual, desde el clima y el inventario hasta los registros de CRM o su propia base de datos, de modo que los asistentes puedan razonar sobre sistemas en tiempo real en lugar de una instantánea congelada.
Lecturas relacionadas
- El bucle de chat sin estado: Llamada a la API de OpenAI a mano en Python — Crea un chat de múltiples turnos con el SDK de Python de OpenAI gestionando tú mismo el historial de mensajes, y descubre por qué este mismo bucle sirve como base para la memoria y los agentes de LangChain.
- Seis primitivas de LangGraph y el modo de fallo oculto en cada una — Aprende sobre el estado, los nodos, las aristas, la enrutación condicional, el guardado de puntos intermedios e las interrupciones en LangGraph a través de los errores específicos que cada una genera y cómo evitarlos.