OpenAI hat kürzlich neue Funktionen eingeführt, die eine agentenähnliche Architektur aufweisen, wie beispielsweise die Assistant API. Laut OpenAI:
Mit der Assistants API können Sie KI-Assistenten in Ihren eigenen Anwendungen erstellen. Ein Assistent verfügt über Anweisungen und kann Modelle, Instruments und Dateien nutzen, um auf Benutzeranfragen zu antworten. Die Assistants API unterstützt derzeit drei Arten von Instruments: Code-Interpreter, Dateisuche und Funktionsaufruf.
Diese Fortschritte sind zwar vielversprechend, hinken aber noch hinter LangChain her. LangChain ermöglicht die Erstellung agentenähnlicher Systeme auf LLM-Foundation mit größerer Flexibilität bei der Verarbeitung natürlicher Spracheingaben und der Ausführung kontextbasierter Aktionen.
Dies ist jedoch nur der Anfang.
Auf einer hohen Ebene kann man sich die Interaktion mit der Assistant API als Schleife vorstellen:
- Bei einer Benutzereingabe wird ein LLM aufgerufen, um zu bestimmen, ob eine Antwort bereitgestellt oder bestimmte Aktionen ausgeführt werden sollen.
- Wenn die Entscheidung des LLM zur Beantwortung der Abfrage ausreicht, endet die Schleife.
- Führt eine Aktion zu einer neuen Beobachtung, wird diese Beobachtung in die Eingabeaufforderung übernommen und das LLM erneut aufgerufen.
- Anschließend wird die Schleife neu gestartet.
Leider fand ich die Dokumentation der API trotz der angekündigten Vorteile schlecht gemacht, insbesondere im Hinblick auf die Interaktion mit benutzerdefinierten Funktionsaufrufen und die Erstellung von Apps mit Frameworks wie Streamlit.
In diesem Blogbeitrag führe ich Sie durch die Erstellung eines KI-Assistenten unter Verwendung der OpenAI Assistant API mit benutzerdefinierten Funktionsaufrufen, gepaart mit einer Streamlit-Schnittstelle, um allen zu helfen, die die Assistant API effektiv nutzen möchten.
In diesem Blogbeitrag werde ich ein einfaches Beispiel demonstrieren: einen KI-Assistenten, der Steuern auf Grundlage eines bestimmten Umsatzes berechnen kann. Langchain-Benutzer können sich die Implementierung leicht vorstellen, indem sie einen Agent mit einem Device zur „Steuerberechnung“.
Dieses Device würde die notwendigen Berechnungsschritte und eine intestine gestaltete Eingabeaufforderung enthalten, um sicherzustellen, dass der LLM weiß, wann er das Device aufrufen muss, wenn eine Frage Einnahmen oder Steuern betrifft.
Dieser Prozess ist jedoch bei der OpenAI Assistant API nicht genau derselbe. Während der Code-Interpreter und die Dateisuchtools direkt und unkompliziert verwendet werden können, gemäß OpenAI-Dokumentationerfordern benutzerdefinierte Instruments einen etwas anderen Ansatz.
assistant = shopper.beta.assistants.create(
identify="Knowledge visualizer",
description="You're nice at creating stunning information visualizations. You analyze information current in .csv information, perceive traits, and provide you with information visualizations related to these traits. You additionally share a short textual content abstract of the traits noticed.",
mannequin="gpt-4o",
instruments=({"kind": "code_interpreter"}),
)
Lassen Sie es uns Schritt für Schritt aufschlüsseln. Unser Ziel ist:
- Definieren Sie eine Funktion, die die Steuer auf Grundlage eines gegebenen Umsatzes berechnet.
- Entwickeln Sie ein Device mit dieser Funktion.
- Erstellen Sie einen Assistenten, der auf dieses Device zugreifen und es aufrufen kann, wenn eine Steuerberechnung erforderlich ist.
Bitte beachten Sie, dass das im folgenden Absatz beschriebene Steuerberechnungstool als Spielzeugbeispiel gedacht ist, um die Verwendung der im Beitrag besprochenen API zu demonstrieren. Es sollte nicht für tatsächliche Steuerberechnungen verwendet werden.
Betrachten Sie die folgende stückweise Funktion, die den Steuerwert für ein bestimmtes Einkommen zurückgibt. Beachten Sie, dass die Eingabe zur einfacheren Analyse als Zeichenfolge festgelegt ist:
def calculate_tax(income: str):
strive:
income = float(income)
besides ValueError:
elevate ValueError("The income must be a string illustration of a quantity.")if income <= 10000:
tax = 0
elif income <= 30000:
tax = 0.10 * (income - 10000)
elif income <= 70000:
tax = 2000 + 0.20 * (income - 30000)
elif income <= 150000:
tax = 10000 + 0.30 * (income - 70000)
else:
tax = 34000 + 0.40 * (income - 150000)
return tax
Als nächstes definieren wir den Assistenten:
function_tools = (
{
"kind": "operate",
"operate": {
"identify": "calculate_tax",
"description": "Get the tax for given income in euro",
"parameters": {
"kind": "object",
"properties": {
"income": {
"kind": "string",
"description": "Annual income in euro"
}
},
"required": ("income")
}
}
}
)
# Outline the assistant
assistant = shopper.beta.assistants.create(
identify="Assistant",
directions="",
instruments=function_tools,
mannequin="gpt-4o",
)
Nun zum wesentlichen Punkt:
Wie verwendet der Assistent die Funktion, wenn „calculate_tax“ aufgerufen wird? Dieser Teil ist im OpenAI-Assistenten schlecht dokumentiert, und viele Benutzer könnten beim ersten Einsatz verwirrt sein. Um dies zu handhaben, müssen wir einen EventHandler
um verschiedene Ereignisse im Antwortstrom zu verwalten, insbesondere, wie das Ereignis behandelt werden soll, wenn das Device „calculate_tax“ aufgerufen wird.
def handle_requires_action(self, information, run_id):
tool_outputs = ()for software in information.required_action.submit_tool_outputs.tool_calls:
if software.operate.identify == "calculate_tax":
strive:
# Extract income from software parameters
income = ast.literal_eval(software.operate.arguments)("income")
# Name your calculate_tax operate to get the tax
tax_result = calculate_tax(income)
# Append software output within the required format
tool_outputs.append({"tool_call_id": software.id, "output": f"{tax_result}"})
besides ValueError as e:
# Deal with any errors when calculating tax
tool_outputs.append({"tool_call_id": software.id, "error": str(e)})
# Submit all tool_outputs on the similar time
self.submit_tool_outputs(tool_outputs)
Der obige Code funktioniert wie folgt: Für jeden Device-Aufruf, der eine Aktion erfordert:
- Überprüfen Sie, ob der Funktionsname „calculate_tax“ lautet.
- Extrahieren Sie den Umsatzwert aus den Toolparametern.
- Ruf den
calculate_tax
Funktion mit den Einnahmen, um die Steuer zu berechnen. (Hier findet die eigentliche Interaktion statt.) - Nachdem Sie alle Device-Aufrufe verarbeitet haben, übermitteln Sie die gesammelten Ergebnisse.
Sie können nun mit dem Assistenten interagieren, indem Sie die folgenden von OpenAI dokumentierten Standardschritte befolgen (aus diesem Grund werde ich in diesem Abschnitt nicht näher auf Particulars eingehen):
- Einen Thread erstellen: Dies stellt eine Konversation zwischen einem Benutzer und dem Assistenten dar.
- Benutzernachrichten hinzufügen: Dies können sowohl Texte als auch Dateien sein, die dem Thread hinzugefügt werden.
- Erstellen Sie einen Lauf: Verwenden Sie das mit dem Assistenten verknüpfte Modell und die Instruments, um eine Antwort zu generieren. Diese Antwort wird dann wieder dem Thread hinzugefügt.
Der folgende Codeausschnitt zeigt, wie der Assistent in meinem speziellen Anwendungsfall ausgeführt wird: Der Code richtet eine Streaming-Interaktion mit einem Assistenten unter Verwendung bestimmter Parameter ein, darunter eine Thread-ID und eine Assistenten-ID. Ein EventHandler
Instanz verwaltet Ereignisse während des Streams. Die stream.until_done()
Methode hält den Stream aktiv, bis alle Interaktionen abgeschlossen sind. Die with
Anweisung stellt sicher, dass der Stream anschließend ordnungsgemäß geschlossen wird.
with shopper.beta.threads.runs.stream(thread_id=st.session_state.thread_id,
assistant_id=assistant.id,
event_handler=EventHandler(),
temperature=0) as stream:
stream.until_done()
Obwohl mein Beitrag hier enden könnte, habe ich zahlreiche Anfragen im Streamlit-Discussion board bemerkt (wie dieser), bei dem Benutzer Probleme haben, Streaming auf der Benutzeroberfläche zum Laufen zu bringen, obwohl es im Terminal einwandfrei funktioniert. Das hat mich dazu veranlasst, tiefer in die Materie einzusteigen.
Um Streaming erfolgreich in Ihre App zu integrieren, müssen Sie die Funktionalität der zuvor erwähnten EventHandler-Klasse erweitern und sich dabei insbesondere auf die Handhabung von Texterstellung, Textdeltas und Textvervollständigung konzentrieren. Hier sind die drei wichtigsten Schritte, die erforderlich sind, um Textual content in der Streamlit-Oberfläche anzuzeigen und gleichzeitig den Chatverlauf zu verwalten:
- Handhabung der Texterstellung (
on_text_created
): Initiiert und zeigt für jede Antwort des Assistenten ein neues Textfeld an und aktualisiert die Benutzeroberfläche, um den Standing vorangegangener Aktionen widerzuspiegeln. - Umgang mit Textdelta (
on_text_delta
): Aktualisiert das aktuelle Textfeld dynamisch, während der Assistent Textual content generiert, und ermöglicht so inkrementelle Änderungen ohne Aktualisierung der gesamten Benutzeroberfläche. - Handhabung der Textvervollständigung (
on_text_done
): Schließt jedes Interaktionssegment ab, indem ein neues leeres Textfeld hinzugefügt wird, um die nächste Interaktion vorzubereiten. Darüber hinaus zeichnet es abgeschlossene Konversationssegmente auf inchat_history
.
Betrachten Sie beispielsweise den folgenden Codeausschnitt zum Verwalten von Textdeltas:
def on_text_delta(self, delta: TextDelta, snapshot: Textual content):
"""
Handler for when a textual content delta is created
"""
# Clear the most recent textual content field
st.session_state.text_boxes(-1).empty()# If there may be new textual content, append it to the most recent factor within the assistant textual content checklist
if delta.worth:
st.session_state.assistant_text(-1) += delta.worth
# Re-display the up to date assistant textual content within the newest textual content field
st.session_state.text_boxes(-1).information("".be a part of(st.session_state("assistant_text")(-1)))
Dieser Code erfüllt drei Hauptaufgaben:
- Löschen des neuesten Textfelds: Leert den Inhalt des aktuellsten Textfelds (
st.session_state.text_boxes(-1)
), um es für neue Eingaben vorzubereiten. - Anhängen des Delta-Werts an den Assistententext: Wenn neuer Textual content (
delta.worth
) vorhanden ist, hängt er diesen an den laufenden Assistententext an, der inst.session_state.assistant_text(-1)
. - Aktualisierten Assistententext erneut anzeigen: Aktualisiert den Inhalt des neuesten Textfelds, um den kombinierten Inhalt aller bisher gesammelten Assistententexte widerzuspiegeln (
st.session_state("assistant_text")(-1)
).
In diesem Blogbeitrag wurde gezeigt, wie Sie mit der OpenAI Assistant API und Streamlit einen KI-Assistenten zur Steuerberechnung erstellen.
Ich habe dieses einfache Projekt durchgeführt, um die Fähigkeiten der Assistant API hervorzuheben, trotz der nicht ganz klaren Dokumentation. Mein Ziel conflict es, Unklarheiten zu beseitigen und einige Hinweise für diejenigen bereitzustellen, die an der Verwendung der Assistant API interessiert sind. Ich hoffe, dieser Beitrag conflict hilfreich und ermutigt Sie, weitere Möglichkeiten mit diesem leistungsstarken Device zu erkunden.
Aus Platzgründen habe ich versucht, unnötige Codeausschnitte zu vermeiden. Falls Sie sie jedoch benötigen, besuchen Sie bitte meine Github-Repository um die vollständige Implementierung anzuzeigen.