Entwicklung von "Auto-Analyst" - Ein KI-gesteuertes System für Datenanalyse Entwicklung von "Auto-Analyst" - Ein KI-gesteuertes System für Datenanalyse

Entwicklung von „Auto-Analyst“ – Ein KI-gesteuertes System für Datenanalyse

Als Datenwissenschaftler und Analyst habe ich KI-gesteuerte Agenten entwickelt, um meinen Arbeitsaufwand zu reduzieren. Entgegen der populären Vorstellung, dass KI menschliche Arbeitsplätze übernimmt, dienen die meisten KI-Agenten in Wirklichkeit dazu, uns bei der effizienteren Arbeit zu unterstützen. Der hier vorgestellte Agent wurde genau zu diesem Zweck konzipiert. Zuvor hatte ich bereits einen Datenvisualisierungsagenten entwickelt, der mir half, Visualisierungen schneller zu erstellen, indem er nur natürlichsprachliche Eingaben verwendete.

Design

Entwicklung von

Abbildung vom Autor

Das Flussdiagramm zeigt ein System, das mit einem vom Benutzer definierten Ziel beginnt. Der Planer-Agent delegiert dann Aufgaben an eine Gruppe von Arbeiter-Agenten, die jeweils für die Generierung von Code zur Lösung eines spezifischen Teils des Problems verantwortlich sind. Schließlich werden alle einzelnen Codeteile von einem Code-Kombinierer-Agenten gesammelt und integriert, was zu einem einzigen, zusammenhängenden Skript führt, das das gesamte Ziel erfüllt.

Hinweis: Der Planer-Agent könnte Aufgaben an einige, nicht unbedingt an alle Agenten delegieren. Außerdem hätte jeder Agent seinen eigenen Satz von Eingaben, die im Diagramm nicht gezeigt werden.

Einzelne Komponenten

Dieser Blogbeitrag führt Sie Schritt für Schritt durch den Aufbau des Agenten und präsentiert Codeblöcke für jede einzelne Komponente. Im folgenden Abschnitt zeigen wir, wie diese Teile nahtlos integriert werden.

Planer-Agent

Der Planer-Agent nimmt drei Eingaben entgegen: das vom Benutzer definierte Ziel, die verfügbaren Datensätze und die Beschreibungen der Agenten. Er gibt einen Plan in diesem Format aus:

Agent1-> Agent2-> Agent3….

# Sie können andere Orchestrierungsbibliotheken verwenden, aber ich fand DSPy
# gut für den Aufbau schneller, einfacherer und auswertbarer Anwendungen (was die Anwendung zuverlässiger macht)
import dspy

# Dieses Objekt erbt von der dspy.Signature-Klasse
# Der Text innerhalb """ ist der Prompt
class analytical_planner(dspy.Signature):
    """ Sie sind ein Datenanalyse-Planer-Agent. Sie haben Zugriff auf drei Eingaben
    1. Datensätze
    2. Beschreibungen der Datenagenten
    3. Vom Benutzer definiertes Ziel
    Sie nehmen diese drei Eingaben, um einen umfassenden Plan zu entwickeln, um das vom Benutzer definierte Ziel mit den verfügbaren Daten & Agenten zu erreichen.
    Falls Sie denken, dass das vom Benutzer definierte Ziel nicht durchführbar ist, können Sie den Benutzer bitten, das Ziel neu zu definieren oder mehr Beschreibungen hinzuzufügen.
    Geben Sie Ihre Ausgabe in diesem Format:
    plan: Agent1->Agent2->Agent3
    plan_desc = Verwenden Sie Agent 1 aus diesem Grund, dann Agent2 aus jenem Grund und zuletzt Agent3 aus diesem Grund.
    Sie müssen nicht alle Agenten in der Antwort auf die Anfrage verwenden
    """
    # Eingabefelder und ihre Beschreibungen
    dataset = dspy.InputField(desc="Verfügbare Datensätze, die im System geladen sind, verwenden Sie dieses df_name,columns set df as copy of df_name")
    Agent_desc = dspy.InputField(desc= "Die im System verfügbaren Agenten")
    goal = dspy.InputField(desc="Das vom Benutzer definierte Ziel")
    # Ausgabefelder und ihre Beschreibung
    plan = dspy.OutputField(desc="Der Plan, der das vom Benutzer definierte Ziel erreichen würde")
    plan_desc= dspy.OutputField(desc="Die Begründung für den gewählten Plan")

Entwicklung von

Beispielausgabe des Planer-Agenten

Analyse-Agenten

Die meisten Analyse-Agenten teilen eine gemeinsame Struktur mit leichten Variationen in ihren Prompts. Sie akzeptieren zwei Eingaben: das vom Benutzer definierte Ziel und den Datensatzindex. Sie produzieren zwei Ausgaben: den Code für die Analyse und einen Kommentar, der für das Debugging oder die Umleitung des Agenten nützlich sein kann.

# Ich definiere Analyse-Agenten als die Agenten, die sich in der mittleren Schicht befinden
# sie produzieren Code für eine spezialisierte Datenanalyseaufgabe
class preprocessing_agent(dspy.Signature):
    """ Sie sind ein Datenvorverarbeitungs-Agent. Ihre Aufgabe ist es, ein vom Benutzer definiertes Ziel und einen verfügbaren Datensatz zu nehmen,
    um eine explorative Analysepipeline zu erstellen. Sie tun dies, indem Sie den erforderlichen Python-Code ausgeben. 
    Sie verwenden nur numpy und pandas, um Vorverarbeitung und einführende Analysen durchzuführen
    """
    dataset = dspy.InputField(desc="Verfügbare Datensätze, die im System geladen sind, verwenden Sie dieses df_name,columns set df as copy of df_name")
    goal = dspy.InputField(desc="Das vom Benutzer definierte Ziel")
    commentary = dspy.OutputField(desc="Die Kommentare darüber, welche Analyse durchgeführt wird")
    code = dspy.OutputField(desc ="Der Code, der die Datenvorverarbeitung und einführende Analyse durchführt")

class statistical_analytics_agent(dspy.Signature):
    """ Sie sind ein statistischer Analyse-Agent. 
    Ihre Aufgabe ist es, einen Datensatz und ein vom Benutzer definiertes Ziel zu nehmen und 
    Python-Code auszugeben, der die entsprechende statistische Analyse durchführt, um dieses Ziel zu erreichen.
    Sie sollten die Python statsmodel-Bibliothek verwenden"""
    dataset = dspy.InputField(desc="Verfügbare Datensätze, die im System geladen sind, verwenden Sie dieses df_name,columns set df as copy of df_name")
    goal = dspy.InputField(desc="Das vom Benutzer definierte Ziel für die durchzuführende Analyse")
    commentary = dspy.OutputField(desc="Die Kommentare darüber, welche Analyse durchgeführt wird")
    code = dspy.OutputField(desc ="Der Code, der die statistische Analyse mit statsmodel durchführt")

class sk_learn_agent(dspy.Signature):
    # Prompt
    """Sie sind ein maschinelles Lern-Agent. 
    Ihre Aufgabe ist es, einen Datensatz und ein vom Benutzer definiertes Ziel zu nehmen und Python-Code auszugeben, der die entsprechende maschinelle Lernanalyse durchführt, um dieses Ziel zu erreichen. 
    Sie sollten die scikit-learn-Bibliothek verwenden."""
    # Eingabefelder
    dataset = dspy.InputField(desc="Verfügbare Datensätze, die im System geladen sind, verwenden Sie dieses df_name,columns. set df as copy of df_name")
    goal = dspy.InputField(desc="Das vom Benutzer definierte Ziel")
    # Ausgabefelder
    commentary = dspy.OutputField(desc="Die Kommentare darüber, welche Analyse durchgeführt wird")
    code = dspy.OutputField(desc ="Der Code, der die explorative Datenanalyse durchführt")
## Ich habe am Datenvisualisierungs-Agenten gearbeitet und ihn bereits mit DSPy optimiert.
## Der einzige große Unterschied ist, dass dieser Agent eine weitere Eingabe des Styling-Index nimmt

Entwicklung von

Beispielausgabe eines der Agenten

Code-Kombinierer-Agent

Der Zweck dieses Agenten ist es, die Ausgabe aller Agenten zu einem kohärenten Skript zu bereinigen. Er nimmt einen langen String einer Liste von Code und gibt Code aus.

class code_combiner_agent(dspy.Signature):
    """ Sie sind ein Code-Kombinierer-Agent, der Python-Code-Ausgaben von vielen Agenten nimmt und die Operationen in 1 Ausgabe kombiniert
    Sie beheben auch alle Fehler im Code"""
    agent_code_list =dspy.InputField(desc="Eine Liste von Code, die von jedem Agenten gegeben wurde")
    refined_complete_code = dspy.OutputField(desc="Verfeinerter vollständiger Codebase")

Optionale Agenten/Indizes

Damit der Agent reibungsloser funktioniert und einige Fehler abfängt, habe ich auch diese zusätzlichen Agenten oder Indizes erstellt.

# Die gleiche Signatur, die im Data Viz Agent-Beitrag verwendet wurde
class Data_Viz(dspy.Signature):
    """
    Sie sind ein KI-Agent, der das Ziel verwendet, um Datenvisualisierungen in Plotly zu generieren.
    Sie müssen die Ihnen zur Verfügung stehenden Werkzeuge verwenden
    {dataframe_index}
    {styling_index}
    Sie müssen eine Ausgabe als Code geben, falls es keine relevanten Spalten gibt, geben Sie einfach an, dass Sie nicht über die relevanten Informationen verfügen
    """
    goal = dspy.InputField(desc="vom Benutzer definiertes Ziel, das Informationen über die Daten und das Diagramm enthält, das sie plotten möchten")
    dataframe_context = dspy.InputField(desc=" Liefert Informationen über die Daten im Datenrahmen. Verwenden Sie nur Spaltennamen und dataframe_name wie in diesem Kontext")
    styling_context = dspy.InputField(desc='Liefert Anweisungen, wie Sie Ihre Plotly-Plots stylen sollen')
    code= dspy.OutputField(desc="Plotly-Code, der visualisiert, was der Benutzer gemäß der Abfrage & dataframe_index & styling_context benötigt")

# Ein optionaler Agent, der prüft, ob das vom Benutzer definierte Ziel gut funktioniert
class goal_refiner_agent(dspy.Signature):
    """Sie nehmen ein vom Benutzer definiertes Ziel, das einem KI-Datenanalyse-Planer-Agenten gegeben wurde, 
    Sie machen das Ziel unter Verwendung der verfügbaren Datensätze und agent_desc ausführlicher"""
    dataset = dspy.InputField(desc="Verfügbare Datensätze, die im System geladen sind, verwenden Sie dieses df_name,columns set df as copy of df_name")
    Agent_desc = dspy.InputField(desc= "Die im System verfügbaren Agenten")
    goal = dspy.InputField(desc="Das vom Benutzer definierte Ziel")
    refined_goal = dspy.OutputField(desc='Verfeinertes Ziel, das dem Planer-Agenten hilft, besser zu planen')

Anstatt Informationen über den gesamten Datensatz zu füttern, habe ich auch einen Retriever erstellt, der Informationen über die verfügbaren Daten aufnimmt.

# Ich wähle einen LLama-Index-basierten Retriever, da er bequemer war.
# Im Grunde können Sie Ihre Daten auf mehrere Arten einspeisen.
# Bereitstellung von Beschreibungen über Spaltennamen, Datenrahmenreferenz
# Und auch zu welchem Zweck die Daten gesammelt wurden usw.
dataframe_index =  VectorStoreIndex.from_documents(docs)
# Ich habe auch einen Styling-Index für den Datenvisualisierungs-Agenten definiert.
# Der natürlichsprachliche Anweisungen enthält, wie verschiedene Visualisierungen zu stylen sind
style_index =  VectorStoreIndex.from_documents(styling_instructions)

Alles als ein System zusammensetzen

In DSPy müssen Sie zur Erstellung einer komplexen LLM-Anwendung ein Modul mit zwei wesentlichen Methoden definieren: __init__ und forward.

Die Methode __init__ initialisiert das Modul, indem sie alle Variablen definiert, die im gesamten Verlauf verwendet werden. Die Methode forward hingegen ist der Ort, an dem die Kernfunktionalität implementiert wird. Diese Methode skizziert, wie die Ausgaben einer Komponente mit anderen Komponenten interagieren und treibt so die Logik der Anwendung effektiv voran.

# Dieses Modul nimmt bei der Initialisierung nur eine Eingabe
class auto_analyst(dspy.Module):
    def __init__(self,agents):
        # Definiert die verfügbaren Agenten, ihre Eingaben und Beschreibung
        self.agents = {}
        self.agent_inputs ={}
        self.agent_desc =[]
        i =0
        for a in agents:
            name = a.__pydantic_core_schema__['schema']['model_name']
            # Verwendung von CoT-Prompting, da es erfahrungsgemäß hilft, bessere Antworten zu generieren
            self.agents[name] = dspy.ChainOfThought(a)
            agent_inputs[name] ={x.strip() for x in str(agents[i].__pydantic_core_schema__['cls']).split('->')[0].split('(')[1].split(',')}
            self.agent_desc.append(str(a.__pydantic_core_schema__['cls']))
            i+=1
        # Definition des Planers, refine_goal & code_combiner Agenten separat
        # da sie keinen Code & keine Analyse generieren, sondern bei der Planung helfen, 
        # bessere Ziele erhalten & den Code kombinieren
        self.planner = dspy.ChainOfThought(analytical_planner)
        self.refine_goal = dspy.ChainOfThought(goal_refiner_agent)
        self.code_combiner_agent = dspy.ChainOfThought(code_combiner_agent)
        # diese zwei Retriever sind mit llama-index Retrievern definiert
        # Sie können dies anpassen, je nachdem, wie Sie Ihre Agenten wünschen
        self.dataset =dataframe_index.as_retriever(k=1)
        self.styling_index = style_index.as_retriever(similarity_top_k=1)
    def forward(self, query):
        # Dieses Dict wird verwendet, um schnell Argumente für Agenteneingaben zu übergeben
        dict_ ={}
        # ruft den relevanten Kontext zur Abfrage ab
        dict_['dataset'] = self.dataset.retrieve(query)[0].text
        dict_['styling_index'] = self.styling_index.retrieve(query)[0].text
        dict_['goal']=query
        dict_['Agent_desc'] = str(self.agent_desc)
        # output_dictionary, das alle Agentenausgaben speichert
        output_dict ={}
        # dies erstellt den Plan
        plan = self.planner(goal =dict_['goal'], dataset=dict_['dataset'], Agent_desc=dict_['Agent_desc'] )
        output_dict['analytical_planner'] = plan
        plan_list =[]
        code_list =[]
        # wenn der Planer wie beabsichtigt funktioniert hat, sollte er Agenten getrennt durch -> ausgeben
        if plan.plan.split('->'):
            plan_list = plan.plan.split('->')
        # falls das Ziel unklar ist, sendet es es an den refined goal agent
        else:
            refined_goal = self.refine_goal(dataset=data, goal=goal, Agent_desc= self.agent_desc)
            forward(query=refined_goal)
        # übergibt das Ziel und andere Eingaben an alle jeweiligen Agenten im Plan
        for p in plan_list:
            inputs = {x:dict_[x] for x in agent_inputs[p.strip()]}
            output_dict[p.strip()]=self.agents[p.strip()](**inputs)
            # erstellt eine Liste aller generierten Codes, die als 1 Skript kombiniert werden sollen
            code_list.append(output_dict[p.strip()].code)
        # Speichert die letzte Ausgabe
        output_dict['code_combiner_agent'] = self.code_combiner_agent(agent_code_list = str(code_list))
        return output_dict

# Sie können alle verfügbaren Agentensignaturen als Liste speichern
agents =[preprocessing_agent, statistical_analytics_agent, sk_learn_agent,data_viz_agent]
# Definieren Sie das Agentensystem
auto_analyst_system = auto_analyst(agents)
# das System ist mit Chicago-Kriminalitätsdaten vorgeladen
goal = "Was ist die Ursache für Kriminalität in Chicago?"
# Bitten Sie das Agentensystem, eine Analyse für diese Abfrage durchzuführen
output = auto_analyst_system(query = goal)

Nun betrachten wir das Ergebnis der Abfrage Schritt für Schritt.

Für diese Abfrage = ‚Was ist die Ursache für Kriminalität in Chicago?‘

Entwicklung von

Ausgabe des Planer-Agenten, der preprocessing_agent->stat_agent->data_viz_agent empfiehlt

Ausführung des Plans, zuerst der Vorverarbeitungsagent

Entwicklung von

Wie aus dem Kommentar & der Begründung ersichtlich, bereinigt er die Daten und führt einige grundlegende zählbasierte Analysen durch.

Als nächstes der statistische Analyseagent

Entwicklung von

Der Agent erstellt ein ARIMA-Modell für die Veränderung der Kriminalitätszahlen im Laufe der Zeit.

Als nächstes der Plotly-Datenvisualisierungsagent

Entwicklung von

Erstellt eine heatmap-ähnliche Visualisierung.

Schließlich der Code-Kombinierer-Agent, um alles zusammenzufügen

Entwicklung von

Kombiniert die Vorverarbeitung, das ARIMA-Modell und die Plotly-Visualisierung

Dies ist die Ausgabe nach der Ausführung des Codes vom letzten Agenten.

Entwicklung von

Erste Hälfte der Ausgabe

Entwicklung von

Die vom Agenten generierte Ausgabe

Einschränkungen

Wie viele Agenten funktioniert er hervorragend, wenn er wie beabsichtigt arbeitet. Dies ist nur die erste Iteration eines Projekts, das ich im Laufe der Zeit verbessern möchte. Bitte folgen Sie mir und FireBird Technologies, um auf dem Laufenden zu bleiben. Hier sind die aktuellen Einschränkungen:

  1. Halluzination: Der Agent produziert manchmal nicht ausführbaren Code aufgrund von Halluzinationen.
  2. Unzuverlässig/Inkonsistent: Die Ausgaben des Agenten sind inkonsistent, wobei verschiedene Variationen derselben Abfrage zu signifikant unterschiedlichem Code führen.
  3. Gemischte Ausgabe: Viele Agenten befassen sich nicht ausschließlich mit separaten Aspekten des Problems. Zum Beispiel generiert der Datenvorverarbeitungsagent seine eigenen Visualisierungen, während der Datenvisualisierungsagent auch seine eigenen erstellt.

Nächste Schritte

Dies ist ein laufendes Projekt; dies sind die Schritte, die ich wahrscheinlich als nächstes unternehmen würde, um den Agenten zu verbessern:

  1. Optimierung der Signatur/Prompt: DSPy ist darauf ausgelegt, LLM-Anwendungen zu evaluieren. Dies war nur die Implementierung, als nächstes müsste ich die besten Präfixe, Signaturen & Prompts herausfinden.
  2. Hinzufügen von Leitplanken: Die automatische Korrektur des vom Agenten generierten Codes ist eine Lösung, die in vielen anderen Agentensystemen verwendet wird. Der Versuch, Prompt-Injection-Angriffe einzuschränken, steht ebenfalls auf der Roadmap.
  3. Hinzufügen von Gedächtnis/Interaktion: Dieser Agent macht alles in einem Schritt, außerdem gibt es keine Interaktion zwischen den einzelnen Komponenten, bei der sie die Ausgaben der anderen sehen.
  4. Aufbau einer Benutzeroberfläche: Im Moment habe ich nur das Backend des Agenten für weitere Tests erstellt. Für Benutzerfeedback würde ich eine Benutzeroberfläche erstellen.

Vielen Dank fürs Lesen!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert