Chatbot que extrae datos del cliente con LangChain
Resumen
Cuando construyes un chatbot que atiende clientes, necesitas recordar datos puntuales: nombre, dirección, teléfono o el número de garantía del producto. La Conversation Entity Memory de LangChain resuelve justo eso, porque extrae entidades de la charla con ayuda de un modelo de lenguaje y las guarda en una estructura lista para consultar.
¿Qué es Conversation Entity Memory y para qué sirve?
Es un tipo de memoria que detecta y almacena entidades clave dentro de una conversación. Piensa en un asesor de ventas digital: si a alguien se le rompió la lavadora, tu bot necesita saber qué pasó, cuándo la compró y quién la atendió. Esa información se vuelve oro para el seguimiento.
¿Qué son las entidades en un chatbot? Son datos concretos que aparecen en la conversación: nombres de personas, direcciones, teléfonos, fechas, números de garantía. La memoria los identifica con un modelo de IA y los guarda como pares clave-valor [02:25].
El punto interesante es que la extracción no es automática por reglas. Detrás hay un modelo que interpreta el texto, decide qué es entidad y qué no, y la asocia con el contexto en el que apareció.
¿Cómo se configura el prompt para una memoria con entidades?
Antes de escribir tu propio prompt, conviene revisar el que LangChain trae por defecto. Lo importas desde la librería de conversation prompts como Entity Memory Conversation Template y, al imprimirlo con el argumento template, ves su estructura interna [01:36].
Ese template incluye tres variables que debes respetar siempre:
Entities: las entidades detectadas hasta el momento.
History: el historial de la conversación.
Input: la pregunta o mensaje actual del usuario.
Con ese formato como base, puedes redactar uno personalizado. En el ejemplo se construye un asistente de ventas para una empresa de máquinas de micheladas, con dos objetivos claros: primero intentar resolver el problema, y si no se puede, agendar una visita técnica pidiendo celular y dirección [03:25]. Además, se le pide hablar con la chispa de una persona nacida en Tepito, México, y siempre preguntar la fecha de compra, el número de garantía y quién atendió originalmente al cliente.
¿Cómo creas tu propio PromptTemplate?
Importas PromptTemplate desde la librería de prompts y le pasas dos cosas: las input_variables (que son entities, history e input) y el template con tu texto en español. Así nace el prompt_tepito_entities, listo para enchufarse a la cadena.
¿Cómo se arma la ConversationChain con dos modelos de lenguaje?
Aquí viene un detalle clave: necesitas dos modelos, no uno. Uno conversa con el cliente, y otro extrae las entidades para la memoria [06:15].
Importa ConversationChain desde la librería de cadenas de LangChain.
Instancia un modelo de chat, por ejemplo ChatGPT 3.5 de OpenAI, para la conversación.
Inicializa un modelo de lenguaje simple con OpenAI, usando model_name="text-davinci-003", temperature=0 y max_tokens=256, para que detecte entidades [07:18].
Crea la memoria con ConversationEntityMemory(llm=llm).
Arma la cadena con tu prompt, el modelo de chat, la memoria y verbose=True para ver qué pasa por dentro.
¿Por qué la memoria pide un modelo de lenguaje aparte? Porque la detección de entidades es una tarea de IA: el modelo lee la conversación, decide qué fragmentos son entidades y los guarda con su descripción. Sin un LLM, esa lógica no existe [06:35].
Un detalle práctico: si escribes temperatura en lugar de temperature, LangChain te avisa del parámetro mal nombrado. Corriges, vuelves a inicializar y listo.
¿Cómo se ve la memoria llenándose en una conversación real?
La charla arranca con un mensaje tipo: Qué onda, mi máquina para hacer micheladas no está funcionando, ando enojado [08:25]. En la primera ronda, las secciones de contexto y conversación actual aparecen vacías. Normal, todavía no hay datos.
En el segundo turno, el cliente suelta: Lo compré en 2015, tiene un número de garantía 0X y me atendió Carlos. Ahí la memoria empieza a poblarse. 0X se asocia al número de garantía y Carlos se identifica como la persona que atendió al humano en 2015 [09:45].
Cuando el cliente pide agendar la visita y comparte su celular y Parque Bolívar 22, la memoria refina las entradas. Algunas entidades quedan más completas que otras al inicio, y se enriquecen conforme avanza la plática.
¿Cómo generas un reporte final de entidades?
Usas pprint de la librería pprint para imprimir el diccionario de forma legible y accedes al almacén con conversation.memory.entity_store [12:08]. El resultado es un reporte con cada clave y su descripción contextual:
0X: número de garantía de la máquina comprada en 2015.
Carlos: persona que atendió al humano cuando compró la máquina.
Parque Bolívar: dirección del humano, ubicada en el número 22.
Número de celular del cliente.
Ese reporte es justo lo que necesita el equipo de seguimiento para cerrar el caso, agendar al técnico o escalar la incidencia. Y aunque el ejemplo gira en torno a máquinas de micheladas, el patrón aplica a soporte técnico, ventas, reservas o cualquier flujo donde los datos del cliente importen.
¿Qué entidades te gustaría que tu propio chatbot capturara? Cuéntame en los comentarios cómo lo adaptarías a tu caso de uso.
Esta guía proporciona un caso de uso sobre “Entity Memory”, una estrategia de memorización basada en la extracción de entidades clave en una conversación.
.
🗂️ Background
Las “Entity Memory” son una estrategia de memoria que se centra en los hechos de entidades específicas en la conversación. Se extrae la información sobre entidades (utilizando un LLM) y construye su conocimiento sobre esa entidad a lo largo del tiempo (también utilizando un LLM).
.
Esta estrategia es particularmente útil para chatbots que necesitan extraer y entender información clave, como nombres de personas, identificadores de productos y otros detalles importantes.
.
Helper Links:
Entity Memory
.
🎯 Problem
Implementar un “Entity Memory” utilizando TypeScript para un Chat con IA, extrayendo campos de interés, como por ejemplo nombre y empresa en una conversación.
.
🚧 Solution
LangChain posee diferentes tipos de estrategias para la memorización de contextos. Así mismo, se poseen diferentes cadenas que, bajo su definición, nos proveen de interfaces a fines a la implementación.
.
LLMChain es una cadena que se enfoca en realizar consultas contra LLMs. Siendo la cadena más especializada en la obtención de información como recuperador.
Dependiendo del escenario, LangChain provee de plantillas que permiten inicializar los LLM bajo un contexto que se irá desarrollando durante el tiempo de servicio. Por ejemplo, la plantilla ENTITY_MEMORY_CONVERSATION_TEMPLATE, nos provee de un prompt para establecer el contexto en la resolución de tareas variadas, desde responder preguntas simples hasta proporcionar explicaciones detalladas y discusiones sobre una amplia gama de temas.
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{entities}
Current conversation:
{history}
Last line:
Human: {input}
You:
Para la memorización, LangChain provee la clase EntityMemory para gestionar la extracción de entidades y el resumen en la memoria en aplicaciones de chatbot.
El ajuste del atributo temperature en los modelos, establece la capacidad determinista del LLM. La recomendación para la EntityMemory es en 0 por la abstracción exacta entre cada interacción. Sin embargo, en la cadena, su LLM dependerá en la depuración a operar según su tipo (Funcional o Utility).
import{LLMChain}from'langchain/chains'import{OpenAI}from'langchain/llms/openai'import{EntityMemory,ENTITY_MEMORY_CONVERSATION_TEMPLATE,}from'langchain/memory'import{PromptTemplate}from'langchain/prompts'constAPI_TOKEN=// 👈 Enter the API Token from OpenAIconst prompt =newPromptTemplate({ inputVariables:ENTITY_MEMORY_CONVERSATION_TEMPLATE.inputVariables, template:ENTITY_MEMORY_CONVERSATION_TEMPLATE.template,})const memory =newEntityMemory({ llm:newOpenAI({ temperature:0, openAIApiKey:API_TOKEN,}),})const chain =newLLMChain({ llm:newOpenAI({ temperature:0.9, openAIApiKey:API_TOKEN,}), memory, prompt, verbose:true,})await chain.call({ input:"Hi! I'm Jim."})await chain.call({ input:'I work in sales. What about you?'})await chain.call({ input:'My office is the Utica branch of Dunder Mifflin. What about you?',})console.log(await memory.loadMemoryVariables({ input:'Who is Jim?'}))
{
history: "Human: Hi! I'm Jim.\nAI: Hi Jim! It's nice to meet you. How can I help you today?\nHuman: I work in sales. What about you?\nAI: I'm an assistant powered by a language model trained by OpenAI. I'm here to help you with any questions or tasks you may have related to the topics I'm familiar with. How can I assist you today?\nHuman: My office is the Utica branch of Dunder Mifflin. What about you?\nAI: I don't have an office, but I can help you with any questions or tasks related to the topics I'm familiar with. So, what can I do for you today?",
entities: {
Jim: "Jim is a human who works in sales and is based in the Utica branch of Dunder Mifflin.",
Utica: "Utica is the location of the Dunder Mifflin branch where Jim works.",
"Dunder Mifflin": "Dunder Mifflin is a company with a Utica branch."
}
}
¿Sería posible pasarle la transcripcion de una conversación entre dos personas para que generase las entidades?
asi es, sin embargo hay que ver el tamanio de la transcripción, dado que puede superar el max_token_len :thinking:
¡Gracias! Al final encontré esto:
OpenAI Whisper Speaker Diarization - Transcription with Speaker Names
En versiones de langchain >= 1.0, se puede replicar este comportamiento con los graph (workflow de nodos) de langgraph
1- Agregamos las importaciones necesarias
import uuid
from pydantic import BaseModel, Field
from typing import Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage
from langgraph.graph import MessagesState, StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from langgraph.store.base import BaseStore
from langchain_core.runnables import RunnableConfig
2- Definimos un schema de pydantic que representara un data-contract cuyos atributos son las entidades que queremos preservar del usuario.
# This entity schema for structured output allows LLM return data in a specific format.# Read docs: https://docs.langchain.com/oss/python/langchain/structured-outputclassExctractedUserEntities(BaseModel): direction: Optional[str]= Field(None, description="The customer's phyisical adress") phone_number: Optional[str]= Field(None, description="The customer's phone number") waranty: Optional[str]= Field(None, description="The machine's warranty number") sold_date: Optional[str]= Field(None, description="The date of purchase of machine by the customer") employee_name: Optional[str]= Field(None, description="The name of the employee who attend served them'")
3- Inicializar instancias de LLM y extraction LLM
# model for chatbot answers and questionsllm = ChatOpenAI( model="gpt-4o-mini", temperature=0.2, max_retries=3, max_tokens=1000)# model extractor will returns outputs formatted to match the given schema.# if `schema` is a pydantic class, then the model output will be a pydantic instance of that classextractor_llm = llm.with_structured_output(schema=ExctractedUserEntities)
4- Definimos una function-node la cual sera un paso en nuestro workflow para extraer entidades desde cada mensaje enviado por el usuario.
# extraction node finds entities in messages and saves to store.defextract_user_entities_node(state: MessagesState, config: RunnableConfig,*, store: BaseStore):""" This node will finds entities in messages
and saves to store.
Args:
state: state of the current conversation, contains 'messages' history.
config: Configuration of the conversation, contains conversation metadata.
store: where the entities will be stored in this conversation, langgraph looks for the store registered in it's workflow and passes directly.
""" user_id = config["configurable"].get("user_id","default_user")# Check if store existsif store isNone:raise ValueError("Store is not initialized.")# Analize only the last user message for new entities last_message = state["messages"][-1].content
# invoke the extrator_llm with the last human message extracted = extractor_llm.invoke(f"Extract information from this message {last_message}")# if found entities, then save on the store under entitiesif extracted: data ={k: v for k, v in extracted.model_dump().items()if v isnotNone}for key, value in data.items(): store.put(("entities", user_id), key, value)# returns empty dict because store updates are side-effectreturn{}
5- Definimos un function-node la cual sera otro paso en nuestro workflow para comunicarnos con el modelo en nuestro question/answering
defcall_model_node(state: MessagesState, config: RunnableConfig,*, store: BaseStore):""" This node will define the system prompt and
handle question/answering between user and LLM
Args:
state: state of the current conversation, contains 'messages' history.
config: Configuration of the conversation, contains conversation metadata.
store: where the entities will be stored in this conversation, langgraph looks for the store registered in it's workflow and passes directly.
""" user_id = config["configurable"].get("user_id","default_user")# Search and retrieve all entities for this user stored_items = store.search(("entities", user_id)) entities_str ="\n".join([f"- {item.key}: {item.value}"for item in stored_items])ifnot entities_str: entities_str ="There is not personalizated information yet." system_instruction =f"""
You are a sales assistant for a photography studio.
You are only designed to (1) try to resolve issues with downloading edited photos that are pending delivery to customers, and if the customer is unable to do so, (2) add the shots (photos) that the customer chose to the editors' task queue.
If the customer cannot find the photos on the download page, then ask if they want to send the shots back for editing, which will take one week. If the customer wants to send the shots back for editing, they must leave their cell phone number and address.
You also have access to personalized information provided by the human in the Context section below.
You are here to help, always with the spark and character of someone born in Medellin, Colombia.
It is essential that you ask for the date of their photo session, their invoice number, and who attended to them. Always ask if the customer's problem or question has been resolved.
Context:
{entities_str}"""# invoke the LLM with whole context with our system prompt with entities if found and the complete messages history messages =[system_instruction]+ state["messages"] response = llm.invoke(messages)return{"messages":[response]}
6- Creamos nuestro workflow the langgraph al cual enlazaremos con los nodos definidos arriba.
# Build and compile the graph# store to handle entities memoriesstore = InMemoryStore()# checkpointer to save each interaction between user and LLMcheckpointer = MemorySaver()# init the workflowworkflow = StateGraph(MessagesState)# Add our two defined nodes to the workflowworkflow.add_node("extract", extract_user_entities_node)workflow.add_node("assistant", call_model_node)# Define steps order of the workflow# init -> extract -> assistant -> endworkflow.add_edge(START,"extract")workflow.add_edge("extract","assistant")workflow.add_edge("assistant", END)# Compile with checkpointer for history and store for entitiesapp = workflow.compile(checkpointer=checkpointer, store=store)
7- Definimos la metadata necesaria para nuestra conversacion
# The user_id ensures the address/phone persists across different conversations config ={"configurable":{"thread_id":"x1s343","user_id":"user_001"}}
8- desde este punto podemos enviar mensajes a nuestro chatbot assistente, repetiremos el mismo codigo con diferentes mensajes para ir viendo las respuestas y asi reformular nuestro siguiente mensaje como mejor nos convenga
msg_1 = HumanMessage(content="Hi, My name is Evert Escalante, I would like support, I cant find my photos on your company url to download them")for chunk in app.stream({"messages":[msg_1]}, config, stream_mode="values"):if chunk["messages"]:print(f"Assistant: {chunk['messages'][-1].content}\n")
mensaje 2:
msg_2 = HumanMessage(content="it was on january 27th, invoice_number is 23332334 and Yurley Munioz attends me")for chunk in app.stream({"messages":[msg_2]}, config, stream_mode="values"):if chunk["messages"]:print(f"Assistant: {chunk['messages'][-1].content}\n")
Si quieren ver las entidades guardadas
all_entities = store.search(("entities","user_001"))for item in all_entities:# item.key is the entity name (e.g., 'direccion')# item.value is the stored valueprint(f"{item.key}: {item.value}")
Porque Los Ejemplos de Caso Uso son tan Informales en Este Curso ? Se vuelve fastidisoso tanto wokinsmo con el tiempo.
A mi me sale NotFoundError Traceback (most recent call last)
<ipython-input-16-750be73a0d15> in <cell line: 1>()
----> 1 conversation.predict(input="Que onda, mi máquina para hacer micheladas no está funcionando. Ando enojado.")
23 frames in _request(self, cast_to, options, remaining_retries, stream, stream_cls)
986
987 log.debug("Re-raising status error")
--> 988 raise self._make_status_error_from_response(err.response) from None
989
990 return self._process_response(
NotFoundError: Error code: 404 - {'error': {'message': 'The model text-davinci-003 has been deprecated, learn more here: ', 'type': 'invalid_request_error', 'param': None, 'code': 'model_not_found'}}...
alguien sabe como solucionar, lo reemplacé por gpt-4 pero sigue saliendo el mimsmo mensaje
Hola Lourdes, me parece que cambio un poco la documentacion, debes importar el modelo de chat de open ai:
from langchain_community.chat_models import ChatOpenAI