Inicio / Artículos / Dejar que Géminis elija la fuente: FAISS, Tavily y respuestas directas en LangGraph

Dejar que Géminis elija la fuente: FAISS, Tavily y respuestas directas en LangGraph

Cree un flujo de trabajo pequeño en LangGraph en el que Gemini dirija cada pregunta a una base de conocimientos FAISS, a una búsqueda web en Tavily o a una respuesta directa, mediante un sistema de enrutamiento con salida estructurada.

3771 palabras

La mayoría de los prototipos de respuesta a preguntas procesan cada consulta a través de un flujo fijo, aunque las preguntas difieren en sus necesidades: algunas dependen de documentos internos de la empresa, otras de hechos que cambian diariamente y algunas solo requieren conocimientos generales. Esta guía crea un flujo de trabajo compacto en Python en el que un modelo de lenguaje analiza primero cada pregunta y la envía al lugar adecuado: un almacenamiento de vectores FAISS para conocimientos internos, Tavily para resultados web en tiempo real, o directamente a Gemini. Al final comprenderá cómo los estados, nodos y aristas condicionales se integran en LangGraph, por qué una salida estructurada hace que un router de LLM sea fiable y dónde este diseño, de tamaño didáctico, necesita mejoras antes de enfrentarse a usuarios reales. Si desea un catálogo más amplio de formas de grafos, consulte el artículo sobre rutización, expansión en abanico.

Los patrones de crítica y aprobación en LangGraph complementan esta implementación práctica.

Tres preguntas, tres fuentes diferentes

Imaginemos a tres usuarios. El primero pregunta sobre la política de vacaciones de la empresa; solo los documentos internos pueden responder a eso. El segundo quiere conocer el clima de hoy en Uttarakhand; ningún documento interno contendrá esa información, por lo que el sistema debe buscar en la web. El tercero pregunta qué es RAG; el modelo ya lo sabe, y buscar cualquier otra cosa solo añadiría demoras.

Por lo tanto, la arquitectura objetivo coloca un enrutador basado en Gemini delante de tres ramas que todas convergen en un único paso de generación de respuesta:

User Question
                               |
                               v
                       +----------------+
                       |   AI Router    |
                       |    (Gemini)    |
                       +----------------+
                         /      |      \
                        /       |       \
                       v        v        v
                    FAISS    Tavily    Gemini
                 Internal DB  Web      Direct
                       \        |        /
                        \       |       /
                         v      v       v
                       +----------------+
                       | Generate Answer|
                       |     Gemini     |
                       +----------------+
                               |
                               v
                            Answer

El enrutador es el corazón del diseño. La solución rápida tentadora es la coincidencia de palabras clave, como se muestra en el pseudocódigo a continuación, donde palabras específicas en la pregunta seleccionan una rama:

if "weather" in question:
    use_tavily()
elif "leave" in question:
    use_faiss()
else:
    use_gemini()

Aquí esa decisión se delega en Gemini. Dado que el modelo interpreta la pregunta en lugar de escanearla en busca de palabras desencadenantes, el flujo de trabajo recibe la etiqueta de agencial.

IA generativa, agentes y flujos de trabajo agenciales

A menudo estos términos se usan de forma intercambiable, pero describen diferentes niveles de participación del modelo.

IA generativa simple

La aplicación más sencilla pasa un prompt al modelo y devuelve lo que este produce:

User Question
      |
      v
     LLM
      |
      v
    Answer

Puede explicar el RAG a partir de los datos de entrenamiento, pero no sabe nada sobre su política de vacaciones.

Agentes que llaman a herramientas

Un agente amplía las capacidades del modelo con herramientas como una base de datos, un motor de búsqueda o una API externa, y le permite decidir si debe utilizar alguna antes de responder:

User Question
      |
      v
     LLM
      |
      +------> Database
      |
      +------> Web Search
      |
      +------> API
      |
      v
    Answer

Por ejemplo, una pregunta sobre el clima puede responderse consultando un servicio meteorológico en lugar de que el modelo invente un pronóstico plausible.

Flujos de trabajo agentes

Un flujo de trabajo agente permite que el modelo influya en la ruta que sigue la ejecución en tiempo de ejecución. En este proyecto, el flujo se presenta de la siguiente manera:

Question
   |
   v
Router
   |
   +----> FAISS
   |
   +----> Tavily
   |
   +----> Gemini

El desarrollador define las tres rutas posibles; el modelo solo selecciona entre ellas para cada pregunta recibida. No se le otorga un control total de la aplicación, sino solo un menú de acciones permitidas. “Flujo de trabajo de enrutamiento agente” es el nombre más preciso para ese arreglo.

Configuración del proyecto

Necesita Python 3.10 o una versión más reciente, una clave de API para Google Gemini y una clave de API para Tavily. Instale las bibliotecas de una sola vez:

pip install -U langchain langchain-google-genai langchain-community langgraph faiss-cpu tavily-python python-dotenv pydantic

Mantenga ambas claves en un archivo .env en lugar de en el código fuente:

GOOGLE_API_KEY=your_google_api_key
TAVILY_API_KEY=your_tavily_api_key

Las importaciones incluyen ayudantes de tipado, Pydantic para el esquema de enrutamiento, los wrappers de documentos y FAISS de LangChain, las clases de chat e inserción de embeddings de Gemini, los primitivos de grafo de LangGraph y el cliente Tavily:

import os
from typing import List, Literal
from typing_extensions import TypedDict
from dotenv import load_dotenv
from pydantic import BaseModel
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_google_genai import (
    ChatGoogleGenerativeAI,
    GoogleGenerativeAIEmbeddings,
)
from langgraph.graph import StateGraph, START, END
from tavily import TavilyClient

El siguiente paso, mostrado como un fragmento de una sola línea, consiste simplemente en cargar las variables de entorno:

Load the environment variables:

Con override=True, los valores de .env tienen prioridad sobre las variables ya definidas en tu shell:

load_dotenv(override=True)

Configuración de Gemini para enrutamiento, respuesta e inserciones de embeddings

El modelo de chat cumple una doble función: primero elige qué fuente debe manejar una pregunta y, posteriormente, compone la respuesta final a partir del contexto recopilado. La configuración siguiente habilita dos intentos automáticos y deja sin establecer los límites de tokens y tiempo de espera:

model = ChatGoogleGenerativeAI(
    model="gemini-3.6-flash",
    max_tokens=None,
    timeout=None,
    max_retries=2,
    api_key=os.getenv("GOOGLE_API_KEY"),
)

Los identificadores de los modelos cambian con frecuencia, por lo que debe confirmar el nombre en el fragmento de código contra la lista actual de modelos de Gemini antes de ejecutarlo.

Los embeddings son un tema distinto gestionado por un modelo separado. Gemini Embedding convierte cada documento en un vector numérico:

embeddings = GoogleGenerativeAIEmbeddings(
    model="models/gemini-embedding-001",
    google_api_key=os.getenv("GOOGLE_API_KEY"),
)

Los vectores son lo que hace posible la búsqueda semántica: los textos con significados similares se encuentran cerca entre sí en el espacio vectorial, de modo que una pregunta puede recuperar pasajes relevantes incluso cuando comparte pocas palabras exactas con ellos.

Una pequeña base de conocimiento interna en FAISS

Para mantener la atención en el flujo de trabajo, la base de conocimientos contiene solo tres documentos cortos: una política de reembolso de 30 días, un permiso de 20 días de vacaciones remuneradas con un requisito de notificación de siete días, y los horarios de soporte durante los días laborables. En un sistema real, estos textos provendrían de manuales, PDFs, páginas de Notion, tickets de soporte, documentos internos o filas de base de datos.

documents = [
    Document(
        page_content="""
        Our company provides a 30-day refund policy.
        Customers can request a refund within 30 days of purchase.
        """
    ),
    Document(
        page_content="""
        Employees receive 20 paid vacation days per year.
        Vacation requests must be submitted at least 7 days in advance.
        """
    ),
    Document(
        page_content="""
        The company provides technical support from Monday to Friday,
        9 AM to 6 PM IST.
        """
    ),
]

Construir la tienda y encapsularla como un recuperador requiere dos llamadas. Al establecer k en 3, se solicitan los tres documentos más cercanos; en este corpus simplificado, eso significa que cada documento se devuelve ordenado según su similitud:

vector_store = FAISS.from_documents(
    documents,
    embeddings,
)

retriever = vector_store.as_retriever(
    search_kwargs={"k": 3}
)

FAISS no comprende el lenguaje; es un índice para búsquedas de vecinos más cercanos. El modelo de embedding convierte la pregunta recibida en un vector, y FAISS devuelve los documentos almacenados cuyos vectores están más cerca de él.

Agregar un cliente Tavily para información en tiempo real

La búsqueda en la web solo necesita una instancia de cliente creada a partir de la segunda clave API:

tavily_client = TavilyClient(
    api_key=os.getenv("TAVILY_API_KEY")
)

Diseño del estado compartido

El estado es la idea central en LangGraph: un diccionario tipado que se desplaza por el grafo, del cual cada nodo lee y al que contribuye. Este flujo de trabajo necesita la pregunta, los documentos recuperados, los resultados de Tavily, la fuente elegida y la respuesta final:

class AgentState(TypedDict):
    question: str
    documents: List[Document]
    tavily_response: str
    source: str
    answer: str

Conceptualmente, funciona como un registro compartido que cada paso puede ver:

AgentState
                    |
        +-----------+-----------+
        |           |           |
    question    documents    source
                    |
             tavily_response
                    |
                  answer

Un nodo recibe el estado actual, utiliza los campos que le interesan y devuelve actualizaciones, las cuales LangGraph fusiona antes de pasar el estado al nodo siguiente.

El nodo de recuperación FAISS

Este nodo toma la pregunta del estado, la procesa a través del recuperador y almacena los documentos que coinciden:

def retrieve_from_faiss(state : AgentState) -> AgentState:
    question = state['question']

    """ Fetch the details from the FAISS vector database
    """

    result = retriever.invoke(question)

    return {**state, "documents": result}

Se ejecuta solo cuando el router ha elegido la base de conocimientos interna. Una pregunta como la siguiente es un desencadenante típico:

"What is our company leave policy?"

Para esa entrada, el recuperador debe mostrar el documento de vacaciones:

Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.

El nodo de búsqueda Tavily

El nodo de búsqueda en la web envía la pregunta a Tavily con search_depth establecido en advanced, y luego extrae el campo content de cada resultado:

def search_with_tavily(state: AgentState) -> AgentState:
    question = state['question']
    """ Using the Tavily to search the web and
      fetch the latest information about user query
    """

    response = tavilyClient.search(
        query=question,
        search_depth='advanced'
    )

    contents = [result["content"] for result in response["results"]]
    return {**state, "tavilyResponse":contents}

Esos fragmentos se convierten en el contexto que Gemini leerá al redactar la respuesta. Tenga en cuenta que el nodo devuelve una lista de cadenas; si prefiere un único bloque de texto, únala antes de almacenarla para que el campo coincida con el tipo str declarado en el estado.

Hacer que el router sea fiable con salida estructurada

Un router ingenuo pide al modelo que responda con una de tres palabras simples:

Return only:
faiss
tavily
gemini

Los modelos de lenguaje no siempre cumplen con las instrucciones de formato. Es posible que se reciba una oración completa como respuesta:

I would choose tavily.

O una redacción ligeramente diferente:

The best option is: tavily

Cualquiera de estas respuestas rompe el grafo, que necesita una cadena exacta para elegir un arista. La salida estructurada cierra esa brecha. Primero, describa las decisiones permitidas como un modelo Pydantic cuyo único campo está restringido a tres valores literales:

class RouteDecision(BaseModel):
    source: Literal["faiss", "tavily", "gemini"]

Luego, derive un modelo de enrutador que debe devolver una instancia de ese esquema. El método json_schema solicita al proveedor que limite la generación al esquema en lugar de depender únicamente de la redacción del prompt:

router_model = model.with_structured_output(
    RouteDecision,
    method="json_schema",
)

La salida se valida contra el tipo Literal, por lo que un valor inválido se muestra como error en lugar de dirigir el grafo a una ubicación indefinida.

Escribiendo el nodo de decisión

La indicación de enrutamiento describe cada opción: faiss para preguntas sobre la política de la empresa y otro conocimiento interno, tavily para cualquier cosa que necesite información actual o basada en la web, y gemini para conocimientos generales que no requieren ninguno de estos:

def decide_source(state:AgentState)-> AgentState:
    question = state["question"]
    prompt = f"""
    Decide the best source for answering this question.

    Choose exactly one:

    faiss:
    Use when the question can be answered using our internal
    knowledge base.related to company policy and all

    tavily:
    Use when the question requires current, recent, or web-based
    information.

    gemini:
    Use when the question is general knowledge and does not
    require our internal documents or current web information.

    Question:
    {question}

    Return only one word:
    faiss, tavily, or gemini
    """

    response = model.invoke(prompt)
    # return response

    return {**state,"source":response.text}

Observe atentamente las últimas líneas. Tal como está escrito, el nodo sigue llamando al model simple y almacenando response.text, que es exactamente el enfoque frágil de texto libre descrito anteriormente. Para aprovechar las ventajas del esquema, llame en su lugar a router_model.invoke(prompt) y almacene el atributo source del resultado. Con ese cambio, el enrutador devuelve un objeto tipado como este en lugar de prosa arbitraria:

RouteDecision(source="faiss")

Indicarle a LangGraph a dónde ir a continuación

Los bordes condicionales necesitan una función que indique qué ramificación seguir. Esta no toma ninguna decisión por sí misma; simplemente lee la elección que el enrutador ya ha guardado en el estado y se la devuelve a LangGraph:

def route_source(
    state: AgentState,
) -> Literal["faiss", "tavily", "gemini"]:
    return state["source"]

Un nodo de respuesta por cada ruta

Todas las tres ramificaciones finalizan en el mismo paso de generación. Este examina source y crea una solicitud en consecuencia: documentos recuperados que se unen para formar el contexto para FAISS, fragmentos de búsqueda como material de referencia para Tavily, o la pregunta directa para obtener respuestas inmediatas:

# Generate the answer for the user
def generateAnswer(state:AgentState) -> AgentState:
    source = state["source"]
    question = state['question']
    documents = state['documents']
    tavilyResponse = state['tavilyResponse']

    if source == "faiss":
        context = "\n\n".join([doc.page_content for doc in documents])
        prompt = f"""Based on the following context answer the question below
           Context:
           {context}

        Question:
        {question}
           """
    elif source == "tavily":
        prompt = f""" Based on the following search result , use this as an reference and provdie the
         answer to the below question

         Context:
         {tavilyResponse}

         Question:
         {question}
        """
    else:
        prompt = f" Answer the following question : {question}"
    response = model.invoke(prompt)
    answer = response.content

    return {**state, "answer":answer}

El mismo modelo Gemini escribe cada respuesta. Lo único que varía es el contexto que se le presenta, que constituye la idea fundamental de RAG: mejores entradas, y no un modelo diferente, generan respuestas fundamentadas.

Mantener la consistencia en los nombres al compilar los fragmentos

Los fragmentos combinan dos estilos de nomenclatura, y las discrepancias causarán errores si se pegan juntos sin modificaciones. El estado declara tavily_response mientras que los nodos leen y escriben tavilyResponse; el cliente se crea como tavily_client pero se llama tavilyClient; y la función de respuesta está definida como generateAnswer pero registrada como generate_answer. Elija una convención y aplíquela en todas partes antes de ejecutar el grafo.

Montaje del grafo

En este punto, las piezas son un nodo de decisión más tres continuaciones posibles:

decide_source
      |
      +----> faiss
      |
      +----> tavily
      |
      +----> gemini

Hay una sutileza aquí. La ruta gemini no es en absoluto un paso de recuperación; significa “saltarse la recuperación y responder directamente”. Por lo tanto, en lugar de crear un nodo vacío para ella, esa rama puede apuntar directamente al nodo compartido de generación.

Comience creando un grafo para el tipo de estado:

workflow = StateGraph(AgentState)

Registre los cuatro nodos:

workflow.add_node("decide", decide_source)
workflow.add_node("faiss", retrieve_from_faiss)
workflow.add_node("tavily", search_with_tavily)
workflow.add_node("generate", generate_answer)

Haga que el nodo de decisión sea el punto de entrada:

workflow.add_edge(START, "decide")

Conecte los bordes condicionales. La asignación convierte cada valor que puede devolver la función de enrutamiento en un nombre de nodo, al cual gemini es enviado directamente a generate:

workflow.add_conditional_edges(
    "decide",
    route_source,
    {
        "faiss": "faiss",
        "tavily": "tavily",
        "gemini": "generate",
    },
)

Luego, las ramas de recuperación y búsqueda deben dirigirse a la generación de respuestas:

workflow.add_edge("faiss", "generate")
workflow.add_edge("tavily", "generate")

La generación es el paso final antes de que termine el grafo:

workflow.add_edge("generate", END)

La compilación convierte la definición en una aplicación ejecutable:

app = workflow.compile()

El grafo finalizado

El flujo completo, desde el inicio hasta el final:

START
                           |
                           v
                    +--------------+
                    |    decide    |
                    |    source    |
                    +--------------+
                     /      |      \
                    /       |       \
                   v        v        v
               +------+ +--------+ +---------+
               |FAISS | | Tavily | | Generate|
               |      | |        | | directly|
               +------+ +--------+ +---------+
                   \        |         /
                    \       |        /
                     v      v       v
                    +----------------+
                    |    generate    |
                    |     answer     |
                    +----------------+
                            |
                            v
                           END

Tenga presente el principio clave: el grafo determina el conjunto de rutas posibles, y el modelo selecciona una de ellas en tiempo de ejecución. Esa combinación es lo que hace que el flujo de trabajo sea agente sin volverse impredecible.

Probando las tres rutas

Un pequeño ayudante crea el estado inicial e invoca la aplicación compilada:

def ask_question(question: str):
    initial_state = {
        "question":question,
        "documents":[],
        "tavilyResponse":"",
        "source":""
    }

    result = app.invoke(initial_state)
    return result

Una pregunta que requiere la web

La pregunta sobre el clima debe enviarse a Tavily:

result = ask_question(
    "What is the current weather in Uttarakhand?"
)

Imprima tanto la fuente elegida como la respuesta generada:

print("Source:", result["source"])
print("Answer:", result["answer"])

La ruta esperada:

Source: tavily

Las condiciones actuales solo existen en la web.

Una pregunta de conocimiento general

A continuación, pregunte sobre la Generación Aumentada por Recuperación de Información:

result = ask_question(
    "What is Retrieval Augmented Generation?"
)

Imprima el resultado de la misma manera:

print("Source:", result["source"])
print("Answer:", result["answer"])

El enrutador debería omitir por completo la recuperación de información:

Source: gemini

El modelo puede explicar el concepto por sí mismo.

Una pregunta sobre la política interna

Finalmente, la pregunta sobre la política de vacaciones:

result = ask_question(
    "What is our company leave policy?"
)

Y las mismas instrucciones de impresión:

print("Source:", result["source"])
print("Answer:", result["answer"])

Esta vez la base de conocimiento interna debería ser la que resuelva el problema:

Source: faiss

El enrutamiento de LLMs es probabilístico, así que considérelos como resultados esperados y no como garantías.

Por qué el enrutamiento semántico supera a las reglas basadas en palabras clave

Aquí está nuevamente la alternativa programada de antemano:

if "weather" in question:
    use_tavily()
elif "leave" in question:
    use_faiss()
else:
    use_gemini()

Reglas como estas pierden validez rápidamente. Imagine a un usuario que pregunta si la oficina está abierta los sábados. En esa oración no se menciona ninguna política, pero la respuesta podría encontrarse en la documentación interna. Un enrutador basado en modelos puede inferir la intención; una lista de palabras clave no puede hacerlo a menos que alguien prevea cada forma de expresarse.

Este enfoque también escala mejor. Cuando aparecen nuevos backends, como los que se muestran a continuación, basta con ampliar el esquema y la instrucción en lugar de crear un laberinto de condicionales:

SQL Database
Internal API
CRM
Customer Support System
Documentation
Web Search

El compromiso es una llamada adicional al modelo, con su costo y latencia, antes de que comience el trabajo real.

¿Es esto realmente un agente de IA?

Es más preciso denominarlo un pequeño flujo de trabajo agente que un agente autónomo. Las acciones disponibles están fijadas de antemano:

FAISS
Tavily
Direct Gemini

El modelo no tiene forma de decidir por sí mismo realizar algo como lo siguiente, ya que nunca se le han otorgado esas capacidades:

delete a database
send an email
call an arbitrary API

La arquitectura funciona como una cadena de responsabilidades:

Developer defines possible actions
              |
              v
        LLM chooses action
              |
              v
        LangGraph executes
              |
              v
           Result

Esa restricción es una ventaja: las opciones limitadas mantienen el comportamiento predecible y auditable en producción.

El papel de cada componente

LangGraph: el motor del flujo de trabajo

LangGraph controla la estructura de la aplicación: el estado, los nodos, las aristas, el enrutamiento condicional y el orden de ejecución. En términos generales, cada paso sigue el mismo patrón:

State
  |
  v
Node
  |
  v
Updated State
  |
  v
Conditional Edge
  |
  +----> Node A
  |
  +----> Node B
  |
  +----> Node C

Un grafo formado por pasos pequeños es más fácil de probar y observar que una función extensa.

FAISS: la capa de recuperación

FAISS proporciona la parte RAG del sistema. De forma simplificada:

Company Documents
       |
       v
   Embeddings
       |
       v
     FAISS
       |
       v
Similar Documents
       |
       v
     Gemini
       |
       v
     Answer

En una implementación real se agrega un pipeline de ingesta delante de él:

Documents
   |
   v
Load
   |
   v
Split into chunks
   |
   v
Generate embeddings
   |
   v
Store vectors
   |
   v
Retrieve relevant chunks
   |
   v
Generate answer

El ejemplo omite la carga y el particionamiento de datos para mantenerse enfocado en el flujo de trabajo; los manuales reales necesitan ambos elementos.

Tavily: búsqueda en la web en tiempo real

Tavily se encarga de la información que cambia con el tiempo:

User Question
      |
      v
   Router
      |
      v
    Tavily
      |
      v
 Search Results
      |
      v
    Gemini
      |
      v
    Answer

Los casos típicos incluyen el clima actual, noticias de última hora, lanzamientos recientes de productos, documentación actualizada, eventos recientes y datos de mercado en tiempo real. En entornos de producción, también hay que decidir cómo se citarán, filtrarán, validarán y presentarán los resultados de búsqueda, ya que el contenido web no está garantizado como preciso ni seguro para ser pasado a un modelo sin revisión.

El patrón que vale la pena recordar

Los componentes son intercambiables. La idea fundamental es el enrutamiento: asociar cada pregunta con la capacidad más adecuada para responderla.

Internal Knowledge
        |
        +------ FAISS
Current Information
        |
        +------ Tavily
General Knowledge
        |
        +------ Gemini

Esa elección de capacidad por solicitud vuelve a aparecer en casi todas las aplicaciones agentes serias.

A dónde ir a continuación

Agregar más herramientas

El enrutador podría elegir entre muchos más servidores backend:

SQL Database
REST APIs
CRM
Email
Calendar
Internal Documentation

Mejorar la recuperación de datos

Tres documentos son una demostración, no una base de conocimientos. Un RAG real necesita cargadores de documentos, procesamiento en fragmentos, metadatos, estrategias de recuperación más eficaces, reclasificación, citaciones de fuentes y control de acceso, para que los usuarios solo recuperen lo que tienen permiso para ver.

Validar las decisiones de enrutamiento

Un paso de validación entre el enrutador y las herramientas puede rechazar opciones poco plausibles y recurrir de forma segura a alternativas:

Router
   |
   v
Validator
   |
   +---- valid ----> Tool
   |
   +---- invalid --> Fallback

Esto es aún más importante a medida que aumenta el número de herramientas.

Gestionar fallos

La salida estructurada garantiza una ruta válida, no necesariamente una llamada a la herramienta exitosa. Una API de búsqueda puede agotar su tiempo de ejecución o no devolver nada:

Router
   |
   v
Tavily
   |
   X
Search failed
   |
   v
Fallback

Los sistemas de producción necesitan intentos repetidos y una solución alternativa, como responder directamente con una advertencia clara. El artículo sobre diseñar gráficos de agentes resilientes con intentos repetidos y soluciones alternativas profundiza en este tema.

Hágalo observable

Cuando una respuesta es incorrecta, es necesario saber en qué etapa falló:

Wrong route?
      |
      v
Bad retrieval?
      |
      v
Bad search results?
      |
      v
Bad generation?

Registrar la fuente seleccionada y los resultados intermedios permite responder a esa pregunta.

Introduzca bucles

El gráfico actual toma exactamente una decisión:

Question
   |
   v
Router
   |
   v
Tool
   |
   v
Answer

Un agente más capaz evalúa lo que encontró y decide si debe actuar nuevamente:

Question
   |
   v
Reason
   |
   v
Tool
   |
   v
Evaluate Result
   |
   +---- Need more information?
   |            |
   |            v
   |          Tool
   |            |
   +------------+
   |
   v
Final Answer

Aquí es donde los sistemas agentes adquieren un verdadero poder: el modelo determina si la información disponible es suficiente o si se requiere otra acción. También aquí es necesario establecer límites de iteración, para que un bucle no pueda ejecutarse indefinidamente.

Puntos clave

Todo el sistema responde a una pregunta: ¿cómo debe decidir una aplicación de IA de dónde proviene la respuesta? El flujo de trabajo final se ve así:

User Question
                   |
                   v
                Gemini
                Router
                   |
        +----------+----------+
        |          |          |
        v          v          v
      FAISS      Tavily    Gemini
   Internal DB    Web      Direct
        |          |          |
        +----------+----------+
                   |
                   v
                Gemini
              Final Answer
  • Defina las capacidades y los límites en el grafo; deje que el modelo elija entre ellos en tiempo de ejecución.
  • Utilice una salida estructurada para las decisiones de enrutamiento, y asegúrese de que el nodo de decisión llame realmente al modelo estructurado.
  • Mantenga un único nodo de generación de respuestas y varíe únicamente el contexto que se le proporciona.
  • Trate el enrutamiento como un componente verificable: registre las decisiones y revísalas frente a preguntas representativas.
  • Agregue validación, soluciones alternativas y capacidades de observabilidad antes de incorporar más herramientas, ya que cada nueva herramienta multiplica las posibilidades de que una solicitud falle.
  • A partir de aquí, las mismas bases se extienden naturalmente a bases de datos SQL, APIs, memoria, aprobación humana, nodos de evaluación, intentos repetidos y configuraciones multiagente. El grafo crece, pero el principio no cambia: proporcione al modelo capacidades útiles, defina cómo pueden utilizarse y déjelo decidir cuál se adapta mejor a la tarea.

    Lecturas relacionadas