Zunächst haben wir bei der Einführung von ChatGPT einfache Prompts verwendet, um Antworten auf unsere Fragen zu erhalten. Dann sind wir auf Probleme mit Halluzinationen gestoßen und haben RAG (Retrieval Augmented Generation) eingesetzt, um LLMs mehr Kontext zu geben. Anschließend haben wir mit AI-Agenten experimentiert, bei denen LLMs als Reasoning-Engine fungieren und entscheiden können, was als Nächstes zu tun ist, welche Tools zu verwenden sind und wann die endgültige Antwort zurückgegeben werden soll.
Der nächste evolutionäre Schritt besteht darin, Teams solcher Agenten zu schaffen, die zusammenarbeiten können. Dieser Ansatz ist logisch, da er menschliche Interaktionen widerspiegelt. Wir arbeiten in Teams, in denen jedes Mitglied eine bestimmte Rolle hat:
- Der Produktmanager schlägt das nächste Projekt vor, an dem gearbeitet werden soll.
- Der Designer gestaltet das Aussehen und die Haptik.
- Der Softwareentwickler entwickelt die Lösung.
- Der Analyst untersucht die Daten, um sicherzustellen, dass sie wie erwartet funktionieren, und um Möglichkeiten zur Verbesserung des Produkts für die Kunden zu identifizieren.
Ähnlich können wir ein Team von AI-Agenten schaffen, von denen sich jeder auf eine Domäne konzentriert. Sie können zusammenarbeiten und gemeinsam zu einer endgültigen Schlussfolgerung gelangen. Wie die Spezialisierung die Leistung im wirklichen Leben verbessert, könnte sie auch die Leistung von AI-Agenten verbessern.
Ein weiterer Vorteil dieses Ansatzes ist die erhöhte Flexibilität. Jeder Agent kann mit seinem eigenen Prompt, seinem eigenen Satz von Tools und sogar seinem eigenen LLM arbeiten. Beispielsweise können wir für verschiedene Teile unseres Systems unterschiedliche Modelle verwenden. Sie können GPT-4 für den Agenten verwenden, der mehr Reasoning benötigt, und GPT-3.5 für den, der nur einfache Extraktionen durchführt. Wir können das Modell sogar für kleine spezifische Aufgaben feinabstimmen und es in unserer Crew von Agenten verwenden.
Die potenziellen Nachteile dieses Ansatzes sind Zeit und Kosten. Mehrere Interaktionen und Wissensaustausch zwischen Agenten erfordern mehr Aufrufe von LLM und verbrauchen zusätzliche Tokens. Dies könnte zu längeren Wartezeiten und erhöhten Kosten führen.
Es gibt heute mehrere Frameworks für Multi-Agenten-Systeme. Hier sind einige der beliebtesten:
- AutoGen: Entwickelt von Microsoft, verwendet AutoGen einen konversationsbasierten Ansatz und war eines der ersten Frameworks für Multi-Agenten-Systeme.
- LangGraph: Obwohl es sich nicht streng genommen um ein Multi-Agenten-Framework handelt, ermöglicht LangGraph die Definition komplexer Interaktionen zwischen Akteuren mithilfe einer Graphenstruktur. Daher kann es auch zur Erstellung von Multi-Agenten-Systemen angepasst werden.
- CrewAI: CrewAI positioniert sich als High-Level-Framework und erleichtert die Erstellung von „Crews“, die aus rollenspielenden Agenten bestehen, die auf verschiedene Weise zusammenarbeiten können.
Ich habe beschlossen, mit den Multi-Agenten-Frameworks von CrewAI zu beginnen, da es ziemlich weit verbreitet und benutzerfreundlich ist. Daher scheint es eine gute Option zu sein, um damit zu beginnen.
In diesem Artikel werde ich Ihnen zeigen, wie Sie CrewAI verwenden. Als Analysten sind wir die Domänenexperten, die dafür verantwortlich sind, verschiedene Datenquellen zu dokumentieren und damit zusammenhängende Fragen zu beantworten. Wir werden untersuchen, wie wir diese Aufgaben mithilfe von Multi-Agenten-Frameworks automatisieren können.
Einrichtung der Umgebung
Lassen Sie uns mit der Einrichtung der Umgebung beginnen. Zunächst müssen wir das Hauptpaket von CrewAI und eine Erweiterung installieren, um mit Tools arbeiten zu können.
pip install crewai
pip install 'crewai[tools]'
CrewAI wurde hauptsächlich für die Arbeit mit der OpenAI-API entwickelt, aber ich möchte es auch mit einem lokalen Modell ausprobieren. Laut ChatBot Arena Leaderboard ist das beste Modell, das Sie auf Ihrem Laptop ausführen können, Llama 3 (8 Milliarden Parameter). Es wird die praktikabelste Option für unseren Anwendungsfall sein.
Wir können auf Llama-Modelle mit Ollama zugreifen. Die Installation ist ziemlich einfach. Sie müssen Ollama von der Website herunterladen und dann den Installationsprozess durchlaufen. Das ist alles.
Jetzt können Sie das Modell in der CLI testen, indem Sie den folgenden Befehl ausführen.
ollama run llama3
Zum Beispiel können Sie etwas wie folgt fragen.
Lassen Sie uns eine benutzerdefinierte Ollama-Modell erstellen, das wir später in CrewAI verwenden können.
Wir werden mit einer ModelFile (Dokumentation) beginnen. Ich habe nur das Basismodell (llama3
), die Temperatur und die Stoppsequenz angegeben. Sie können jedoch weitere Funktionen hinzufügen. Beispielsweise können Sie die Systemnachricht mithilfe des SYSTEM
-Schlüsselworts bestimmen.
FROM llama3
# set parameters
PARAMETER temperature 0.5
PARAMETER stop Result
Ich habe es in eine Llama3ModelFile
-Datei gespeichert.
Lassen Sie uns ein Bash-Skript erstellen, um das Basismodell für Ollama zu laden und das benutzerdefinierte Modell zu erstellen, das wir in ModelFile definiert haben.
#!/bin/zsh
# define variables
model_name="llama3"
custom_model_name="crewai-llama3"
# load the base model
ollama pull $model_name
# create the model file
ollama create $custom_model_name -f ./Llama3ModelFile
Lassen Sie uns diese Datei ausführen.
chmod +x ./llama3_setup.sh
./llama3_setup.sh
Sie können beide Dateien auf GitHub finden: Llama3ModelFile und llama3_setup.sh
Wir müssen die folgenden Umgebungsvariablen initialisieren, um das lokale Llama-Modell mit CrewAI zu verwenden.
os.environ["OPENAI_API_BASE"]='http://localhost:11434/v1'
os.environ["OPENAI_MODEL_NAME"]='crewai-llama3'
# custom_model_name from the bash script
os.environ["OPENAI_API_KEY"] = "NA"
Wir haben die Einrichtung abgeschlossen und sind bereit, unsere Reise fortzusetzen.
Anwendungsfälle: Arbeit mit Dokumentation
Als Analysten übernehmen wir häufig die Rolle von Fachexperten für Daten und einige datenbezogene Tools. In meinem vorherigen Team hatten wir einen Kanal mit fast 1.000 Teilnehmern, in dem wir viele Fragen zu unseren Daten und der ClickHouse-Datenbank, die wir als Speicher verwendeten, beantwortet haben. Es hat uns ziemlich viel Zeit gekostet, diesen Kanal zu verwalten. Es wäre interessant zu sehen, ob solche Aufgaben mit LLMs automatisiert werden können.
Für dieses Beispiel werde ich die ClickHouse-Datenbank verwenden. Wenn Sie interessiert sind, können Sie mehr über ClickHouse und wie Sie es lokal einrichten, in meinem vorherigen Artikel erfahren. Wir werden jedoch keine ClickHouse-spezifischen Funktionen verwenden, daher können Sie sich an die Datenbank halten, die Sie kennen.
Ich habe ein ziemlich einfaches Datenmodell erstellt, mit dem wir arbeiten können. Es gibt nur zwei Tabellen in unserem DWH (Data Warehouse): ecommerce_db.users
und ecommerce_db.sessions
. Wie Sie vielleicht vermuten, enthält die erste Tabelle Informationen über die Benutzer unseres Dienstes.
Die Tabelle ecommerce_db.sessions
speichert Informationen über Benutzersitzungen.
Bei der Verwaltung von Datenquellen übernehmen Analysten in der Regel Aufgaben wie das Schreiben und Aktualisieren von Dokumentationen und das Beantworten von Fragen zu diesen Daten. Daher werden wir LLM verwenden, um die Dokumentation für die Tabelle in der Datenbank zu schreiben und es zu lehren, Fragen zu Daten oder ClickHouse zu beantworten.
Bevor wir jedoch zur Umsetzung übergehen, lassen Sie uns mehr über das CrewAI-Framework und seine Kernkonzepte erfahren.
CrewAI-Grundlagen
Der Eckpfeiler eines Multi-Agenten-Frameworks ist das Agentenkonzept. In CrewAI werden Agenten durch Rollenspiel gesteuert. Rollenspiel ist eine Taktik, bei der Sie einen Agenten bitten, eine Persönlichkeit anzunehmen und sich wie ein Top-Backend-Entwickler oder hilfreicher Kundensupport-Mitarbeiter zu verhalten. Daher müssen Sie beim Erstellen eines CrewAI-Agenten die Rolle, das Ziel und die Hintergrundgeschichte jedes Agenten angeben, damit LLM genügend Informationen hat, um diese Rolle zu spielen.
Die Fähigkeiten der Agenten sind ohne Tools (Funktionen, die Agenten ausführen und Ergebnisse erhalten können) begrenzt. Mit CrewAI können Sie eines der vordefinierten Tools verwenden (z. B. um das Internet zu durchsuchen, eine Website zu parsen oder RAG auf einem Dokument durchzuführen), ein benutzerdefiniertes Tool erstellen oder LangChain-Tools verwenden. So können Sie einen leistungsstarken Agenten erstellen.
Lassen Sie uns von den Agenten zu der Arbeit übergehen, die sie erledigen. Agenten arbeiten an Aufgaben (spezifischen Aufträgen). Für jede Aufgabe müssen wir eine Beschreibung, die erwartete Ausgabe (Definition of Done), einen Satz verfügbarer Tools und einen zugewiesenen Agenten definieren. Mir gefällt, dass diese Frameworks den managerialen Best Practices folgen, wie z. B. einer klaren Definition of Done für Aufgaben.
Die nächste Frage ist, wie die Ausführungsreihenfolge für Aufgaben definiert wird: welche zuerst, welche parallel ausgeführt werden können usw. CrewAI hat Prozesse implementiert, um die Aufgaben zu orchestrieren. Es bietet ein paar Optionen:
- Sequenziell – der einfachste Ansatz, bei dem die Aufgaben nacheinander aufgerufen werden.
- Hierarchisch – wenn es einen Manager (als LLM-Modell spezifiziert) gibt, der Aufgaben erstellt und an die Agenten delegiert.
CrewAI arbeitet auch an einem Konsensprozess. In einem solchen Prozess können Agenten gemeinsam Entscheidungen treffen, wobei ein demokratischer Ansatz verfolgt wird.
Es gibt andere Hebel, mit denen Sie den Prozess der Aufgabenausführung beeinflussen können:
- Sie können Aufgaben als „asynchron“ markieren, dann werden sie parallel ausgeführt, so dass Sie schneller eine Antwort erhalten.
- Sie können die Eingabe „menschliche Eingabe“ für eine Aufgabe verwenden, und dann fragt der Agent nach menschlicher Bestätigung, bevor er die Ausgabe dieser Aufgabe finalisiert. Dies kann eine Überwachung des Prozesses ermöglichen.
Wir haben alle primären Bausteine definiert und können über das Heilige Gral von CrewAI sprechen – das Crew-Konzept. Die Crew repräsentiert das Team von Agenten und den Satz von Aufgaben, an denen sie arbeiten werden. Der Ansatz für die Zusammenarbeit (Prozesse, die wir oben diskutiert haben) kann auch auf Crew-Ebene definiert werden.
Wir können auch den Speicher für eine Crew einrichten. Der Speicher ist entscheidend für eine effiziente Zusammenarbeit zwischen den Agenten. CrewAI unterstützt drei Speicherebenen:
- Kurzzeitgedächtnis speichert Informationen im Zusammenhang mit der aktuellen Ausführung. Es hilft Agenten, bei der aktuellen Aufgabe zusammenzuarbeiten.
- Langzeitgedächtnis sind Daten über frühere Ausführungen, die in der lokalen Datenbank gespeichert sind. Diese Art von Speicher ermöglicht es Agenten, aus früheren Iterationen zu lernen und sich im Laufe der Zeit zu verbessern.
- Entitätenspeicher erfasst und strukturiert Informationen über Entitäten (wie Personas, Städte usw.).
Derzeit können Sie für eine Crew nur den gesamten Speicher aktivieren, ohne weitere Anpassungen vorzunehmen. Es funktioniert jedoch nicht mit den Llama-Modellen.
Wir haben genug über das CrewAI-Framework gelernt, daher ist es an der Zeit, dieses Wissen in der Praxis anzuwenden.
Anwendungsfall: Dokumentation schreiben
Lassen Sie uns mit einer einfachen Aufgabe beginnen: die Dokumentation für unser DWH erstellen. Wie wir oben diskutiert haben, gibt es zwei Tabellen in unserem DWH, und ich möchte detaillierte Beschreibungen für sie mithilfe von LLMs erstellen.
Erster Ansatz
Zunächst müssen wir über die Teamstruktur nachdenken. Denken Sie daran als eine typische manageriale Aufgabe. Wen würden Sie für eine solche Aufgabe einstellen?
Ich würde die Aufgabe in zwei Teile aufteilen: das Abrufen von Daten aus einer Datenbank und das Schreiben von Dokumentation. Daher benötigen wir einen Datenbankspezialisten und einen technischen Redakteur. Der Datenbankspezialist benötigt Zugriff auf eine Datenbank, während der Schriftsteller keine speziellen Tools benötigt.
Jetzt haben wir einen groben Plan. Lassen Sie uns die Agenten erstellen.
Für jeden Agenten habe ich die Rolle, das Ziel und die Hintergrundgeschichte angegeben. Ich habe versucht, den Agenten so viel Kontext wie möglich zur Verfügung zu stellen.
database_specialist_agent = Agent(
role = "Datenbankspezialist",
goal = "Bereitstellung von Daten zur Beantwortung von Geschäftsfragen mithilfe von SQL",
backstory = '''Sie sind ein Experte für SQL und können dem Team dabei helfen, benötigte Daten zu sammeln, um ihre Entscheidungen zu untermauern. Sie sind sehr genau und berücksichtigen alle Nuancen in den Daten.''',
allow_delegation = False,
verbose = True
)
tech_writer_agent = Agent(
role = "Technischer Redakteur",
goal = '''Erstellen von ansprechenden und fachlich korrekten technischen Dokumentationen für Datenquellen oder Tools''',
backstory = '''Sie sind ein Experte sowohl für Technologie als auch für Kommunikation und können daher auch komplexe Konzepte leicht erklären. Ihre Texte sind prägnant und können von einem breiten Publikum leicht verstanden werden. Sie verwenden einen professionellen, aber eher informellen Stil in Ihrer Kommunikation.''',
allow_delegation = False,
verbose = True
)
Wir werden einen einfachen sequenziellen Prozess verwenden, daher müssen die Agenten keine Aufgaben aneinander delegieren. Daher habe ich allow_delegation = False
angegeben.
Der nächste Schritt besteht darin, den Agenten Aufgaben zuzuweisen. Bevor wir jedoch zu ihnen übergehen, müssen wir ein benutzerdefiniertes Tool erstellen, um eine Verbindung zur Datenbank herzustellen.
Zunächst habe ich eine Funktion erstellt, um ClickHouse-Abfragen mithilfe der HTTP-API auszuführen.
CH_HOST = 'http://localhost:8123' # Standardadresse
def get_clickhouse_data(query, host = CH_HOST, connection_timeout = 1500):
r = requests.post(host, params = {'query': query},
timeout = connection_timeout)
if r.status_code == 200:
return r.text
else:
return 'Datenbank gab folgenden Fehler zurück:\n' + r.text
Bei der Arbeit mit LLM-Agenten ist es wichtig, dass Tools fehlertolerant sind. Beispielsweise gibt mein Code bei einem Datenbankfehler (status_code != 200
) keine Ausnahme zurück, sondern gibt stattdessen die Fehlerbeschreibung an LLM zurück, damit es versuchen kann, das Problem zu beheben.
Um ein benutzerdefiniertes CrewAI-Tool zu erstellen, müssen wir unsere Klasse von crewai_tools.BaseTool
ableiten, die Methode _run
implementieren und dann eine Instanz dieser Klasse erstellen.
from crewai_tools import BaseTool
class DatabaseQuery(BaseTool):
name: str = "Datenbankabfrage"
description: str = "Gibt das Ergebnis der SQL-Abfrageausführung zurück"
def _run(self, sql_query: str) -> str:
# Implementierung geht hier hin
return get_clickhouse_data(sql_query)
database_query_tool = DatabaseQuery()
Jetzt können wir den Agenten Aufgaben zuweisen. Auch hier ist es wichtig, LLM klare Anweisungen und so viel Kontext wie möglich zur Verfügung zu stellen.
table_description_task = Task(
description = '''Beschreibung der Tabelle {table} mit allen Spalten und Datentypen sowie Beispieldaten.''',
expected_output = '''Umfassende Übersicht über {table} im md-Format. Enthält zwei Abschnitte: Spalten (Liste der Spalten mit ihren Typen) und Beispiele (die ersten 30 Zeilen aus der Tabelle).''',
tools = [database_query_tool],
agent = database_specialist_agent
)
table_documentation_task = Task(
description = '''Verwendung der bereitgestellten Informationen über die Tabelle, um eine detaillierte Dokumentation für diese Tabelle zu erstellen, damit Menschen sie in der Praxis verwenden können.''',
expected_output = '''Gut geschriebene detaillierte Dokumentation, die das Datenschema für die Tabelle {table} im Markdown-Format beschreibt. Sie gibt eine Übersicht über die Tabelle in 1-2 Sätzen und besch reibt dann jede Spalte. Strukturiere die Spaltenbeschreibung als Markdown-Tabelle mit Spaltenname, Typ und Beschreibung.''',
tools = [],
output_file="table_documentation.md",
agent = tech_writer_agent
)
Sie haben möglicherweise bemerkt, dass ich den {table}
-Platzhalter in den Aufgabenbeschreibungen verwendet habe. Wir werden table
als Eingabevariable verwenden, wenn wir die Crew ausführen, und dieser Wert wird in alle Platzhalter eingefügt.
Außerdem habe ich für die Dokumentationsaufgabe der Tabelle die Ausgabedatei angegeben, um das endgültige Ergebnis lokal zu speichern.
Wir haben alles, was wir brauchen. Jetzt ist es an der Zeit, eine Crew zu erstellen und den Prozess auszuführen, wobei wir die Tabelle angeben, die uns interessiert. Lassen Sie uns das mit der Benutzertabelle versuchen.
crew = Crew(
agents = [database_specialist_agent, tech_writer_agent],
tasks = [table_description_task, table_documentation_task],
verbose = 2
)
result = crew.kickoff({'table': 'ecommerce_db.users'})
Es ist ein aufregender Moment, und ich freue mich sehr, das Ergebnis zu sehen. Machen Sie sich keine Sorgen, wenn die Ausführung einige Zeit in Anspruch nimmt. Agenten führen mehrere LLM-Aufrufe durch, daher ist es völlig normal, dass es einige Minuten dauert. Auf meinem Laptop hat es 2,5 Minuten gedauert.
Wir haben LLM aufgefordert, die Dokumentation im Markdown-Format zurückzugeben. Wir können den folgenden Code verwenden, um das formatierte Ergebnis in Jupyter Notebook anzuzeigen.
from IPython.display import Markdown
Markdown(result)
Auf den ersten Blick sieht es gut aus. Wir haben eine gültige Markdown-Datei, die die Benutzertabelle beschreibt.
Aber Moment, es ist falsch. Lassen Sie uns sehen, welche Daten wir in unserer Tabelle haben.
Die in der Dokumentation aufgeführten Spalten stimmen überhaupt nicht mit dem überein, was wir in der Datenbank haben. Es handelt sich um einen Fall von LLM-Halluzinationen.
Wir haben verbose = 2
gesetzt, um ausführliche Protokolle von CrewAI zu erhalten. Lassen Sie uns die Ausführungsprotokolle lesen, um die Ursache des Problems zu ermitteln.
Zunächst konnte der Datenbankspezialist die Datenbank aufgrund von Problemen mit Anführungszeichen nicht abfragen.
Der Spezialist konnte das Problem nicht lösen. Schließlich wurde diese Kette von CrewAI mit der folgenden Ausgabe beendet: Agent stopped due to iteration limit or time limit
.
Das bedeutet, dass der technische Schriftsteller keine faktischen Informationen über die Daten erhalten hat. Der Agent hat jedoch weitergemacht und völlig falsche Ergebnisse produziert. So sind wir schließlich mit einer falschen Dokumentation konfrontiert.
Fehlerbehebung
Auch wenn unsere erste Iteration nicht erfolgreich war, haben wir viel gelernt. Wir haben (mindestens) zwei Bereiche für Verbesserungen:
- Unser Datenbanktool ist für das Modell zu schwierig, und der Agent kämpft damit, es zu verwenden. Wir können das Tool toleranter machen, indem wir Anführungszeichen am Anfang und Ende der Abfragen entfernen. Diese Lösung ist nicht ideal, da gültiges SQL mit einem Anführungszeichen enden kann, aber wir können es versuchen.
- Unser technischer Schriftsteller basiert seine Ausgabe nicht auf der Eingabe des Datenbankspezialisten. Wir müssen den Prompt anpassen, um die Wichtigkeit der Bereitstellung nur faktischer Informationen hervorzuheben.
Lassen Sie uns versuchen, diese Probleme zu beheben. Zunächst werden wir das Tool reparieren – wir können strip
verwenden, um Anführungszeichen zu entfernen.
CH_HOST = 'http://localhost:8123' # Standardadresse
def get_clickhouse_data(query, host = CH_HOST, connection_timeout = 1500):
r = requests.post(host, params = {'query': query.strip('"').strip("'")},
timeout = connection_timeout)
if r.status_code == 200:
return r.text
else:
return 'Datenbank gab folgenden Fehler zurück:\n' + r.text
Dann ist es an der Zeit, den Prompt zu aktualisieren. Ich habe Aussagen hinzugefügt, die die Wichtigkeit der Einhaltung der Fakten in beiden Agenten- und Aufgabendefinitionen hervorheben.
tech_writer_agent = Agent(
role = "Technischer Redakteur",
goal = '''Erstellen von ansprechenden und fachlich korrekten technischen Dokumentationen für Datenquellen oder Tools''',
backstory = '''Sie sind ein Experte sowohl für Technologie als auch für Kommunikation und können daher auch komplexe Konzepte leicht erklären. Ihre Texte sind prägnant und können von einem breiten Publikum leicht verstanden werden. Sie verwenden einen professionellen, aber eher informellen Stil in Ihrer Kommunikation. Sie basieren Ihre Arbeit auf den faktischen Informationen, die von Ihren Kollegen bereitgestellt werden. Sie halten sich an die Fakten in der Dokumentation und fügen nichts hinzu.''',
allow_delegation = False,
verbose = True
)
table_documentation_task = Task(
description = '''Verwendung der bereitgestellten Informationen über die Tabelle, um eine detaillierte Dokumentation für diese Tabelle zu erstellen, damit Menschen sie in der Praxis verwenden können.''',
expected_output = '''Gut geschriebene detaillierte Dokumentation, die das Datenschema für die Tabelle {table} im Markdown-Format beschreibt. Sie gibt eine Übersicht über die Tabelle in 1-2 Sätzen und beschreibt dann jede Spalte. Strukturiere die Spaltenbeschreibung als Markdown-Tabelle mit Spaltenname, Typ und Beschreibung. Die Dokumentation basiert nur auf den Informationen, die vom Datenbankspezialisten bereitgestellt werden, ohne etwas hinzuzufügen.''',
tools = [],
output_file="table_documentation.md",
agent = tech_writer_agent
)
Lassen Sie uns unsere Crew erneut ausführen und die Ergebnisse betrachten.
Wir haben ein etwas besseres Ergebnis erzielt. Unser Datenbankspezialist konnte Abfragen ausführen und die Daten anzeigen, was ein großer Gewinn für uns ist. Außerdem sehen wir alle relevanten Felder in der Ergebnistabelle, obwohl es auch viele andere Felder gibt. Es ist also noch nicht ganz richtig.
Ich habe erneut die Ausführungsprotokolle von CrewAI durchgesehen, um herauszufinden, was schiefgelaufen ist. Das Problem liegt in der Abfrage der Spaltenliste. Es gibt keine Filterung nach Datenbank, daher werden einige nicht relevante Spalten zurückgegeben, die in das Ergebnis aufgenommen werden.
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'users'
Außerdem habe ich nach mehrmaligem Versuch bemerkt, dass der Datenbankspezialist von Zeit zu Zeit die Abfrage select * from <table>
ausführt. Dies kann im Produktionsbetrieb zu Problemen führen, da es möglicherweise große Datenmengen erzeugt und an LLM sendet.
Spezialisiertere Tools
Wir können unserem Agenten spezialisiertere Tools zur Verfügung stellen, um unsere Lösung zu verbessern. Derzeit verfügt der Agent über ein Tool zur Ausführung beliebiger SQL-Abfragen, was flexibel und leistungsstark, aber anfällig für Fehler ist. Wir können spezialisiertere Tools erstellen, wie z. B. das Abrufen der Tabellenstruktur und der ersten N Zeilen aus der Tabelle. Hoffentlich wird dies die Anzahl der Fehler verringern.
class TableStructure(BaseTool):
name: str = "Tabellenstruktur"
description: str = "Gibt die Liste der Spalten und ihre Typen zurück"
def _run(self, table: str) -> str:
table = table.strip('"').strip("'")
return get_clickhouse_data(
'describe {table} format TabSeparatedWithNames'\
.format(table = table)
)
class TableExamples(BaseTool):
name: str = "Tabellenbeispiele"
description: str = "Gibt die ersten N Zeilen aus der Tabelle zurück"
def _run(self, table: str, n: int = 30) -> str:
table = table.strip('"').strip("'")
return get_clickhouse_data(
'select * from {table} limit {n} format TabSeparatedWithNames'
\.format(table = table, n = n)
)
table_structure_tool = TableStructure()
table_examples_tool = TableExamples()
Jetzt müssen wir diese Tools in der Aufgabe angeben und unser Skript erneut ausführen. Nach dem ersten Versuch habe ich die folgende Ausgabe vom technischen Schriftsteller erhalten.
Task output: This final answer provides a detailed and factual description
of the ecommerce_db.users table structure, including column names, types,
and descriptions. The documentation adheres to the provided information
from the database specialist without any additions or modifications.
Spezialisiertere Tools haben dem Datenbankspezialisten geholfen, die korrekten Tabelleninformationen abzurufen. Der Schriftsteller hatte jedoch alle notwendigen Informationen, wir haben aber nicht das erwartete Ergebnis erhalten.
Wie wir wissen, sind LLMs wahrscheinlichkeitsbasiert, daher habe ich es noch einmal versucht. Und hurra, dieses Mal war das Ergebnis ziemlich gut.
Es ist nicht perfekt, da es noch immer einige irrelevante Kommentare enthält und eine allgemeine Beschreibung der Tabelle fehlt. Die Bereitstellung spezialisierterer Tools hat sich jedoch definitiv ausgezahlt. Es hat auch geholfen, Probleme zu vermeiden, bei denen der Agent versucht hat, alle Daten aus der Tabelle zu laden.
Qualitätssicherungsspezialist
Wir haben ziemlich gute Ergebnisse erzielt, aber lassen Sie uns sehen, ob wir sie noch weiter verbessern können. Eine gängige Praxis in Multi-Agenten-Setups ist die Qualitätssicherung, die eine abschließende Überprüfungsstufe vor der Finalisierung der Ergebnisse hinzufügt.
Lassen Sie uns einen neuen Agenten erstellen – einen Qualitätssicherungsspezialisten, der für die Überprüfung zuständig ist.
qa_specialist_agent = Agent(
role = "Qualitätssicherungsspezialist",
goal = '''Sicherstellung der höchsten Qualität der von uns bereitgestellten Dokumentation (dass sie korrekt und leicht verständlich ist)''',
backstory = '''Sie arbeiten als Qualitätssicherungsspezialist und überprüfen die Arbeit des technischen Schriftstellers, um sicherzustellen, dass sie unseren höchsten Standards entspricht. Sie müssen sicherstellen, dass der technische Schriftsteller vollständige Antworten gibt und keine Annahmen trifft. Außerdem müssen Sie sicherstellen, dass die Dokumentation alle Fragen beantwortet und leicht verständlich ist.''',
allow_delegation = False,
verbose = True
)
Jetzt ist es an der Zeit, die Überprüfungsaufgabe zu beschreiben. Ich habe den context
-Parameter verwendet, um anzugeben, dass diese Aufgabe die Ausgaben von table_description_task
und table_documentation_task
erfordert.
qa_review_task = Task(
description = '''Überprüfen Sie den Dokumentationsentwurf, den der technische Schriftsteller bereitgestellt hat. Stellen Sie sicher, dass die Dokumentation alle Fragen vollständig beantwortet: den Zweck der Tabelle und ihre Struktur in Form einer Tabelle. Stellen Sie sicher, dass die Dokumentation mit den Informationen übereinstimmt, die der Datenbankspezialist bereitgestellt hat. Überprüfen Sie, ob es in der endgültigen Version der Dokumentation keine irrelevanten Kommentare gibt.''',
expected_output = '''Die endgültige Version der Dokumentation im Markdown-Format, die veröffentlicht werden kann. Die Dokumentation sollte alle Fragen vollständig beantworten, konsistent sein und unseren professionellen, aber informellen Tonfall einhalten.''',
tools = [],
context = [table_description_task, table_documentation_task],
output_file="checked_table_documentation.md",
agent = qa_specialist_agent
)
Lassen Sie uns unsere Crew aktualisieren und ausführen.
full_crew = Crew(
agents=[database_specialist_agent, tech_writer_agent, qa_specialist_agent],
tasks=[table_description_task, table_documentation_task, qa_review_task],
verbose = 2,
memory = False # funktioniert nicht mit Llama
)
full_result = full_crew.kickoff({'table': 'ecommerce_db.users'})
Wir haben jetzt eine strukturiertere und detailliertere Dokumentation, dank der Hinzufügung der QS-Stufe.
Delegierung
Mit der Hinzunahme des QS-Spezialisten wäre es interessant, die Delegationsmechanismus zu testen. Der QS-Spezialist könnte Fragen oder Anfragen haben, die er an andere Agenten delegieren könnte.
Ich habe versucht, die Delegation mit Llama 3 zu verwenden, aber es hat nicht gut funktioniert. Llama 3 hatte Probleme damit, das co_worker
-Tool korrekt aufzurufen. Es konnte den Namen des Kollegen nicht korrekt angeben.
Wir haben mit einem lokalen Modell, das auf jedem Laptop ausgeführt werden kann, ziemlich gute Ergebnisse erzielt, aber jetzt ist es an der Zeit, die Gänge zu wechseln und das viel leistungsstärkere Modell GPT-4o zu verwenden.
Um dies zu tun, müssen wir die folgenden Umgebungsvariablen aktualisieren.
os.environ["OPENAI_MODEL_NAME"] = 'gpt-4o'
os.environ["OPENAI_API_KEY"] = config['OPENAI_API_KEY'] # Ihr OpenAI-Schlüssel
Um die Delegation zu aktivieren, müssen wir allow_delegation = True
für den QS-Spezialisten-Agenten angeben.
Außerdem können wir die praktische Speicherfunktion für OpenAI-Modelle nutzen, wie oben erwähnt. Der Speicher ermöglicht es unseren Agenten, ihr Wissen während der Ausführung mit ihren Kollegen zu teilen und Informationen aus früheren Ausführungen über den Langzeitspeicher zu beziehen.
full_crew = Crew(
agents = [database_specialist_agent, tech_writer_agent, qa_specialist_agent],
tasks = [table_description_task, table_documentation_task, qa_review_task],
verbose = 2,
memory = True
)
full_result = full_crew.kickoff({'table': 'ecommerce_db.users'})
Lassen Sie uns die Ausführungsprotokolle von CrewAI betrachten, um zu verstehen, wie die Delegierung funktioniert. Hier sind alle Protokolle für den QS-Spezialisten. Wir können sehen, dass er sich an den Datenbankspezialisten gewandt hat, um die Informationen zu überprüfen.
Thought: I need to review the provided draft documentation to ensure it
fully answers all the questions and is complete, correct, and easy
to understand. Specifically, I need to ensure that it explains the purpose
of the table, its structure, and includes relevant examples without
any irrelevant comments.
First, I will compare the two drafts to identify any discrepancies and
missing information. Then, I will verify the accuracy and completeness of
the information provided.
**Comparison:**
1. **Introduction:**
- First draft: Includes an introduction and examples.
- Second draft: Adds a more detailed introduction about the purpose and importance of the table.
2. **Columns:**
- Both drafts have the same column structure and descriptions.
3. **Additional Information:**
- Both drafts include similar additional information, but the second draft is more concise.
4. **Examples:**
- The first draft includes examples, which are missing in the second draft.
5. **Final Checks:**
- Ensure there are no irrelevant comments.
- Verify consistency with information provided by the database specialist.
Action: Ask question to co-worker
Action Input: {
"question": "Can you confirm the accuracy of the provided column descriptions
and examples for the `ecommerce_db.users` table?",
"context": "We are finalizing the documentation for the `ecommerce_db.users`
table. The table includes columns `user_id`, `country`, `is_active`, and
`age`. Here are the examples provided:
| user_id | country | is_active | age |
|---------|----------------|-----------|-----|
| 1000001 | United Kingdom | 0 | 70 |
| 1000002 | France | 1 | 87 |
| 1000003 | France | 1 | 88 |
| 1000004 | Germany | 1 | 25 |
| 1000005 | Germany | 1 | 48 |
| 1000006 | France | 1 | 78 |
| 1000007 | Netherlands | 1 | 65 |
| 1000008 | Germany | 1 | 31 |
| 1000009 | Germany | 1 | 66 |
| 1000010 | Germany | 1 | 73 |",
"coworker": "database specialist"}
<!-- omitted logs from the database specialist -->
Based on the provided context and examples for the `ecommerce_db.users` table,
the column descriptions and examples appear accurate. Here are the details:
- `user_id`: This column contains a unique identifier for each user.
The provided examples (e.g., 1000001, 1000002) align with this description
as they are unique numeric identifiers.
- `country`: This column specifies the country of the user. The provided
examples (e.g., United Kingdom, France, Germany, Netherlands) are
all valid country names.
- `is_active`: This column indicates whether the user is active (1) or not (0). The provided examples (e.g., 0, 1) correctly represent this binary status.
- `age`: This column shows the age of the user. The provided examples (e.g., 70,
87, 88, 25, 48, 78, 65, 31, 66, 73) are all valid ages.
Therefore, the column descriptions and examples provided for the
`ecommerce_db.users` table are accurate and meet the expected criteria.
Thought: Based on the observation from the database specialist,
the column descriptions and examples provided for the `ecommerce_db.users`
table are accurate. Now, I will consolidate the best elements from both drafts,
ensuring the documentation is complete, clear, and free of irrelevant comments.
Action: Finalize the documentation in markdown format, incorporating
the detailed introduction, examples, and ensuring the tone is professional
but informal.
Beim ersten Versuch der Delegation habe ich den Speicher nicht aktiviert, was zu falschen Ergebnissen geführt hat. Der Datenbankspezialist und der technische Schriftsteller haben zunächst die korrekten Informationen zurückgegeben. Als jedoch der QS-Spezialist mit Nachfragen zurückkam, begannen sie zu halluzinieren. Es scheint also, dass die Delegierung besser funktioniert, wenn der Speicher aktiviert ist.
Hier ist das endgültige Ergebnis von GPT-4o. Das Ergebnis sieht jetzt ziemlich gut aus. Wir können definitiv LLMs verwenden, um die Dokumentation zu automatisieren.
Damit ist die erste Aufgabe gelöst!
Ich habe dasselbe Skript verwendet, um auch eine Dokumentation für die Tabelle ecommerce_db.sessions
zu erstellen. Das wird für unsere nächste Aufgabe hilfreich sein. Also lassen Sie uns keine Zeit verlieren und weiter machen.
Anwendungsfall: Beantwortung von Fragen
Unsere nächste Aufgabe besteht darin, Fragen auf der Grundlage der Dokumentation zu beantworten, da dies für viele Datenanalysten (und andere Fachleute) üblich ist.
Wir werden einfach beginnen und nur zwei Agenten erstellen:
- Der Dokumentationssupport-Spezialist wird Fragen auf der Grundlage der Dokumentation beantworten.
- Der Support-QS-Agent wird die Antwort überprüfen, bevor sie dem Kunden mitgeteilt wird.
Wir müssen dem Dokumentationsspezialisten ein paar Tools zur Verfügung stellen, die es ihm ermöglichen, alle Dateien in einem Verzeichnis anzuzeigen und die Dateien zu lesen. Das ist ziemlich einfach, da CrewAI solche Tools implementiert hat.
from crewai_tools import DirectoryReadTool, FileReadTool
documentation_directory_tool = DirectoryReadTool(
directory = '~/crewai_project/ecommerce_documentation')
base_file_read_tool = FileReadTool()
Da Llama 3 jedoch weiterhin Probleme mit Anführungszeichen hat, wenn Tools aufgerufen werden, musste ich ein benutzerdefiniertes Tool auf der Basis von FileReaderTool
erstellen, um dieses Problem zu umgehen.
from crewai_tools import BaseTool
class FileReadToolUPD(BaseTool):
name: str = "Lese den Inhalt einer Datei"
description: str = "Ein Tool, das verwendet werden kann, um den Inhalt einer Datei zu lesen."
def _run(self, file_path: str) -> str:
# Implementierung geht hier hin
return base_file_read_tool._run(file_path = file_path.strip('"').strip("'"))
file_read_tool = FileReadToolUPD()
Als Nächstes müssen wir, wie wir es oben getan haben, Agenten, Aufgaben und eine Crew erstellen.
data_support_agent = Agent(
role = "Senior Data Support Agent",
goal = "Sei der hilfreichste Support für deine Kollegen",
backstory = '''Du arbeitest als Support für datenbezogene Fragen im Unternehmen. Obwohl du ein großer Experte für unser Data Warehouse bist, überprüfst du alle Fakten in der Dokumentation. Unsere Dokumentation ist absolut aktuell, daher kannst du dich voll und ganz darauf verlassen, wenn du Fragen beantwortest (du musst die tatsächlichen Daten nicht in der Datenbank überprüfen). Deine Arbeit ist sehr wichtig für den Erfolg des Teams. Denke jedoch daran, dass Beispiele für Tabellenzeilen nicht alle möglichen Werte anzeigen. Du musst sicherstellen, dass du den bestmöglichen Support bietest: alle Fragen beantworten, keine Annahmen treffen und nur faktische Daten weitergeben. Sei kreativ und versuche, das Kundenproblem zu lösen.''',
allow_delegation = False,
verbose = True
)
qa_support_agent = Agent(
role = "Support Qualitätssicherung Agent",
goal = '''Sicherstellung der höchsten Qualität der an die Kunden bereitgestellten Antworten''',
backstory = '''Du arbeitest als Qualitätssicherungsspezialist und überprüfst die Arbeit der Support-Agenten, um sicherzustellen, dass sie unseren höchsten Standards entsprechen. Du musst sicherstellen, dass der Agent vollständige und korrekte Antworten gibt und keine Annahmen trifft. Außerdem musst du sicherstellen, dass die Antwort alle Fragen beantwortet und leicht verständlich ist.''',
allow_delegation = False,
verbose = True
)
draft_data_answer = Task(
description = '''Ein sehr wichtiger Kunde {customer} hat sich mit folgender Frage an dich gewandt:
{question}
Deine Aufgabe ist es, die beste Antwort auf alle Punkte in der Frage zu geben, indem du alle verfügbaren Informationen verwendest und keine Annahmen triffst. Wenn du nicht genügend Informationen hast, um die Frage zu beantworten, sag einfach, dass du es nicht weißt.''',
expected_output = '''Die detaillierte und informative Antwort auf die Frage des Kunden, die alle in der Frage erwähnten Punkte anspricht. Stelle sicher, dass die Antwort vollständig und sachlich korrekt ist (ohne zusätzliche Informationen, die nicht auf faktischen Daten basieren).''',
tools = [documentation_directory_tool, file_read_tool],
agent = data_support_agent
)
answer_review = Task(
description = '''Überprüfe den Entwurf der Antwort, den der Support-Agent bereitgestellt hat. Stelle sicher, dass er alle in der ursprünglichen Anfrage erwähnten Fragen vollständig beantwortet. Überprüfe, ob die Antwort konsistent ist und keine Annahmen enthält.''',
expected_output = '''Die endgültige Version der Antwort im Markdown-Format, die mit dem Kunden geteilt werden kann. Die Antwort sollte alle Fragen vollständig beantworten, konsistent sein und unseren professionellen, aber informellen Tonfall einhalten. Wir sind ein sehr lockeres und freundliches Unternehmen, also vergiss nicht, alle höflichen Floskeln einzubauen.''',
tools = [],
agent = qa_support_agent
)
qna_crew = Crew(
agents = [data_support_agent, qa_support_agent],
tasks = [draft_data_answer, answer_review],
verbose = 2,
memory = False # funktioniert nicht mit Llama
)
Lassen Sie uns sehen, wie es in der Praxis funktioniert.
result = qna_crew.kickoff(
{'customer': "Max",
'question': """Hey team, I hope you're doing well. I need to find
the numbers before our CEO presentation tomorrow, so I will really
appreciate your help.
I need to calculate the number of sessions from our Windows users in 2023. I've tried to find the table with such data
in our data warehouse, but wasn't able to.
Do you have any ideas whether we store the needed data somewhere,
so that I can query it? """
}
)
Wir haben eine höfliche, praktische und hilfreiche Antwort erhalten. Das ist wirklich großartig.
**Hello Max,**
Thank you for reaching out with your question! I'm happy to help you
find the number of sessions from Windows users in 2023.
After reviewing our documentation, I found that we do store data
related to sessions and users in our ecommerce database, specifically in
the `ecommerce_db.sessions` table.
To answer your question, I can provide you with a step-by-step guide
on how to query this table using SQL. First, you can use the `session_id`
column along with the `os` column filtering for "Windows" and
the `action_date` column filtering for dates in 2023.
Then, you can group the results by `os` using the `GROUP BY` clause
to count the number of sessions that meet these conditions.
Here's a sample SQL query that should give you the desired output:
```sql
SELECT COUNT(*)
FROM ecommerce_db.sessions
WHERE os = 'Windows'
AND action_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY os;
``
This query will return the total number of sessions from Windows
users in 2023. I hope this helps! If you have any further questions or
need more assistance, please don't hesitate to ask.
Lassen Sie uns die Aufgabe etwas komplizierter gestalten. Angenommen, wir erhalten nicht nur Fragen zu unseren Daten, sondern auch zu unserem Tool (ClickHouse). Dann werden wir einen weiteren Agenten in der Crew haben – den ClickHouse-Guru. Um unserem CH-Agenten etwas Wissen zu vermitteln, werde ich eine Dokumentationswebsite mit ihm teilen.
from crewai_tools import ScrapeWebsiteTool, WebsiteSearchTool
ch_documentation_tool = ScrapeWebsiteTool(
'https://clickhouse.com/docs/en/guides/creating-tables')
Wenn Sie mit einem langen Dokument arbeiten müssen, können Sie versuchen, RAG (Retrieval Augmented Generation) zu verwenden – WebsiteSearchTool
. Es berechnet Einbettungen und speichert sie lokal in ChromaDB. In unserem Fall werden wir bei einem einfachen Website-Scraper-Tool bleiben.
Jetzt, da wir zwei Fachexperten haben, müssen wir entscheiden, wer an den Fragen arbeiten wird. Daher ist es an der Zeit, einen hierarchischen Prozess zu verwenden und einen Manager hinzuzufügen, der alle Aufgaben orchestriert.
CrewAI stellt die Managerimplementierung bereit, daher müssen wir nur das LLM-Modell angeben. Ich habe GPT-4o ausgewählt.
from langchain_openai import ChatOpenAI
from crewai import Process
complext_qna_crew = Crew(
agents = [ch_support_agent, data_support_agent, qa_support_agent],
tasks = [draft_ch_answer, draft_data_answer, answer_review],
verbose = 2,
manager_llm = ChatOpenAI(model='gpt-4o', temperature=0),
process = Process.hierarchical,
memory = False
)
An diesem Punkt musste ich von Llama 3 zu OpenAI-Modellen wechseln, um einen hierarchischen Prozess auszuführen, da er mit Llama nicht funktioniert hat (ähnlich wie in diesem Problem).
Jetzt können wir unsere neue Crew mit verschiedenen Arten von Fragen (entweder bezogen auf unsere Daten oder die ClickHouse-Datenbank) testen.
ch_result = complext_qna_crew.kickoff(
{'customer': "Maria",
'question': """Good morning, team. I'm using ClickHouse to calculate
the number of customers.
Could you please remind whether there's an option to add totals
in ClickHouse?"""
}
)
doc_result = complext_qna_crew.kickoff(
{'customer': "Max",
'question': """Hey team, I hope you're doing well. I need to find
the numbers before our CEO presentation tomorrow, so I will really
appreciate your help.
I need to calculate the number of sessions from our Windows users
in 2023. I've tried to find the table with such data
in our data warehouse, but wasn't able to.
Do you have any ideas whether we store the needed data somewhere,
so that I can query it. """
}
)
Wenn wir uns die endgültigen Antworten und Protokolle (ich habe sie hier ausgelassen, da sie ziemlich lang sind, aber Sie können sie auf GitHub finden) ansehen, werden wir sehen, dass der Manager in der Lage war, zu orchestrieren und Aufgaben an Mitarbeiter mit relevanten Kenntnissen zu delegieren, um die Frage des Kunden zu beantworten. Für die erste (ClickHouse-bezogene) Frage haben wir eine detaillierte Antwort mit Beispielen und möglichen Auswirkungen der Verwendung der WITH TOTALS
-Funktionalität erhalten. Für die datenbezogene Frage haben die Modelle im Wesentlichen dieselben Informationen zurückgegeben, die wir oben gesehen haben.
Daher haben wir eine Crew erstellt, die verschiedene Arten von Fragen auf der Grundlage der Dokumentation beantworten kann, sei es aus einer lokalen Datei oder einer Website. Ich denke, das ist ein großartiges Ergebnis.
Sie können den gesamten Code auf GitHub finden.
## Zusammenfassung
In diesem Artikel haben wir die Verwendung des CrewAI-Multi-Agenten-Frameworks zur Erstellung einer Lösung für das Schreiben von Dokumentationen auf der Grundlage von Tabellen und das Beantworten damit zusammenhängender Fragen untersucht.
Insgesamt halte ich CrewAI für ein unglaublich nützliches Framework für Multi-Agenten-Systeme:
- Es ist einfach und Sie können Ihren ersten Prototypen schnell erstellen.
- Seine Flexibilität ermöglicht es, komplexe Geschäftsprobleme zu lösen.
- Es fördert gute Praktiken wie Rollenspiel.
- Es bietet viele nützliche Tools aus der Box, wie z. B. RAG und einen Website-Parser.
- Die Unterstützung unterschiedlicher Arten von Speicher verbessert die Zusammenarbeit der Agenten.
- Integrierte Schutzmechanismen verhindern, dass Agenten in sich wiederholenden Schleifen stecken bleiben.
Es gibt jedoch Bereiche, die verbessert werden könnten:
- Während das Framework einfach und benutzerfreundlich ist, ist es nicht sehr anpassbar. Beispielsweise können Sie derzeit keinen benutzerdefinierten LLM-Manager erstellen, um die Prozesse zu orchestrieren.
- Manchmal ist es ziemlich schwierig, alle Details aus der Dokumentation zu erhalten. Zum Beispiel ist in der Dokumentation nicht vollständig erklärt, wie die Guardrails funktionieren, um repetitive Funktionsaufrufe zu vermeiden.
- Ein weiterer Verbesserungsbereich ist die Transparenz. Ich mag es, zu verstehen, wie Frameworks unter der Haube funktionieren. In Langchain können Sie beispielsweise
langchain.debug = True
verwenden, um alle LLM-Aufrufe anzuzeigen. Ich habe jedoch noch nicht herausgefunden, wie ich mit CrewAI das gleiche Maß an Detailgenauigkeit erreichen kann. - Die vollständige Unterstützung für lokale Modelle wäre eine großartige Ergänzung, da die derzeitige Implementierung entweder einige Funktionen nicht unterstützt oder schwierig einzurichten ist.
Der Domain und die Tools für LLMs entwickeln sich rasch weiter, daher bin ich zuversichtlich, dass wir in naher Zukunft viele Fortschritte sehen werden.