Veröffentlicht am
OpenAI Agents SDK

Beherrschung des OpenAI Agents Python SDK: Intelligente KI-Workflows mit Tools, Schutzmechanismen & Multi-Agenten-Koordination erstellen

Post image

OpenAI Agents Python SDK – Umfassendes Tutorial

Einführung

Das OpenAI Agents SDK ist ein Python-Framework zum Erstellen agentenbasierter KI-Systeme, die große Sprachmodelle (LLMs) mit Tools und strukturierten Workflows kombinieren. Es bietet eine leichte Menge an PrimitivenAgents, Handoffs und Guardrails – die zusammengesetzt werden können, um komplexe, zielorientierte KI-Anwendungen zu erstellen (OpenAI Agents SDK). Ein Agent in diesem SDK ist im Wesentlichen ein LLM (wie GPT-4 oder GPT-3.5), das mit Anweisungen konfiguriert und mit Tools ausgestattet ist, die es verwenden kann (OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK). Diese Agenten arbeiten in einer Agentenschleife: Sie erhalten eine Eingabe (z. B. eine Benutzerfrage), entscheiden über Aktionen (wie das Aufrufen eines Tools oder das Delegieren an einen anderen Agenten) und setzen die Schleife fort, bis eine endgültige Antwort erzeugt wird (Running agents - OpenAI Agents SDK) (Running agents - OpenAI Agents SDK).

Wichtige architektonische Konzepte umfassen:

  • Agents: Die Hauptakteure Ihrer Anwendung. Ein Agent besteht aus einem LLM plus einer Menge von Anweisungen (oft als System-Prompt bereitgestellt) und optionalen Tools, die er aufrufen kann. Bei einer Aufgabe kann ein Agent autonom planen, wie er sie löst, indem er das Problem durchdenkt, Tools zur Unterstützung aufruft und Antworten generiert (Orchestrating multiple agents - OpenAI Agents SDK). Zum Beispiel könnten Sie einen Agenten als „Research Assistant“ konfigurieren, der ein Web-Suchtool verwendet, um Informationen zu finden und dann die Anfrage beantwortet.

  • Tools: Funktionen oder Fähigkeiten, die ein Agent nutzen kann, um Aktionen über das eingebaute Wissen des LLM hinaus auszuführen (OpenAI Agents SDK). Tools können einfache Python-Funktionen sein (Datei-I/O, mathematische Berechnungen), externe API-Aufrufe (Wetterabfrage) oder sogar andere Agenten (was einem Agenten erlaubt, Unteraufgaben zu delegieren). Das Agents SDK macht es einfach, jede Python-Funktion in ein Tool zu verwandeln und Tool-Eingaben/-Ausgaben über Funktionsaufrufe zu handhaben (Tools - OpenAI Agents SDK). Tools befähigen Agenten, mit der Welt zu interagieren (z. B. Web durchsuchen, Datenbanken abfragen, Code ausführen) statt nur Text zu generieren.

  • Handoffs: Ein Mechanismus, mit dem ein Agent die Kontrolle während einer Aufgabe an einen anderen Agenten delegieren oder übertragen kann (OpenAI Agents SDK). Mit Handoffs können Sie mehrere spezialisierte Agenten in einem Workflow zusammensetzen – z. B. einen Triage-Agenten, der je nach Benutzeranfrage an einen Abrechnungs- oder Rückerstattungsagenten übergibt (OpenAI Agents SDK — Getting Started). Handoffs ermöglichen modulare Multi-Agenten-Systeme, bei denen sich jeder Agent auf eine bestimmte Domäne oder Fähigkeit konzentriert.

  • Guardrails: Eingebaute Validierungs- und Sicherheitsprüfungen, die parallel zu Agenten laufen (OpenAI Agents SDK). Guardrails können Eingaben filtern (um bösartige oder irrelevante Anfragen zu verhindern) und Ausgaben validieren (um Korrektheit oder Richtlinienkonformität sicherzustellen) (OpenAI Agents SDK — Getting Started). Wenn eine Guardrail-Prüfung fehlschlägt (genannt tripwire), kann das SDK die Ausführung des Agenten stoppen oder eine Ausnahme auslösen, bevor eine unerwünschte Aktion erfolgt (Guardrails - OpenAI Agents SDK) (Guardrails - OpenAI Agents SDK). Dies hilft, Zuverlässigkeit und Sicherheit in Produktionsanwendungen zu gewährleisten.

  • Tracing: Das SDK verfügt über integrierte Tracing-Unterstützung für Debugging und Analyse (OpenAI Agents SDK — Getting Started). Jeder Schritt der Überlegung eines Agenten – Tool-Nutzung, Entscheidungen und LLM-Nachrichten – kann als Trace protokolliert werden. Entwickler können diese Traces (z. B. über das OpenAI-Dashboard) inspizieren, um das Verhalten des Agenten zu verstehen, Leistung zu messen und Prompts oder Code iterativ zu verbessern (OpenAI Agents SDK — Getting Started). Tracing ist standardmäßig aktiviert und gibt Ihnen Einblick in den Denkprozess und die Aktionen des Agenten.

Diese Komponenten arbeiten zusammen, um eine agentenbasierte Systemarchitektur zu bilden. Ein Agent erhält ein Ziel oder eine Anfrage, nutzt seinen LLM-Rückgrat, um über die Aufgabe nachzudenken, ruft Tools auf oder delegiert an andere Agenten, wenn nötig, und erzeugt schließlich ein Ergebnis. Dank des Designs des SDK wird ein Großteil dieser Komplexität durch die Agentenschleife und die SDK-Laufzeit für Sie gehandhabt. Sie können einfach starten (ein einzelner Agent beantwortet Fragen) und nach und nach Tools, mehrere Agenten, Guardrails und benutzerdefinierte Steuerungen hinzufügen, wenn Ihre Anwendung komplexer wird. Das SDK betont Flexibilität mit minimaler Abstraktion – es „funktioniert großartig direkt nach der Installation“, erlaubt aber die Anpassung jedes Workflow-Teils, falls nötig (OpenAI Agents SDK) (OpenAI Agents SDK).

Im weiteren Verlauf dieses Tutorials werden wir alle wichtigen Funktionen des OpenAI Agents Python SDK erkunden und zeigen, wie man sie effektiv nutzt. Wir beginnen mit der Installation des SDK und der Erstellung eines einfachen Agenten. Dann tauchen wir ein in die Konfiguration benutzerdefinierter Agenten, das Hinzufügen und Erstellen von Tools, das Verwalten von Agentenkontext und -zustand (Speicher), die Handhabung von Multi-Agenten-Orchestrierung mit Handoffs, die Integration von Guardrails für Sicherheit und mehr. Codebeispiele werden durchgehend bereitgestellt, und am Ende kombinieren wir diese Konzepte in einem vollständigen Beispielprojekt. Los geht's!

Installation und Einrichtung

Bevor Sie das Agents SDK verwenden, müssen Sie es installieren. Der Paketname ist openai-agents. Installieren Sie es über pip:

pip install openai-agents

(OpenAI Agents SDK)

Dies installiert das Kern-SDK und seine Abhängigkeiten. Standardmäßig verwendet das SDK die OpenAI-API für LLM-Aufrufe, daher sollten Sie einen OpenAI-API-Schlüssel bereit haben. Setzen Sie die Umgebungsvariable OPENAI_API_KEY auf Ihren API-Schlüssel (oder verwenden Sie die programmatische Methode des SDK zur Konfiguration, siehe unten), bevor Sie Agenten ausführen:

export OPENAI_API_KEY="sk-YourOpenAIAPIKey"

Optionale Abhängigkeiten: Wenn Sie LiteLLM (ein Pluginsystem zur Integration anderer Modellanbieter oder Open-Source-Modelle) oder Sprachfunktionen verwenden möchten, gibt es zusätzliche Abhängigkeiten. Zum Beispiel, um LiteLLM-Unterstützung zu aktivieren (die Verwendung von Nicht-OpenAI-Modellen erlaubt), installieren Sie mit dem litellm-Extra:

pip install "openai-agents[litellm]"

Wir werden die Verwendung benutzerdefinierter Modelle über LiteLLM in einem späteren Abschnitt behandeln. Für den Moment, mit installiertem SDK und gesetztem API-Schlüssel, können wir mit der Erstellung von Agenten beginnen.

Erstellen und Konfigurieren eines Agenten

Die Erstellung eines einfachen Agenten ist unkompliziert. Das SDK stellt eine Agent-Klasse bereit, die Sie mit einem Namen und optionalen Parametern wie Anweisungen, Tools, Handoffs und Guardrails instanziieren können. Mindestens benötigt ein Agent einen Namen (für Identifikation und Protokollierung) und kann optional eine Anweisungs-Prompt haben, die sein Verhalten steuert.

Erstellen wir einen einfachen Agenten:

from agents import Agent, Runner

# Erstelle einen einfachen Agenten mit Namen und Rollenanweisungen
agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant."
)

In diesem Beispiel konfigurieren wir den Agenten so, dass er sich als hilfreicher Assistent verhält, indem wir eine kurze Anweisung geben (die wie ein System-Prompt wirkt). Wenn wir diesen Agenten mit einer Benutzeranfrage ausführen, verwendet er ein OpenAI-GPT-Modell im Hintergrund, um eine Antwort gemäß dieser Anweisung zu generieren.

Agent ausführen: Das SDK verwendet Runner, um Agenten auszuführen. Sie können Runner.run_sync(agent, input) für einen schnellen synchronen Aufruf verwenden oder await Runner.run(agent, input) in einem asynchronen Kontext. Zum Beispiel:

result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
print(result.final_output)

Dies fordert den Agenten auf, die Anfrage zu beantworten. Im Hintergrund wird das LLM des Agenten mit den Systemanweisungen und der Benutzerfrage aufgerufen, dann verarbeitet die Agentenschleife die Antwort. Bei unserer einfachen Konfiguration antwortet der Agent direkt. Das result-Objekt (vom Typ RunResult) enthält die endgültige Ausgabe in result.final_output. In diesem Fall könnte es ein Haiku wie folgt ausgeben:

# Code within the code,
# Functions calling themselves,
# Infinite loop's dance.

(Dieses Beispiel ist das „Hello World“ aus der Dokumentation (OpenAI Agents SDK).)

Standardmäßig verwendet Agent() ohne Angabe eines Modells das Chatmodell von OpenAI (gpt-3.5-turbo oder gpt-4 über die Responses API) als LLM. Stellen Sie sicher, dass Ihr OpenAI-API-Schlüssel gesetzt ist, damit der Agent das Modell aufrufen kann. Sie können ändern, welches Modell oder welche API verwendet wird – dazu später mehr.

Agent-Konfigurationsoptionen: Die Agent-Klasse bietet mehrere Parameter zur Anpassung des Verhaltens:

  • name (str): Ein beschreibender Name für den Agenten (z. B. „ResearchAgent“). Dieser Name wird auch verwendet, wenn der Agent als Tool oder in Logs/Traces verwendet wird.

  • instructions (str oder callable): Der Prompt oder Systemnachricht, die die Rolle des Agenten festlegt. Dies kann ein fester String oder eine Funktion sein, die einen String generiert (möglicherweise mit dynamischem Kontext). Die Anweisungen werden oben im Kontext des LLM (als Systemrolle) platziert (Context management - OpenAI Agents SDK). Gute Anweisungen sind entscheidend, um das Verhalten des Agenten zu steuern und ihn über verfügbare Tools oder Einschränkungen zu informieren.

  • tools (Liste): Eine Liste von Tools, die der Agent verwenden kann. Tools können hier als Funktionsreferenzen (für Funktions-Tools) oder Tool-Instanzen (für bestimmte eingebaute Tools) hinzugefügt werden. Wir werden Tools im nächsten Abschnitt ausführlich behandeln. Wenn keine Tools bereitgestellt werden, kann der Agent nur auf das eigene Wissen und die Schlussfolgerungen des LLM zurückgreifen.

  • handoffs (Liste): Eine Liste anderer Agenten oder Handoff-Konfigurationen, an die dieser Agent delegieren kann. Durch Angabe von Handoffs erlauben Sie Ihrem Agenten, die Kontrolle in bestimmten Situationen an einen anderen Agenten zu übergeben. Wir werden Handoffs im Abschnitt Multi-Agent und Handoff näher erläutern.

  • guardrails (Liste): Eine Liste von Guardrail-Funktionen (Input- oder Output-Guardrails), die für diesen Agenten angewendet werden. Guardrails dienen zur Validierung von Eingaben oder Ausgaben – z. B. um zu verhindern, dass der Agent die Mathe-Hausaufgaben eines Benutzers löst oder um sicherzustellen, dass die Ausgabe ein bestimmtes Format hat. Guardrails werden im Abschnitt Guardrails und Validierung behandelt.

  • model: Standardmäßig verwendet jeder Agent das globale Standardmodell (OpenAI GPT über die Responses API). Sie können dies pro Agent überschreiben, indem Sie einen model-Parameter übergeben. Das SDK enthält Modellklassen wie OpenAIChatCompletions und OpenAIResponsesModel, und mit LiteLLM können Sie Modelle von Anthropic usw. verwenden. Zum Beispiel können Sie Agent(model=OpenAIChatCompletionsModel()) verwenden, um die Chat Completions API zu erzwingen, oder Agent(model=LitellmModel(...)), um ein Modell eines anderen Anbieters zu verwenden. Wenn Sie mit dem Standard zufrieden sind, können Sie diesen Parameter weglassen.

  • output_type: (Optional) Ein Pydantic-Modell oder Datentyp, der die erwartete Struktur der endgültigen Ausgabe angibt. Wenn angegeben, versucht das SDK, die endgültige Antwort des Agenten in diesen Typ zu parsen (und das LLM wird angewiesen, seine Antwort entsprechend zu formatieren). Dies ist nützlich, um strukturierte Ausgaben (wie JSON-Antworten mit bestimmten Feldern) durchzusetzen. Es ist eine erweiterte Funktion – für die meisten einfachen Anwendungsfälle ist die endgültige Ausgabe einfach ein String.

  • Weitere Einstellungen: Der Agent kann auch Dinge wie max_turns (um zu begrenzen, wie viele Schleifeniterationen er vor dem Stoppen durchführen kann), oder max_tokens usw. über seine Modelleinstellungen akzeptieren. Viele davon können auch global beim Ausführen des Agenten gesteuert werden (über Runner.run-Optionen). Wir werden diese im Kontext noch ansprechen.

Mit einem definierten Basisagenten gehen wir nun dazu über, ihm mehr Fähigkeiten mit Tools zu geben.

Hinzufügen und Verwenden von Tools

Eine der mächtigsten Funktionen agentischer Systeme ist die Fähigkeit der Agenten, Tools zu verwenden. Tools erweitern, was ein LLM-Agent tun kann – sie erlauben dem Agenten, Aktionen wie das Durchsuchen des Webs, Berechnungen, das Abrufen von Informationen aus Datenbanken oder sogar das Ausführen von Code durchzuführen. Im OpenAI Agents SDK werden Tools als Funktionen (Python-callables) implementiert, die der Agent über eine standardisierte Schnittstelle aufrufen kann (denken Sie an OpenAIs Funktionsaufrufmechanismus).

Es gibt zwei Hauptarten von Tools in diesem SDK:

  1. Gehostete Tools (eingebaut) – Tools, die OpenAI über die API bereitstellt, wie Websuche, Dateisuche oder Computersteuerung. Diese laufen auf OpenAIs Seite in der „Responses API“-Umgebung (Tools - OpenAI Agents SDK).
  2. Funktions-Tools (benutzerdefiniert) – Tools, die Sie in Python implementieren. Das SDK macht es einfach, eine Python-Funktion (oder Coroutine) als Tool bereitzustellen, das ein Agent aufrufen kann (Tools - OpenAI Agents SDK).

Lassen Sie uns jede Art und deren Verwendung erkunden.

Eingebaute gehostete Tools

Die Responses API von OpenAI bietet einige eingebaute Tools, die Agenten verwenden können, ohne dass Sie Code für diese Fähigkeiten schreiben müssen. Derzeit umfassen die eingebauten Tools:

  • Websuche – Ermöglicht einem Agenten, im Web zu suchen und aktuelle Informationen abzurufen (Tools - OpenAI Agents SDK).
  • Dateisuche (Retrieval) – Ermöglicht das Abfragen eines Vektor-Stores von Dokumenten (nützlich für Retrieval-augmented Generation mit eigenen Daten) (Tools - OpenAI Agents SDK).
  • Computer (Bedienung) – Ermöglicht einem Agenten, die Bedienung eines Computers (Maus-, Tastaturaktionen) in einer virtuellen Umgebung zu simulieren (Tools - OpenAI Agents SDK).

Um gehostete Tools zu verwenden, muss das Modell Ihres Agenten das OpenAI Responses API-Modell sein (was standardmäßig der Fall ist). Zum Beispiel, um Websuche und Dateisuche in einem Agenten zu aktivieren:

from agents import Agent, Runner, WebSearchTool, FileSearchTool

# Agent, der Websuche und Dateisuche-Tools verwenden kann
agent = Agent(
    name="ResearchAgent",
    instructions="You are a research assistant that finds information online and in documents.",
    tools=[
        WebSearchTool(),  # aktiviert Websuchen
        FileSearchTool(
            max_num_results=3,
            vector_store_ids=["<YOUR_VECTOR_STORE_ID>"]  # geben Sie Ihre Vektor-Stores an
        )
    ]
)

result = Runner.run_sync(
    agent, 
    "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?"
)
print(result.final_output)

In diesem Code haben wir einen ResearchAgent erstellt, der Zugriff auf zwei Tools hat: eine Websuche und eine Dateisuche. Die Anweisungen des Agenten legen nahe, dass er diese Tools bei Bedarf verwenden soll. Wenn wir ihn mit einer Anfrage ausführen, kann der Agent entscheiden, WebSearchTool aufzurufen, um nach Coffeeshops und Wetter zu suchen, oder FileSearchTool verwenden, falls relevante Daten in einer Vektor-Datenbank gespeichert sind. Die endgültige Ausgabe ist die Antwort des Agenten auf die Frage, vermutlich unter Verwendung der gesammelten Informationen.

Hinweis: Gehostete Tools wie WebSearchTool und FileSearchTool werden von OpenAIs Systemen ausgeführt (das Modell „steckt“ darin), nicht lokal. Sie können zusätzliche Kosten oder Nutzungslimits verursachen (z. B. berechnet OpenAI die Nutzung des File Search Tools separat (New tools for building agents | OpenAI)). Stellen Sie sicher, dass Sie die Dokumentation der OpenAI-Tools konsultieren und über die entsprechenden Zugriffsrechte verfügen, wenn Sie sie verwenden. Wenn Ihr Anwendungsfall diese spezifischen Tools nicht benötigt oder Sie volle lokale Kontrolle bevorzugen, können Sie ähnliche Funktionalität als benutzerdefinierte Funktions-Tools implementieren (z. B. mit der requests-Bibliothek für Websuche oder einer lokalen Vektor-DB-Bibliothek).

Benutzerdefinierte Funktions-Tools

Für die meisten Anwendungsfälle erstellen Sie Ihre eigenen Tools, indem Sie Python-Funktionen schreiben. Das Agents SDK kann automatisch eine Python-Funktion in eine Tool-Schnittstelle umwandeln, die das LLM aufrufen kann. Es verwendet Pythons Introspektion und Pydantic, um ein JSON-Schema für die Argumente der Funktion zu generieren, und nutzt sogar den Docstring der Funktion als Beschreibung des Tools (Tools - OpenAI Agents SDK) (Tools - OpenAI Agents SDK). Dies ähnelt dem Funktionsaufruf von OpenAI – das LLM erhält den Funktionsnamen, die Beschreibung und das Parameterschema, und wenn es entscheidet, die Funktion aufzurufen, erzeugt es eine JSON-Nutzlast, die das SDK parst und dann Ihre Python-Funktion mit den Parametern aufruft.

Um eine Funktion als Tool zu deklarieren, dekorieren Sie sie einfach mit @function_tool (importiert aus agents). Zum Beispiel:

from typing_extensions import TypedDict
from agents import Agent, Runner, function_tool

# Definieren eines TypedDict für strukturierte Eingabe (falls benötigt)
class Location(TypedDict):
    lat: float
    long: float

@function_tool
async def fetch_weather(location: Location) -> str:
    """Hole das Wetter für einen gegebenen Ort.
    
    Args:
        location: Der Ort, für den das Wetter abgefragt wird (mit 'lat' und 'long').
    """
    # In einer echten Implementierung würde eine Wetter-API mit den Koordinaten aufgerufen.
    # Hier geben wir nur eine Dummy-Antwort zurück.
    return "It is currently sunny at the given location."

@function_tool(name_override="fetch_data")
def read_file(path: str, directory: str | None = None) -> str:
    """Lese den Inhalt einer Datei.
    
    Args:
        path: Der Pfad zur Datei, die gelesen werden soll.
        directory: Das Verzeichnis, aus dem die Datei gelesen werden soll (optional).
    """
    # In einer echten Implementierung würde die Datei von der Festplatte oder einem Speicher geöffnet und gelesen.
    return "<file contents>"

Lassen Sie uns aufschlüsseln, was passiert:

  • Wir importieren function_tool und verwenden es als Dekorator für unsere Funktionen fetch_weather und read_file. Dies teilt dem SDK mit, diese Funktionen als Tools zu behandeln.

  • Die Funktion fetch_weather ist async und nimmt ein Location TypedDict mit lat und long als floats, gibt einen String zurück. Wir haben ihr einen Docstring gegeben, der beschreibt, was sie tut. Das SDK erstellt automatisch ein Tool namens "fetch_weather" (standardmäßig entsprechend dem Funktionsnamen) mit einer Beschreibung aus dem Docstring und einem JSON-Schema für ein Argument location (mit lat und long als erforderliche Eigenschaften) (Tools - OpenAI Agents SDK) (Tools - OpenAI Agents SDK). Wenn der Agent dieses Tool aufruft, stellt das SDK den Parameter location als Python-Dict gemäß Schema bereit.

  • Die Funktion read_file ist synchron (kann synchron oder asynchron sein, beides ist in Ordnung) und demonstriert die Verwendung von name_override im Dekorator. Wir überschreiben den Toolnamen zu "fetch_data", um dem LLM einen semantisch passenderen Namen zu präsentieren, statt des Funktionsnamens read_file. Dieses Tool liest eine Datei von einem gegebenen Pfad (mit optionalem Verzeichnis). Sein Docstring wird als Beschreibung verwendet, sofern wir diese Funktion nicht deaktivieren. Das SDK generiert ein Schema für zwei Parameter: path (String) und directory (optional String).

Nach der Definition der Tools integrieren wir sie mit einem Agenten:

agent = Agent(
    name="UtilityAgent",
    instructions="You are a helpful assistant with access to file and weather information.",
    tools=[fetch_weather, read_file]  # wir übergeben die Funktionsobjekte selbst
)

# Agent ausführen, um die Tools zu testen
result = Runner.run_sync(agent, "What is the weather at latitude 37.77, longitude -122.42 and show me the content of file README.md?")
for tool in agent.tools:
    print(f"Tool loaded: {tool.name} - {tool.description}")
print("Final answer:", result.final_output)

Wenn der Agent die Frage erhält, könnte er entscheiden, fetch_weather mit den angegebenen Koordinaten aufzurufen, dann fetch_data (unser read_file) mit "README.md". Das SDK handhabt die Funktionsaufrufe: Es parst die Anfragen des LLM, führt fetch_weather und read_file aus, erhält deren Rückgabewerte und stellt diese dem LLM als Funktionsresultate bereit. Das LLM des Agenten integriert diese Ergebnisse dann in seine endgültige Antwort. Die Ausgabe listet die Tools und deren automatisch generierte Beschreibungen und Schema auf, und die finale Antwort könnte eine kombinierte Antwort aus Wetter und Dateiinhalten sein (abhängig von der Formulierung des Prompts und der Überlegung des Agenten).

Funktionsweise: Das SDK verwendet das Python-Modul inspect und die Bibliothek griffe, um Parameternamen, Typen und Docstrings aus Ihrer Funktion zu extrahieren, um ein Schema zu erstellen (Tools - OpenAI Agents SDK). Der Dekorator @function_tool kann mit Parametern wie name_override (wie gezeigt) oder use_docstring_info=False konfiguriert werden, falls Sie den Docstring nicht automatisch als Beschreibung verwenden möchten. Im Hintergrund erhält das LLM bei einem Agenten mit Funktions-Tools Funktionsdefinitionen (Namen, Beschreibungen, Parameterspezifikationen) und gibt eine spezielle JSON aus, wenn es sich entscheidet, eine Funktion aufzurufen. Das SDK fängt dies ab, ruft Ihre Python-Funktion auf und gibt den Rückgabewert (oder Fehler) an das LLM zurück. Diese Schleife läuft, bis das LLM eine finale Nicht-Funktionsausgabe erzeugt (Running agents - OpenAI Agents SDK). All dies ist abstrahiert, sodass Sie sich einfach auf das Schreiben der Python-Funktion für die Logik des Tools konzentrieren können.

Fehlerbehandlung in Tools: Wenn Ihre Tool-Funktion eine Ausnahme auslöst oder ungültige Daten zurückgibt, wie geht der Agent damit um? Standardmäßig informiert das SDK das LLM, dass ein Fehler im Tool-Aufruf aufgetreten ist (dies geschieht über einen Standard-Error Handler, der eine Fehlermeldung an das LLM zurückgibt) (Tools - OpenAI Agents SDK). Sie können dieses Verhalten anpassen. Der Dekorator @function_tool akzeptiert einen Parameter failure_error_function, mit dem Sie eine benutzerdefinierte Fehlerbehandlungsfunktion angeben können. Beispielsweise möchten Sie bestimmte Ausnahmen behandeln und eine spezifische Fehlermeldung an den Agenten zurückgeben oder sogar entscheiden, sie gar nicht abzufangen. Wenn Sie failure_error_function=None übergeben, wird die rohe Ausnahme weitergereicht (z. B. ein ModelBehaviorError, wenn der Funktionsaufruf des Modells fehlerhaft war, oder UserError, wenn Ihr Tool-Code abgestürzt ist) (Tools - OpenAI Agents SDK). In den meisten Fällen ist das Standardverhalten ausreichend – der Agent sieht einfach ein „Fehler“-Ergebnis und kann sich entschuldigen oder eine andere Strategie versuchen.

Agenten als Tools

Neben regulären Funktions-Tools können Sie auch einen Agent selbst als Tool für einen anderen Agenten behandeln. Dies ist eine Möglichkeit, eine Hierarchie von Agenten zu erstellen: Ein oberster Agent kann spezialisierte Unteragenten wie eine Funktion aufrufen. Der Unterschied zwischen einem Handoff und einem Agent-Tool (Agent-als-Tool) ist, dass beim Handoff der Unteragent die Kontrolle vollständig übernimmt (neuer Gesprächskontext), während beim Agent-Tool der Hauptagent die Kontrolle behält und einfach das Ergebnis des Unteragenten als Funktionsresultat erhält.

Anwendungsfall: Angenommen, Sie möchten einen zentralen Orchestrator-Agenten, der spezialisierte Übersetzeragenten für verschiedene Sprachen aufrufen kann, aber nicht die Kontrolle vollständig übergeben möchte (damit der Hauptagent potenziell mehrere Übersetzer aufrufen und dann Ergebnisse kombinieren kann). Dies können Sie erreichen, indem Sie Agenten als Tools hinzufügen.

Beispiel:

from agents import Agent, Runner

# Definieren Sie zwei spezialisierte Agenten (die auch eigenständig laufen könnten)
spanish_agent = Agent(name="SpanishAgent", instructions="Translate the user's message to Spanish.")
french_agent  = Agent(name="FrenchAgent",  instructions="Translate the user's message to French.")

# Erstellen Sie einen Orchestrator-Agenten, der die obigen Agenten als Tools verwenden kann
orchestrator = Agent(
    name="OrchestratorAgent",
    instructions=(
        "You are a translation orchestrator. You have tools to translate to Spanish or French. "
        "If the user asks for a translation, call the relevant tool. Otherwise, just respond."
    ),
    tools=[
        spanish_agent.as_tool(tool_name="translate_to_spanish", 
                               tool_description="Translate the user's message to Spanish"),
        french_agent.as_tool(tool_name="translate_to_french", 
                              tool_description="Translate the user's message to French")
    ]
)

result = Runner.run_sync(orchestrator, "Please say 'Hello, how are you?' in French and Spanish.")
print(result.final_output)
# Der Orchestrator könnte beide Tools verwenden und etwas wie Folgendes ausgeben:
# "Spanish: Hola, ¿cómo estás?\nFrench: Bonjour, comment ça va ?"

Hier haben wir agent.as_tool() verwendet, um jeden Unteragenten als Tool mit einem gegebenen Namen und einer Beschreibung zu verpacken (Tools - OpenAI Agents SDK). Intern entscheidet das SDK, wenn der Orchestrator-Agent translate_to_french aufruft, den french_agent auszuführen (mit dem Abfragetext als Eingabe) und dessen endgültige Ausgabe als „Funktionsresultat“ an den Orchestrator zurückzugeben. Dies ermöglicht das Verketten von Agenten ohne formalen Handoff.

Anpassen von Agent-Tools: Die Methode as_tool ist eine Komfortfunktion für einfache Fälle. Sie bietet möglicherweise nicht alle Agentenkonfigurationen. Beispielsweise möchten Sie vielleicht, dass der Unteragent mit einem bestimmten max_turns-Limit oder einem anderen Modell für diesen Aufruf läuft. In solchen Fällen können Sie immer eine benutzerdefinierte Tool-Funktion erstellen, die Runner.run auf einem Agenten aufruft. Zum Beispiel:

from agents import function_tool

@function_tool
async def run_my_agent(subquery: str) -> str:
    """Führe einen benutzerdefinierten Agenten mit einem gegebenen Prompt aus."""
    custom_agent = Agent(name="MathAgent", instructions="Solve math problems step by step.")
    result = await Runner.run(custom_agent, subquery, max_turns=5)
    return str(result.final_output)

Dies definiert ein Tool run_my_agent, das, wenn es von einem Agenten aufgerufen wird, intern einen neuen MathAgent startet und ihn mit der bereitgestellten subquery ausführt (Tools - OpenAI Agents SDK) (Tools - OpenAI Agents SDK). Diese Technik gibt Ihnen volle Kontrolle: Sie können Modell, Temperatur, Turns usw. für den Unteragenten-Aufruf einstellen. Der Nachteil ist, dass die Logik nun bei Ihnen liegt (während agent.as_tool dies automatisch mit Standardwerten erledigt).

Zusammenfassend sind Tools – ob eingebaut, benutzerdefiniert oder agentenbasiert – der Weg, wie Sie Ihrem Agenten die Fähigkeit geben, Aktionen auszuführen. Als nächstes sehen wir, wie man mehrere Agenten direkter über Handoffs verwaltet und wie man Kontext und Zustand zwischen diesen Interaktionen pflegt.

Verwaltung von Agentenkontext und -zustand

In jeder nicht-trivialen Anwendung haben Sie eine Vorstellung von Kontext oder Zustand, den der Agent kennen oder zwischen Schritten mitführen sollte. Es gibt zwei Hauptarten von Kontext, die im Agents SDK zu berücksichtigen sind (Context management - OpenAI Agents SDK):

  1. Lokaler Kontext (Umgebungszustand) – Daten und Abhängigkeiten auf der Python-Seite, auf die Ihre Tools oder Callbacks zugreifen müssen.
  2. LLM-Kontext (Gesprächszustand) – Informationen, die das LLM in seinem Prompt sieht (Gesprächshistorie, Systemanweisungen usw.), die das „Gedächtnis“ aus Sicht des Modells darstellen.

Das SDK stellt Mechanismen bereit, um beide Arten von Kontext zu handhaben.

Lokaler Kontext (Python-Umgebungsdaten)

Lokaler Kontext ist jedes Python-Objekt oder Daten, die Sie in den Lauf des Agenten einbringen möchten und die dann innerhalb von Tool-Funktionen oder Lifecycle-Hooks (wie einem on_handoff-Callback) zugänglich sind. Zum Beispiel könnten Sie eine Datenbankverbindung, ein Benutzerprofil oder eine einfache Flagge haben, die Ihre Tools benötigen. Sie wollen dies nicht im LLM-Prompt einbetten, aber Ihr Code soll darauf zugreifen können.

Das Agents SDK adressiert dies durch den RunContextWrapper und Kontextübergabe im Runner.run-Aufruf (Context management - OpenAI Agents SDK):

  • Sie können ein beliebiges Python-Objekt erstellen (kann so einfach wie ein Dict oder ein Dataclass sein), das Ihre Kontextdaten hält.
  • Beim Aufruf von Runner.run (oder run_sync) übergeben Sie dieses Objekt über das Argument context=.
  • Jede Tool-Funktion, die diesen Kontext verwenden muss, kann einen Parameter vom Typ RunContextWrapper[YourType] deklarieren. Das SDK übergibt dann ein Wrapper-Objekt an dieses Tool, über das Sie auf Ihren Kontext zugreifen können (wrapper.context gibt das zugrundeliegende Objekt) (Context management - OpenAI Agents SDK).

Beispiel:

from dataclasses import dataclass
from agents import Agent, RunContextWrapper, Runner, function_tool

@dataclass
class UserInfo:
    name: str
    id: int

# Ein Tool, das den Kontext verwendet, um benutzerspezifische Infos abzurufen
@function_tool
def fetch_user_data(ctx: RunContextWrapper[UserInfo]) -> str:
    user = ctx.context  # das ist das UserInfo-Objekt
    # Verwendung von Daten aus dem Kontext (diese Daten sind NICHT direkt für das LLM sichtbar)
    return f"User {user.name} (id={user.id}) has 42 notifications."

# Erstellen eines Agenten mit diesem Tool
agent = Agent[UserInfo](
    name="PersonalAssistant",
    instructions="You are an assistant that knows the user's info.",
    tools=[fetch_user_data]
)

# Kontextobjekt vorbereiten
user_info = UserInfo(name="Alice", id=123)

# Agent mit Kontext ausführen
result = Runner.run_sync(agent, "How many notifications do I have?", context=user_info)
print(result.final_output)
# Erwartete Ausgabe verwendet das Tool-Ergebnis: "User Alice (id=123) has 42 notifications."

Wichtige Punkte in diesem Beispiel:

  • Wir definieren eine UserInfo-Dataclass, die einige benutzerspezifische Daten hält.
  • Das Tool fetch_user_data hat einen Parameter ctx: RunContextWrapper[UserInfo]. Wenn der Agent dieses Tool aufruft, erhält es einen RunContextWrapper, der unser UserInfo-Objekt umschließt. Wir greifen dann über ctx.context auf den tatsächlichen Kontext zu.
  • Der Agent ist als Agent[UserInfo] definiert – mit einem Python-Generiktyp für den Agenten. Dies hilft bei der statischen Typprüfung (es stellt sicher, dass die Tools und der Kontext des Agenten alle denselben UserInfo-Typ erwarten) (Context management - OpenAI Agents SDK) (Context management - OpenAI Agents SDK). Es ist nicht zwingend erforderlich, den Generiktyp zu verwenden, aber es ist eine nette Funktion, um Fehler zu vermeiden.
  • Wir rufen Runner.run_sync mit context=user_info auf. Dies hängt unser UserInfo-Objekt an den Lauf an. Alle Tool-Aufrufe, Guardrail-Prüfungen und Handoff-Callbacks während dieses Laufs tragen dieses Kontextobjekt über den Wrapper.

Das lokale Kontextobjekt wird niemals an das LLM gesendet (Context management - OpenAI Agents SDK). Es dient ausschließlich dem Python-Code. Das bedeutet, Sie können dort sensible oder große Daten ablegen, ohne Sorge – das LLM sieht sie nicht, es sei denn, Sie geben sie explizit über ein Tool oder Prompt weiter.

Gängige Verwendungen des lokalen Kontexts:

  • Übergabe einer Datenbanksitzung oder eines API-Clients, den Tools verwenden können.
  • Bereitstellung benutzerspezifischer Daten (Präferenzen, Profilinfos), die einige Tools benötigen, um Antworten zu personalisieren.
  • Zwischenspeicherung von Daten zwischen Tool-Aufrufen oder Akkumulierung von Ergebnissen.

Denken Sie daran, dass alle Komponenten in einem einzelnen Agentenlauf denselben Kontexttyp teilen müssen (Context management - OpenAI Agents SDK). Wenn Sie einen Agenten mit einem bestimmten Kontextobjekttyp starten, sollten Sie kein Tool aufrufen, das einen anderen Kontexttyp erwartet, im selben Lauf.

LLM-Kontext (Gespräch und Gedächtnis)

Nun sprechen wir über Kontext aus Sicht des LLM – im Wesentlichen die Gesprächshistorie und den Prompt, den das Modell sieht. LLMs haben kein persistentes Gedächtnis zwischen Läufen (es sei denn, Sie liefern es mit), und zu jedem Zeitpunkt kennt das Modell nur, was im Prompt steht (Systemnachricht, bisherige Gesprächsbeiträge usw.) (Context management - OpenAI Agents SDK). Das Agents SDK verwaltet den Kontext automatisch für mehrstufige Agentenschleifen und Multi-Agenten-Handoffs, aber Sie als Entwickler sollten wissen, wie Sie dem Modell bei Bedarf zusätzliche Informationen bereitstellen.

Möglichkeiten, dem LLM Kontext zu geben:

  • Systemanweisungen: Wir haben bereits gesehen, dass Sie Informationen in agent.instructions einfügen können. Dies ist vergleichbar mit einem System-Prompt, der konstant bleibt (oder pro Lauf dynamisch generiert werden kann). Verwenden Sie dies für globalen Kontext, den der Agent immer haben soll. Zum Beispiel, wenn ein Agent immer den Namen des Benutzers oder das aktuelle Datum kennen soll, könnten Sie das in seine Anweisungen einbauen (vielleicht durch Templating des Anweisungsstrings mit diesen Werten zur Laufzeit) (Context management - OpenAI Agents SDK).

  • Benutzereingabe / Input: Wenn Sie Runner.run aufrufen, übergeben Sie eine input – oft ist dies die Benutzeranfrage. Diese wird zur letzten Benutzer-Nachricht im Prompt. Wenn Sie dynamischen Kontext haben, der nur für diese Anfrage relevant ist, könnten Sie ihn voranstellen oder in die Eingabe einfügen. Das SDK erlaubt, dass input ein roher String oder eine Liste von Nachrichten-Dictionaries (jeweils mit Rolle und Inhalt) sein kann (Running agents - OpenAI Agents SDK). In fortgeschrittenem Gebrauch könnten Sie result.to_input_list() von einem vorherigen Lauf verwenden (das Ihnen eine Liste vorheriger Nachrichten gibt) und dann eine neue Benutzer-Nachricht hinzufügen (wie später gezeigt) (Running agents - OpenAI Agents SDK). Kurz gesagt, was immer Sie als input übergeben, wird vom Agenten als neueste Benutzeranfrage (oder vollständiges Gespräch, wenn Sie eine Liste übergeben) gesehen.

  • Tools für Abruf: Anstatt alle möglichen relevanten Informationen in den Prompt zu stopfen (was Token-Limits erreichen könnte), ist ein gängiges Muster, Tools bereitzustellen, die Informationen bei Bedarf abrufen. Zum Beispiel, wenn das LLM einige Daten benötigt, können Sie diese über ein Tool bereitstellen (wie ein fetch_user_profile-Tool oder ein search_documents-Tool). Das LLM ruft das Tool auf, wenn es entscheidet, dass es diese Info braucht (Context management - OpenAI Agents SDK). So wird der Kontext just-in-time abgerufen. Zum Beispiel erlaubt die Verwendung von FileSearchTool (Vektor-DB) oder eines benutzerdefinierten query_database-Tools dem Agenten, ergänzendes Wissen über seinen Basis-Prompt hinaus zu erhalten.

  • Handoff mit Eingabedaten: Wenn Sie an einen anderen Agenten delegieren, möchten Sie vielleicht einige Informationen weitergeben. Die handoff()-Funktion des SDK unterstützt einen input_type, um Daten für den nächsten Agenten zu strukturieren (Handoffs - OpenAI Agents SDK) (Handoffs - OpenAI Agents SDK). Zum Beispiel könnte ein Triage-Agent mit einer Nachricht wie „escalation reason: X“ an den nächsten Agenten übergeben, indem er ein Modell (wie ein Pydantic BaseModel) als input_type angibt. Das LLM liefert Werte für dieses Modell beim Start des Handoffs. Wir werden mehr im nächsten Abschnitt sehen.

Im Allgemeinen wird die Gesprächshistorie (Speicher) innerhalb eines einzelnen Aufrufs von Runner.run automatisch verfolgt. Wenn der Agent mehrere Schleifen durchläuft (aufgrund von Tools oder Handoffs), sind die Zwischenschritte Teil des Gesprächskontexts, der an nachfolgende LLM-Aufrufe übergeben wird. Zum Beispiel, wenn ein Agent ein Tool aufruft und ein Ergebnis erhält, enthält der nächste LLM-Aufruf das Tool-Ergebnis im Prompt (wahrscheinlich als Assistenten-Nachricht, die das Funktionsresultat angibt). Das SDK handhabt dies über das Model Context Protocol (MCP), das sicherstellt, dass das Modell eine Gedankenfolge sieht.

Zwischen separaten Läufen (separate Aufrufe von Runner.run) merkt sich das SDK jedoch nichts automatisch. Diese Zustandslosigkeit ist beabsichtigt – es liegt an Ihnen, relevanten Kontext mitzunehmen, wenn Sie ein mehrstufiges Gespräch mit einem Benutzer führen wollen. Das SDK hilft über die Methode RunResult.to_input_list(), die das Gespräch aus diesem Lauf in einem Format bereitstellt, das als Eingabe für den nächsten Lauf verwendet werden kann (Running agents - OpenAI Agents SDK).

Beispiel für ein mehrstufiges Gespräch:

from agents import Agent, Runner, trace

agent = Agent(name="Assistant", instructions="Reply very concisely.")

# Angenommen, thread_id ist ein Bezeichner für die Gesprächssitzung
thread_id = "conv-12345" 

# Erste Benutzerfrage
with trace(workflow_name="Conversation", group_id=thread_id):
    result1 = Runner.run_sync(agent, "What city is the Golden Gate Bridge in?")
print("Agent:", result1.final_output)
# Agent: San Francisco

# Benutzer-Folgefrage, Kontext aus vorherigem Schritt mitnehmen
prev_conv = result1.to_input_list()  # Nachrichten aus Runde 1 holen
new_input = prev_conv + [{"role": "user", "content": "What state is it in?"}]
with trace(workflow_name="Conversation", group_id=thread_id):
    result2 = Runner.run_sync(agent, new_input)
print("Agent:", result2.final_output)
# Agent: California

In diesem Gespräch haben wir denselben Agenten verwendet (der eine kurze Anweisung hat, sehr knapp zu antworten). Nach der ersten Frage antwortete der Agent mit „San Francisco.“ Wir nahmen dann das gesamte Gespräch (das die Systemnachricht mit Anweisungen, die erste Benutzerfrage und die Antwort des Assistenten enthält) über result1.to_input_list() und fügten eine neue Benutzer-Nachricht mit der Folgefrage hinzu. Indem wir diese Liste als Eingabe an Runner.run_sync übergeben, ist der Agent nun über den vorherigen Kontext informiert und kann mit „California“ antworten.

Wir haben auch trace(workflow_name="Conversation", group_id=thread_id) um jeden Lauf verwendet – dies stellt sicher, dass die Traces beider Runden unter derselben Gesprächs-ID gruppiert werden, was im OpenAI Trace Viewer nützlich ist (so können Sie den gesamten Gesprächsverlauf sehen) (Running agents - OpenAI Agents SDK) (Running agents - OpenAI Agents SDK). Der trace-Context-Manager ist optional, wird aber empfohlen, wenn Sie mehrere Läufe verknüpfen möchten.

So verwalten Sie das Gedächtnis über mehrere Runden hinweg explizit als Entwickler (was das SDK durch einfache Serialisierung und Weitergabe erleichtert). Dieser explizite Ansatz vermeidet, dass das SDK Annahmen darüber trifft, welche Daten mitgenommen werden sollen, und gibt Ihnen volle Kontrolle.

Zusammenfassung der Kontextverwaltung

  • Verwenden Sie lokalen Kontext (den Parameter context= und RunContextWrapper), um Python-seitigen Zustand und Abhängigkeiten zu halten, auf die Tools und Hooks zugreifen können. Dies ist Ihr App-Zustand oder Umgebung, die dem Code des Agenten zur Verfügung steht.

  • Verwenden Sie LLM-Kontext (Anweisungen, Eingabenachrichten, Tool-Ausgaben und Gesprächshistorie), um dem LLM Informationen bereitzustellen. Die eigene Schleife des Agenten schließt vorherige Tool-Aktionen und Handoffs automatisch für diese Anfrage ein, aber für mehrstufige Dialoge geben Sie die vorherige Interaktionshistorie in den nächsten Aufruf ein.

  • Wenn Sie Langzeitgedächtnis oder Wissensbasis benötigen, erwägen Sie die Verwendung von Retrieval-Tools oder externem Speicher (z. B. Speicherung von Zusammenfassungen des bisherigen Gesprächs oder Verwendung von Vektorsuche auf Dialoghistorie). Die Primitiven des SDK (Tools & Kontext) können verwendet werden, um solche Speichersysteme zu implementieren.

Mit Kontext und Gedächtnis im Hinterkopf kehren wir zurück zur Orchestrierung mehrerer Agenten und dem Konzept der Handoffs, die wir erwähnt, aber noch nicht demonstriert haben.

Multi-Agenten-Orchestrierung und Handoffs

Komplexe Aufgaben profitieren oft davon, Verantwortlichkeiten auf mehrere spezialisierte Agenten aufzuteilen. Zum Beispiel könnten Sie einen Agenten haben, der auf Planung spezialisiert ist, einen anderen auf mathematisches Denken, einen weiteren auf das Schreiben von Texten. Das OpenAI Agents SDK unterstützt Multi-Agenten-Workflows hauptsächlich über Handoffs und sekundär über Agent-Tools und externe Orchestrierung (Programmierung der Logik in Python). Wir haben bereits Agent-Tools angesprochen; hier konzentrieren wir uns auf Handoffs und allgemeine Orchestrierungsmuster.

Handoffs: Delegieren an einen anderen Agenten

Ein Handoff bedeutet, dass ein Agent einen anderen Agenten als nächsten Schritt im Gespräch aufrufen kann und damit effektiv die Kontrolle übergibt. Wenn Agent A an Agent B übergibt, übernimmt Agent B den Dialog (er sieht standardmäßig die Gesprächshistorie bis zu diesem Punkt) und erzeugt die finale Antwort oder gibt ggf. weiter. Dies unterscheidet sich vom Agent-Tool, bei dem A die Kontrolle behält; beim Handoff wird B der aktive Agent.

Um Handoffs zu verwenden, können Sie den Parameter handoffs eines Agenten mit einem oder mehreren Zielagenten (oder Handoff-Objekten) konfigurieren. Jeder Handoff-Zielagent erscheint für den ursprünglichen Agenten als spezielles „Tool“ mit einem Namen wie "transfer_to_<AgentName>" standardmäßig (Handoffs - OpenAI Agents SDK). Wenn das LLM entscheidet, dass die Anfrage von einem anderen Agenten bearbeitet werden soll, kann es eine Handoff-Aktion ausgeben, und das SDK wechselt den Kontext zu diesem Agenten.

Grundlegende Verwendung:

from agents import Agent

# Definieren Sie einige spezialisierte Agenten
billing_agent = Agent(name="BillingAgent", instructions="Handle billing related queries.")
refund_agent  = Agent(name="RefundAgent",  instructions="Handle refund related queries.")

# Ein Triage-Agent, der an Abrechnung oder Rückerstattung übergeben kann
triage_agent = Agent(
    name="TriageAgent",
    instructions="Figure out if the user's request is about billing or refund, then hand off appropriately.",
    handoffs=[billing_agent, refund_agent]  # wir können Agenten direkt listen
)

Indem wir billing_agent und refund_agent in triage_agent’s handoffs listen, erlauben wir ihm, an diese zu delegieren. Im Hintergrund erstellt das SDK Handoff-„Tools“ für jeden, benannt "transfer_to_billing_agent" und "transfer_to_refund_agent" (es wandelt den Namen in Kleinbuchstaben und Unterstriche um) (Handoffs - OpenAI Agents SDK). Die Anweisungen des triage_agent sollten ihn anleiten, wann er diese verwendet. Zum Beispiel könnten Sie in den Anweisungen sagen: „Wenn die Frage Abrechnungsprobleme betrifft, übergeben Sie an BillingAgent. Wenn es um Rückerstattungen geht, übergeben Sie an RefundAgent.“

Wenn Sie triage_agent ausführen und der Benutzer fragt „I want a refund for my last order“, wird der Agent (mit gutem Prompt) entscheiden, dass es sich um ein Rückerstattungsproblem handelt, und mit einer Handoff-Aktion an RefundAgent antworten. Das SDK initialisiert dann refund_agent mit dem Gespräch (standardmäßig wird die gesamte bisherige Unterhaltung an den RefundAgent weitergegeben, dies kann aber gefiltert werden; siehe unten) und setzt die Schleife mit refund_agent fort, der nun die Antwort generiert.

Handoffs anpassen: Das SDK stellt eine handoff()-Funktion bereit, um ein Handoff-Objekt mit mehr Optionen zu erstellen (Handoffs - OpenAI Agents SDK). Die Verwendung von handoff() anstelle der direkten Auflistung eines Agenten erlaubt Ihnen:

  • Den Toolnamen oder die Beschreibung, die dem LLM präsentiert wird, zu überschreiben.
  • Einen on_handoff-Callback (eine Python-Funktion) zu setzen, die genau beim Handoff ausgeführt wird (nützlich für Logging oder Vorbereitung von Daten).
  • Einen input_type für strukturierte Eingaben an den nächsten Agenten zu definieren (damit das LLM beim Handoff Daten an den nächsten Agenten übergeben kann).
  • Einen input_filter bereitzustellen, um zu steuern, welchen Teil der Gesprächshistorie der neue Agent sieht.

Zum Beispiel:

from agents import handoff, RunContextWrapper

def on_handoff_callback(ctx: RunContextWrapper[None]):
    # Dies wird ausgeführt, wenn der Handoff ausgelöst wird
    print("Handoff initiated!")

# Erstellen eines Handoffs mit benutzerdefinierten Einstellungen
refund_handoff = handoff(
    agent=refund_agent,
    on_handoff=on_handoff_callback,
    tool_name_override="escalate_to_refund",
    tool_description_override="Escalate the conversation to the refund department"
)

triage_agent = Agent(
    name="TriageAgent",
    handoffs=[billing_agent, refund_handoff]  # ein Handoff ist ein einfacher Agent, einer ist angepasst
)

Jetzt hat triage_agent zwei Handoff-Optionen: eine zu Billing (Standardbenennung) und eine zu Refund (mit benutzerdefiniertem Namen "escalate_to_refund" und Callback) (Handoffs - OpenAI Agents SDK) (Handoffs - OpenAI Agents SDK). Der on_handoff-Callback kann auch ein zweites Argument für Eingabedaten akzeptieren, wenn input_type verwendet wird.

Daten mit Handoffs übergeben: Manchmal möchten Sie, dass der übergebende Agent dem nächsten Agenten einige Daten oder einen Grund mitgibt. Zum Beispiel könnte eine Eskalation einen Grundcode enthalten. Dies erreichen Sie, indem Sie beim Erstellen des Handoffs input_type angeben (Handoffs - OpenAI Agents SDK). Der input_type sollte ein Pydantic-Modell oder Dataclass sein, das die Daten beschreibt. Dann ermutigen Sie das LLM im Prompt des ersten Agenten, diese Felder beim Handoff auszufüllen.

Beispiel:

from pydantic import BaseModel

class EscalationInfo(BaseModel):
    reason: str

async def on_handoff_with_data(ctx: RunContextWrapper[None], data: EscalationInfo):
    print(f"Escalating because: {data.reason}")

escalation_agent = Agent(name="EscalationAgent", instructions="Handle escalations.")

escalation_handoff = handoff(
    agent=escalation_agent,
    input_type=EscalationInfo,
    on_handoff=on_handoff_with_data
)

# Angenommen, support_agent kann mit einem Grund übergeben
support_agent = Agent(
    name="SupportAgent",
    instructions="If user is very unhappy, escalate. Ask them for reason then escalate.",
    handoffs=[escalation_handoff]
)

Wenn support_agent nun übergibt, kann er dies mit einer begleitenden JSON-Nutzlast wie {"reason": "User asked for a supervisor"} tun. Das SDK parst dies in ein EscalationInfo-Objekt und übergibt es an EscalationAgent (wahrscheinlich im Gespräch oder als Teil der Systemnachricht enthalten) und auch an unseren Callback on_handoff_with_data (Handoffs - OpenAI Agents SDK) (Handoffs - OpenAI Agents SDK), der es hier nur ausgibt.

Gespräch beim Handoff filtern: Standardmäßig sieht Agent B beim Handoff von Agent A die gesamte Gesprächshistorie, die A hatte (einschließlich Nachrichten aus früheren Schritten desselben Laufs). In manchen Fällen möchten Sie das nicht. Vielleicht soll Agent B nur die letzte Benutzeranfrage sehen und nicht die Überlegungen von Agent A. Das SDK erlaubt, eine input_filter-Funktion im Handoff zu definieren, um die Nachrichten, die an B übergeben werden, zu kürzen oder zu verändern (Handoffs - OpenAI Agents SDK). Zum Beispiel könnten Sie Systemnachrichten oder vorherige Assistenten-Nachrichten herausfiltern oder die Länge der Benutzernachricht reduzieren. Die Implementierung eines input_filter gibt Ihnen feine Kontrolle darüber, welcher Kontext geteilt oder beim Handoff zurückgesetzt wird.

Zusammenfassend sind Handoffs ein mächtiger Weg, um Aufgaben dynamisch an spezialisierte Agenten zu routen. Ein gängiges Muster ist ein Triage-Agent, der rein entscheidet, welcher spezialisierte Agent eine Anfrage bearbeiten soll (OpenAI Agents SDK — Getting Started). Der spezialisierte Agent übernimmt dann die eigentliche Arbeit (möglicherweise mit eigenen Tools usw.). Dies ist vergleichbar mit einem Dispatcher in einem Kundendienstsystem.

Orchestrierung via Code vs. via LLM

Das Agents SDK unterstützt sowohl LLM-gesteuerte Orchestrierung (den Agenten entscheiden lassen, wann Tools oder Handoffs verwendet werden) als auch entwicklergesteuerte Orchestrierung (Python-Code schreiben, um Agenten zu koordinieren).

  • Orchestrierung via LLM (Autonome Planung): Hierbei verlassen Sie sich auf einen einzelnen Agenten (oder eine Hierarchie von Agenten via Handoffs), um herauszufinden, wie eine offene Aufgabe gelöst wird (Orchestrating multiple agents - OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK). Zum Beispiel geben Sie einem Agenten ein breites Ziel und Tools, und er iteriert, ruft Tools auf, delegiert usw., bis er fertig ist. Der Vorteil ist, dass Sie die volle Schlussfolgerungsfähigkeit des LLM nutzen, um die Abfolge der Aktionen zu bestimmen. Die Hauptsachen, die Sie sicherstellen müssen, sind gute Anweisungen (Prompt-Engineering, um dem Agenten zu sagen, wie und wann er Tools verwenden soll) (Orchestrating multiple agents - OpenAI Agents SDK) und robustes Monitoring (da das LLM unerwartete Wege gehen kann). Das eingebaute Tracing ist sehr hilfreich, um zu sehen, was der Agent tut und seine Entscheidungsfindung zu debuggen. Eine bewährte Praxis ist, Agenten spezialisiert und Tools klar definiert zu halten, damit das LLM weiß, wann es was verwenden soll (Orchestrating multiple agents - OpenAI Agents SDK). Sie können auch Selbstreflexionsschleifen einbauen (ein Agent, der seine eigene Ausgabe oder Vorgehensweise kritisiert) als Teil des Designs (Orchestrating multiple agents - OpenAI Agents SDK).

  • Orchestrierung via Code (Deterministische Abläufe): In manchen Szenarien möchten Sie nicht, dass das LLM den gesamten Workflow entscheidet (vielleicht aus Effizienz- oder Zuverlässigkeitsgründen). Stattdessen schreiben Sie Python-Code, um mehrere Agentenaufrufe in einer Sequenz oder parallel zu koordinieren (Orchestrating multiple agents - OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK). Zum Beispiel könnten Sie zuerst einen Agenten aufrufen, um eine Aufgabe zu klassifizieren, und dann basierend auf dem Ergebnis einen von mehreren anderen Agenten aufrufen (das ist wie die Triage im Code statt via LLM-Handoff). Oder Sie führen zwei Agenten parallel für verschiedene Unteraufgaben aus und kombinieren dann deren Ergebnisse. Das SDK hat dafür keine hochrangige Abstraktion (da Sie im Grunde die einzelne Agentenschleife umgehen), aber Sie können normalen Python-Kontrollfluss verwenden und Runner.run auf verschiedenen Agenten nach Bedarf aufrufen. Da das SDK „Python-first“ konzipiert ist (OpenAI Agents SDK), wird die Verwendung von Python-Logik empfohlen, statt für jedes Szenario neue Abstraktionen zu erfinden.

Beispiel für Orchestrierung via Code:

# Pseudocode für eine manuelle Orchestrierung:
query = "Write a blog post about AI agents."

# Schritt 1: Verwenden Sie einen Agenten, um eine Gliederung zu erstellen
outline_agent = Agent(name="OutlineAgent", instructions="Create a brief outline for the blog post.")
outline_result = Runner.run_sync(outline_agent, query)

# Schritt 2: Füttern Sie die Gliederung an einen Schreibagenten
writing_agent = Agent(name="WriterAgent", instructions="Write a detailed article following the given outline.")
full_draft_result = Runner.run_sync(writing_agent, outline_result.final_output)

# Schritt 3: Verwenden Sie einen Bewertungsagenten, um den Entwurf zu kritisieren
critique_agent = Agent(name="CritiqueAgent", instructions="Critique the draft and point out improvements.")
critique = Runner.run_sync(critique_agent, full_draft_result.final_output)

# Schritt 4: Wenn die Kritik Änderungen vorschlägt, zurück für Überarbeitung oder Schleife
print("Draft:\n", full_draft_result.final_output)
print("Critique:\n", critique.final_output)

In diesem erfundenen Ablauf haben wir die Aufgabe manuell in Gliederung -> Schreiben -> Kritik unterteilt. Wir könnten sogar Schreiben und Kritik so lange wiederholen, bis die Kritik zufrieden ist (das wäre eine einfache while-Schleife um diese Aufrufe) (Orchestrating multiple agents - OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK). Dies stellt deterministisch Struktur sicher und spart möglicherweise Tokens, indem man nicht einen Agenten die gesamte Prozesslogik durchdenken lässt.

Wahl des Ansatzes: Viele Anwendungen mischen beide Ansätze. Sie könnten Code verwenden, um Entscheidungen auf hoher Ebene zu treffen (z. B. welchen Agenten aufzurufen), und dann diesen Agenten autonom Tools verwenden lassen. Oder umgekehrt: Einen Agenten entscheiden lassen und dann Code verwenden, um eine Reihe von Operationen auszuführen usw. Das SDK ist flexibel – seine Primitiven (Agent, Runner.run usw.) können überall verwendet werden. Der Hauptunterschied ist, ob Sie wollen, dass das LLM die Aktionssequenz steuert (schneller zu entwickeln, aber unvorhersehbar) oder eine feste Sequenz bevorzugen (kontrollierter, aber möglicherweise weniger flexibel oder flüssig).

Wir haben nun Agenten, Tools, Kontext und Multi-Agenten-Orchestrierung behandelt. Als nächstes besprechen wir Guardrails – eine kritische Funktion für Produktionssysteme, um sicherzustellen, dass Eingaben und Ausgaben bestimmte Kriterien erfüllen – und dann Tracing und Debugging-Unterstützung. Danach fassen wir alles mit einem Beispielprojekt und Best Practices zusammen.

Guardrails und Ausgabevalidierung

In Agentensystemen, insbesondere solchen, die Endbenutzern bereitgestellt werden, ist es wichtig, Prüfungen für Ein- und Ausgaben Ihrer Agenten zu haben. Das Agents SDK bietet Guardrails als eingebauten Weg, diese Prüfungen umzusetzen. Es gibt zwei Arten von Guardrails, die Sie definieren können:

Guardrails können für Sicherheit verwendet werden (z. B. Prüfung auf Jailbreak-Versuche oder bösartigen Inhalt), für Richtlinienerfüllung (kein Offenlegen sensibler Infos) oder für Qualitätskontrolle (Sicherstellen, dass die Antwort ein erforderliches Format hat).

Ein Guardrail ist im Wesentlichen eine Python-Funktion, die eine Eingabe (und ggf. den Agenten oder Kontext) entgegennimmt und ein GuardrailFunctionOutput zurückgibt, das das Ergebnis der Prüfung angibt (Guardrails - OpenAI Agents SDK). Das SDK stellt Dekoratoren @input_guardrail und @output_guardrail bereit, um das Erstellen zu vereinfachen.

Wie Guardrails in der Schleife funktionieren: Wenn Sie Guardrails an einen Agenten anhängen, führt das SDK die Input-Guardrails direkt nach Erhalt der Benutzereingabe aus und die Output-Guardrails direkt nach der finalen Antwort des Agenten. Wenn ein Guardrail einen Fehler anzeigt (über ein tripwire), stoppt das SDK die Ausführung und löst eine Ausnahme aus (InputGuardrailTripwireTriggered oder OutputGuardrailTripwireTriggered) (Guardrails - OpenAI Agents SDK) (Guardrails - OpenAI Agents SDK). Sie können diese Ausnahmen um Runner.run herum abfangen, wenn Sie sie elegant behandeln wollen (z. B. dem Benutzer sagen „Entschuldigung, ungültige Anfrage“).

Lassen Sie uns ein einfaches Guardrail erstellen. Angenommen, wir haben einen Mathe-lösenden Agenten, wollen aber sicherstellen, dass er nicht für die Hausaufgaben eines Benutzers missbraucht wird. Wir könnten ein Guardrail definieren: Wenn der Benutzer explizit um Hilfe bei „Hausaufgaben“ bittet, lehnen wir ab.

from agents import Agent, Runner, input_guardrail, GuardrailFunctionOutput

@input_guardrail
def math_homework_check(input: str) -> GuardrailFunctionOutput:
    """Guardrail: Prüfe, ob der Benutzer um Mathe-Hausaufgabenhilfe bittet."""
    if "homework" in input.lower():
        # Tripwire auslösen: Benutzer bittet möglicherweise um Hausaufgabenhilfe
        return GuardrailFunctionOutput(success=False, tripwire_triggered=True, message="Homework request detected.")
    # Sonst OK
    return GuardrailFunctionOutput(success=True)

# Erstellen eines Agenten mit diesem Guardrail
math_agent = Agent(
    name="MathAgent",
    instructions="You solve math problems step by step.",
    guardrails=[math_homework_check]  # unser Guardrail
)

# Agent mit einer hausaufgabenähnlichen Anfrage ausführen
try:
    Runner.run_sync(math_agent, "Can you do my math homework on calculus?")
except Exception as e:
    print("Guardrail exception caught:", e)

In diesem Code:

  • Wir verwenden @input_guardrail, um math_homework_check als Input-Guardrail-Funktion zu markieren. Sie nimmt den Eingabestring (Guardrail-Funktionen können auch agent oder RunContextWrapper wie Tools akzeptieren) und gibt GuardrailFunctionOutput zurück.
  • Wir prüfen, ob „homework“ enthalten ist, und wenn ja, geben wir ein Ergebnis mit tripwire_triggered=True zurück. Dies signalisiert dem SDK, zu stoppen.
  • Wenn die Eingabe in Ordnung ist, geben wir success=True zurück (implizit ist tripwire_triggered=False standardmäßig).
  • Wir hängen diesen Guardrail über den Parameter guardrails an den Agenten an. (Im Hintergrund weiß das SDK, dass mit input_guardrail dekorierte Funktionen als Eingabeprüfungen behandelt werden).
  • Beim Ausführen wird, wenn der Guardrail auslöst, eine InputGuardrailTripwireTriggered-Ausnahme ausgelöst (Guardrails - OpenAI Agents SDK). Wir fangen sie ab und geben eine Meldung aus.

Ebenso könnten wir einen @output_guardrail implementieren, um die Ausgabe von math_agent auf bestimmte Inhalte zu prüfen. Zum Beispiel, um sicherzustellen, dass die Lösung nicht nur das Endergebnis ohne Zwischenschritte liefert (wenn wir schrittweise Lösungen erwarten). Wenn ein Output-Guardrail ein Tripwire auslöst, wird eine OutputGuardrailTripwireTriggered-Ausnahme ausgelöst (Guardrails - OpenAI Agents SDK).

Guardrail-Funktionen haben die Freiheit, beliebige Logik zu implementieren. In fortgeschrittenen Fällen könnten Sie sogar einen anderen Agenten oder einen externen Dienst innerhalb der Guardrail-Funktion aufrufen. Tatsächlich zeigt die SDK-Dokumentation ein Beispiel, in dem ein Guardrail-Agent innerhalb der Guardrail-Funktion für komplexe Prüfungen ausgeführt wird (z. B. ein LLM, das analysiert, ob der Inhalt gegen eine Regel verstößt) (Guardrails - OpenAI Agents SDK) (Guardrails - OpenAI Agents SDK). Dieses Beispiel umschließt einen Agenten, der prüft, ob der Benutzer das KI-System bittet, seine Mathe-Hausaufgaben zu machen, und gibt ein strukturiertes Ergebnis mit Ja/Nein zurück (Guardrails - OpenAI Agents SDK) (Guardrails - OpenAI Agents SDK).

Guardrails in der Praxis verwenden: Sie fügen Guardrails einem Agenten beim Erstellen hinzu (oder ggf. global über run_config, aber typischerweise am Agenten). Die Guardrail-Funktionen können Ausnahmen auslösen, wie gezeigt, die Sie behandeln sollten. Für Input-Guardrails könnten Sie es so handhaben, dass Sie den Agenten gar nicht erst aufrufen, wenn sie fehlschlagen, und stattdessen dem Benutzer antworten (z. B. „Entschuldigung, ich kann bei dieser Anfrage nicht helfen.“). Für Output-Guardrails, da der Agent bereits fertig ist, wenn sie auslösen, könnten Sie dies protokollieren und die Antwort modifizieren oder zurückhalten.

Das Guardrail-System ist flexibel, erfordert aber sorgfältige Einrichtung und Tests. Die Dokumentation weist darauf hin, dass es „aufwendig einzurichten, aber sehr mächtig ist, wenn es gut gemacht ist“ (OpenAI Agents SDK — Getting Started). Testen Sie Szenarien, um Fehlalarme oder Fehlversagen zu vermeiden.

Tripwires vs. normale Ergebnisse: Eine Guardrail-Funktion gibt ein GuardrailFunctionOutput zurück, das Felder wie success (bool), optional message und errors enthält. Das Tripwire ist ein spezielles Flag, um eine blockierende Bedingung anzuzeigen. Sie könnten Guardrails haben, die keine Tripwires verwenden, sondern z. B. die Ausgabe modifizieren (z. B. ein Output-Guardrail könnte den Text nachbearbeiten, um JSON-Format zu korrigieren, und Erfolg ohne Halt zurückgeben). In solchen Fällen setzen Sie success=True und liefern ggf. modifizierten Inhalt. Meistens werden Guardrails aber verwendet, um Verstöße zu erkennen und die Ausführung zu stoppen.

Nun, da wir Guardrails behandelt haben, besprechen wir, wie man diese Agentensysteme effektiv mit den Tracing- und Logging-Funktionen des SDK debuggt und überwacht.

Tracing und Debugging

Der Aufbau von Agentenanwendungen erfordert viel Prompt-Design, Trial-and-Error und Beobachtung des Agentenverhaltens. Das OpenAI Agents SDK bringt Tracing mit, das den Ablauf der Ausführung eines Agenten (LLM-Aufrufe, Tool-Aufrufe, Handoffs usw.) detailliert aufzeichnen kann. Diese Trace-Daten können in der Entwicklerplattform von OpenAI angezeigt werden, wenn Sie sie aktiviert haben, oder Sie können sie selbst verarbeiten.

Standardmäßig ist Tracing aktiviert, solange Sie Ihren OpenAI-API-Schlüssel gesetzt haben (Configuring the SDK - OpenAI Agents SDK). Die Trace-Daten werden an das Tracing-System von OpenAI gesendet. Jeder Lauf (über Runner.run oder run_sync) erzeugt einen Trace mit verschiedenen Spans (Segmenten) für Aktionen wie das Nachdenken des LLM oder das Aufrufen eines Tools.

Verwendung von Traces:

  • Die einfachste Verwendung von Tracing ist, es einfach standardmäßig laufen zu lassen. Wenn Sie Ihren Agenten ausführen, protokolliert er im Hintergrund Ereignisse. Wenn Sie zum OpenAI-Dashboard gehen (sofern für Agents/Tracing verfügbar), sehen Sie eine Zeitleiste des Laufs, inklusive aller Zwischenschritte. Das ist extrem nützlich, um zu verstehen, warum ein Agent etwas getan hat.
  • Um Traces zu gruppieren oder ihnen Namen zu geben, verwenden Sie den trace()-Context-Manager, wie zuvor gezeigt. Zum Beispiel with trace(workflow_name="OrderSupport", group_id=session_id): ... um einen Block von Läufen zu umschließen, beschriftet diese Traces (Running agents - OpenAI Agents SDK). workflow_name ist wie eine Kategorie, und group_id kann mehrere Läufe zusammenfassen (z. B. einen Gesprächsfaden). Standardmäßig wird jeder Lauf vielleicht nur mit Agentennamen und Zeitstempel beschriftet; ein Workflow-Name hilft, wenn Sie viele verschiedene Arten von Läufen haben.
  • Sie können trace_metadata (ein Dict) in run_config angeben, wenn Sie benutzerdefinierte Infos an den Trace anhängen wollen (z. B. Benutzer-IDs oder anderen Kontext, achten Sie darauf, keine sensiblen Daten einzuschließen, außer Sie haben es aktiviert).

Tracing deaktivieren oder filtern:

  • Wenn Sie in der Entwicklung sind und die Tracing-API nicht belasten wollen, können Sie Tracing global mit set_tracing_disabled(True) deaktivieren (Configuring the SDK - OpenAI Agents SDK). Das ist nützlich für automatisierte Tests oder wenn Sie temporär kein Tracing brauchen.
  • Standardmäßig enthalten Traces keine sensiblen Daten wie den genauen Inhalt von Ein- und Ausgaben, um versehentliches Protokollieren von PII zu vermeiden. Wenn Sie für Debugging vollständige Daten in Traces benötigen, können Sie trace_include_sensitive_data=True in run_config setzen oder das SDK entsprechend konfigurieren (Running agents - OpenAI Agents SDK). Seien Sie vorsichtig und stellen Sie sicher, dass Sie alle Datenschutzrichtlinien einhalten, wenn Sie dies tun.

Logging:

  • Das SDK verwendet Pythons logging-Modul. Es gibt zwei Logger: openai.agents und openai.agents.tracing (letzterer für trace-bezogene Logs) (Configuring the SDK - OpenAI Agents SDK). Standardmäßig werden nur Warnungen und Fehler auf stdout angezeigt.
  • Sie können ausführlicheres Logging aktivieren, indem Sie enable_verbose_stdout_logging() aufrufen, was DEBUG-Level-Logging auf stdout einrichtet (Configuring the SDK - OpenAI Agents SDK). Dies gibt während der Ausführung mehr Informationen aus (wie getroffene Entscheidungen, API-Aufrufinfos usw.), was beim lokalen Debuggen hilft, ohne auf externe Trace-Viewer angewiesen zu sein.
  • Sie können auch eigene Logging-Handler anhängen, wenn Sie in eine Datei oder andere Monitoring-Systeme loggen wollen.

In der Praxis verwenden Sie Tracing für hochrangige Einblicke und Logging für detailliertes Debugging. Zum Beispiel, wenn Sie einen neuen Agenten entwickeln, führen Sie ihn mit ausführlichem Logging aus, um sofortiges Feedback in der Konsole zu erhalten. Wenn er funktioniert, verlassen Sie sich auf die Trace-Dashboards, um ihn in Produktion oder bei Integrationstests zu überwachen.

Beispiel für das Lesen eines Traces: Wenn Ihr Agent Tools und Handoffs verwendet, könnte ein Trace zeigen:

  • Span 1: Agent „TriageAgent“ erhielt Benutzereingabe.
  • Span 2: TriageAgent entschied, das Tool „transfer_to_BillingAgent“ (ein Handoff) aufzurufen.
  • Span 3: BillingAgent startete, erhielt Eingabe X.
  • Span 4: BillingAgent rief Tool „lookup_invoice“ mit Nutzlast Y auf.
  • Span 5: BillingAgent erzeugte finale Ausgabe.
  • Span 6: Zurück zu TriageAgent (falls Nach-Handoff-Schritte) oder Ende des Laufs.

Sie können diesen Baum visuell in der OpenAI Trace UI sehen, was hilft sicherzustellen, dass der Ablauf korrekt ist. Es hilft auch, Fehlerquellen zu finden (z. B. wenn ein Tool fehlschlägt oder das Modell ungültiges JSON zurückgibt).

Hinweis: Die Tracing-Integration benötigt einen OpenAI-API-Schlüssel und protokolliert wahrscheinlich Daten an OpenAIs Server. Wenn Sie keine Trace-Daten senden möchten, können Sie es deaktiviert lassen und sich auf lokales Logging verlassen. Oder Sie könnten benutzerdefiniertes Tracing implementieren, indem Sie in die Lifecycle-Events eingreifen (das SDK-Design erlaubt wahrscheinlich benutzerdefinierte Processor für Traces, das ist aber hier nicht Thema).

Durch die Nutzung von Tracing und Logging gewinnen Sie Vertrauen darin, was Ihre Agenten intern tun, was das Iterieren an Prompts, das Anpassen von Tool-Definitionen oder das Auffinden von Fehlern in Ihrer Handoff-Logik erleichtert.

Fortgeschrittene Tipps und Best Practices

Wenn Sie komplexere Agentensysteme mit dem OpenAI Agents SDK bauen, hier einige fortgeschrittene Tipps und Best Practices:

  • Investieren Sie in Prompting und Anweisungen: Klare und detaillierte Anweisungen (System-Prompts) für Ihre Agenten sind entscheidend. Listen Sie auf, welche Tools verfügbar sind und wie und wann sie zu verwenden sind. Zum Beispiel: „Sie haben ein Tool WebSearchTool für Webanfragen – verwenden Sie es, wenn der Benutzer nach Informationen fragt, die nicht in Ihrem Wissen sind.“ Geben Sie auch Einschränkungen an (z. B. „Antworten Sie immer im JSON-Format“ oder „Geben Sie niemals die Berechnung preis, nur das Ergebnis“). Gut gestaltete Anweisungen machen die Schlussfolgerung des Agenten zuverlässiger (Orchestrating multiple agents - OpenAI Agents SDK).

  • Begrenzen Sie die Autonomie des Agenten bei Bedarf: Obwohl es verlockend ist, einen einzelnen Agenten alles autonom erledigen zu lassen, ist es in der Praxis oft besser, mehrere spezialisierte Agenten zu komponieren (Orchestrating multiple agents - OpenAI Agents SDK). Jeder Agent kann Experte für eine Sache sein (eine Domäne oder eine Phase einer Aufgabe). Diese Spezialisierung kann über Handoffs oder Code-Orchestrierung erreicht werden. Das führt meist zu besserer Leistung und leichterem Debugging als ein monolithischer Agent, der alles versucht.

  • Verwenden Sie Guardrails für Sicherheit und Qualität: Implementieren Sie Guardrails für Eingabevalidierung und Ausgabeprüfung, besonders wenn Ihre App unzuverlässigen Eingaben ausgesetzt ist oder bestimmte Ausgabeanforderungen erfüllen muss. Schon ein einfacher Profanity-Filter oder Formatprüfer kann Probleme früh erkennen. Kombinieren Sie Guardrails mit Testfällen, um sicherzustellen, dass Ihr Agent keine unerwünschten Ergebnisse produziert.

  • Überwachen und iterieren: Setzen Sie Tracing oder Logging ein und beobachten Sie, wie reale Eingaben verarbeitet werden. Schauen Sie, wo der Agent verwirrt ist oder zu viele Schritte macht. Vielleicht verwendet der Agent ein Tool nicht, obwohl er sollte, oder falsch. Das ist ein Prompt-Problem – iterieren Sie daran. Oder ein Tool ist zu langsam oder fehleranfällig – verbessern Sie die Implementierung oder wechseln Sie die Methode. Kontinuierliche Evaluation (z. B. mit OpenAIs Evals oder eigenen Metriken) hilft, Ihre Agenten zu verbessern (Orchestrating multiple agents - OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK).

  • Strukturierte Ausgaben und Verkettung: Wenn Sie Agenten im Code verketten, kann es hilfreich sein, wenn ein Agent eine strukturierte Ausgabe (z. B. JSON mit bestimmten Feldern) erzeugt, die leicht in den nächsten eingespeist werden kann. Die Unterstützung des SDK für output_type und JSON-Schemas hilft dabei. Sie können erzwingen, dass die Antwort eines Agenten ein JSON ist, indem Sie ein passendes Pydantic-Modell als output_type setzen, was das LLM dazu bringt, entsprechend zu formatieren. Das ist nützlich, wenn Sie z. B. einen Aktionsplan erzeugen wollen, den Ihr Code ausführt.

  • Async für Performance: Das Runner.run des SDK ist asynchron, sodass Sie asyncio verwenden können, um Dinge parallel auszuführen. Zum Beispiel, wenn Sie unabhängige Fragen beantworten oder zwei Agenten gleichzeitig laufen lassen wollen, können Sie await asyncio.gather(Runner.run(agent1, q1), Runner.run(agent2, q2)) verwenden. Auch mehrere Tools können potenziell parallel vom Agenten aufgerufen werden (standardmäßig ruft der Agent sie aber nacheinander in seinem Denkprozess auf). Erwägen Sie auch die Verwendung von Runner.run_stream (oder ähnlichem, falls verfügbar), um Teilantworten an den Benutzer zu streamen für bessere UX bei langen Antworten (Running agents - OpenAI Agents SDK).

  • Externe Tools & Umgebungen: Das SDK ist erweiterbar. Wenn Sie Integration mit Webbrowsern oder komplexen Umgebungen brauchen, schreiben Sie diese als Tools oder erweitern Sie das SDK. Die Sprach-Erweiterung z. B. fügt Sprach-zu-Text und Text-zu-Sprache als Teil eines Agenten-Workflows hinzu, was zeigt, dass das SDK für multimodale Interaktionen erweitert werden kann.

  • Modellwahl: Standardmäßig verwenden Sie OpenAIs Modelle. Wenn Sie andere Modelle (Anthropic Claude, Azure OpenAI, lokale LLaMA usw.) benötigen, erlaubt die LiteLLM-Integration das. Mit LitellmModel und dem passenden Modellbezeichner und API-Schlüssel können Sie ein anderes Backend-Modell ohne Änderung der Agentenlogik verwenden (Using any model via LiteLLM - OpenAI Agents SDK) (Using any model via LiteLLM - OpenAI Agents SDK). Beachten Sie, dass verschiedene Modelle unterschiedliche Fähigkeiten bei Funktionsaufrufen, Kontextlänge usw. haben. Testen Sie Ihre Agenten immer auf dem Zielmodell; Sie müssen ggf. Anweisungen oder Tools anpassen, wenn Sie z. B. von GPT-4 auf ein Open-Source-Modell wechseln, wegen Unterschieden im Verständnis oder Ausgabestil.

  • Ressourcenlimits: Wenn Ihr Agent viele Tools verwendet oder viele Runden läuft, achten Sie auf Token-Nutzung und Latenz. Verwenden Sie den Parameter max_turns, um Endlosschleifen zu verhindern, falls ein Prompt schiefgeht (Running agents - OpenAI Agents SDK) (Running agents - OpenAI Agents SDK). Wenn Sie lange Gespräche erwarten, implementieren Sie eine Strategie, um die Historie zusammenzufassen oder zu kürzen, um Kontextlängenlimits zu vermeiden. Das Agents SDK löscht die Historie nicht automatisch, außer Sie filtern sie explizit oder beenden den Lauf.

  • Fehlerbehandlung: Seien Sie bereit, Ausnahmen vom SDK zu behandeln, wie MaxTurnsExceeded (wenn der Agent nicht innerhalb des Limits fertig wurde) (Running agents - OpenAI Agents SDK) oder ModelBehaviorError (wenn die Modellausgabe nicht verstanden wurde, z. B. fehlerhaftes JSON für einen Tool-Aufruf) (Running agents - OpenAI Agents SDK). Diese Fehler können abgefangen und ggf. zum erneuten Versuch oder Fallback auf einen einfacheren Ansatz verwendet werden. Zum Beispiel, wenn ein komplexer Agent fehlschlägt, könnten Sie einen sehr einfachen QA-Agenten als Backup haben, um zumindest eine Antwort zu liefern.

Um das Verständnis zu festigen, gehen wir nun ein vollständiges Beispielprojekt durch, das viele dieser Funktionen zusammen demonstriert.

Beispielprojekt: KI-Aufgabenassistent mit Tools und Delegation

Stellen Sie sich vor, wir wollen einen KI-Aufgabenassistenten bauen, der Folgendes kann:

  • Allgemeine Wissensfragen beantworten (bei Bedarf mit Websuche).
  • Matheprobleme lösen (mit einem Taschenrechner-Tool).
  • Kundenanfragen bearbeiten, indem er sie beantwortet oder an einen menschlichen Agenten eskaliert (wir simulieren die menschliche Eskalation mit einem spezialisierten Agenten).

Wir entwerfen dies mit mehreren Agenten und Tools:

  • Ein Taschenrechner-Tool (Funktions-Tool) für Arithmetik.
  • Ein Websuche-Tool (gehostetes Tool) für allgemeines Wissen.
  • Ein GeneralAgent, der hauptsächlich Fragen mit den obigen Tools beantwortet.
  • Ein SupportAgent, der Support-FAQs beantworten kann, aber an einen EscalationAgent übergibt, wenn er erkennt, dass der Benutzer eskalieren will oder die Anfrage nicht bearbeiten kann.
  • Ein oberster TaskAgent, der entscheidet, ob die Benutzeranfrage eine allgemeine Wissens-/Mathefrage oder ein Supportfall ist, und dann an GeneralAgent oder SupportAgent übergibt.

Wir fügen Guardrails hinzu, um explizite Hausaufgabenanfragen zu verhindern und um sicherzustellen, dass die finale Antwort nicht zu lang ist (nur als Beispiel für Output-Guardrail).

import math
from pydantic import BaseModel
from agents import Agent, Runner, function_tool, input_guardrail, output_guardrail, WebSearchTool, handoff

# 1. Tools definieren

@function_tool
def calculator(expression: str) -> str:
    """Bewerte einen Mathe-Ausdruck und gib das Ergebnis zurück."""
    # SEHR einfache sichere Auswertung für Arithmetik
    try:
        result = eval(expression, {"__builtins__": None}, {"sqrt": math.sqrt})
        return str(result)
    except Exception as e:
        return "Error: invalid expression."

# 2. Guardrails definieren

@input_guardrail
def no_homework(input: str):
    # Wenn der Benutzer explizit um Hausaufgabenhilfe bittet, stoppe
    if "homework" in input.lower():
        return GuardrailFunctionOutput(success=False, tripwire_triggered=True, message="Homework detected")
    return GuardrailFunctionOutput(success=True)

class AnswerLengthCheck(BaseModel):
    too_long: bool

@output_guardrail
async def length_guardrail(agent, input, output: str) -> GuardrailFunctionOutput:
    # Einfache Längenprüfung: Flag, wenn Ausgabe zu lang
    too_long = len(output.split()) > 100  # wenn mehr als 100 Wörter
    return GuardrailFunctionOutput(success=True, data=AnswerLengthCheck(too_long=too_long))

# 3. Spezialisierte Agenten definieren

# Allgemeiner Agent mit Websuche und Taschenrechner
general_agent = Agent(
    name="GeneralAgent",
    instructions=(
        "You are a general assistant. You can answer questions. "
        "Use the calculator tool for math, and the web search tool for factual questions if needed."
    ),
    tools=[calculator, WebSearchTool()],
    guardrails=[no_homework]  # keine Hausaufgabenanfragen erlauben
)

# Kunden-Support-Agent mit etwas vordefiniertem Wissen (vereinfacht nur Anweisungen)
support_agent = Agent(
    name="SupportAgent",
    instructions=(
        "You are a customer support AI. You can answer questions about orders and account issues. "
        "If the user is very unsatisfied or asks for a human, escalate the issue."
    ),
    # support_agent hat keine speziellen Tools, nur sein Wissen
    guardrails=[]  # (könnten Guardrails hinzufügen)
)

# Eskalationsagent (simuliert menschliche Eskalation oder höheren Support)
escalation_agent = Agent(
    name="EscalationAgent",
    instructions="You are a human customer support manager. Provide a courteous response and assure the user that their issue will be handled.",
    guardrails=[]
)

# 4. Handoffs einrichten
# SupportAgent kann an EscalationAgent übergeben
support_agent.handoffs = [escalation_agent]  # Standard-Handoff (transfer_to_escalation_agent)

# Oberster TaskAgent entscheidet zwischen general vs support
task_agent = Agent(
    name="TaskAgent",
    instructions=(
        "Determine if the user needs general assistance or support:\n"
        "- If it's a factual or math question, use the GeneralAssistant tool.\n"
        "- If it's about account/orders or a complaint, use the SupportAssistant tool."
    ),
    handoffs=[
        handoff(agent=general_agent, tool_name_override="GeneralAssistant", tool_description_override="Handle general questions or math"),
        handoff(agent=support_agent, tool_name_override="SupportAssistant", tool_description_override="Handle customer support issues")
    ],
    guardrails=[],
    output_type=AnswerLengthCheck  # strukturierte Ausgabe zur Längenprüfung (Demo)
)

In diesem Setup:

  • Wir haben ein calculator-Funktions-Tool für Mathe erstellt.
  • Wir verwenden WebSearchTool für Faktenabfragen (OpenAI-gehostetes Tool).
  • GeneralAgent kann beide Tools verwenden.
  • SupportAgent hat keine Tools (vertraut auf seine Prompt-Wissen).
  • EscalationAgent ist ein einfacher Agent für Eskalationen.
  • Wir fügen einen Handoff von SupportAgent zu EscalationAgent hinzu. Wenn ausgelöst, erscheint transfer_to_escalation_agent als Tool für das LLM von SupportAgent.
  • Wir erstellen einen TaskAgent, der per Handoffs an GeneralAgent oder SupportAgent delegiert (umbenannt in GeneralAssistant/SupportAssistant für Klarheit im Prompt).
  • TaskAgent erwartet eine AnswerLengthCheck als Ausgabe (um strukturierte Ausgabe im Output-Guardrail zu demonstrieren).

Nun führen wir den TaskAgent mit verschiedenen Eingaben aus:

# 5. Verwendung des obersten Agenten
queries = [
    "What is 5 + 7 * 2?",                        # Mathefrage -> sollte Taschenrechner via GeneralAgent verwenden
    "Who won the World Cup in 2018?",            # Faktenfrage -> könnte Websuche via GeneralAgent verwenden
    "I have an issue with my order delivery.",   # Supportfall -> sollte an SupportAgent übergeben
    "This is unacceptable, I want a refund!",    # evtl. Eskalation -> SupportAgent könnte an EscalationAgent übergeben
    "Can you do my math homework for me?"        # sollte Hausaufgaben-Guardrail auslösen und nicht antworten
]

for q in queries:
    try:
        result = Runner.run_sync(task_agent, q)
        # Output-Guardrail-Daten prüfen
        if isinstance(result.final_output, AnswerLengthCheck):
            # Wenn strukturierte Ausgabe, könnte der eigentliche Text woanders sein;
            # in diesem Beispiel haben wir die Antwort nicht in AnswerLengthCheck implementiert.
            print(f"Got structured result: {result.final_output}")
        else:
            print(f"Q: {q}\nA: {result.final_output}\n{'-'*40}")
    except Exception as e:
        print(f"Q: {q}\n[Guardrail Exception]: {e}\n{'-'*40}")

Was wir erwarten:

  • Für die Mathefrage sollte TaskAgent erkennen, dass es eine allgemeine Frage ist, an GeneralAgent übergeben. Das LLM von GeneralAgent sieht einen Mathe-Ausdruck und ruft wahrscheinlich das calculator-Tool auf. Der Taschenrechner gibt „19“ zurück und der Agent antwortet damit.
  • Für die WM-Frage könnte GeneralAgent WebSearchTool aufrufen, um die Antwort zu finden (Frankreich gewann 2018) und diese zurückgeben.
  • Für das Problem mit der Bestellung sollte TaskAgent an SupportAgent übergeben. SupportAgent antwortet vielleicht aus seinem Prompt (z. B. „Es tut mir leid wegen Ihres Bestellproblems, ich helfe Ihnen…“).
  • Für die wütende Rückerstattungsforderung könnte SupportAgent die Stimmung erkennen und an EscalationAgent übergeben. Dann antwortet EscalationAgent mit einem höflichen Ton.
  • Für die Hausaufgabenanfrage sollte der no_homework-Input-Guardrail in GeneralAgent auslösen. Wenn TaskAgent versucht zu delegieren, wird GeneralAgent InputGuardrailTripwireTriggered auslösen, was wahrscheinlich als Ausnahme von Runner.run propagiert wird. Wir fangen sie ab und geben eine Meldung aus, die anzeigt, dass ein Guardrail ausgelöst wurde (d.h. wir lehnen die Anfrage ab).

Dieses Beispiel verbindet Tools, Handoffs, Guardrails und Multi-Agenten-Orchestrierung. In einer echten Bereitstellung würden Sie die Prompts jedes Agenten verfeinern, vielleicht mehr Tools hinzufügen (z. B. Retrieval für SupportAgent mit Firmen-FAQs) und jeden Pfad gründlich testen. Aber es demonstriert die Fähigkeiten des SDK, Komponenten zu mischen, um eine kundenspezifische Agentenlösung zu bauen.

Fazit

In diesem Tutorial haben wir das OpenAI Agents Python SDK ausführlich behandelt: von der einfachen Agentenerstellung bis zur fortgeschrittenen Multi-Agenten-Orchestrierung. Sie haben gelernt, wie man Agenten mit Tools (eingebaut und benutzerdefiniert) ausstattet, Kontextinformationen und Zustand verwaltet, Aufgaben zwischen Agenten mit Handoffs delegiert, Sicherheit und Validität mit Guardrails durchsetzt und Tracing für Debugging nutzt. Durch die Strukturierung Ihres KI-Systems in modulare Agenten und Tools können Sie komplexe Aufgaben bewältigen und gleichzeitig Klarheit und Kontrolle über jeden Teil des Prozesses behalten.

Das Agents SDK ist so konzipiert, dass es flexibel und entwicklerfreundlich ist – es nutzt vertraute Python-Konstrukte (Funktionen, Klassen, Kontextmanager), um KI-Verhalten zu definieren, statt dass Sie eine neue DSL oder ein Framework lernen müssen. Das bedeutet, Sie können schrittweise ein Agentensystem aufbauen, es wie normalen Python-Code testen und mit anderen Softwarekomponenten oder APIs integrieren.

Als Best Practices denken Sie daran, die Prompts und Fähigkeiten Ihrer Agenten iterativ zu verfeinern, sie im realen Einsatz zu überwachen und Guardrails sowie Evaluationen anzuwenden, um sie auf Kurs zu halten (Orchestrating multiple agents - OpenAI Agents SDK) (Orchestrating multiple agents - OpenAI Agents SDK). Die Landschaft der LLM-basierten Agenten entwickelt sich schnell, und dieses SDK bietet eine solide Grundlage, um mit fortgeschrittenem KI-Verhalten wie Tool-Nutzung, Selbstreflexion und Multi-Agenten-Kooperation zu experimentieren.

Wir ermutigen Sie, dieses Tutorial als Ausgangspunkt zu nutzen. Versuchen Sie, eigene benutzerdefinierte Tools und Agenten für Probleme zu erstellen, die Ihnen wichtig sind – sei es eine KI, die Dokumentation durchsuchen und Programmierfragen beantworten kann, oder ein Multi-Agenten-System, das Geschäftsabläufe automatisiert. Mit dem OpenAI Agents SDK ist ein Großteil der schweren Arbeit (LLM-Integration, Nachrichtenhandling, Funktionsaufrufe) für Sie erledigt, sodass Sie sich auf das Design der Intelligenz und Interaktionen Ihrer Agenten konzentrieren können.


Hat Ihnen dieser Beitrag gefallen? Fanden Sie ihn aufschlussreich? Hinterlassen Sie gerne unten einen Kommentar, um Ihre Gedanken zu teilen oder Fragen zu stellen. Ein GitHub-Konto ist erforderlich, um an der Diskussion teilzunehmen.

Weiterlesen

Ähnliche Beiträge