Creación de una aplicación de chat multilingüe y multiagente con LangGraph (parte I) | por Roshan Santhosh | septiembre de 2024

La columna vertebral de esta aplicación son los agentes y sus interacciones. En general, teníamos dos tipos diferentes de agentes:

  1. Agentes de usuario: agentes asociados a cada usuario. Su principal tarea es traducir los mensajes entrantes al idioma preferido del usuario.
  2. Agentes de Aya: varios agentes asociados con Aya, cada uno con su propio rol/trabajo específico.

Agentes de usuario

La clase UserAgent se utiliza para definir un agente que se asociará con cada parte de usuario de la sala de chat. Algunas de las funciones implementadas por la clase UserAgent:

1. Traducir los mensajes entrantes al idioma preferido del usuario

2. Activar/Invocar gráfico cuando un usuario envía un mensaje

3. Mantenga un historial de chat para ayudar a proporcionar contexto a la tarea de traducción y permitir una traducción “sensible al contexto”.

class UserAgent(object):

def __init__(self, llm, userid, user_language):
self.llm = llm
self.userid = userid
self.user_language = user_language
self.chat_history = []

prompt = ChatPromptTemplate.from_template(USER_SYSTEM_PROMPT2)

self.chain = prompt | llm

def set_graph(self, graph):
self.graph = graph

def send_text(self,text:str, debug = False):

message = ChatMessage(message = HumanMessage(content=text), sender = self.userid)
inputs = {"messages": [message]}
output = self.graph.invoke(inputs, debug = debug)
return output

def display_chat_history(self, content_only = False):

for i in self.chat_history:
if content_only == True:
print(f"{i.sender} : {i.content}")
else:
print(i)

def invoke(self, message:BaseMessage) -> AIMessage:

output = self.chain.invoke({'message':message.content, 'user_language':self.user_language})

return output

En su mayor parte, la implementación de UserAgent es un código LangChain/LangGraph bastante estándar:

  • Defina una cadena LangChain (una plantilla de solicitud + LLM) que sea responsable de realizar la traducción real.
  • Define una función send_text que se utiliza para invocar el gráfico siempre que un usuario quiera enviar un mensaje nuevo.

En su mayor parte, el rendimiento de este agente depende de la calidad de la traducción del LLM, ya que la traducción es el objetivo principal de este agente. Y el rendimiento del LLM puede variar significativamente para la traducción, especialmente según los idiomas involucrados. Algunos idiomas con pocos recursos no tienen una buena representación en los datos de entrenamiento de algunos modelos y esto afecta la calidad de la traducción de esos idiomas.

Agentes de Aya

Para Aya, en realidad tenemos un sistema de agentes separados que contribuyen al asistente general. En concreto, tenemos

  1. AyaSupervisor: Agente de control que supervisa el funcionamiento de los demás agentes de Aya.
  2. AyaQuery: Agente para ejecutar preguntas y respuestas basadas en RAG
  3. AyaSummarizer: Agente para generar resúmenes de chat y realizar identificación de tareas
  4. AyaTranslator: Agente para traducir mensajes al inglés
class AyaTranslator(object):

def __init__(self, llm) -> None:
self.llm = llm
prompt = ChatPromptTemplate.from_template(AYA_TRANSLATE_PROMPT)
self.chain = prompt | llm

def invoke (self, message: str) -> AIMessage:
output = self.chain.invoke({'message':message})
return output

class AyaQuery(object):

def __init__(self, llm, store, retriever) -> None:
self.llm = llm
self.retriever = retriever
self.store = store
qa_prompt = ChatPromptTemplate.from_template(AYA_AGENT_PROMPT)
self.chain = qa_prompt | llm

def invoke(self, question : str) -> AIMessage:

context = format_docs(self.retriever.invoke(question))
rag_output = self.chain.invoke({'question':question, 'context':context})
return rag_output

class AyaSupervisor(object):

def __init__(self, llm):

prompt = ChatPromptTemplate.from_template(AYA_SUPERVISOR_PROMPT)
self.chain = prompt | llm

def invoke(self, message : str) -> str:
output = self.chain.invoke(message)
return output.content

class AyaSummarizer(object):

def __init__(self, llm):

message_length_prompt = ChatPromptTemplate.from_template(AYA_SUMMARIZE_LENGTH_PROMPT)
self.length_chain = message_length_prompt | llm

prompt = ChatPromptTemplate.from_template(AYA_SUMMARIZER_PROMPT)
self.chain = prompt | llm

def invoke(self, message : str, agent : UserAgent) -> str:

length = self.length_chain.invoke(message)

try:
length = int(length.content.strip())
except:
length = 0

chat_history = agent.chat_history

if length == 0:
messages_to_summarize = [chat_history[i].content for i in range(len(chat_history))]
else:
messages_to_summarize = [chat_history[i].content for i in range(min(len(chat_history), length))]

print(length)
print(messages_to_summarize)

messages_to_summarize = "\n ".join(messages_to_summarize)

output = self.chain.invoke(messages_to_summarize)
output_content = output.content

print(output_content)

return output_content

La mayoría de estos agentes tienen una estructura similar, que consiste principalmente en una cadena LangChain que consta de un mensaje personalizado y un LLM. Las excepciones incluyen el agente AyaQuery, que tiene un recuperador de base de datos de vectores adicional para implementar RAG, y AyaSummarizer, que tiene múltiples funciones LLM implementadas dentro de él.

Consideraciones de diseño

Rol del Agente Supervisor de Aya:En el diseño del gráfico, teníamos un borde fijo que iba desde el nodo supervisor a los nodos de usuario. Lo que significaba que todos los mensajes que llegaban al nodo supervisor se enviaban a los nodos de usuario. Por lo tanto, en los casos en los que se dirigía a Aya, Tuvimos que asegurarnos de que solo se enviara un único resultado final de Aya a los usuarios.No queríamos que los mensajes intermedios, si los hubiera, llegaran a los usuarios. Por lo tanto, teníamos al agente AyaSupervisor que actuaba como único punto de contacto para el agente de Aya. Este agente era el principal responsable de interpretar la intención del mensaje entrante, dirigir el mensaje al agente específico de la tarea correspondiente y luego generar el mensaje final para compartirlo con los usuarios.

Diseño de AyaSummarizer: El agente AyaSummarizer es un poco más complejo en comparación con los demás agentes de Aya, ya que lleva a cabo un proceso de dos pasos. En el primer paso, el agente determina primero la cantidad de mensajes que se deben resumir, lo que es una llamada LLM con su propio mensaje. En el segundo paso, una vez que sabemos la cantidad de mensajes que se deben resumir, recopilamos los mensajes necesarios y los pasamos al LLM para generar el resumen real. Además del resumen, en este paso en sí, el LLM también identifica los elementos de acción que estaban presentes en los mensajes y los enumera por separado.

En líneas generales, había tres tareas: determinar la longitud de los mensajes que se debían resumir, resumir los mensajes e identificar los elementos de acción. Sin embargo, dado que la primera tarea estaba resultando un poco difícil para el LLM sin ejemplos explícitos, decidí que se tratara de una convocatoria LLM independiente y luego combinar las dos últimas tareas como una convocatoria LLM propia.

Es posible eliminar la convocatoria adicional de LLM y combinar las tres tareas en una sola convocatoria. Las posibles opciones incluyen:

  1. Proporcionar ejemplos muy detallados que cubren las tres tareas en un solo paso.
  2. Generar una gran cantidad de ejemplos para perfeccionar un LLM para que pueda desempeñarse bien en esta tarea

Rol de AyaTranslator: Uno de los objetivos con respecto a Aya era convertirla en un asistente de IA multilingüe que pudiera comunicarse en el idioma preferido del usuario. Sin embargo, sería difícil gestionar diferentes idiomas internamente dentro de los agentes de Aya. En concreto, si el mensaje de los agentes de Aya está en inglés y el mensaje del usuario está en un idioma diferente, podría generar problemas. Por lo tanto, para evitar tales situaciones, como paso de filtrado, Traducimos todos los mensajes entrantes de los usuarios de Aya al inglés.Como resultado, todo el trabajo interno dentro del grupo de agentes de Aya se realizó en inglés, incluido el resultado. No tuvimos que traducir el resultado de Aya al idioma original porque cuando el mensaje llega a los usuarios, los agentes de usuario se encargarán de traducir el mensaje a su respectivo idioma asignado.