Erstelle ein Multi-Agenten-System mit Novita und CrewAI

Erstelle ein Multi-Agenten-System mit Novita und CrewAI

Als die KI-Branche begann, die Schlussfolgerungsfähigkeiten von Large Language Models (LLMs) zu erkennen, haben wir beschlossen, sie agentischer zu machen, indem wir ihnen ermöglichen, komplexere Aktionen auszuführen. Diese Kategorie von Large Language Models wurde als Agenten bekannt.

So entstanden Single-Agenten-Systeme. Aber es wurde schnell deutlich, dass die Zuweisung mehrerer Aktionen an einen einzelnen Agenten nicht die klügste Methode war, um agentische Systeme zu bauen. Ein besserer Ansatz war die Verwendung mehrerer Agenten, von denen jeder seine eigene Aufgabe hat und zusammenarbeitet, um ein vom Benutzer definiertes Ziel zu erreichen.

Allerdings trat ein weiteres Problem auf. Die Verwaltung mehrerer Agenten ist nicht so einfach wie die eines einzelnen. Um dies zu lösen, wurden mehrere Frameworks für Multi-Agenten-Systeme eingeführt, und eines der vielversprechendsten ist CrewAI.

CrewAI hilft Entwicklern beim Erstellen von Multi-Agenten-Systemen. Mit CrewAI kannst du deine Agenten, deren Aufgaben und deren Workflow verwalten. In diesem Artikel verwenden wir CrewAI zusammen mit der Vielzahl an LLMs, die auf Novita verfügbar sind, um praktische und intelligente Multi-Agenten-Systeme zu bauen.

Was ist ein Multi-Agenten-System und warum brauchen wir es?

Seit der Einführung von Large Language Models (LLMs) wurden mehrere Ansätze erforscht, um sie autonomer zu machen. Einer der beliebtesten war das ReAct-Muster, das in dem Paper Synergizing Reasoning and Acting in Language Models vorgestellt wurde. Frameworks wie LangChain haben ReAct schnell implementiert, um LLMs agentische Fähigkeiten zu verleihen.

Tools wie AutoGPT gingen noch weiter, indem sie robustere KI-Agenten bauten, die fast jede Aufgabe, die der Benutzer wünschte, nur mit einem Text-Prompt ausführen konnten. Während diese Single-Agenten-Systeme sehr vielversprechend waren, hatten sie auch Einschränkungen. Ein einzelner Agent verlässt sich typischerweise auf ein LLM, um jede Aufgabe zu bearbeiten, was zu einigen kritischen Nachteilen führt:

  • Begrenzter Kontext: Ein einzelner Agent muss alle Aufgaben mit einem einzigen Kontextfenster verwalten. Je weiter der Workflow fortschreitet, desto mehr läuft der Agent irgendwann aus dem Kontextspeicher.
  • Erhöhte Komplexität: Je mehr Aufgaben an einen einzelnen Agenten übergeben werden, desto schwieriger wird die Schlussfolgerung zu verwalten, was den Agenten anfälliger für Halluzinationen oder Fehler macht.
  • Single Point of Failure: Wenn ein Agent für alles verantwortlich ist, gibt es kein Backup. Wenn er eine Aufgabe nicht bewältigen kann, scheitert das gesamte System.
  • Fehlende Parallelisierung: Aufgaben werden nacheinander verarbeitet. Es gibt keine integrierte Unterstützung für Agenten, die parallel arbeiten, um Dinge zu beschleunigen.

Multi-Agenten-Systeme wurden eingeführt, um diese Herausforderungen zu bewältigen. Anstatt sich auf einen einzelnen Agenten zu verlassen, der mehrere Aufgaben bearbeitet, verteilt ein Multi-Agenten-System die Verantwortlichkeiten auf mehrere zusammenarbeitende Agenten. Dies hilft, Probleme wie das Überschreiten der Kontextlänge zu vermeiden, da jeder Agent nur seinen eigenen Teil des Workflows verwalten muss und die gesamte Kontextlast reduziert wird.

Multi-Agenten-Setups ermöglichen es dir auch, verschiedene Modelle für unterschiedliche Zwecke zu verwenden: Einige können für spezifische Aufgaben feinabgestimmt werden, einige können mit vordefinierten Prompts ausgestattet sein, einige können mit spezialisierten Tools ausgestattet sein und andere können für Schlussfolgerungen optimiert werden. Diese Vielfalt verbessert nicht nur die Leistung, sondern ermöglicht auch Parallelisierung, wodurch Multi-Agenten-Systeme schneller und effizienter sind als ihre Single-Agenten-Pendants.

Um ein Multi-Agenten-System zu bauen, brauchst du zwei Kernkomponenten: einen Modellanbieter, der die LLMs bereitstellt, und ein Multi-Agenten-Framework, um den gesamten agentischen Workflow zu verwalten. In diesem Artikel verwenden wir Novita als unseren Modellanbieter und CrewAI als unser Framework der Wahl.

Warum du ein Framework wie CrewAI brauchst

Das Erstellen eines Multi-Agenten-Systems ist weitaus komplexer als das Erstellen eines Single-Agenten-Systems. Beispielsweise kannst du jedes Modell auf Novita leicht zu einem Agenten machen, indem du ihm mit Funktionsaufrufen Tools zur Verfügung stellst. Aber sobald du mehrere Agenten verbindest, steigt die Komplexität deutlich an. Du musst nun mehrere bewegliche Teile verwalten, darunter:

  • Kommunikation und Aufgaben Delegation: Du musst koordinieren, wie Agenten miteinander kommunizieren und wie Aufgaben zwischen ihnen übergeben werden.
  • Tool-Verwaltung: Ohne Framework bist du dafür verantwortlich, Tool-Definitionen und JSON-Schemas manuell zu verwalten – etwas, das schnell unübersichtlich werden kann.
  • Agentische Architekturen und Muster: Multi-Agenten-Frameworks verfügen oft über integrierte Unterstützung für bewährte agentische Muster. Wenn du von Grund auf baust, musst du über diese sich entwickelnden Muster auf dem Laufenden bleiben und sie selbst implementieren.
  • Beobachtbarkeit: Ohne Framework bist du auch dafür verantwortlich, deine eigenen Tools zum Überwachen, Debuggen und Visualisieren der Interaktion und Leistung deiner Agenten einzurichten.

Ein Multi-Agenten-Framework kann helfen, all diese Komplexitäten zu verwalten. In diesem Artikel verwenden wir CrewAI als unser Framework der Wahl. CrewAI bietet mehrere Vorteile gegenüber anderen Frameworks: Es zeichnet sich durch seine Einfachheit aus, verwendet intuitive Konzepte zur Modellierung von Multi-Agenten-Systemen, ist ausgereift und gut etabliert und bietet eine hervorragende Entwicklererfahrung.

Verstehen der Kernkonzepte von CrewAI

Um Multi-Agenten-Systeme mit CrewAI zu bauen, musst du mit einigen Kernkonzepten vertraut sein, die das Framework einführt. Hier ist eine kurze Übersicht der wichtigsten, mit denen wir in diesem Artikel arbeiten werden:

  • Agenten
  • Aufgaben
  • Crews
  • Prozesse
  • Abläufe

Agenten

Agenten sind der Kern von CrewAI. Sie repräsentieren die eigentlichen KI-Arbeiter, die von einem LLM angetrieben werden. Jeder Agent in CrewAI wird durch eine Rolle, ein Ziel, das seine Entscheidungsfindung leitet, und eine Hintergrundgeschichte, die seine Persönlichkeit prägt, definiert. Diese werden alle als Text-Prompts dargestellt, die an das LLM gesendet werden. Agenten können auch mit Tools ausgestattet werden, aus denen sie wählen können, um spezifische Aktionen auszuführen. Das Hauptziel eines Agenten in CrewAI ist es, die ihm zugewiesene(n) Aufgabe(n) zu erledigen.

Aufgaben

Aufgaben definieren die Arbeit, die erledigt werden muss. In CrewAI kannst du Aufgaben erstellen und sie entweder direkt bestimmten Agenten zuweisen oder Agenten sie auswählen lassen, wenn sie den Anforderungen der Aufgabe entsprechen. Jede Aufgabe enthält typischerweise eine klare Beschreibung, eine erwartete Ausgabe und andere relevante Parameter wie einen optionalen Agenten, der die Aufgabe bearbeiten kann.

Crew

Dies ist das Kernkonzept von CrewAI. In CrewAI wird eine Gruppe von Agenten, die zusammenarbeiten, um Aufgaben zu erledigen, als Crew bezeichnet. Eine Crew definiert auch den Workflow der Agenten innerhalb von ihr.

Prozesse

Ein Prozess ist ein vordefinierter Workflow, der einer Crew sagt, wie sie ihre Aufgaben ausführen soll. CrewAI unterstützt zwei Haupttypen von Prozessen:

  • Sequenziell: Aufgaben werden nacheinander von den Agenten in der Crew bearbeitet.
  • Hierarchisch: In diesem Prozess agiert ein Agent als Manager, koordiniert andere Agenten und delegiert Aufgaben nach Bedarf.

Abläufe

Während Prozesse vordefinierte Workflows sind, geben CrewAI-Abläufe Entwicklern die Flexibilität, benutzerdefinierte Agenten-Workflows zu entwerfen. Ein Ablauf kann Agenten, Aufgaben und sogar ganze Crews umfassen, was komplexere Interaktionen ermöglicht.

Crews mit Novita erstellen

Nachdem wir nun die Kernkonzepte von CrewAI behandelt haben, gehen wir das Erstellen einer Crew durch. In diesem Beispiel erstellen wir eine Crew, die Benutzern hilft, schnell ein Minimum Viable Product (MVP) zu bauen. Diese Crew besteht aus drei spezialisierten Agenten:

  • Architekt: Definiert die Softwarestruktur und umreißt den MVP-Umfang. Dieser Agent wird von dem auf Novita gehosteten Modell moonshotai/kimi-k2-instruct angetrieben.
  • Coder: Implementiert den Plan des Architekten und schreibt den eigentlichen Code. Er verwendet ein FileWriter-Tool, um alle Projektdateien zu erstellen, und läuft auf dem Modell qwen/qwen3-coder-480b-a35b-instruct von Novita.
  • Reviewer: Analysiert den vom Coder generierten Code und schlägt Verbesserungen mit Diff-Feedback vor. Wie der Architekt verwendet auch er das Modell moonshotai/kimi-k2-instruct.

Unsere Crew folgt einem sequenziellen Prozess.

Installation und Einrichtung

Nachdem wir nun einen Plan haben, beginnen wir mit der Einrichtung unserer Umgebung. Wir starten mit der Installation von CrewAI zusammen mit seiner integrierten Tool-Unterstützung:

pip install 'crewai[tools]'

Als Nächstes brauchst du deinen Novita-API-Schlüssel. Sobald du ihn hast, füge ihn zu deinen Umgebungsvariablen als NOVITA_API_KEY hinzu:

export NOVITA_API_KEY=your_api_key_here

Damit sind wir bereit, mit dem Erstellen der Crew zu beginnen.

Erstellen der LLMs

Beginnen wir mit dem Importieren der notwendigen Abhängigkeiten:

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

Als Nächstes erstellen wir eine Instanz des FileWriterTool, die vom Coder-Agenten verwendet wird, um Dateien auf die Festplatte zu schreiben:

file_writer_tool = FileWriterTool()

Mit den eingerichteten Tools können wir nun die LLMs für jeden unserer Agenten initialisieren:

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']
)

Diese drei LLM-Instanzen entsprechen den in unserem Plan outlined Modellen. Sie werden auf Novita gehostet und über CrewAI aufgerufen. Hinter den Kulissen verwendet CrewAI LiteLLM, um eine Verbindung zu diesen Modellen herzustellen und sie zu verwalten.

Definieren der Agenten

Nun erstellen wir die drei Agenten:

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]
)

Jedem Agenten wird eine spezifische Rolle, ein Ziel und eine Hintergrundgeschichte zugewiesen.

Definieren der Aufgaben

Lass uns die Aufgaben der Agenten erstellen:

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"
)

Erstellen der Crew

Nachdem wir nun unsere Agenten, deren jeweilige LLMs und Aufgaben definiert haben, bringen wir alles zusammen, indem wir die Crew zusammenstellen:

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

Mit allem eingerichtet können wir die Crew nun ausführen:

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

Wir haben nun eine voll funktionsfähige Crew, die in der Lage ist, ein MVP-Projekt basierend auf einer benutzerdefinierten Spezifikation zu generieren. In dem obigen Beispiel haben wir die Crew gebeten, eine einfache To-Do-App zu bauen.

So funktioniert es:

  1. Der Architekt-Agent startet, indem er die Softwarearchitektur entwirft und den Plan in einer Datei namens architecture.md speichert.
  2. Der Coder-Agent nimmt dann die Ausgabe des Architekten auf und erstellt die notwendigen Verzeichnisse und Codedateien, um die To-Do-App zu implementieren.
  3. Schließlich überprüft der Reviewer-Agent den generierten Code, gibt Vorschläge mit Diffs und speichert sein Feedback in einer Datei namens code_review_diffs.md.

Dieser crew-basierte Entwicklungs-Workflow ist leistungsstark und modular, hat aber auch einige Einschränkungen:

  • Hartcodierte Prompts: Durch das Einbetten von Prompts direkt in den Code wird es schwieriger, sie im Laufe der Zeit zu warten oder zu verbessern.
  • Verstreute Struktur: Agenten und Aufgaben werden inline definiert, was zu einem unübersichtlichen und schwerer zu navigierenden Skript führen kann, wenn das Projekt wächst.

Im nächsten Abschnitt sehen wir uns an, wie wir dies aufräumen können, indem wir Agentendefinitionen und Aufgaben-Prompts in YAML-Dateien auslagern.

Erstellen von klassenbasierten Crews

CrewAI ermöglicht es uns, klassenbasierte Crews zu erstellen. Aktualisieren wir unseren vorherigen Code, um dieser Struktur zu folgen. Erstelle zuerst einen Ordner namens config. Dieser Ordner enthält YAML-Dateien, die die Konfiguration für die Agenten und Aufgaben definieren.

Lass uns eine Datei namens agents.yaml erstellen, die als Agenten-Konfigurationsdatei dient:

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.

Diese Datei definiert die Rollen, Ziele und Hintergrundgeschichten für jeden Agenten.

Als Nächstes erstellen wir eine Datei tasks.yaml, die die Aufgabenkonfigurationen enthält:

Stattdessen speichere alle deine vorgeschlagenen Diffs in einer Markdown-Datei mit dem FileWriter-Tool.

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

Als Nächstes erstellen wir unsere klassenbasierte Crew. Dazu definieren wir eine Klasse und dekorieren sie mit dem von CrewAI bereitgestellten @CrewBase-Dekorator.

@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

In dieser Struktur:

  • Der @agent-Dekorator wird verwendet, um Methoden zu definieren, die Agenten erzeugen.
  • Der @task-Dekorator markiert Methoden, die Aufgaben erzeugen.
  • Der @crew-Dekorator gibt die Methode an, die die gesamte Crew zusammenstellt.

Nun werfen wir einen Blick auf die vollständige Implementierung:

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
       )

Dieser Code definiert alle notwendigen Methoden und enthält Attribute sowohl für Agenten als auch für Aufgaben. Er gibt auch die Pfade zu den Konfigurationsdateien an, die innerhalb der Agenten- und Aufgabenmethoden verwendet werden, um ihre jeweiligen Einstellungen zu laden.

Du kannst die Crew nun ausführen, indem du den folgenden Code zum Skript hinzufügst:

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

Abläufe mit Novita erstellen

Wir haben zuvor eine Crew erstellt, die einem sequenziellen Prozess folgte. Nun erkunden wir, wie man einen Ablauf erstellt, der einem benutzerdefinierten Workflow folgt.

Unser Workflow

Wir möchten ein Multi-Agenten-System für Kundensupport entwerfen, das einen Leit-Agenten hat, der für die Klassifizierung von Kundenanfragen verantwortlich ist. Basierend auf der Klassifizierung bestimmt das System, welcher spezialisierte Agent am besten geeignet ist, die Anfrage zu bearbeiten. Das Setup besteht aus vier Agenten:

  • Kunden-Agent: Dies ist der Leit-Agent. Er erhält die Anfrage des Kunden und klassifiziert sie in eine der folgenden Kategorien: Abrechnung, Technik oder Allgemein.
  • Abrechnungs-Agent: Bearbeitet alle abrechnungsbezogenen Anfragen.
  • Technik-Agent: Bearbeitet technische Anfragen.
  • Allgemeiner Agent: Bearbeitet alle Anfragen, die nicht in die oben genannten Kategorien fallen.

The Customer Support Flow

Definieren des Zustands des Ablaufs

Abläufe in CrewAI haben einen gemeinsamen Zustand. Der Zustand ist für alle Agenten und Crews innerhalb des Ablaufs zugänglich. Für unseren Kundensupport-Ablauf enthält der Zustand das Issue-Label und die Anfrage des Benutzers.

Lass uns die notwendigen Module importieren und unseren Zustand definieren:

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 = ""

Erstellen des Ablaufs

Abläufe in CrewAI sind graphbasiert und verwenden ein ereignisgesteuertes System, um Übergänge zwischen Knoten zu verarbeiten. Um unseren Ablauf zu erstellen, beginnen wir mit einem Knoten, der die Anfrage klassifiziert, gefolgt von einem Router-Knoten, der basierend auf der Klassifizierung bestimmt, zu welchem Handler-Knoten weitergeleitet wird.

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

Die mit @start() dekorierte Methode ist der Einstiegspunkt des Ablaufs. In diesem Fall handelt es sich um die Issue-Klassifizierung. Die mit @router() dekorierte Methode wird nach classify_issue ausgeführt und bestimmt, zu welchem Agenten weitergeleitet wird. Die anderen Methoden verwenden das ereignisgesteuerte System, um zu reagieren, wenn sie weitergeleitet werden.

Implementieren der Ablaufmethoden

Nun implementieren wir jede Methode und weisen jedem den passenden Agenten zu:

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)

Die Methode classify_issue verwendet Pydantic, um sicherzustellen, dass das Modell eine gültige Antwort zurückgibt. Mit diesem abgeschlossenen Setup können wir den Ablauf nun mit dem folgenden Code ausführen:

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()

Fazit

Um ein Multi-Agenten-System zu bauen, brauchst du zwei Schlüsselkomponenten: einen Modellanbieter, der eine große Auswahl an Modellen anbietet, und ein Multi-Agenten-Framework, um deren Interaktionen zu orchestrieren. In diesem Artikel haben wir gezeigt, wie Novita und CrewAI diese Rollen effektiv erfüllen können. Um mehr über CrewAI zu erfahren, solltest du unbedingt deren Dokumentation erkunden. Und wenn du mit weiteren Modellen für deine Crews experimentieren möchtest, schau dir den Novita LLM-Spielplatz an.

Repo mit Dateien - https://github.com/novitalabs/Novita-CollabHub/tree/main/examples/novita-crewai

Über Novita AI

NovitaAI ist eine KI-Cloud-Plattform, die Entwicklern eine einfache Möglichkeit bietet, KI-Modelle über unsere einfache API bereitzustellen, und gleichzeitig eine erschwingliche und zuverlässige GPU-Cloud zum Erstellen und Skalieren bereitstellt.