In meinem letzter Beitrag. Instrument Calling ist der Mechanismus, der es einem KI-Modell ermöglicht, zu entscheiden, welche Funktion mit welchen Argumenten verwendet werden muss, anstatt nur Textual content als Ausgabe zu generieren. Am Ende dieses Beitrags hatten wir ein Setup, das eine Entscheidung treffen konnte get_current_weather oder convert_currencyoder beides gleichzeitig ausführen, indem Sie sie parallel aufrufen, oder keines von beiden, und einfach Textual content generieren. Mit anderen Worten: Das Modell entscheidet, was es als nächstes tun muss. Wir (der Relaxation des Codes) führen diese Entscheidung aus, geben das Ergebnis an das Modell zurück und das Modell stellt dem Benutzer letztendlich eine fundierte Antwort im Textformat bereit.
Eine fortgeschrittenere Model dieser Schleife endet nicht nach nur einer Runde Modellentscheidung – Codeausführung – Rückgabe des Ergebnisses – Modellantwort. Anstatt am Ende eine Antwort zu generieren, Das Modell kann anhand des Ergebnisses eines Werkzeugaufrufs entscheiden, ob und welches Werkzeug als nächstes aufgerufen werden soll. Wie bereits am Ende erwähnt der Instrument Calling-Beitragdas ist ein ReAct-Schleife (Grund + Handeln) und ermöglicht es Agenten genau, Aufgaben zu erledigen, die nicht in einem einzigen Anruf gelöst werden können.
Doch was wäre eine solche Aufgabe? Im Beispiel des Parallelanrufs im vorherigen Beitrag haben wir gefragt What is the climate in Athens and the way a lot is 100 USD in EUR?Dabei handelt es sich um zwei separate Dinge, die den Einsatz zweier separater Instruments erfordern, um eine Antwort zu erhalten, die aber auch voneinander unabhängig sind. Mit anderen Worten: Wir können diese beiden Fragen unabhängig voneinander gleichzeitig beantworten, ohne dass für die Beantwortung der zweiten Frage Informationen aus der ersten Frage erforderlich sind.
Aber was ist, wenn wir so etwas fragen I guess my buddy 100 EUR that it might rain in Athens at the moment. If I gained, what number of USD is that? Hier kann das Modell nicht entscheiden, ob ein Anruf erforderlich ist convert_currency bis es zum ersten Mal ruft get_current_weather und findet heraus, ob es tatsächlich geregnet hat. Einfach gesagt, Die Antwort auf die zweite Frage hängt vollständig vom Ergebnis der ersten Frage ab. Dies ist genau die Artwork von Abhängigkeit, die parallele Toolaufrufe nicht in einer Runde auflösen können, und genau dafür ist eine ReAct-Schleife konzipiert.
Additionally schauen wir mal rein!
🍨 DataCream ist ein Publication über KI, Daten und Technologie. Wenn Sie sich für diese Themen interessieren, Abonnieren Sie hier!
Aber was genau ist eine ReAct-Schleife?
A ReAct-Schleife besteht aus nur drei Schritten, die nacheinander wiederholt werden:
- Grund
- Akt
- Beobachten

Am Anfang der Schleife das Modell Gründe darüber, welche Informationen ihm bereits bekannt sind und welche zusätzlichen Informationen fehlen, um eine korrekte Antwort auf die Anfrage des Benutzers zu geben. Es dann Handlungen indem ein entsprechendes Instrument aufgerufen wird, um diese fehlenden Informationen zu erhalten. Sobald schließlich der jeweilige Werkzeugaufruf ausgeführt und sein Ergebnis an das Modell zurückgegeben wird, wird das Modell beobachtet das Ergebnis (fügt das Ergebnis des Instruments in seinen Kontext ein). Dann kehrt es wieder zur Argumentation zurück, außer dass dieses Mal diese neue Beobachtung in ihrem Kontext steht. Diese Schleife wird wiederholt, bis das Modell feststellt, dass die verfügbaren Informationen ausreichen, um die Anfrage des Benutzers zu beantworten. An diesem Punkt ruft es keine Instruments mehr auf und antwortet nur noch mit Textual content.
Aber ist das nicht dasselbe wie die Toolaufrufe, die wir bereits kennen? Irgendwie, aber nicht genau. Der Teil, der dies von dem unterscheidet, was wir im Beitrag „Instrument Calling“ behandelt haben, ist die Schleife selbst. In einem einzigen Instrument-Aufruf fragt das Modell nach etwas, bekommt es, und das ist für diesen Aufruf das Ende der Transaktion. In der ReAct-Schleife bleibt die Konversation offen, da jede neue Beobachtung zu einem neuen Kontext für den nächsten Argumentationsschritt wird und das Modell seinen Plan basierend auf dem, was es gerade gelernt hat, ändern kann.
Gleiche Werkzeuge, neuer Trick
Um dies zu konkretisieren, gehen wir noch einmal auf das Wettbeispiel aus der Einleitung zurück und überlegen, was das Modell tatsächlich tun muss, um uns eine zuverlässige Antwort zu liefern. Die Frage ist: I guess my buddy 100 EUR that it might rain in Athens at the moment. If I gained, what number of USD is that? Beachten Sie die bedingte Anweisung in der Mitte: if I gained. Ob das Modell überhaupt eine Währung umrechnen muss, hängt davon ab, was der Wetteraufruf zurückgibt. Wenn es geregnet hat, muss das Modell anrufen convert_currency mit 100 EUR als Eingabeparameter und gib den umgerechneten Gewinn zurück. Wenn es nicht geregnet hat, ist die Wette verloren, convert_currency ist irrelevant und das Modell sollte den entsprechenden Textual content einfach direkt zurückgeben, ohne einen zweiten Aufruf zu tätigen.
Anders ausgedrückt: Das Modell kann seine vollständige Abfolge von Werkzeugaufrufen tatsächlich nicht im Voraus planen. Es muss zunächst das Wetter überprüfen, das Ergebnis beobachten, überlegen, was dieses Ergebnis für die Wettbedingungen bedeutet, und erst dann entscheiden, ob ein zweiter Instrument-Aufruf erforderlich ist. Im Gegensatz zum parallelen Instrument-Aufruf, der für die Beantwortung intestine funktionierte What is the climate in Athens and the way a lot is 100 USD in EUR?diese Frage erfordert eine Schleife.
Das Schöne an einer ReAct-Schleife ist, dass sie keine neuen Instruments benötigt. Wir können immer noch dieselben Funktionen nutzen, nur auf andere Weise. Additionally werden wir es verwenden get_current_weather Und convert_currency genau so, wie wir sie das letzte Mal mit Open-Meteo für das Wetter und Frankfurter für die Währungsumrechnung erstellt haben (beide erfordern immer noch keinen API-Schlüssel):
import requests
import json
from openai import OpenAI
consumer = OpenAI(api_key="your_api_key")
def get_current_weather(metropolis: str, unit: str = "celsius") -> dict:
# Step 1: geocode the town title to coordinates
geo = requests.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"title": metropolis, "rely": 1}
).json()
lat = geo("outcomes")(0)("latitude")
lon = geo("outcomes")(0)("longitude")
# Step 2: fetch present climate
climate = requests.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"present": "temperature_2m,precipitation",
"temperature_unit": unit
}
).json()
return {
"metropolis": metropolis,
"temperature": climate("present")("temperature_2m"),
"precipitation_mm": climate("present")("precipitation"),
"unit": unit
}
def convert_currency(quantity: float, from_currency: str, to_currency: str) -> dict:
response = requests.get(
f"https://api.frankfurter.dev/v2/fee/{from_currency}/{to_currency}"
).json()
fee = response("fee")
transformed = spherical(quantity * fee, 2)
return {
"quantity": quantity,
"from_currency": from_currency,
"to_currency": to_currency,
"converted_amount": transformed,
"fee": fee
}
Beachten Sie eine kleine Ergänzung im Vergleich zum letzten Mal: get_current_weather kommt jetzt auch zurück precipitation_mmda dies das Feld ist, das das Modell benötigt, um die Einsatzbedingung auszuwerten. Alles andere ist gleich. Der instruments Auch das Schema ist gegenüber unserem vorherigen Beitrag unverändert:
instruments = (
{
"kind": "operate",
"operate": {
"title": "get_current_weather",
"description": "Get the present climate for a given metropolis, together with temperature and precipitation",
"parameters": {
"kind": "object",
"properties": {
"metropolis": {"kind": "string", "description": "The title of the town"},
"unit": {"kind": "string", "enum": ("celsius", "fahrenheit")}
},
"required": ("metropolis")
}
}
},
{
"kind": "operate",
"operate": {
"title": "convert_currency",
"description": "Convert an quantity from one foreign money to a different",
"parameters": {
"kind": "object",
"properties": {
"quantity": {"kind": "quantity", "description": "The quantity to transform"},
"from_currency": {"kind": "string", "description": "The supply foreign money code, e.g. EUR"},
"to_currency": {"kind": "string", "description": "The goal foreign money code, e.g. USD"}
},
"required": ("quantity", "from_currency", "to_currency")
}
}
}
)
Wir müssen auch ein Nachschlagewörterbuch definieren, das unser Code verwendet, um die Werkzeugauswahl des Modells an die eigentliche Python-Funktion weiterzuleiten:
available_functions = {
"get_current_weather": get_current_weather,
"convert_currency": convert_currency
}
Auf diese Weise können wir von einem Toolnamen, den uns das Modell als String zurückgibt, zur eigentlichen Python-Funktion gelangen, die wir ausführen. Wir werden diese Zuordnung gleich benötigen, da wir dieses Mal nicht im Voraus wissen, wie viele Toolaufrufe wir auflösen müssen oder ob es mehr als einen sein wird.
Beobachten Sie die Schleife beim Nachdenken
Hier ist der Teil, der tatsächlich neu ist. Anstatt eine Anfrage zu stellen und den Instrument-Aufruf abzulesen, Wir wickeln den gesamten Austausch in eine Schleife. Bei jedem Durchgang senden wir dem Modell die vollständige bisherige Konversation, überprüfen, ob es nach einem Instrument gefragt hat, führen dieses Instrument aus, wenn ja, hängen das Ergebnis an und gehen noch einmal herum. Wir hören erst auf, wenn das Modell mit Klartext antwortet und keine Toolaufrufe mehr erforderlich sind.
messages = (
{
"position": "person",
"content material": "I guess my buddy 100 EUR that it might rain in Athens at the moment. If I gained, what number of USD is that?"
}
)
max_iterations = 5
for i in vary(max_iterations):
print(f"--- Step {i + 1}: Motive ---")
response = consumer.chat.completions.create(
mannequin="gpt-4o-mini",
messages=messages,
instruments=instruments
)
message = response.selections(0).message
messages.append(message)
# If there is not any device name, the mannequin is able to reply
if not message.tool_calls:
print("Closing reply:")
print(message.content material)
break
# In any other case, act on each device name the mannequin requested
for tool_call in message.tool_calls:
function_name = tool_call.operate.title
function_args = json.masses(tool_call.operate.arguments)
print(f"--- Step {i + 1}: Act ({function_name}) ---")
print(f"Calling {function_name} with {function_args}")
function_response = available_functions(function_name)(**function_args)
print(f"--- Step {i + 1}: Observe ---")
print(function_response)
# Feed the remark again in so the following Motive step can use it
messages.append({
"position": "device",
"tool_call_id": tool_call.id,
"content material": json.dumps(function_response)
})
Beachten Sie auch die max_iterations Kappe verhindert ein Modell, das entscheidet, was es braucht „Nur noch eine Info“ von einer Endlosschleife abzuhalten. Dies ist von besonderer Bedeutung weil wir für jeden Anruf beim Mannequin bezahlen innerhalb jeder dieser Schleifen.
Abschließend wird die resultierende Beobachtung der Schleife als angehängt position: "device" Nachricht, die an das Spezifische gebunden ist tool_call_id. Dadurch kann das Modell jedes Ergebnis wieder dem Aufruf zuordnen, der es erzeugt hat.
Und nachdem wir nun alles eingerichtet haben, können wir endlich die ReAct-Schleife in Aktion sehen.
Unsere Wettfrage kann sich additionally auf zwei Arten auswirken, je nachdem, wie das Wetter tatsächlich ist. Schauen wir uns beide an.
1. Wenn es in Athen regnen würde, würde unser Code im Terminal etwa Folgendes ausgeben:
--- Step 1: Motive ---
--- Step 1: Act (get_current_weather) ---
Calling get_current_weather with {'metropolis': 'Athens'}
--- Step 1: Observe ---
{'metropolis': 'Athens', 'temperature': 17.4, 'precipitation_mm': 3.2, 'unit': 'celsius'}
--- Step 2: Motive ---
--- Step 2: Act (convert_currency) ---
Calling convert_currency with {'quantity': 100, 'from_currency': 'EUR', 'to_currency': 'USD'}
--- Step 2: Observe ---
{'quantity': 100, 'from_currency': 'EUR', 'to_currency': 'USD', 'converted_amount': 108.5, 'fee': 1.085}
--- Step 3: Motive ---
Closing reply:
It did rain in Athens at the moment (3.2mm of precipitation), so that you gained the guess!
Your 100 EUR comes out to 108.50 USD at at the moment's alternate fee.
2. Und wenn es in Athen nicht regnen würde, würden wir folgenden Ausdruck erhalten:
--- Step 1: Motive ---
--- Step 1: Act (get_current_weather) ---
Calling get_current_weather with {'metropolis': 'Athens'}
--- Step 1: Observe ---
{'metropolis': 'Athens', 'temperature': 34.1, 'precipitation_mm': 0.0, 'unit': 'celsius'}
--- Step 2: Motive ---
Closing reply:
Sadly, it didn't rain in Athens at the moment, so it appears to be like such as you misplaced the guess.
No foreign money conversion wanted!
Schauen Sie sich an, was im zweiten Szenario passiert ist: Die Schleife wurde genau einmal ausgeführt. Das Modell hat das beobachtet precipitation_mm Warfare 0.0kam zu dem Schluss, dass die Einsatzbedingung nicht erfüllt conflict, und stoppte, ohne jemals mitzugehen convert_currency. Niemand hat ihm gesagt, dass er den zweiten Instrument-Aufruf überspringen soll, sondern er hat dies vielmehr allein entschieden, basierend auf dem, was er beim ersten Durchlauf der Schleife beobachtet hat.
Dies ist (zumindest für dieses einfache Szenario) der Hauptunterschied zwischen parallelen Toolaufrufen und der ReAct-Schleife. Beim parallelen Werkzeugaufruf könnten wir den gesamten Prozess nicht vorzeitig beenden und den Aufruf nicht ausführen convert_currency. Stattdessen wären in einem parallelen Setup beide Instruments im Voraus aufgerufen worden und das Modell würde die endgültige Antwort später zusammenstellen. Dies ist besonders wichtig, denn denken Sie daran! Wir bezahlen für jeden Anruf zum Modell. Daher ist die Möglichkeit, die Aufrufe des KI-Modells architektonisch auf das einzuschränken, was wir benötigen, ohne unnötige zusätzliche Aufrufe durchzuführen, von großer Bedeutung.
In meinen Gedanken
Wann schlägt additionally eine ReAct-Schleife tatsächlich den parallelen Werkzeugaufruf?
Die Antwort lautet: Immer wenn die Anzahl der Instrument-Aufrufe oder die Argumente für diese Aufrufe nur ermittelt werden können, nachdem ein früheres Ergebnis angezeigt wurde.
In unserem Wettbeispiel kann das Modell nicht entscheiden, ob es mitgeht convert_currency überhaupt bis get_current_weather sagt, ob es geregnet hat. Keine noch so große Vorabbegründung kann dieses Downside lösen, da die Informationen in der Welt des Modells einfach noch nicht vorhanden sind. Wir müssen aus der Welt des Modells heraustreten, externe Informationen aus der Wetter-API abrufen und sie dem Kontext des Modells hinzufügen. Im Gegensatz dazu wird beim parallelen Werkzeugaufruf davon ausgegangen, dass das Modell bereits weiß, was es benötigt, bevor es Werkzeugaufrufe initiiert. Eine ReAct-Schleife erfordert diese Annahme nicht: Sie lässt das Modell im Laufe der Zeit entdecken, was es benötigt.
Insbesondere in den folgenden Fällen überzeugt eine ReAct-Schleife gegenüber parallelen Toolaufrufen:
- Bei einem Ergebnis handelt es sich um eine Bedingung dafür, ob überhaupt ein weiterer Name erforderlich ist, wie im Wettbeispiel.
- Wenn die Argumente eines späteren Aufrufs vom Wert abhängen, der von einem früheren Aufruf zurückgegeben wurde. Wenn das Modell zum Beispiel erst nachschauen müsste, welche Währung eine Stadt verwendet, bevor es anrufen kann
convert_currencymit dem richtigen Code. - Wenn unerwartet ein früheres Ergebnis zurückkommt, kann der Benutzer beispielsweise einen Städtenamen angeben, der nicht geokodiert werden kann, oder eine API gibt einen Fehler zurück und das Modell muss seinen Plan anpassen, anstatt einfach alles zurückzumelden, was es erhalten hat.
Dennoch ist in einem einfachen Fall, in dem alle benötigten Instruments und ihre Argumente allein aus der Nachricht des Benutzers offensichtlich sind, der parallele Instrument-Aufruf tatsächlich die bessere Wahl, da wir auf diese Weise weniger Roundtrips, weniger Latenz und das gleiche Ergebnis erhalten.
Für mich ist der interessanteste Teil des Übergangs vom parallelen Toolaufruf zur ReAct-Schleife, wie wenig Code tatsächlich benötigt wurde 😅: a for Schleife, ein if -Anweisung und eine Wörterbuchsuche. Dennoch bewirkt diese kleine Menge Code Wunder. Diese ReAct-Schleife ist in der einen oder anderen Type der eigentliche Mechanismus hinter den meisten Dingen, die man unter einem „Agenten“ versteht.
✨ Danke fürs Lesen! ✨
Wenn du es bis hierher geschafft hast, Vielleicht finden Sie Pialgorithmen nützlich – eine von uns entwickelte Plattform, die Groups dabei hilft, organisatorisches Wissen sicher an einem Ort zu verwalten.
Hat Ihnen dieser Beitrag gefallen? Begleiten Sie mich 💌Unterstapel und 💼LinkedIn
Alle Bilder vom Autor, sofern nicht anders angegeben ansonsten
