Inicio / Artículos / Agentes ReAct en LangGraph: Paso a paso por el pensamiento, la acción y la observación

Agentes ReAct en LangGraph: Paso a paso por el pensamiento, la acción y la observación

Implemente el bucle ReAct como nodos de gráfico explícitos con estado tipado, llamadas a herramientas y condiciones de detención que pueda probar.

4143 palabras

Esta guía reconstruye un camino funcional para: ReAct Agents Explained: Una implementación paso a paso utilizando LangGraph. Se enfoca en contratos, verificaciones y código que se puede insertar en un repositorio sin tener que adivinar la intención. Para obtener una visión general, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado.

Introducción

Como introducción, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Considere esta fase como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Separe la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

¿Qué es un agente ReAct?

En “¿Qué es un agente ReAct?”, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Se deben registrar los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Es necesario separar la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

Por qué ReAct es mejor que la cadena de pensamiento pura

Para explicar por qué ReAct es mejor que el enfoque puro de cadena de pensamiento, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Se debe mantener la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Se debe separar la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo. Para explicar por qué ReAct es mejor que el enfoque puro de cadena de pensamiento, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Es preferible utilizar unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad.

en lugar de una cadena de procesos enredada.

Thought: I don’t know the answer yet. I should search.
Action: Search("Paris weather this week")
Observation: It will rain on Thursday.
Thought: I should suggest indoor activities.
Final Answer: ...

ReAct Prompting

En el caso de ReAct Prompting, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre excesivos invitan a inyecciones y hacen que las auditorías sean costosas.

Propósito de ReAct Prompting

Para el propósito de la instrucción ReAct, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Delimite estrictamente los esquemas de las herramientas; los argumentos de texto libre excesivos facilitan inyecciones y encarecen las auditorías.

Elementos clave de la instrucción ReAct

Para los elementos clave de la técnica ReAct Prompting, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben encontrarse en un lugar donde los operadores puedan realizar auditorías sin necesidad de leer todo el sistema. Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre excesivos facilitan la inyección de datos y hacen que las auditorías sean costosas. Para los elementos clave de la técnica ReAct Prompting, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complejo y entrelazado.

1. Razonamiento por cadena de pensamientos

En el caso del 1. Razonamiento por cadena de pensamientos, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Se debe tratar esta etapa como un contrato entre las entradas y los resultados validados. Es necesario nombrar los artefactos, definir las verificaciones de éxito y rechazar cualquier completación parcial silenciosa. Se debe establecer un punto de control después de llamadas al modelo costosas para que una repetición no genere nuevos costos por el mismo trabajo.

2. Espacio de acciones explícito

Para el Espacio de Acción Explícito 2, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Realice un punto de control después de llamadas al modelo costosas para que una repetición no genere nuevos costos por el mismo trabajo.

3. Integración de Observaciones

Para la Integración de Observaciones 3, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben encontrarse en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Coloque puntos de control después de llamadas a modelos costosas para que una repetición no genere nuevos costos por el mismo trabajo. Para la Integración de Observaciones 3, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado.

4. Bucles iterativos

Para el bucle iterativo 4, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Separe la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

5. Generación de la respuesta final

Para el paso 5: Generación de la respuesta final, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Separe la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

Estructura canónica del prompt ReAct

Para la estructura canónica de prompt ReAct, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben encontrarse en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Separe la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo. Para la estructura canónica de prompt ReAct, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado.

...

Question: <user question>Thought: <reason about what to do next>

Action: <selected tool>
Action Input: <tool input>
Observation: <tool output>
... (repeat as needed)
Thought: I now know the final answer
Final Answer: <answer to the user>

Inducción de prompts Zero-Shot ReAct

Para la inducción de prompts Zero-Shot ReAct, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Se debe tratar esta etapa como un contrato entre las entradas y los resultados validados. Es necesario nombrar los artefactos, definir las verificaciones de éxito y rechazar cualquier completación parcial silenciosa. Los esquemas de las herramientas deben estar estrictamente delimitados; los argumentos de texto libre excesivos facilitan la inyección de datos y hacen que las auditorías sean costosas.

Inducción de prompts ReAct vs agentes ReAct

Para ReAct Prompting frente a ReAct Agents, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Delimite estrictamente los esquemas de las herramientas; los argumentos de texto libre excesivos facilitan inyecciones y encarecen las auditorías.

¿Por qué LangGraph para ReAct Agents?

Para “¿Por qué LangGraph para agentes ReAct?”, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un único lugar que los operadores puedan auditar sin tener que leer todo el grafo. Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre excesivos invitan a inyecciones y hacen que las auditorías sean costosas. Para “¿Por qué LangGraph para agentes ReAct?”, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una etapa falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado.

El problema fundamental: ReAct es una máquina de estados, no un prompt

En “El problema fundamental: ReAct es una máquina de estados, no un prompt”, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar un paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Se debe tratar esta etapa como un contrato entre las entradas y los resultados validados. Es necesario nombrar los artefactos, definir las verificaciones de éxito y rechazar cualquier completación parcial silenciosa. Se debe establecer un punto de control después de cada llamada al modelo costosa, para que una repetición no genere nuevos costos por el mismo trabajo.

Qué falla sin LangGraph

Para “What Breaks Without LangGraph”, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Realice un punto de control después de llamadas al modelo costosas para que una repetición no genere nuevos costos por el mismo trabajo.

1. Flujo de control implícito

Para el Flujo de Control Implícito 1, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben encontrarse en un lugar que los operadores puedan auditar sin necesidad de leer todo el sistema. Coloque puntos de control después de llamadas a modelos costosas para que una repetición no genere nuevos costos por el mismo trabajo. Para el Flujo de Control Implícito 1, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado.

while True:
    llm_output = llm(prompt)
    if "Action:" in llm_output:
        tool_result = call_tool(...)
    else:
        break

2. Gestión frágil de estados

2. Gestión de estados frágiles: defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Separe la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

3. Sin semántica de bucles de primera clase

Para el punto 3: Al no existir una semántica de bucle de primera clase, se deben definir las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido, sin tener que adivinar el estado oculto. Se deben registrar los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el flujo pasa de entornos de demostración a entornos compartidos. Se debe separar la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

4. Baja preparación para la producción

Para el punto 4: Baja preparación para producción, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben encontrarse en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Separe la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo. Para el punto 4: Baja preparación para producción, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complicado.

Conceptos clave en LangGraph (visión centrada en el agente)

En los Conceptos clave de LangGraph (visión centrada en el agente), se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Asegúrese de que los esquemas de las herramientas estén estrictamente delimitados; los argumentos de texto libre excesivos facilitan inyecciones y encarecen las auditorías.

1. Estado: La memoria del agente

Para el punto 1: Memoria del Agente. Defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando la tarea pasa de un entorno de demostración a uno compartido. Delimite estrictamente los esquemas de las herramientas; los argumentos de texto libre excesivos facilitan inyecciones maliciosas y encarecen las auditorías.

2. Nodos: Unidades cognitivas y operativas

Para los nodos 2: Unidades Cognitivas y Operativas, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan realizar auditorías sin tener que leer todo el sistema. Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre excesivos facilitan la inyección de datos y hacen que las auditorías sean costosas. Para los nodos 2: Unidades Cognitivas y Operativas, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complejo y entrelazado.

3. Bordes: Flujo de control explícito

En la sección 3. Bordes: Flujo de control explícito, se deben definir las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Realice un punto de control después de llamadas costosas al modelo para que una repetición no genere nuevos costos por el mismo trabajo.

4. Ejecución determinística con flexibilidad

Para 4. Ejecución determinística con flexibilidad, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y costos junto con los resultados funcionales. La visibilidad temprana evita facturas inesperadas cuando el flujo pasa de entornos de demostración a entornos compartidos. Realice un punto de control después de llamadas al modelo costosas para que una repetición no genere nuevos costos por el mismo trabajo.

ReAct + LangGraph: Una combinación ideal

Para ReAct + LangGraph: Una combinación ideal, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben encontrarse en un único lugar que los operadores puedan auditar sin necesidad de leer todo el sistema. Utilice puntos de control después de llamadas al modelo costosas para que un intento de repetición no genere nuevos costos por el mismo trabajo. Para ReAct + LangGraph: Una combinación ideal, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complicado.

Caso de uso: Asistente para cancelaciones de hoteles (política + cálculo de reembolsos)

Para el caso de uso: Asistente para cancelaciones de hoteles (política + cálculo de reembolsos), defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Trate esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Separe la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

Enunciado del problema

Para la descripción del problema, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Separe la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

Paso 1: Instalar dependencias

Separe la planificación de la ejecución de las herramientas: el planificador propone; el ejecutor realiza los cambios; el verificador comprueba los resultados en relación con el objetivo.

pip install -U langgraph langchain langchain-openai
export OPENAI_API_KEY="..."

Paso 2: Definir herramientas (sus “Acciones”)

Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre amplios facilitan la inyección de datos y hacen que las auditorías sean costosas.

from typing import TypedDict, Annotated
from datetime import datetime
import json

from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
    BaseMessage,
    HumanMessage,
    ToolMessage,
    SystemMessage
)
from langchain_core.tools import tool
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import tools_condition

@tool
def get_cancellation_policy(rate_plan: str) -> str:
    """
    Returns cancellation policy text for a given rate plan.
    """
    policies = {
        "flexible": "Free cancellation until 24 hours before check-in. After that, first night is charged.",
        "semi-flex": "Free cancellation until 72 hours before check-in. After that, 50% of the stay is charged.",
        "non-refundable": "No refund after booking. Full stay amount is charged on cancellation."
    }
    key = rate_plan.strip().lower()
    return policies.get(key, "Policy not found. Supported: flexible, semi-flex, non-refundable.")
@tool
def calculate_refund(
    rate_plan: str,
    check_in: str,
    cancel_date: str,
    nightly_rate: float,
    nights: int
) -> str:
    """
    Calculates refund amount based on a simplified policy model.
    Dates format: YYYY-MM-DD
    """
    rp = rate_plan.strip().lower()
    ci = datetime.strptime(check_in, "%Y-%m-%d").date()
    cd = datetime.strptime(cancel_date, "%Y-%m-%d").date()
    total = nightly_rate * nights
    days_before = (ci - cd).days
    if rp == "non-refundable":
        refund = 0.0
        charged = total
        rule = "Non-refundable: no refund."
    elif rp == "flexible":
        if days_before >= 1:
            refund = total
            charged = 0.0
            rule = "Flexible: cancelled >= 24h before check-in, full refund."
        else:
            charged = nightly_rate  # 1 night penalty
            refund = max(total - charged, 0.0)
            rule = "Flexible: late cancel, 1 night charged."
    elif rp == "semi-flex":
        if days_before >= 3:
            refund = total
            charged = 0.0
            rule = "Semi-flex: cancelled >= 72h before check-in, full refund."
        else:
            charged = 0.5 * total
            refund = total - charged
            rule = "Semi-flex: late cancel, 50% charged."
    else:
        return "Unsupported rate plan. Use: flexible, semi-flex, non-refundable."
    return (
        f"Rule: {rule}\n"
        f"Days before check-in: {days_before}\n"
        f"Total: ${total:.2f}\n"
        f"Charged: ${charged:.2f}\n"
        f"Refund: ${refund:.2f}"
    )

Paso 3: Crear un bucle ReAct en LangGraph (Razonamiento → Herramienta → Razonamiento)

Delimite estrictamente los esquemas de las herramientas. Los argumentos de texto libre amplios facilitan la inyección de datos y hacen que las auditorías sean costosas.

from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
from langgraph.prebuilt import ToolNode, tools_condition
# 1) Define state
class AgentState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    booking_id: str
# 2) Define structured Output schema
class RefundDecision(BaseModel):
    booking_id: str
    rate_plan: str
    total_amount: float
    charged_amount: float
    refund_amount: float
    policy_summary: str
    explanation: str
# 2) Choose model and System prompt
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
SYSTEM_PROMPT = SystemMessage(
    content="""
You are a hotel cancellation assistant.
Rules:
- Use tools when needed.
- Never guess policy or refund.
- Final answer MUST be valid JSON with this schema:
{
  "booking_id": "...",
  "rate_plan": "...",
  "total_amount": number,
  "charged_amount": number,
  "refund_amount": number,
  "policy_summary": "...",
  "explanation": "..."
}
"""
)
# 3) Register tools
tools = [get_cancellation_policy, calculate_refund]
def safe_tool_node(state):
    last_msg = state["messages"][-1]
    if not hasattr(last_msg, "tool_calls") or not last_msg.tool_calls:
        return {}
    tool_call = last_msg.tool_calls[0]
    tool_name = tool_call["name"]
    if tool_name not in ALLOWED_TOOLS:
        return {
            "messages": [
                ToolMessage(
                    content=f"Tool '{tool_name}' is not allowed.",
                    tool_call_id=tool_call["id"]
                )
            ]
        }
    for tool in tools:
        if tool.name == tool_name:
            result = tool.invoke(tool_call["args"])
            return {
                "messages": [
                    ToolMessage(
                        content=result,
                        tool_call_id=tool_call["id"]
                    )
                ]
            }
# 4)Before reasoning, check if we already processed this booking.
REFUND_MEMORY = {}
def memory_lookup_node(state):
    booking_id = state["booking_id"]
    if booking_id in REFUND_MEMORY:
        return {
            "messages": [
                HumanMessage(
                    content=f"Cached decision found:\n{REFUND_MEMORY[booking_id]}"
                )
            ]
        }
    return {}
# 5) Reasoning node: LLM decides next action (tool call) or final answer
def agent_node(state: AgentState):
    # Bind tools so the model can produce tool calls
    llm_with_tools = llm.bind_tools(tools)
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}
# 6) After final decision, store it.
def memory_write_node(state):
    booking_id = state["booking_id"]
    final_answer = state["messages"][-1].content
    REFUND_MEMORY[booking_id] = final_answer
    return {}

Construir y compilar el grafo

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

# 7) Build the graph
builder = StateGraph(AgentState)

# Nodes
builder.add_node("memory_lookup", memory_lookup_node)
builder.add_node("agent", agent_node)
builder.add_node("tools", safe_tool_node)
builder.add_node("memory_write", memory_write_node)
# Flow
builder.add_edge(START, "memory_lookup")
builder.add_edge("memory_lookup", "agent")
builder.add_conditional_edges(
    "agent",
    tools_condition,
    {
        "tools": "tools",   # model wants to act
        END: "memory_write" # model finished reasoning
    }
)
builder.add_edge("tools", "agent")
builder.add_edge("memory_write", END)
graph = builder.compile()

Paso 4: Ejecutar el agente en el caso de uso

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

query = """
Booking details:
Rate plan: Non-Refundable
Check-in: 2026-01-20
Nights: 2
Nightly rate: 120
Cancelled on: 2026-01-18
"""

result = graph.invoke({
    "booking_id": "BKG-12345",
    "messages": [
        SYSTEM_PROMPT,
        HumanMessage(content=query)
    ]
})
final_output = result["messages"][-1].content
print(final_output)

Resultado

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

{
  "booking_id": "BKG-12345",
  "rate_plan": "Non-Refundable",
  "total_amount": 240.0,
  "charged_amount": 240.0,
  "refund_amount": 0.0,
  "policy_summary": "Non-refundable bookings do not allow refunds after confirmation.",
  "explanation": "The booking was made under a non-refundable rate plan, which charges the full stay amount regardless of cancellation timing."
}

Qué ocurre internamente (comportamiento ReAct)

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

1) Pensamiento (Razonamiento)

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

2) Acción (Llamada a herramienta)

Los esquemas de las herramientas deben estar estrictamente delimitados. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

3) Observación (resultados de las herramientas)

Punto de control después de llamadas al modelo costosas para que un intento de repetición no vuelva a facturar el mismo trabajo.

4) Respuesta final

Punto de control después de llamadas al modelo costosas para que un intento de repetición no vuelva a facturar el mismo trabajo.

Por qué esto es “ReAct” (y no solo herramientas)

Punto de control después de llamadas al modelo costosas para que un intento de repetición no vuelva a facturar el mismo trabajo.

Conclusión

Separar la planificación de la ejecución de las herramientas. El planificador propone; el ejecutor modifica; el verificador verifica los resultados en relación con el objetivo.

Lista de verificación operativa

Delimitar estrictamente los esquemas de las herramientas. Los argumentos de texto libre amplios invitan a inyecciones y hacen que las auditorías sean costosas.

Los bordes condicionales deben codificar las reglas de negocio como funciones con nombre, no como texto oculto en los prompts.

Colocar los tipos junto con los componentes y mantener las propiedades limitadas. Los conjuntos amplios de propiedades se convierten en la deuda que TypeScript estaba destinado a prevenir.

Escribe un manual breve: cómo rotar claves, cómo vaciar la cola de tareas y cómo revertir el último cambio.

Lecturas relacionadas