Construye un Sistema Multi-Agente con Novita y CrewAI

Construye un Sistema Multi-Agente con Novita y CrewAI

A medida que la industria de la IA empezó a reconocer las capacidades de razonamiento de los Modelos de Lenguaje Grande (LLMs, por sus siglas en inglés), decidimos dotarlos de capacidades agente al permitirles realizar acciones más complejas. Estas categorías de Modelos de Lenguaje Grande pasaron a conocerse como agentes.

Así nacieron los sistemas de agente único. Pero pronto quedó claro que asignar varias acciones a un solo agente no era la forma más inteligente de construir sistemas agentes. Un enfoque mejor era usar múltiples agentes, cada uno con su propia tarea que realizar, trabajando juntos para alcanzar un objetivo definido por el usuario.

Sin embargo, surgió otro problema. Gestionar múltiples agentes no es tan fácil como gestionar uno solo. Para solucionar esto, se han introducido varios frameworks de Sistemas Multi-Agente, y uno de los más prometedores es CrewAI.

CrewAI ayuda a los desarrolladores a construir Sistemas Multi-Agente. Con CrewAI, puedes gestionar tus agentes, sus tareas y su flujo de trabajo. En este artículo, usaremos CrewAI junto con la gran cantidad de LLMs disponibles en Novita para construir Sistemas Multi-Agente prácticos e inteligentes.

¿Qué es un Sistema Multi-Agente y por qué lo necesitamos?

Desde la introducción de los Modelos de Lenguaje Grande (LLMs), se han explorado varios enfoques para hacerlos más autónomos. Uno de los más populares fue el patrón ReAct introducido en el artículo Synergizing Reasoning and Acting in Language Models. Frameworks como LangChain implementaron rápidamente ReAct para dar a los LLMs capacidades agente.

Herramientas como AutoGPT llevaron las cosas aún más lejos al construir agentes de IA más robustos que podían realizar casi cualquier tarea que el usuario deseara con solo un prompt de texto. Si bien estos sistemas de agente único mostraban un gran potencial, tenían limitaciones. Un agente único suele depender de un solo LLM para manejar todas las tareas, lo que genera algunos inconvenientes críticos:

  • Contexto limitado: Un agente único tiene que gestionar todas las tareas usando una sola ventana de contexto. A medida que avanza el flujo de trabajo, el agente eventualmente se queda sin espacio de contexto.
  • Mayor complejidad: A medida que se asignan más tareas a un solo agente, el razonamiento se vuelve más difícil de gestionar, lo que hace que el agente sea más propenso a alucinaciones o fallos.
  • Punto único de fallo: Con un solo agente responsable de todo, no hay respaldo. Si no puede manejar una tarea, el sistema falla por completo.
  • Falta de paralelización: Las tareas se procesan una tras otra. No hay soporte integrado para que los agentes trabajen en paralelo para acelerar los procesos.

Se introdujeron los sistemas multi-agente para abordar estos desafíos. En lugar de depender de un solo agente para manejar múltiples tareas, un sistema multi-agente distribuye las responsabilidades entre varios agentes que trabajan en colaboración. Esto ayuda a evitar problemas como superar la longitud del contexto, ya que cada agente solo necesita gestionar su propia parte del flujo de trabajo, y la carga de contexto general se reduce.

Las configuraciones multi-agente también te permiten usar diferentes modelos para diferentes propósitos: algunos se pueden ajustar finamente para tareas específicas, otros pueden venir con prompts predefinidos, algunos pueden estar equipados con herramientas especializadas y otros se pueden optimizar para el razonamiento. Esta diversidad no solo mejora el rendimiento, sino que también permite la paralelización, haciendo que los sistemas multi-agente sean más rápidos y eficientes que sus equivalentes de agente único.

Para construir un sistema multi-agente, necesitas dos componentes principales: un proveedor de modelos que suministre los LLMs, y un framework multi-agente para gestionar todo el flujo de trabajo agente. En este artículo, usaremos Novita como nuestro proveedor de modelos y CrewAI como framework elegido.

Por qué necesitas un framework como CrewAI

Construir un sistema multi-agente es mucho más complejo que construir un sistema de agente único. Por ejemplo, puedes hacer fácilmente que cualquier modelo de Novita se convierta en un agente dándole herramientas mediante el uso de llamadas a funciones. Pero una vez que empiezas a conectar múltiples agentes, la complejidad aumenta significativamente. Ahora tienes que gestionar varias partes en movimiento, entre ellas:

  • Comunicación y delegación de tareas: Necesitas coordinar cómo se comunican los agentes entre sí y cómo se transfieren las tareas entre ellos.
  • Gestión de herramientas: Sin un framework, eres responsable de gestionar las definiciones de herramientas y los esquemas JSON manualmente, algo que puede volverse desordenado rápidamente.
  • Arquitecturas y patrones agente: Los frameworks multi-agente suelen incluir soporte integrado para patrones agente bien establecidos. Si estás construyendo desde cero, deberás mantenerte actualizado con estos patrones en evolución e implementarlos tú mismo.
  • Observabilidad: Sin un framework, también eres responsable de configurar tus propias herramientas para monitorear, depurar y visualizar cómo interactúan y funcionan tus agentes.

Un framework Multi-Agente puede ayudar a gestionar todas estas complejidades. En este artículo, usaremos CrewAI como framework elegido. CrewAI ofrece varias ventajas sobre otros frameworks: destaca por su simplicidad, usando conceptos intuitivos para modelar Sistemas Multi-Agente; es maduro y bien establecido; y proporciona una excelente experiencia de desarrollador.

Comprensión de los conceptos básicos de CrewAI

Para construir sistemas multi-agente con CrewAI, necesitas estar familiarizado con algunos conceptos básicos introducidos por el framework. Aquí tienes un breve resumen de los principales con los que trabajaremos en este artículo:

  • Agentes
  • Tareas
  • Tripulaciones
  • Procesos
  • Flujos

Agentes

Los agentes son el núcleo de CrewAI. Representan a los trabajadores de IA reales impulsados por un LLM. Cada agente en CrewAI se define por un rol, un objetivo que guía su toma de decisiones y una historia de fondo que da forma a su personalidad. Todos estos se representan como prompts de texto que se envían al LLM. Los agentes también pueden estar equipados con herramientas, de las que pueden elegir para realizar acciones específicas. El objetivo principal de un agente en CrewAI es completar la tarea o tareas que se le asignen.

Tareas

Las tareas definen el trabajo que debe completarse. En CrewAI, puedes crear tareas y asignarlas directamente a agentes específicos o dejar que los agentes las recojan si coinciden con los requisitos de la tarea. Cada tarea suele incluir una descripción clara, un resultado esperado y otros parámetros relevantes, como un agente opcional que pueda manejar la tarea.

Tripulación

Este es el concepto central de CrewAI. En CrewAI, un grupo de agentes que trabajan juntos para realizar tareas se denomina tripulación. Una tripulación también define el flujo de trabajo de los agentes que la componen.

Procesos

Un proceso es un flujo de trabajo predefinido que indica a una tripulación cómo ejecutar sus tareas. CrewAI admite dos tipos principales de procesos:

  • Secuencial: Las tareas se manejan de una en una por los agentes de la tripulación.
  • Jerárquico: En este proceso, un agente actúa como gestor, coordinando a otros agentes y delegando tareas según sea necesario.

Flujos

Si bien los procesos son flujos de trabajo predefinidos, los flujos de CrewAI dan a los desarrolladores la flexibilidad de diseñar flujos de trabajo de agentes personalizados. Un flujo puede incluir agentes, tareas e incluso tripulaciones enteras, lo que permite interacciones más complejas.

Construcción de tripulaciones con Novita

Ahora que hemos repasado los conceptos básicos de CrewAI, vamos a repasar el proceso de construcción de una tripulación. En este ejemplo, crearemos una tripulación diseñada para ayudar a los usuarios a construir rápidamente un producto mínimo viable (MVP, por sus siglas en inglés). Esta tripulación consta de tres agentes especializados:

  • Arquitecto: Define la estructura del software y describe el alcance del MVP. Este agente funciona con el modelo moonshotai/kimi-k2-instruct, alojado en Novita.
  • Desarrollador: Implementa el plan del Arquitecto y escribe el código real. Usa la herramienta FileWriter para crear todos los archivos del proyecto y funciona con el modelo qwen/qwen3-coder-480b-a35b-instruct de Novita.
  • Revisor: Analiza el código generado por el Desarrollador y sugiere mejoras mediante comentarios en formato diff. Al igual que el Arquitecto, también usa el modelo moonshotai/kimi-k2-instruct.

Nuestra tripulación seguirá un proceso secuencial.

Instalación y configuración

Ahora que tenemos un plan listo, vamos a empezar configurando nuestro entorno. Comenzaremos instalando CrewAI junto con su soporte de herramientas integrado:

pip install 'crewai[tools]'

A continuación, necesitarás tu clave API de Novita. Una vez que la tengas, agrégala a tus variables de entorno como NOVITA_API_KEY:

export NOVITA_API_KEY=your_api_key_here

Con eso listo, estamos preparados para empezar a construir la tripulación.

Creación de los LLMs

Vamos a empezar importando las dependencias necesarias:

import os
from crewai import Agent, Task, Crew, Process, LLM
from crewai_tools import FileWriterTool

A continuación, crearemos una instancia de FileWriterTool, que usará el agente Desarrollador para escribir archivos en el disco:

file_writer_tool = FileWriterTool()

Con las herramientas listas, ahora podemos inicializar los LLMs para cada uno de nuestros agentes:

architect_llm = LLM(
   model="novita/moonshotai/kimi-k2-instruct",
   temperature=0.5,
   api_base="https://api.novita.ai/v3/openai",
   api_key=os.environ['NOVITA_API_KEY']
)

coder_llm = LLM(
   model="novita/qwen/qwen3-coder-480b-a35b-instruct",
   temperature=0.4,
   api_base="https://api.novita.ai/v3/openai",
   api_key=os.environ['NOVITA_API_KEY']
)

reviewer_llm = LLM(
   model="novita/moonshotai/kimi-k2-instruct",
   temperature=0.5,
   api_base="https://api.novita.ai/v3/openai",
   api_key=os.environ['NOVITA_API_KEY']
)

Estas tres instancias de LLM corresponden a los modelos descritos en nuestro plan. Están alojados en Novita y se accede a ellos a través de CrewAI. Entre bastidores, CrewAI usa LiteLLM para conectarse y gestionar estos modelos.

Definición de los agentes

Ahora vamos a crear los tres agentes:

architect = Agent(
   role="Software Architect for MVP Projects",
   goal="Define a basic system structure and simple feature set to guide MVP development",
   backstory="""You specialize in quickly outlining software architectures and project scopes for
minimum viable products. Your plans help guide coders with enough structure to get started while staying lean.""",
   llm=architect_llm,
   verbose=True
)

coder = Agent(
   role="Developer for MVP Projects",
   goal="Implement the MVP using simple, clear code, and write all necessary code files using the FileWriter tool",
   backstory="""You're a practical developer focused on speed. You prioritize working code over polish.
Simulate any runtime behavior if needed, and make sure to keep code clear and modular.""",
   llm=coder_llm,
   verbose=True,
   tools=[file_writer_tool]
)

reviewer = Agent(
   role="Code Reviewer for MVP Projects",
   goal="Read the code files, provide helpful feedback, and write improvements as diffs into a markdown file",
   backstory="""You're a fast but thoughtful reviewer. You check code for clarity, obvious bugs, and
provide improvements as diffs. Instead of modifying code directly, you 
save your suggestions in a markdown file
for easy inspection.""",
   llm=reviewer_llm,
   verbose=True,
   tools=[file_writer_tool]
)

A cada agente se le asigna un rol, un objetivo y una historia de fondo específicos.

Definición de las tareas

Vamos a crear las tareas de los agentes:

architect_task = Task(
   description="""
Create a basic plan for building a simple MVP version of a {project}.
Focus on the core features, basic file structure, and tech stack.
Make the structure minimal but enough to get a working demo.
""",
   expected_output="""
A simple architectural overview with:
- Project goals
- Key components or files
- Basic data flow or structure
""",
   agent=architect,
   output_file="architecture.md"
)

coder_task = Task(
   description="""
Based on the architect's plan, implement the core parts of the {project} MVP.
Keep it lean and functional. Use the FileWriter tool to save all your code files.
If you need to simulate behavior (e.g. without a real interpreter), do so with clear comments or mocked logic.
""",
   expected_output="""
Working code files saved using the FileWriter tool that implement the key features defined in the plan.
Code should be readable and logically structured.
""",
   agent=coder,
   context=[architect_task]
)

review_task = Task(
   description="""
Review the code files created for the {project} MVP. Read each file using the FileRead tool.
Suggest improvements using diffs. Do not modify the original code files directly.
Instead, save all your suggested diffs into a markdown file using the FileWriter tool.
""",
   expected_output="""
A markdown file containing diff-style suggestions for each file reviewed.
""",
   agent=reviewer,
   context=[coder_task],
   output_file="code_review_diffs.md"
)

Creación de la tripulación

Ahora que hemos definido nuestros agentes, sus respectivos LLMs y tareas, vamos a unir todo armando la tripulación:

code_crew = Crew(
   agents=[architect, coder, reviewer],
   tasks=[architect_task, coder_task, review_task],
   process=Process.sequential,
   verbose=True
)

Con todo configurado, ahora podemos ejecutar la tripulación:

if __name__ == "__main__":
   code_crew.kickoff(inputs={"project": "Todo App"})

Ahora tenemos una tripulación completamente funcional capaz de generar un proyecto MVP basado en una especificación definida por el usuario. En el ejemplo anterior, le pedimos a la tripulación que construyera una aplicación de lista de tareas sencilla.

Así es como funciona:

  1. El agente Arquitecto inicia el proceso diseñando la arquitectura del software y guardando el plan en un archivo llamado architecture.md.
  2. El agente Desarrollador recoge el resultado del arquitecto y crea los directorios y archivos de código necesarios para implementar la aplicación de lista de tareas.
  3. Finalmente, el agente Revisor revisa el código generado y proporciona sugerencias mediante diffs, guardando sus comentarios en un archivo llamado code_review_diffs.md.

Este flujo de trabajo de desarrollo basado en tripulaciones es potente y modular, pero no está exento de algunas limitaciones:

  • Prompts hardcodeados: Al incrustar prompts directamente en el código, se vuelve más difícil mantenerlos o mejorarlos con el tiempo.
  • Estructura dispersa: Los agentes y las tareas se definen en línea, lo que puede generar un script desordenado y más difícil de navegar a medida que crece el proyecto.

En la siguiente sección, veremos cómo solucionar esto externalizando las definiciones de agentes y los prompts de tareas en archivos YAML.

Construcción de tripulaciones basadas en clases

CrewAI nos permite crear tripulaciones basadas en clases. Vamos a actualizar nuestro código anterior para seguir esta estructura. Primero, crea una carpeta llamada config. Esta carpeta contendrá archivos YAML que definan la configuración de los agentes y las tareas.

Vamos a crear un archivo llamado agents.yaml, que servirá como archivo de configuración de agentes:

architect:
 role: >
   Software Architect for MVP Projects
 goal: >
   Define a basic system structure and simple feature set to guide MVP development
 backstory: >
   You specialize in quickly outlining software architectures and project scopes for minimum viable products.
  Your plans help guide coders with enough structure to get started while staying lean.

coder:
 role: >
   Developer for MVP Projects
 goal: >
   Implement the MVP using simple, clear code, and write all necessary code files using the FileWriter tool
 backstory: >
   You're a practical developer focused on speed. You prioritize working code over polish.
  Simulate any runtime behavior if needed, and make sure to keep code clear and modular.

reviewer:
 role: >
   Code Reviewer for MVP Projects
 goal: >
   Read the code files, provide helpful feedback, and write improvements as diffs into a markdown file
 backstory: >
   You're a fast but thoughtful reviewer. You check code for clarity, obvious bugs, and provide improvements
  as diffs. Instead of modifying code directly, you save your suggestions in a markdown file for easy inspection.

Este archivo define los roles, objetivos e historias de fondo de cada agente.

A continuación, crearemos un archivo tasks.yaml para almacenar las configuraciones de las tareas:

En su lugar, guarda todos los diffs sugeridos en un archivo markdown usando la herramienta FileWriter.

architect_task:
 description: >
   Create a basic plan for building a simple MVP version of a {project}.
  Focus on the core features, basic file structure, and tech stack.
  Make the structure minimal but enough to get a working demo.
 expected_output: >
   A simple architectural overview with:
  - Project goals
  - Key components or files
  - Basic data flow or structure
 agent: architect
 output_file: architecture.md

coder_task:
 description: >
   Based on the architect's plan, implement the core parts of the {project} MVP.
  Keep it lean and functional. Use the FileWriter tool to save all your code files.
  If you need to simulate behavior (e.g. without a real interpreter), do so with clear comments or mocked logic.
 expected_output: >
   Working code files saved using the FileWriter tool that implement the key features defined in the plan.
  Code should be readable and logically structured.
 agent: coder
 context:
   - architect_task

review_task:
 description: >
   Review the code files created for the {project} MVP. Read each file using the FileRead tool.
  Suggest improvements using diffs. Do not modify the original code files directly.
  Instead, save all your suggested diffs into a markdown file using the FileWriter tool.
 expected_output: >
   A markdown file containing diff-style suggestions for each file reviewed.
 agent: reviewer
 context:
   - coder_task
 output_file: code_review_diffs.md

A continuación, vamos a crear nuestra tripulación basada en clases. Para hacer esto, definimos una clase y la decoramos con el decorador @CrewBase proporcionado por CrewAI.

@CrewBase
class CodeCrew():
   """Three-agent code crew: architect, coder, reviewer"""

   @agent
   def architect(self) -> Agent:
       pass

   @agent
   def coder(self) -> Agent:
       pass

   @agent
   def reviewer(self) -> Agent:
       pass

   @task
   def architect_task(self) -> Task:
       pass

   @task
   def code_task(self) -> Task:
       pass

   @task
   def review_task(self) -> Task:
       pass

   @crew
   def crew(self) -> Crew:
       pass

En esta estructura:

  • El decorador @agent se usa para definir métodos que producen agentes.
  • El decorador @task marca los métodos que producen tareas.
  • El decorador @crew especifica el método que arma la tripulación completa.

Ahora, veamos la implementación completa:

import os
from crewai import Agent, Crew, Process, Task, LLM
from crewai.project import CrewBase, agent, crew, task
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List

from crewai_tools import FileWriterTool

# Instantiate tool
file_writer_tool = FileWriterTool()

@CrewBase
class CodeCrew():

   agents: List[BaseAgent]
   tasks: List[Task]

   agents_config = 'config/agents.yaml'
   tasks_config = 'config/tasks.yaml'
  
   @agent
   def architect(self) -> Agent:
       llm = LLM(
           model="novita/moonshotai/kimi-k2-instruct",
           temperature=0.5,
           api_base="https://api.novita.ai/v3/openai",
           api_key=os.environ['NOVITA_API_KEY']
       )
       return Agent(
           config=self.agents_config['architect'],
           llm=llm,
           verbose=True
       )

   @agent
   def coder(self) -> Agent:
       llm = LLM(
           model="novita/qwen/qwen3-coder-480b-a35b-instruct",
           temperature=0.4,
           api_base="https://api.novita.ai/v3/openai",
           api_key=os.environ['NOVITA_API_KEY']
       )
       return Agent(
           config=self.agents_config['coder'],
           llm=llm,
           verbose=True,
           tools=[file_writer_tool]
       )

   @agent
   def reviewer(self) -> Agent:
       llm = LLM(
           model="novita/moonshotai/kimi-k2-instruct",
           temperature=0.5,
           api_base="https://api.novita.ai/v3/openai",
           api_key=os.environ['NOVITA_API_KEY']
       )
       return Agent(
           config=self.agents_config['reviewer'],
           llm=llm,
           verbose=True,
       )

   @task
   def architect_task(self) -> Task:
       return Task(config=self.tasks_config['architect_task'])

   @task
   def coder_task(self) -> Task:
       return Task(config=self.tasks_config['coder_task'])

   @task
   def review_task(self) -> Task:
       return Task(config=self.tasks_config['review_task'])

   @crew
   def crew(self) -> Crew:
       return Crew(
           agents=self.agents,
           tasks=self.tasks,
           process=Process.sequential,
           verbose=True
       )

Este código define todos los métodos necesarios e incluye atributos tanto para agentes como para tareas. También especifica las rutas a los archivos de configuración, que se usan dentro de los métodos de agente y tarea para cargar sus respectivos ajustes.

Ahora puedes ejecutar la tripulación agregando el siguiente código al script:

def main():
   code_crew = CodeCrew()
   code_crew.crew().kickoff(inputs={"project": "Todo App"})
if __name__ == '__main__':
   main()

Construcción de flujos con Novita

Anteriormente construimos una tripulación que seguía un proceso secuencial. Ahora, vamos a explorar cómo construir un flujo que siga un flujo de trabajo diseñado a medida.

Nuestro flujo de trabajo

Queremos diseñar un Sistema Multi-Agente de Atención al Cliente que tenga un agente principal responsable de clasificar los problemas de los clientes. Según la clasificación, el sistema determinará qué agente especializado es el más adecuado para manejar el problema. La configuración consta de cuatro agentes:

  • Agente de Cliente: Este es el agente principal. Recibe el problema del cliente y lo clasifica en una de las siguientes categorías: Facturación, Técnico o General.
  • Agente de Facturación: Maneja todos los problemas relacionados con la facturación.
  • Agente Técnico: Maneja los problemas técnicos.
  • Agente General: Maneja cualquier problema que no entre en las categorías anteriores.

The Customer Support Flow

Definición del estado del flujo

Los flujos en CrewAI tienen un estado compartido. El estado es accesible para todos los agentes y tripulaciones dentro del flujo. Para nuestro flujo de atención al cliente, el estado contiene la etiqueta del problema y la consulta del usuario.

Vamos a importar los módulos necesarios y definir nuestro estado:

import json
import os
from typing import Literal, Dict, Any
from pydantic import BaseModel, ValidationError
from crewai import Agent, LLM
from crewai.flow.flow import Flow, start, router, listen

# Define the flow state
class SupportState(BaseModel):
   issue: Literal["billing", "technical", "general"] = "general"
   message: str = ""

Creación del flujo

Los flujos en CrewAI se basan en grafos y usan un sistema basado en eventos para manejar las transiciones entre nodos. Para crear nuestro flujo, empezaremos por un nodo que clasifique el problema, seguido de un nodo de enrutamiento que determine a qué nodo de gestión enrutar según la clasificación.

class CustomerSupportFlow(Flow[SupportState]):

   @start()
   def classify_issue(self):
       pass

   @router(classify_issue)
   def route_based_on_issue(self) -> str:
       print(f"📨 Routing to agent for issue type: {self.state.issue}")
       if self.state.issue == "billing":
           return "billing"
       if self.state.issue == "technical":
           return "technical"
       else:
           return "general"       

   @listen("billing")
   def handle_billing(self):
       pass

   @listen("technical")
   def handle_technical(self):
       pass

   @listen("general")
   def handle_general(self):
       pass

El método decorado con @start() es el punto de entrada del flujo. En este caso, se encarga de la clasificación de problemas. El método decorado con @router() se ejecuta después de classify_issue y determina a qué agente enrutar. Los demás métodos usan el sistema basado en eventos para responder cuando se enruta a ellos.

Implementación de los métodos del flujo

Ahora vamos a implementar cada método y asignar el agente correspondiente a cada uno:

class CustomerSupportFlow(Flow[SupportState]):

   @start()
   def classify_issue(self) -> Dict[str, Any]:
       intake_agent = Agent(
           role="User Intake Agent",
           goal="Classify user support issue and summarize the message",
           backstory="You classify the user query into billing, technical, or general categories.",
           llm=LLM(
               model="novita/qwen/qwen2.5-vl-72b-instruct",
               temperature=0.3,
               api_base="https://api.novita.ai/v3/openai",
               api_key=os.environ["NOVITA_API_KEY"]
           ),
           verbose=True
       )
      
       prompt = f"""
           A user submitted this message: "{self.state.message}"

           Your task:
           1. Identify whether the issue is "billing", "technical", or "general".
           2. Rephrase or extract the message clearly.

           Respond in valid JSON format like the example below:

           {{
               "issue": "billing",
               "message": "The user has been charged twice for their subscription and is requesting a refund."
           }}
       """

       output = intake_agent.kickoff(prompt, response_format=SupportState)
      
       try:
           # Parse the JSON string
           print(output.raw)
           parsed_json = json.loads(output.raw)

           # Validate with Pydantic
           validated = SupportState(**parsed_json)

           # Save to state
           self.state.issue = validated.issue
           self.state.message = validated.message

       except (json.JSONDecodeError, ValidationError) as e:
           print("❌ Failed to parse or validate response:", e)
           self.state.issue = "general"

   @router(classify_issue)
   def route_based_on_issue(self) -> str:
       print(f"📨 Routing to agent for issue type: {self.state.issue}")
       if self.state.issue == "billing":
           return "billing"
       if self.state.issue == "technical":
           return "technical"
       else:
           return "general"       

   @listen("billing")
   def handle_billing(self):
       agent = Agent(
           role="Billing Agent",
           goal="Handle billing questions, refunds, and invoice issues",
           backstory="You resolve billing-related queries effectively.",
           llm=LLM(
               model="novita/meta-llama/llama-3.3-70b-instruct",
               temperature=0.5,
               api_base="https://api.novita.ai/v3/openai",
               api_key=os.environ["NOVITA_API_KEY"]
           ),
           verbose=True
       )
       result = agent.kickoff(self.state.message)
       print("💰 Billing Agent Response:", result)

   @listen("technical")
   def handle_technical(self):
       agent = Agent(
           role="Technical Support Agent",
           goal="Help users resolve technical issues",
           backstory="You're a technical expert providing troubleshooting support.",
           llm=LLM(
               model="novita/meta-llama/llama-3.1-8b-instruct",
               temperature=0.5,
               api_base="https://api.novita.ai/v3/openai",
               api_key=os.environ["NOVITA_API_KEY"]
           ),
           verbose=True
       )
       result = agent.kickoff(self.state.message)
       print("🛠 Technical Agent Response:", result)

   @listen("general")
   def handle_general(self):
       agent = Agent(
           role="General Support Agent",
           goal="Provide helpful answers to non-technical and non-billing questions",
           backstory="You're friendly and well-informed about our services.",
           llm=LLM(
               model="novita/minimaxai/minimax-m1-80k",
               temperature=0.5,
               api_base="https://api.novita.ai/v3/openai",
               api_key=os.environ["NOVITA_API_KEY"]
           ),
           verbose=True
       )
       result = agent.kickoff(self.state.message)
       print("📋 General Agent Response:", result)

El método classify_issue usa Pydantic para asegurarse de que el modelo devuelva una respuesta válida. Con esta configuración completa, ahora podemos ejecutar el flujo usando el siguiente código:

def run():
   flow = CustomerSupportFlow()
   flow.plot("CustomerSupportFlowPlot")

   # Example input message
   example_input = {
       "message": "Hello, I was charged twice for my subscription and need a refund."
   }

   flow.kickoff(inputs=example_input)

if __name__ == "__main__":
   run()

Conclusión

Para construir un Sistema Multi-Agente, necesitas dos componentes clave: un proveedor de modelos que ofrezca una amplia variedad de modelos, y un Framework Multi-Agente para orquestar sus interacciones. En este artículo, hemos mostrado cómo Novita y CrewAI pueden cumplir estos roles de forma efectiva. Para obtener más información sobre CrewAI, asegúrate de explorar su documentación. Y si buscas experimentar con más modelos para tus tripulaciones, consulta el patio de juegos de LLM de Novita.

Repositorio con archivos: https://github.com/novitalabs/Novita-CollabHub/tree/main/examples/novita-crewai

Sobre Novita AI

NovitaAI es una plataforma de IA en la nube que ofrece a los desarrolladores una forma sencilla de desplegar modelos de IA mediante nuestra API simple, además de proporcionar una nube de GPU asequible y fiable para construir y escalar.