Un chain o “cadena” es el componente básico de langchain y pueden verse como “eslabones” de una cadena, en el que cada uno de ellos tiene una funcionalidad.
Por ejemplo, el chain más básico entrega a un modelo una pregunta (pueden usarse plantillas de prompts) y éste devuelve una respuesta.
Pueden combinarse diferentes, de forma que la salida de uno sea la entrada del siguiente y de este modo crear flujos más o menos complejos.
A continuación un ejemplo de un chain simple.
# Importamos dependencias # Cuando no son plantillas estáticas, si no que pueden variar como resultado de una # conversación de chat, usamos 'ChatPromptTemplate', en lugar de 'PromptTemplate' from langchain_ollama import ChatOllama from langchain.prompts import ChatPromptTemplate from langchain.chains.llm import LLMChain # Definimos el modelo con el que trabajaremos modelo = ChatOllama( model = "llama3.2:1b", temperature = 0.9, verbose = False ) #Definimos el prompt prompt = """Escribe SÓLO UN título para una novela \ del género: {genero}.""" plantilla_prompt = ChatPromptTemplate.from_template(prompt) # Definimos el "chain" o el eslabón de cadena, a la que pasamos cadena = LLMChain(llm=modelo, prompt=plantilla_prompt) # Chain básico: pasamos una pregunta al modelo y éste contesta # Ya sólo nos queda definir los datos de entrada y "correr" el preograma genero = "terror" titulo = cadena.run(genero) print(titulo)
La unión de diferentes chains, puede dar lugar a flujos, de modo que la salida de uno sea la entrada de otro.
A continuación vemos un ejemplo de hasta 4 chains seguidos.
# A partir de la transcripción de una reunión entre 3 profesores que pasamos en un archivo txt, realizamos varias acciones: # 1. Chain 1: Resumimos el contenido de la transcripción. # 2. Chain 2: Lo pasamos a inglés. # 3. Chain 3: El director responde en inglés a los profesores que mantuvieron la reunión. # 4. Chain 4: Se traduce a español la respuesta del director. # En primer lugar cargamos las dependencias from langchain_ollama import ChatOllama from langchain.prompts import ChatPromptTemplate from langchain.chains.llm import LLMChain from langchain.chains.sequential import SequentialChain import pprint # Definimos el modelo con el que trabajaremos modelo = ChatOllama( model = "llama3.2", temperature = 0.9, verbose = False ) # La transcripción se puede poner aquí, aunque también se podría poner al final si el código se va a reutilizar #with open("transcripcion_reunion.txt", 'r') as transcripcion: # reunion = transcripcion.read() # Primera plantilla de prompt: Crea un resumen a partir de la transcripción de la reunión prompt1 = ChatPromptTemplate.from_template( "Crea un resumen de la reunión mantenida por tres profesores, \ de la cual tenemos la siguiente transcripción: " "\n\n{reunion}" ) # Eslabón 1: input = transcripcion de la reunión, output = acta eslabon1 = LLMChain(llm=modelo, prompt=prompt1, output_key="resumen_es") # Segundo prompt: Traducción del resumen de la reunión del español al inglés prompt2 = ChatPromptTemplate.from_template( "Traduce el siguiente texto al inglés: " "\n\n{resumen_es}" ) # Segundo eslabón: input = resumen en español, output = resumen en inglés eslabon2 = LLMChain(llm=modelo, prompt=prompt2, output_key="resumen_en") # tercer prompt: Respuesta del director en inglés prompt3 = ChatPromptTemplate.from_template( "El director del instituto, que habla inglés, lee el resumen de la reunión mantenida por los profesores: " "\n\n{resumen_en}\n" "Escribe la respuesta, en inglés, del director a los profesores explicando si le parece bien o no el cambio." ) # tercer eslabón: input = resumen en inglés, output = respuesta en inglés eslabon3 = LLMChain(llm=modelo, prompt=prompt3, output_key="respuesta_en") # cuarto prompt: Traducción de la respuesta del inglés al español prompt4 = ChatPromptTemplate.from_template( "Traduce el siguiente texto del inglés a español: " "\n\n{respuesta_en}" ) # cuarto eslabón: input = acta en español, output = acta en inglés eslabon4 = LLMChain(llm=modelo, prompt=prompt4, output_key="respuesta_es") # Creamos el flujo, esto es la cadena total uniendo todos los eslabones cadena = SequentialChain( chains = [eslabon1, eslabon2, eslabon3, eslabon4], input_variables=["reunion"], output_variables=["resumen_es", "resumen_en", "respuesta_en", "respuesta_es"], verbose=True ) # Obtenemos la transcripción de la reunión with open("transcripcion_reunion.txt", 'r') as transcripcion: reunion = transcripcion.read() # Ejecutamos el flujo, pasando al flujo creado la entrada (trnacripción), y obtenemos todas las salidas intermedias y final contestacion = cadena(reunion) pprint.pprint(contestacion)
En este caso la cadena tendrá diferentes ramas en paralelo y el sistema deberá rutear el flujo por una o por otra, en función del contexto, según los datos de entrada.
Vamos a ver un ejemplo de cadena con ruteo. Para construir el flujo, seguimos los sigientes pasos:
# Como ejemplo, tendremos como entrada una consulta a una empresa de # informática y electrónica que se ocupa tanto de servicio técnico # como de venta de artículos. El sistema deberá pasar la consulta # al departamento técnico o comercial, según proceda from langchain_ollama import ChatOllama from langchain.prompts import ChatPromptTemplate from langchain.prompts import PromptTemplate from langchain.chains.llm import LLMChain from langchain.chains.router import MultiPromptChain from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser import pprint # Definimos el modelo con el que trabajaremos # En este caso una temperatura alta nos entorpece la salida, que debe de ser # un JSON. Al tener una temperatura alta, el modelo se vuelve "parlanchin" y # añade texto explicativo innecesario que genera un error modelo = ChatOllama( model = "llama3.2", temperature = 0.0, verbose = False ) # Definimos las plantillas de los distintos departamentos plantilla_comercial = """Eres un simpático comercial, comprensivo, empático \ y divertido. Estás encantado de responder a las consultas que te formulan \ los clientes, siempre encuentras una salida a las preguntas difíciles y \ sueles conseguir vender lo que te propones. Responde a la siguiente consulta de un cliente: {input}""" plantilla_tecnico = """Eres un técnico informático con conocimientos avanzados \ de electrónica. Eres serio, conciso, y que le gusta ir al grano. \ Respondes a las cuestiones técnicas que te plantean los clientes de forma profesional, \ y precisa. Sabes adaptar las explicaciones cuando el cliente parace tener pocos \ conocimientos técnicos. Responde a la siguiente consulta de un cliente: {input}""" # Definimos una lista con información de los departamentos info_departamentos = [ { "nombre": "comercial", "descripcion": "Bueno para contestar a preguntas acerca de cuestiones comerciales", "plantilla": plantilla_comercial }, { "nombre": "tecnico", "descripcion": "Bueno para contestar a preguntas técnicas de usuarios sin conocimientos de informática o electrónica", "plantilla": plantilla_tecnico } ] # 1. Ahora definimos la variable "ramal" que será un diccionario en el que cada elemento se corresponde # con uno de los posibles ramales de ruteo. Cada elemento tendrá como nombre el especificado en # info_departamentos, y su contenido será un chain de tipo LLMChain con la plantilla correspondiente # y el modelo de IA a usar ramales = {} for info_dpto in info_departamentos: nombre = info_dpto["nombre"] plantilla = info_dpto["plantilla"] prompt = ChatPromptTemplate.from_template(template=plantilla) eslabon = LLMChain(llm=modelo, prompt=prompt) # El objetivo del bucle es crear este chain y ... ramales[nombre] = eslabon # ... meterlo en una lista donde cada elemento son los posibles chains en paralelo # destinos: lista (también otra variable destinos_str tipo cadena de caracteres) que tiene en cada # elemento cadena "nombre: descripción" de cada departamento destinos = [f"{p['nombre']}:{p['descripcion']}" for p in info_departamentos] destinos_str = "\n".join(destinos) #pprint.pprint(destinos) #pprint.pprint(destinos_str) # 2. Definimos un prompt "por defecto" para usar si el sistema no identifica la consulta con ninguno # de los dos departamentos default_prompt = ChatPromptTemplate.from_template("{input}") default_chain = LLMChain(llm=modelo, prompt=default_prompt) # 3. Escribimos el prompt que seleccionará el ruteo (ramal) correspondiente plantilla_multiprompt_ruteo = """Dado un texto sin procesar correspondiente a una consulta \ para una empresa de informática y electrónica, selecciona la opción que mejor se adapte a \ la naturaleza de dicha consulta. Se proporcionarán los nombres \ de las opciones disponibles y una descripción para discernir cuál es la más adecuada. \ También puedes revisar la entrada original si crees que al revisarla, se obtendrá \ una mejor respuesta del modelo. <<FORMATO>> Devuelve SÓLO y ÚNICAMENTE un código en formato JSON de la siguiente manera: '''json {{{{ "destination": string \ nombre del mensaje a utilizar o "DEFAULT" "next_inputs": string \ una versión potencialmente modificada de la entrada original }}}} ''' RECUERDA: "destination" DEBE ser uno de los nombres candidatos especificados a continuación \ O puede ser "DEFAULT" si el mensaje no es adecuado para ninguno de los mensajes candidatos. RECUERDA: "next_inputs" puede ser simplemente el mensaje original si no crees \ que se necesiten modificaciones. RECUERDA: Devuelve SÓLO y ÚNICAMENTE el output en formato JSON <<PROMPTS CANDIDATOS>> {destinos} <<INPUT>> {{input}} <<OUTPUT>> """ # Sustituimos en la plantilla multiprompt, {destinos} por la cadena de destinos plantilla_ruteo = plantilla_multiprompt_ruteo.format(destinos=destinos_str) # 4. Creamos el prompt de ruteo a partir de las plantilla multiprompt prompt_ruteo = PromptTemplate( template=plantilla_ruteo, input_variables=["input"], output_parser=RouterOutputParser(), ) # 5. Creamos el "chain" de tipo "router chain", especificando modelo y prompt que define el ruteo cadena_ruteo = LLMRouterChain.from_llm(modelo,prompt_ruteo) # 6. Creamos el flujo completo definiendo los posibles destinos cadena_consulta = MultiPromptChain(router_chain=cadena_ruteo, destination_chains=ramales, default_chain=default_chain, verbose=True ) # 7. Ejecutamos #respuesta = cadena_consulta.run("Buenos días, tras cambiar algunos componentes del PC, no arranca y emite un pitido intermitente ¿Qué puedo hacer?") #respuesta = cadena_consulta.run("Buenas tardes, quiero saber qué ordenador necesito para trabajar. Sobre todo, mirar el correo, manejar programas ofimáticos y navegar port internet ¿Cuánto puede costar?") respuesta = cadena_consulta.run("Buenos días, me pongo en contacto con ustedes desde un distribuidor de componentes electrónicos. Nos gustaría concertar una cita para enseñarles nuestro catálogo y poder hacer negocios juntos.") pprint.pprint(respuesta)