Construcción de un Agente RAG con LangChain

En los dos artículos anteriores preparamos el almacenamiento vectorial y el recuperador.

En este artículo los integramos en el Agent para construir un agente RAG completo: un asistente inteligente capaz de responder preguntas basándose en una base de conocimientos privada.


Crear la herramienta Retriever

Si envolvemos el recuperador como una herramienta, el Agent podrá buscar automáticamente en la base de conocimientos cuando sea necesario.

Si no tienes una clave de OpenAI, puedes usar el servicio de Embedding de Alibaba Bailian. Consulta la configuración.https://www.example.com/langchain/langchain-alibailian.html:

# OpenAI 的文本嵌入模型
# 将文本转换为向量(一组浮点数)

# 使用阿里云百炼(DashScope)的通义千问 Embedding 服务
embeddings = OpenAIEmbeddings(
    model="text-embedding-v4",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    check_embedding_ctx_length=False,
    chunk_size=10,
)

Ejemplo (OpenAI)

import os
from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter

# ----- Paso 1: Preparar la base de conocimientos -----

# Simular los documentos de conocimiento de EJEMPLO
knowledge_docs = [
    EJEMPLO (tutorial para novatos) fue fundado en 2013 y es una plataforma de aprendizaje de programación completamente gratuita.,
    "La plataforma ha lanzado más de 300 tutoriales, que cubren áreas como frontend, backend, bases de datos y desarrollo móvil.",
    "El tutorial básico de Python3 es el curso más popular de la plataforma, con más de 5 millones de visitas de aprendizaje acumuladas.",
    "El tutorial básico de Python3 consta de 30 capítulos, que incluyen configuración del entorno, sintaxis básica, funciones, clases, manejo de excepciones, etc.",
    "El tutorial básico de HTML consta de 25 capítulos, desde la estructura básica de HTML hasta formularios y elementos multimedia.",
    "EJEMPLO admite la ejecución de código en línea; los estudiantes pueden escribir y ejecutar código sin instalar ningún software.",
    "La plataforma está adaptada a dispositivos móviles, por lo que los usuarios pueden aprender a programar en cualquier momento y lugar desde su teléfono.",
    "El servicio de membresía de EJEMPLO ofrece servicios de valor agregado como cursos en video, proyectos prácticos y consultas individuales.",
]

# Dividir
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200, chunk_overlap=30
)
chunks = text_splitter.create_documents(knowledge_docs)

# Almacenamiento vectorizado -- aquí puedes cambiarlo a Alibaba Bailian si no tienes clave de OpenAI
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")


vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
)
retriever = vector_store.as_retriever(search_kwargs={"k": 3})

# ----- Paso 2: Crear la herramienta de recuperación -----

@tool
def search_knowledge_base(query: str) -> str:
    """Busca información relevante en la base de conocimientos de EJEMPLO.

Cuando el usuario pregunte por información específica sobre EJEMPLO (como número de cursos, historia de la plataforma, características funcionales, etc.),
debes usar esta herramienta para consultar la base de conocimientos y obtener información precisa.

    Args:
query: palabra clave o pregunta de búsqueda
    """

    docs = retriever.invoke(query)
    if not docs:
        return "No se encontró información relevante en la base de conocimientos."

    results = []
    for i, doc in enumerate(docs, 1):
        results.append(f"[{i}] {doc.page_content}")

    return "\n\n".join(results)


# ----- Paso 3: Crear el agente RAG -----

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[search_knowledge_base],
    system_prompt="""Eres el asistente de atención al cliente inteligente de EJEMPLO.

## Reglas
1. Cuando el usuario pregunte por información específica sobre EJEMPLO, debes usar la herramienta search_knowledge_base para consultar.
2. Responde basándote en la información recuperada; no inventes contenido que no esté en la base de conocimientos.
3. Si la base de conocimientos no tiene información relevante, dímelo honestamente al usuario.
4. Las respuestas deben ser amables, concisas y precisas."""
,
)

# ----- Paso 4: Prueba -----

questions = [
    "¿Cuándo fue fundado EJEMPLO?",
    "¿Cuántos capítulos tiene el tutorial básico de Python3?",
    "¿Cuántos tutoriales tiene EJEMPLO en total?",
]

for q in questions:
    result = agent.invoke({"messages": [HumanMessage(content=q)]})
    print(f"Q: {q}")
    print(f"A: {result['messages'][-1].content}")
    print("-" * 60)

Resultado de ejecución:

Q: Ejemplo是什么时候创立的?
A: Ejemplo创立于 2013 年,是一个完全免费的编程学习平台。
------------------------------------------------------------
Q: Python3 基础教程有多少章?
A: Python3 基础教程共 30 章,包含环境搭建、基本语法、函数、类、异常处理等内容。
------------------------------------------------------------
Q: Ejemplo一共有多少套教程?
A: Ejemplo已上线 300+ 套教程,涵盖前端、后端、数据库、移动开发等多个领域。
------------------------------------------------------------

Flujo de ejecución del agente RAG

Para la tercera pregunta anterior, "¿Cuántos tutoriales tiene EJEMPLO en total?", el flujo de ejecución del Agent es:

  1. El usuario hace una pregunta
  2. El modelo determina que necesita consultar la base de conocimientos → llama a search_knowledge_base("cantidad de tutoriales de EJEMPLO")
  3. El recuperador busca en la base de datos vectorial los fragmentos de documentos semánticamente más similares.
  4. Devuelve los resultados de la recuperación al modelo.
  5. El modelo genera una respuesta precisa basada en los resultados de la recuperación.

Agregar fuentes de referencia

Los sistemas RAG profesionales suelen incluir fuentes de referencia para que el usuario sepa de dónde proviene la información:

Ejemplo

from langchain_core.documents import Document


@tool
def search_with_sources(query: str) -> str:
    """Busca en la base de conocimientos de EJEMPLO y devuelve resultados con la fuente indicada.

    Args:
query: palabra clave de búsqueda
    """

    docs = retriever.invoke(query)
    if not docs:
        return "No se encontró información relevante."

    results = []
    for i, doc in enumerate(docs, 1):
        source = doc.metadata.get("source", "Base de conocimientos de EJEMPLO")
        results.append(f"[Fuente {i}: {source}]\n{doc.page_content}")

    return "\n\n".join(results)


# Si necesitas conservar la información de la fuente en el documento, puedes agregar metadatos al crearlo.
doc_with_meta = Document(
    page_content="El tutorial básico de Python3 tiene 30 capítulos...",
    metadata={"source": "Tutorial básico de Python3 - Introducción al curso", "url": "https://www.example.com/python3/"}
)

Persistencia del almacenamiento vectorial

En proyectos reales, no reconstruyes el índice vectorial cada vez. Chroma admite persistencia local:

Ejemplo

# Crear almacenamiento vectorial persistente (primera ejecución)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./ejemplo_vector_db",  # Directorio de persistencia
)

# En ejecuciones posteriores, carga directamente
loaded_store = Chroma(
    persist_directory="./ejemplo_vector_db",
    embedding_function=embeddings,
)
retriever = loaded_store.as_retriever()

# ¡No es necesario recalcular los vectores!

La persistencia del almacenamiento vectorial puede mejorar enormemente la velocidad de inicio. Con una gran cantidad de documentos (decenas de miles), recalcular los embeddings de todos los vectores puede tardar decenas de minutos. Tras la persistencia, solo hay que cargarlos.

Otras extensiones