Implementación de un sistema RAG conversacional con LangGraph: recuperación multi-turno y optimización adaptativa de consultas

Los sistemas RAG (Retrieval-Augmented Generation) se han convertido en un componente esencial en las aplicaciones modernas de modelos de lenguaje. Sin embargo, las implementaciones tradicionales suelen limitarse a un flujo básico de "recuperar y generar". Los escenarios conversacionales reales presentan desafíos mucho más complejos: las consultas de los usuarios pueden ser ambiguas, incluir preguntas de seguimiento, o incluso contener temas completamente ajenos al dominio del sistema.

Este artículo detalla cómo construir un sistema RAG funcional y robusto utilizando LangGraph, capaz de gestionar preguntas de seguimiento, filtrar consultas irrelevantes, evaluar la calidad de los documentos recuperados y mantener un historial completo de la conversación.

Los enfoques RAG convencionales consisten simplemente en recuperar los k documentos más similares y proporcionarlos al modelo generativo para producir una respuesta. No obstante, este enfoque presenta limitaciones significativas en contextos conversacionales: las consultas pueden ser vagas o constituir preguntas de seguimiento; los documentos recuperados pueden no ser pertinentes; y algunas preguntas pueden estar fuera del ámbito de conocimiento del sistema.

La solución propuesta integra varios componentes avanzados: historial de conversación, puntuación de relevancia, iteración de consultas y enrutamiento inteligente. Este组合 permite que el sistema comprenda el contexto y se autocorrija cuando es necesario.

El sistema finalSerá capaz de manejar las siguientes funcionalidades:

Procesar consultas conversacionales incluyendo preguntas de seguimiento, reformular preguntas para que sean independientes y autocontenidas, determinar si la pregunta está dentro del ámbito respondible, recuperar documentos relevantes y evaluar su calidad, optimizar automáticamente la consulta cuando no se encuentran documentos adecuados, proporcionar una respuesta o indicar imposibilidad de responder tras agotar los intentos, y mantener un historial completo de la conversación durante todo el proceso.

Configuración del entorno

Es necesario verificar que se dispone de Python 3.10 o superior. A continuación, se crea la estructura del proyecto:

mkdir -p conversational-rag-system
cd conversational-rag-system
touch rag_system.ipynb

Se inicializa el entorno virtual:

uv init
uv venv
source .venv/bin/activate

Se instalan las dependencias necesarias:

uv add langchain langgraph langchain-google-genai mypy pillow chromadb

Es preciso obtener una clave API de Google AI Studio y guardarla en el archivo .env:

touch .env

Agregar la siguiente línea con la clave:

GOOGLE_API_KEY=<tu_clave_api>

Actualizar el archivo .gitignore:

echo ".env" >> .gitignore

Implementación del núcleo del sistema

Cargar las variables de configuración:

from dotenv import load_dotenv
load_dotenv()

Preparar los datos de prueba utilizando documentos relacionados con un restaurante italiano:

from langchain.schema import Document

documents = [
    Document(
        page_content=(
            "La Trattoria Milano es dirigida por el chef Marco Benedetti, quien cuenta con más de 25 años de experiencia "
            "en la gastronomía italiana. Fundó La Trattoria Milano con el objetivo de acercar los sabores auténticos de Italia al público local."
        ),
        metadata={"source": "propietario.txt"},
    ),
    Document(
        page_content=(
            "La Trattoria Milano ofrece una variedad de platos con precios adaptados a diferentes presupuestos. "
            "Los entrantes comienzan en $10, los platos principales oscilan entre $18 y $40, y los postres tienen un precio entre $8 y $14."
        ),
        metadata={"source": "menu.txt"},
    ),
    Document(
        page_content=(
            "La Trattoria Milano atiende de lunes a domingo. El horario entre semana es de 12:00 PM a 9:00 PM, "
            "mientras que los fines de semana el horario se extiende desde las 12:00 PM hasta las 10:00 PM."
        ),
        metadata={"source": "informacion_restaurante.txt"},
    ),
    Document(
        page_content=(
            "La Trattoria Milano cuenta con diversos menús: menú de almuerzo, menú de cena y un especial menú de brunch de fin de semana. "
            "El menú de almuerzo ofrece opciones ligeras de cocina italiana, la carta de cena presenta una selección más extensa de platos tradicionales y contemporáneos, "
            "y el brunch incluye tanto desayunos clásicos como especialidades italianas."
        ),
        metadata={"source": "informacion_restaurante.txt"},
    ),
]

Almacenar los documentos en ChromaDB utilizando el modelo de embedding de Google, configurando la recuperación de los 2 documentos más relevantes:

from langchain_community.vectorstores import Chroma
from langchain_google_genai import GoogleGenerativeAIEmbeddings

embedding_model = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
vector_store = Chroma.from_documents(documents, embedding_model)
search_engine = vector_store.as_retriever(search_kwargs={"k": 2})

Verificar el funcionamiento del sistema de recuperación:

search_engine.invoke("¿Quién es el propietario de La Trattoria Milano?")

El resultado debería incluir información sobre el chef Marco Benedetti:

[Document(metadata={'source': 'propietario.txt'}, page_content='La Trattoria Milano es dirigida por el chef Marco Benedetti, quien cuenta con más de 25 años de experiencia en la gastronomía italiana. Fundó La Trattoria Milano con el objetivo de acercar los sabores auténticos de Italia al público local.'),
 Document(metadata={'source': 'informacion_restaurante.txt'}, page_content='La Trattoria Milano atiende de lunes a domingo. El horario entre semana es de 12:00 PM a 9:00 PM, mientras que los fines de semana el horario se extiende desde las 12:00 PM hasta las 10:00 PM.')]

Definir la plantilla de prompts con soporte para historial de conversación:

from langchain_core.prompts import ChatPromptTemplate
from langchain_google_genai import ChatGoogleGenerativeAI

system_template = """
Responde a la pregunta basándote en el siguiente contexto y el historial de chat:
Historial: {history}
Contexto: {context}
Pregunta: {question}
"""
prompt_template = ChatPromptTemplate.from_template(system_template)
language_model = ChatGoogleGenerativeAI(model="gemini-2.0-flash")
generation_pipeline = prompt_template | language_model

Definir la estructura de estado mediante la clase DialogState para rastrear la información durante la conversación:

from typing import List, TypedDict
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage

class DialogState(TypedDict):
    messages: List[BaseMessage]
    documents_found: List[Document]
    topic_validation: str
    processed_query: str
    has_valid_context: bool
    optimization_attempts: int
    current_question: HumanMessage

El sistema se компонент de múltiples nodos funcionales. Cada nodo выполняет una tarea específica dentro del flujo de procesamiento.

El reformulador de consultas transforma las preguntas de seguimiento en preguntas independientes y completas. El clasificador de temas determina si la consulta está dentro del ámbito de conocimiento del sistema. El enrutador decide el camino a seguir según la clasificación. El recuperador obtiene los documentos pertinentes. El evaluador utiliza el modelo de lenguaje para determinar si los documentos realmente son relevantes. El optimizador reescribe la consulta cuando no se encuentra enformación adecuada. El generador produce la respuesta final basada en los documentos y el historial. El manejador de casos fuera de ámbito procesa las consultas que no pueden ser respondidas.

La implementación completa es la siguiente:

from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
from langchain.schema import Document
from pydantic import BaseModel, Field
from langgraph.graph import StateGraph, END

class TopicClassifier(BaseModel):
    within_scope: str = Field(
        description="¿La pregunta está dentro de los temas objetivo? Si es sí -> 'Sí'; si no -> 'No'"
    )

def transform_query(state: DialogState):
    print(f"Ingresando a transform_query con estado: {state}")
    state["documents_found"] = []
    state["topic_validation"] = ""
    state["processed_query"] = ""
    state["has_valid_context"] = False
    state["optimization_attempts"] = 0
    
    if "messages" not in state or state["messages"] is None:
        state["messages"] = []
    
    if state["current_question"] not in state["messages"]:
        state["messages"].append(state["current_question"])
    
    if len(state["messages"]) > 1:
        conversation_context = state["messages"][:-1]
        query_text = state["current_question"].content
        prompt_messages = [
            SystemMessage(
                content="Eres un asistente útil que reformula la pregunta del usuario para que sea una pregunta independiente optimizada para recuperación."
            )
        ]
        prompt_messages.extend(conversation_context)
        prompt_messages.append(HumanMessage(content=query_text))
        formatted_prompt = ChatPromptTemplate.from_messages(prompt_messages).format()
        model_response = language_model.invoke(formatted_prompt)
        transformed = model_response.content.strip()
        print(f"transform_query: Reformulada a: {transformed}")
        state["processed_query"] = transformed
    else:
        state["processed_query"] = state["current_question"].content
    
    return state

def validate_topic(state: DialogState):
    print("Ingresando a validate_topic")
    system_msg = SystemMessage(
        content="""Eres un clasificador que determina si la pregunta del usuario trata sobre alguno de los siguientes temas:
1. Información sobre el propietario de La Trattoria Milano, que es Marco Benedetti.
2. Precios de los platos en La Trattoria Milano (restaurante).
3. Horarios de apertura de La Trattoria Milano (restaurante).
Si la pregunta TRATA sobre cualquiera de estos temas, responde con 'Sí'. De lo contrario, responde con 'No'."""
    )
    user_msg = HumanMessage(content=f"Pregunta del usuario: {state['processed_query']}")
    prompt = ChatPromptTemplate.from_messages([system_msg, user_msg])
    structured_llm = language_model.with_structured_output(TopicClassifier)
    classifier = prompt | structured_llm
    result = classifier.invoke({})
    state["topic_validation"] = result.within_scope.strip()
    print(f"validate_topic: topic_validation = {state['topic_validation']}")
    return state

def route_by_topic(state: DialogState):
    print("Ingresando a route_by_topic")
    if state.get("topic_validation", "").strip().lower() == "sí" or state.get("topic_validation", "").strip().lower() == "si":
        print("Enrutando a retrieve_documents")
        return "retrieve_documents"
    else:
        print("Enrutando a block_off_topic")
        return "block_off_topic"

def retrieve_documents(state: DialogState):
    print("Ingresando a retrieve_documents")
    docs = search_engine.invoke(state["processed_query"])
    print(f"retrieve_documents: Recuperados {len(docs)} documentos")
    state["documents_found"] = docs
    return state

class RelevanceEvaluator(BaseModel):
    is_relevant: str = Field(
        description="¿El documento es relevante para la pregunta del usuario? Si es sí -> 'Sí'; si no -> 'No'"
    )

def assess_relevance(state: DialogState):
    print("Ingresando a assess_relevance")
    system_msg = SystemMessage(
        content="""Eres un evaluador que determina la relevancia de un documento recuperado respecto a una pregunta del usuario.
Responde únicamente con 'Sí' o 'No'.
Si el documento contiene información relevante para la pregunta del usuario, responde con 'Sí'.
De lo contrario, responde con 'No'."""
    )
    structured_llm = language_model.with_structured_output(RelevanceEvaluator)
    relevant_docs = []
    
    for doc in state["documents_found"]:
        user_msg = HumanMessage(
            content=f"Pregunta del usuario: {state['processed_query']}\n\nDocumento recuperado:\n{doc.page_content}"
        )
        prompt = ChatPromptTemplate.from_messages([system_msg, user_msg])
        evaluator = prompt | structured_llm
        result = evaluator.invoke({})
        print(f"Evaluando documento: {doc.page_content[:30]}... Resultado: {result.is_relevant.strip()}")
        
        if result.is_relevant.strip().lower() == "sí" or result.is_relevant.strip().lower() == "si":
            relevant_docs.append(doc)
    
    state["documents_found"] = relevant_docs
    state["has_valid_context"] = len(relevant_docs) > 0
    print(f"assess_relevance: has_valid_context = {state['has_valid_context']}")
    return state

def route_by_decision(state: DialogState):
    print("Ingresando a route_by_decision")
    attempts = state.get("optimization_attempts", 0)
    
    if state.get("has_valid_context", False):
        print("Enrutando a generate_answer")
        return "generate_answer"
    elif attempts >= 2:
        print("Enrutando a handle_failure")
        return "handle_failure"
    else:
        print("Enrutando a refine_query")
        return "refine_query"

def refine_query(state: DialogState):
    print("Ingresando a refine_query")
    attempts = state.get("optimization_attempts", 0)
    
    if attempts >= 2:
        print("Máximo de intentos alcanzado")
        return state
    
    original = state["processed_query"]
    system_msg = SystemMessage(
        content="""Eres un asistente útil que refina ligeramente la pregunta del usuario para mejorar los resultados de recuperación.
Proporciona una versión ligeramente ajustada de la pregunta."""
    )
    user_msg = HumanMessage(content=f"Pregunta original: {original}")
    prompt = ChatPromptTemplate.from_messages([system_msg, user_msg]).format()
    response = language_model.invoke(prompt)
    refined = response.content.strip()
    print(f"refine_query: Refinada a: {refined}")
    state["processed_query"] = refined
    state["optimization_attempts"] = attempts + 1
    return state

def generate_answer(state: DialogState):
    print("Ingresando a generate_answer")
    
    if "messages" not in state or state["messages"] is None:
        raise ValueError("El estado debe incluir 'messages' antes de generar una respuesta.")
    
    conversation_history = state["messages"]
    context = state["documents_found"]
    question = state["processed_query"]
    
    response = generation_pipeline.invoke({
        "history": conversation_history,
        "context": context,
        "question": question
    })
    
    answer = response.content.strip()
    state["messages"].append(AIMessage(content=answer))
    print(f"generate_answer: Respuesta generada: {answer}")
    return state

def handle_failure(state: DialogState):
    print("Ingresando a handle_failure")
    
    if "messages" not in state or state["messages"] is None:
        state["messages"] = []
    
    state["messages"].append(
        AIMessage(content="Lo siento, pero no he podido encontrar la información que buscas.")
    )
    return state

def block_off_topic(state: DialogState):
    print("Ingresando a block_off_topic")
    
    if "messages" not in state or state["messages"] is None:
        state["messages"] = []
    
    state["messages"].append(
        AIMessage(content="¡No puedo responder a eso!")
    )
    return state

Finalmente, conectar los nodos utilizando StateGraph de LangGraph y MemorySaver para persistir el historial de conversación:

from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
workflow = StateGraph(DialogState)

workflow.add_node("transform_query", transform_query)
workflow.add_node("validate_topic", validate_topic)
workflow.add_node("block_off_topic", block_off_topic)
workflow.add_node("retrieve_documents", retrieve_documents)
workflow.add_node("assess_relevance", assess_relevance)
workflow.add_node("generate_answer", generate_answer)
workflow.add_node("refine_query", refine_query)
workflow.add_node("handle_failure", handle_failure)

workflow.add_edge("transform_query", "validate_topic")

workflow.add_conditional_edges(
    "validate_topic",
    route_by_topic,
    {
        "retrieve_documents": "retrieve_documents",
        "block_off_topic": "block_off_topic",
    },
)

workflow.add_edge("retrieve_documents", "assess_relevance")

workflow.add_conditional_edges(
    "assess_relevance",
    route_by_decision,
    {
        "generate_answer": "generate_answer",
        "refine_query": "refine_query",
        "handle_failure": "handle_failure",
    },
)

workflow.add_edge("refine_query", "retrieve_documents")
workflow.add_edge("generate_answer", END)
workflow.add_edge("handle_failure", END)
workflow.add_edge("block_off_topic", END)

workflow.set_entry_point("transform_query")

graph = workflow.compile(checkpointer=checkpointer)

Es posible visualizar el diagrama de flujo del sistema:

from IPython.display import Image, display
from langchain_core.runnables.graph import MermaidDrawMethod

display(
    Image(
        graph.get_graph().draw_mermaid_png(
            draw_method=MermaidDrawMethod.API,
        )
    )
)

Demostración del sistema

Escenario 1: Consulta fuera del ámbito

Preguntar sobre un tema no relacionado con el restaurante:

input_data = {"current_question": HumanMessage(content="¿Qué tiempo hace hoy?")}
graph.invoke(input=input_data, config={"configurable": {"thread_id": 1}})

Resultado:

Ingresando a transform_query con estado: {'current_question': HumanMessage(content='¿Qué tiempo hace hoy?', additional_kwargs={}, response_metadata={})}
Ingresando a validate_topic
validate_topic: topic_validation = No
Ingresando a route_by_topic
Enrutando a block_off_topic
Ingresando a block_off_topic
{'messages': [HumanMessage(content='¿Qué tiempo hace hoy?', additional_kwargs={}, response_metadata={}),
  AIMessage(content='¡No puedo responder a eso!', additional_kwargs={}, response_metadata={})],
 'documents_found': [],
 'topic_validation': 'No',
 'processed_query': '¿Qué tiempo hace hoy?',
 'has_valid_context': False,
 'optimization_attempts': 0,
 'current_question': HumanMessage(content='¿Qué tiempo hace hoy?', additional_kwargs={}, response_metadata={})}

El sistema identifica correctamente que la consulta está fuera del ámbito y la rechaza.

Escenario 2: Consulta sin información disponible

Preguntar sobre información no presente en los documentos:

input_data = {"current_question": HumanMessage(content="¿Qué edad tiene el propietario del restaurante La Trattoria Milano?")}
graph.invoke(input=input_data, config={"configurable": {"thread_id": 2}})

Resultado:

Ingresando a transform_query con estado: {'current_question': HumanMessage(content='¿Qué edad tiene el propietario del restaurante La Trattoria Milano?', additional_kwargs={}, response_metadata={})}
Ingresando a validate_topic
validate_topic: topic_validation = Sí
Ingresando a route_by_topic
Enrutando a retrieve_documents
Ingresando a retrieve_documents
retrieve_documents: Recuperados 2 documentos
Ingresando a assess_relevance
Evaluando documento: La Trattoria Milano es dirig... Resultado: No
Evaluando documento: La Trattoria Milano atiende... Resultado: No
assess_relevance: has_valid_context = False
Ingresando a route_by_decision
Enrutando a refine_query
Ingresando a refine_query
refine_query: Refinada a: Pregunta revisada: ¿Cuál es la edad del propietario de La Trattoria Milano?
Ingresando a retrieve_documents
retrieve_documents: Recuperados 2 documentos
Ingresando a assess_relevance
Evaluando documento: La Trattoria Milano es dirig... Resultado: No
Evaluando documento: La Trattoria Milano atiende... Resultado: No
assess_relevance: has_valid_context = False
Ingresando a route_by_decision
Enrutando a refine_query
Ingresando a refine_query
refine_query: Refinada a: Pregunta original: ¿Cuál es la capital de Australia?
Pregunta revisada: ¿Cuál es la capital de Australia?
Ingresando a retrieve_documents
retrieve_documents: Recuperados 2 documentos
Ingresando a assess_relevance
Evaluando documento: La Trattoria Milano ofrece un... Resultado: No
Evaluando documento: La Trattoria Milano atiende... Resultado: No
assess_relevance: has_valid_context = False
Ingresando a route_by_decision
Enrutando a handle_failure
Ingresando a handle_failure
{'messages': [HumanMessage(content='¿Qué edad tiene el propietario del restaurante La Trattoria Milano?', additional_kwargs={}, response_metadata={}),
  AIMessage(content='Lo siento, pero no he podido encontrar la información que buscas.', additional_kwargs={}, response_metadata={})],
 'documents_found': [],
 'topic_validation': 'Sí',
 'processed_query': 'Pregunta original: ¿Cuál es la capital de Australia?\nPregunta revisada: ¿Cuál es la capital de Australia?',
 'has_valid_context': False,
 'optimization_attempts': 2,
 'current_question': HumanMessage(content='¿Qué edad tiene el propietario del restaurante La Trattoria Milano?', additional_kwargs={}, response_metadata={})}

La consulta está dentro del ámbito del sistema, pero los documentos recuperados no son relevantes. El sistema intenta reformular la consulta y realizar nuevas búsquedas en dos ocasiones, y finalmente admite honestamente que no puede encontrar la información solicitada.

Escenario 3: Conversación normal con seguimiento

Primero preguntar por el horario y luego hacer una pregunta de seguimiento sobre el domingo:

graph.invoke(input={"current_question": HumanMessage(content="¿Cuándo abre La Trattoria Milano?")}, config={"configurable": {"thread_id": 3}})
graph.invoke(input={"current_question": HumanMessage(content="¿También el domingo?")}, config={"configurable": {"thread_id": 3}})

Resultado:

Ingresando a transform_query con estado: {'current_question': HumanMessage(content='¿Cuándo abre La Trattoria Milano?', additional_kwargs={}, response_metadata={})}
Ingresando a validate_topic
validate_topic: topic_validation = Sí
Ingresando a route_by_topic
Enrutando a retrieve_documents
Ingresando a retrieve_documents
retrieve_documents: Recuperados 2 documentos
Ingresando a assess_relevance
Evaluando documento: La Trattoria Milano atiende de lun... Resultado: Sí
Evaluando documento: La Trattoria Milano ofrece un... Resultado: No
assess_relevance: has_valid_context = True
Ingresando a route_by_decision
Enrutando a generate_answer
Ingresando a generate_answer
generate_answer: Respuesta generada: La Trattoria Milano abre a las 12:00 PM todos los días.

Ingresando a transform_query con estado: {'messages': [HumanMessage(content='¿Cuándo abre La Trattoria Milano?', additional_kwargs={}, response_metadata={}), AIMessage(content='La Trattoria Milano abre a las 12:00 PM todos los días.', additional_kwargs={}, response_metadata={})], 'documents_found': [Document(metadata={'source': 'informacion_restaurante.txt'}, page_content='La Trattoria Milano atiende de lunes a domingo. El horario entre semana es de 12:00 PM a 9:00 PM, mientras que los fines de semana el horario se extiende desde las 12:00 PM hasta las 10:00 PM.')], 'topic_validation': 'Sí', 'processed_query': '¿Cuándo abre La Trattoria Milano?', 'has_valid_context': True, 'optimization_attempts': 0, 'current_question': HumanMessage(content='¿También el domingo?', additional_kwargs={}, response_metadata={})}
transform_query: Reformulada a: IA: ¿La Trattoria Milano está abierta los domingos?
Ingresando a validate_topic
validate_topic: topic_validation = Sí
Ingresando a route_by_topic
Enrutando a retrieve_documents
Ingresando a retrieve_documents
retrieve_documents: Recuperados 2 documentos
Ingresando a assess_relevance
Evaluando documento: La Trattoria Milano atiende de lun... Resultado: Sí
Evaluando documento: La Trattoria Milano ofrece un... Resultado: No
assess_relevance: has_valid_context = True
Ingresando a route_by_decision
Enrutando a generate_answer
Ingresando a generate_answer
generate_answer: Respuesta generada: Sí, La Trattoria Milano está abierta los domingos.
{'messages': [HumanMessage(content='¿Cuándo abre La Trattoria Milano?', additional_kwargs={}, response_metadata={}),
  AIMessage(content='La Trattoria Milano abre a las 12:00 PM todos los días.', additional_kwargs={}, response_metadata={}),
  HumanMessage(content='¿También el domingo?', additional_kwargs={}, response_metadata={}),
  AIMessage(content='Sí, La Trattoria Milano está abierta los domingos.', additional_kwargs={}, response_metadata={})],
 'documents_found': [Document(metadata={'source': 'informacion_restaurante.txt'}, page_content='La Trattoria Milano atiende de lunes a domingo. El horario entre semana es de 12:00 PM a 9:00 PM, mientras que los fines de semana el horario se extiende desde las 12:00 PM hasta las 10:00 PM.')],
 'topic_validation': 'Sí',
 'processed_query': 'IA: ¿La Trattoria Milano está abierta los domingos?',
 'has_valid_context': True,
 'optimization_attempts': 0,
 'current_question': HumanMessage(content='¿También el domingo?', additional_kwargs={}, response_metadata={})}

El sistema utiliza correctamente el historial de la conversación para comprender el seguimiento "¿También el domingo?", lo reformula como una pregunta completa y logra recuperar y responder exitosamente.

Con esto se ha completado la implementación de un sistema RAG con capacidades de adaptación, que va mucho más allá de una simple búsqueda por similitud.

Consideraciones finales

La arquitectura basada en grafos de LangGraph facilita el desarrollo y la iteración de este tipo de sistemas, permitiendo agregar nuevos nodos, integrar APIs externas o conectar con otros agentes para crear flujos de trabajo más complejos.

Sin embargo, es importante considerar algunas limitaciones. La calidad de la recuperación depende fundamentalmente de la efectividad del modelo de embedding; aunque la reformulación de consultas ayuda a mejorar los resultados, no resuelve el problema de raíz. El uso del modelo de lenguaje para evaluar cada documento añade latencia a las respuestas y costos deAPI, lo cual puede ser significativo cuando se procesan grandes volúmenes de documentos. El número de reintentos está configurado de forma estática, pero en un entorno de producción podría ser necesario ajustarlo dinámicamente según el contexto. Actualmente solo se mentiene la memoria de la sesión; una memoria a largo plazo real requeriría diseñar una solución que combine bases de datos vectoriales con bases de datos relacionales. Finalmente, la lógica del clasificador estácodificada en el prompt; expandir a nuevos dominios requeriría modificar las instrucciones o entrenar un clasificador específico.

Etiquetas: LangGraph RAG retrieval-augmented-generation conversational-ai vector-database

Publicado el 7-15 21:59