Construir un chatbot que responda preguntas sobre documentación técnica requiere conectar un modelo de lenguaje con una base de datos vectorial. Aquí verás cómo integrar ChatOpenAI con ChromaDB usando LangChain para crear un sistema de preguntas y respuestas sobre documentación de Hugging Face, ideal para desarrolladores que exploran Retrieval Augmented Generation (RAG).
Cómo inicializar el modelo de chat con LangChain
El primer paso es importar la clase que conecta tu aplicación con OpenAI. Desde langchain.chat_models traes ChatOpenAI y creas una instancia llamada llm, que es la convención para referirse a un Large Language Model.
python
from langchain.chat_models import ChatOpenAI
Cada parámetro tiene un propósito específico que afecta el comportamiento del chatbot:
model_name: define qué modelo de OpenAI usarás. gpt-3.5-turbo es una opción equilibrada entre costo y calidad.
temperature: controla la creatividad de las respuestas. Un valor de 0.2 hace que el modelo sea sobrio y predecible.
max_tokens: limita la longitud de la respuesta. 1000 tokens permite respuestas amplias sin escatimar contexto.
¿Qué hace el parámetro temperature en un modelo de lenguaje? Controla la aleatoriedad de las respuestas. Valores bajos (0.2) generan respuestas más consistentes y técnicas; valores altos (0.8) producen salidas más creativas y variables.
Por qué convertir un vector store en retriever
Una base de datos vectorial almacena embeddings, pero el modelo necesita un componente que recupere los fragmentos relevantes. Ese componente es el retriever, y se obtiene con el método as_retriever() sobre la instancia de Chroma.
El parámetro k define cuántos fragmentos similares regresa el retriever por consulta. Con k=2 obtienes los dos pasajes más parecidos a la pregunta. Puedes subirlo a 3 o 4 si necesitas más contexto, pero ten cuidado: cada fragmento adicional aumenta los tokens del prompt y, por lo tanto, el costo de la llamada.
¿Cuál es el valor mínimo recomendado para k en un retriever? Dos fragmentos. Con uno solo rara vez hay contexto suficiente para responder bien una pregunta, así que dos es el piso práctico.
Cómo diseñar la función run_conversation
La función run_conversation orquesta toda la interacción. Recibe el vector store y un chat_type que define el comportamiento, en este caso "QA" para preguntas y respuestas sin memoria.
python
def run_conversation(vector_store, chat_type, llm):
console.print("Hola, ¿quieres preguntarme sobre transformers e inteligencia artificial?", style="blue")
if chat_type =="QA":console.print("Estás utilizando el chatbot en modo preguntas y respuestas...", style="green")retriever = vector_store.as_retriever(search_kwargs={"k":2})whileTrue:console.print("Tú:", style="blue") query =get_query_from_user()if query.lower()=="salir":breakif chat_type =="QA": response =process_qa_query(query, retriever, llm)console.print(f"IA: {response}", style="red")
Qué diferencia hay entre un chat con memoria y uno sin memoria
La variable chat_type abre la puerta a dos comportamientos distintos. En modo QA, cada pregunta se resuelve de forma aislada: el modelo no recuerda lo que preguntaste antes. Esto es útil cuando consultas documentación puntual y no necesitas hilar respuestas.
Un chat con memoria, en cambio, conserva el historial y permite hacer preguntas de seguimiento como “¿y eso cómo se aplica al ejemplo anterior?”. Aquí se implementa solo el modo Question Answering para mantener el flujo simple.
Cómo manejar el loop de conversación con el usuario
Un while True simula la experiencia continua de un chat. Dentro del loop, la función get_query_from_user (definida en utils) captura el texto que escribe la persona en la consola.
El criterio de salida es sencillo: si la query convertida a minúsculas es igual a "salir", el loop se rompe con break. Convertir a minúsculas evita que la persona tenga que escribir exactamente como esperas.
Cómo se procesa una pregunta con process_qa_query
Cuando la query no es un comando de salida y el chat_type es QA, la función delega el trabajo a process_qa_query. Esta recibe tres argumentos:
La query del usuario.
El retriever que busca en ChromaDB los fragmentos relevantes.
El llm que genera la respuesta final.
El modelo llm no estaba definido dentro de run_conversation originalmente, así que se pasa como argumento desde main. Esta separación mantiene la función desacoplada y reutilizable: puedes cambiar el modelo sin tocar la lógica de conversación.
La respuesta se imprime con el prefijo IA: en color rojo, distinguiendo visualmente quién habla en cada turno. Esa pequeña decisión de diseño hace que la consola se sienta como una conversación real, no como un volcado de logs.
¿Vas a ajustar los hiperparámetros como temperature, max_tokens o k en tu propio proyecto? Cuéntame en los comentarios qué combinación te funcionó mejor.
Esta muy bueno para repo para aprender de hecho y tener un asistente de estudio segun la data especifica que le tengamos!, voy a compartir mi propuesta de repo para mejorarlo :green_heart:
Me acabas de hacer imaginar una clase donde mi profesor me de un texto mediano a leer y tengamos como alumnos un modelo LLM con un embedding de documentos que profundizan en el tema para que, como alumno, pueda hacerle preguntas al modelo que me ayuden a profundizar en mi conocimiento y entendimiento.
Esto potencialmente podria hacerme profundizar en un tema como si le preguntara a un tutor 0w0
Que es mejor hacer fine tunning o cargar nuestros documentos como lo muestra el curso para responder preguntas sobre nuestra informacion.
Los modelos de chat están específicamente tuneados para comportarse según las instrucciones y el contexto dado, por lo que es más barato y eficaz generar una base de datos de Documentos para que el modelo de chat solo se encargue de formatear los resultados del retriever.
depende tu caso de uso pero en la mayoria de las veces es mejor crear un base de conocimiento cargarla a una base de datos vectorial y extraer conocimiento de ella