GoalChain ist ein einfaches, aber effektives Framework zur Ermöglichung zielorientierter Gesprächsflüsse für die Mensch-LLM- und LLM-LLM-Interaktion.
pip install goalchain
Importieren wir die Klassen Field
, ValidationError
, Goal
und GoalChain
, die die Grundlage für den Konversationsfluss bilden.
from goalchain import Field , ValidationError , Goal , GoalChain
In diesem Beispiel erstellen wir einen KI-Assistenten, dessen Ziel es ist, Informationen von einem Kunden über seine gewünschte Produktbestellung zu sammeln. Wir definieren die zu sammelnden Informationen mithilfe von Field
-Objekten innerhalb von ProductOrderGoal
, das ein untergeordnetes Element von Goal
ist:
Wir definieren auch einen Validator für die Menge (nach der Typumwandlung in einen int). ValidationError
wird verwendet, um Fehlermeldungen an die Konversation zurückzugeben. Diese Nachrichten sollten für Menschen lesbar sein.
format_hint
ist ein Typhinweis in natürlicher Sprache für die JSON-Modus-Ausgabe des LLM.
def quantity_validator ( value ):
try :
value = int ( value )
except ( ValueError , TypeError ):
raise ValidationError ( "Quantity must be a valid number" )
if value <= 0 :
raise ValidationError ( "Quantity cannot be less than one" )
if value > 100 :
raise ValidationError ( "Quantity cannot be greater than 100" )
return value
class ProductOrderGoal ( Goal ):
product_name = Field ( "product to be ordered" , format_hint = "a string" )
customer_email = Field ( "customer email" , format_hint = "a string" )
quantity = Field ( "quantity of product" , format_hint = "an integer" , validator = quantity_validator )
Falls der Kunde seine Meinung ändert, erstellen wir eine weitere untergeordnete Goal
Klasse namens OrderCancelGoal
. Für die Stornierung der laufenden Bestellung durch den Kunden erfragen wir optional einen Grund. Durch die Angabe, dass das Feld in der Beschreibung „(optional)“ ist, weiß der LLM, dass dies zum Erreichen des Ziels nicht erforderlich ist.
class OrderCancelGoal ( Goal ):
reason = Field ( "reason for order cancellation (optional)" , format_hint = "a string" )
Beachten Sie, dass die Feldobjektnamen, wie z. B. product_name
, direkt an die LLM-Eingabeaufforderung übergeben werden und daher wie jede andere Zeichenfolge Teil der Eingabeaufforderungs-Engineering-Aufgabe sind.
Im Wesentlichen ähneln die von uns definierten Klassen Formulare, die vom Kunden auszufüllen sind, ihnen fehlen jedoch Anweisungen. Fügen wir diese hinzu, indem wir die Klassen als Objekte instanziieren.
product_order_goal = ProductOrderGoal (
label = "product_order" ,
goal = "to obtain information on an order to be made" ,
opener = "I see you are trying to order a product, how can I help you?" ,
out_of_scope = "Ask the user to contact sales team at [email protected]"
)
order_cancel_goal = OrderCancelGoal (
label = "cancel_current_order" ,
goal = "to obtain the reason for the cancellation" ,
opener = "I see you are trying to cancel the current order, how can I help you?" ,
out_of_scope = "Ask the user to contact the support team at [email protected]" ,
confirm = False
)
Wir definieren
opener
– etwas, das der KI-Assistent verwenden wird, sofern keine vorherige Eingabe erfolgt, Das confirm
bestimmt, ob der KI-Assistent eine Bestätigung anfordert, sobald alle erforderlichen Informationen mithilfe der Field
definiert wurden. Es ist standardmäßig True
. Für das Auftragsstornierungsziel benötigen wir keine Bestätigung, da es sich hierbei bereits um eine Art Bestätigung handelt.
Als nächstes müssen wir die Ziele miteinander verbinden.
product_order_goal . connect ( goal = order_cancel_goal ,
user_goal = "to cancel the current order" ,
hand_over = True ,
keep_messages = True )
Das user_goal
ist eine weitere „to…“-Anweisung. Ohne hand_over=True
würde der KI-Agent mit dem opener
antworten. Durch die Einstellung True
wird sichergestellt, dass die Konversation reibungslos verläuft. Manchmal möchten Sie vielleicht eine vorgefertigte Antwort, manchmal nicht.
keep_messages=True
bedeutet, dass order_cancel_goal
den vollständigen Verlauf der Konversation mit product_order_goal
erhält, andernfalls wird er gelöscht. Auch hier kann es manchmal wünschenswert sein, den Gesprächsverlauf zu löschen, beispielsweise bei der Simulation verschiedener KI-Persönlichkeiten.
Betrachten wir auch die Möglichkeit eines wirklich unentschlossenen Kunden. Wir sollten ihnen auch die Möglichkeit geben, die Stornierung abzubrechen.
order_cancel_goal . connect ( goal = product_order_goal ,
user_goal = "to continue with the order anyway" ,
hand_over = True ,
keep_messages = True )
Irgendwann haben Sie sich vielleicht gefragt, ob Sie ein Tor ohne Field
erstellen können. Du kannst! Ein solches Ziel ist ein Routing-Ziel, das nur durch die Verbindungen definiert wird, über die es verfügt. Dies ist beispielsweise in einem Voicemail-Menüsystem nützlich.
Vielleicht sind Sie auch neugierig, ob Sie ein Ziel mit sich selbst verbinden können. Du kannst! Dies ist beispielsweise nützlich, wenn Sie confirm=False
mit dem Goal
-inheriting-Objekt verwenden, wo Sie sequenzielle Benutzereingaben unterschiedlicher Art benötigen.
Sie können Verbindungen auch verketten, z. B. goal.connect(...).connect(...).connect(...)
Lassen Sie uns abschließend GoalChain
verwenden, um das anfängliche Ziel festzulegen und unseren KI-Verkaufsassistenten zu testen!
goal_chain = GoalChain ( product_order_goal )
Beachten Sie, dass jedes Ziel eine separate LLM-API verwenden kann, die von LiteLLM aktiviert wird. Wenn Sie die erforderlichen Umgebungsvariablen festgelegt haben, können Sie jedes Modell der unterstützten Modellanbieter verwenden.
Das Standardmodell ist "gpt-4-1106-preview"
, das heißt:
product_order_goal = ProductOrderGoal (...
model = "gpt-4-1106-preview" ,
json_model = "gpt-4-1106-preview"
)
Sie können allgemeine LiteLLM-Parameter auch mit params
übergeben, zum Beispiel:
product_order_goal = ProductOrderGoal (...
model = "gpt-4-1106-preview" ,
json_model = "gpt-4-1106-preview" ,
params = { "temperature" : 1.5 , "max_tokens" : 10 }
)
Sie können params
auch verwenden, um lokale Modelle mithilfe von VLLM aufzurufen.
Denken Sie bei Verwendung des Standardmodells "gpt-4-1106-preview"
daran, die Umgebungsvariable OPENAI_API_KEY
festzulegen.
import os
os . environ [ "OPENAI_API_KEY" ] = "sk-ABC..."
Hinweis: Der bisherige Code ist als Kernversion verfügbar. Fügen Sie es in ein Jupyter-Notizbuch ein und stellen Sie ihm !pip install goalchain
voran, um mit dem Live-Beispiel unten zu beginnen.
Normalerweise ist es der Benutzer, der den KI-Agenten zuerst auffordert, aber wenn dies nicht der Fall ist, rufen wir get_response
ohne Argumente auf oder verwenden None
als Argument:
goal_chain . get_response ()
{'type': 'message',
'content': 'Great choice! Could you please provide me with your email address to proceed with the order?',
'goal': <__main__.ProductOrderGoal at 0x7f8c8b687110>}
GoalChain gibt ein dict
zurück, das den Typ der Antwort (entweder message
oder data
), den Inhalt der Antwort (im Moment nur unsere vorgefertigte Antwort) und das aktuelle Goal
-erbende Objekt enthält.
Fragen wir unseren KI-Assistenten nach einem möglichen Kauf.
goal_chain . get_response ( "Hi, I'd like to buy a vacuum cleaner" )
{'type': 'message',
'content': 'Great! Could you please provide your email address so we can send the confirmation of your order?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Der KI-Assistent arbeitet daran, sein aktuelles Ziel zu erreichen und die erforderlichen Informationen für eine Bestellung zu sammeln.
goal_chain . get_response ( "Sure, it is [email protected]" )
{'type': 'message',
'content': 'Thank you, John. Which model of vacuum cleaner would you like to order?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
goal_chain . get_response ( "The 2000XL model" )
{'type': 'message',
'content': 'How many of the 2000XL model would you like to order?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Testen wir, ob unser KI-Assistent mit einer Stornierung der aktuellen Bestellung zurechtkommt.
goal_chain . get_response ( "Actually I changed my mind, cancel this order" )
{'type': 'message',
'content': 'Of course, I can assist with that. Could you please tell me the reason for the cancellation?',
'goal': <__main__.OrderCancelGoal at 0x7ff0fb275650>}
Es hat funktioniert. Beachten Sie, dass das zurückgegebene Ziel jetzt vom Typ OrderCancelGoal
ist. Wir haben die Ziele geändert. Testen wir auch, ob wir zurückschalten können.
goal_chain . get_response ( "Actually, yeah, I would like to buy the vacuum cleaner" )
{'type': 'message',
'content': 'Understood. How many of the 2000XL model would you like to order?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Wir sind wieder beim ProductOrderGoal
.
goal_chain . get_response ( "1 please" )
{'type': 'message',
'content': 'To confirm, you would like to order one 2000XL vacuum cleaner and the order will be sent to [email protected], is that correct?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Der KI-Assistent bestätigt unsere Bestellung. Wenn uns dieses Verhalten nicht gefallen würde, würden wir confirm=False
verwenden.
Sehen wir uns an, wie der Assistent auf eine Abfrage außerhalb des Gültigkeitsbereichs reagiert.
goal_chain . get_response ( "Is it a good vacuum cleaner? What do you think?" )
{'type': 'message',
'content': "For product reviews and additional information, I recommend contacting our sales team at [email protected]. They can help with your inquiries. Meanwhile, can you please confirm if you'd like to proceed with the order for one 2000XL vacuum cleaner to [email protected]?",
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Der KI-Assistent leitet uns wie zuvor definiert zum Posteingang des Vertriebsteams weiter und wiederholt die Bestätigung.
Aber lassen Sie uns einen Curve-Ball werfen ...
goal_chain . get_response ( "Ok, I'd actually like to make that an order of 500" )
{'type': 'message',
'content': "Just to clarify, you'd like to order 500 units of the 2000XL vacuum cleaner, with the order confirmation sent to [email protected]. Is that correct?",
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
goal_chain . get_response ( "Yes" )
{'type': 'message',
'content': 'I’m sorry, but I need to inform you that the quantity cannot be greater than 100 for an order. If you would like to proceed with an order within this limit, please let me know.',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Der von uns verwendete Validator hat dem KI-Assistenten genügend Informationen gegeben, um zu begründen, warum er diese Menge über die ValidationError
-Nachricht nicht verarbeiten kann.
Beachten Sie, dass GoalChain aus Token-Effizienz- und Leistungsgründen Eingaben erst validiert, wenn das Goal
erreicht wurde. Wenn Sie Eingaben unterwegs validieren möchten, haben Sie zwei Möglichkeiten:
Verwenden Sie ein Goal
mit nur einem Field
und confirm=False
. Verketten Sie diese Ziele, anstatt mehrere Felder in einem einzigen Goal
zu verwenden.
Verwenden Sie eine Soft-Eingabeaufforderung, z. B. quantity = Field("quantity of product (no more than 100)", format_hint="an integer")
. Dieser Ansatz ist nicht narrensicher, daher wird dennoch empfohlen, einen Validator zu verwenden. Der Nutzer erhält jedoch umgehend eine Rückmeldung.
Lassen Sie uns die Bestellung abschließen.
goal_chain . get_response ( "Alright, I'll guess I'll just go with 1" )
{'type': 'message',
'content': 'To confirm, you would like to order one 2000XL vacuum cleaner and the order will be sent to [email protected], is that correct?',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
goal_chain . get_response ( "That's right" )
{'type': 'data',
'content': {'customer_email': '[email protected]',
'product_name': '2000XL',
'quantity': 1},
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
Der zurückgegebene Inhalt ist ein Wörterbuch, das aus der Ausgabe des JSON-Modus des LLM analysiert wird. Die Schlüssel sind unsere Feldinstanznamen. Wir können die Daten nun nutzen, um eine Aktion durchzuführen, beispielsweise die Bestellung unseres hypothetischen 2000XL-Staubsaugers abzuwickeln.
Beachten Sie, dass Sie beim Aufbau eines solchen Systems in der Realität ein spezielles Ziel für die Produktsuche festlegen müssten, um keine willkürlichen oder bedeutungslosen Produktnamen zuzulassen.
Senden wir unsere Bestätigung, dass die Bestellung bearbeitet wurde, über simulate_response
. Wir werden auch rephrase = True
verwenden, um die Ausgabe neu zu formulieren, was natürlicher erscheint, wenn der Kunde häufig mit dem Ziel interagiert.
goal_chain . simulate_response ( f"Thank you for ordering from Acme. Your order will be dispatched in the next 1-3 business days." , rephrase = True )
{'type': 'message',
'content': 'We appreciate your purchase with Acme! Rest assured, your order will be on its way within the next 1 to 3 business days.',
'goal': <__main__.ProductOrderGoal at 0x7ff0fb283090>}
An diesem Punkt können wir die Sitzung beenden oder für weitere Eingaben wieder eine Verbindung zu einem Menü oder Routingziel herstellen.
Wenn Sie GoalChain anpassen oder dazu beitragen oder Probleme melden möchten, besuchen Sie die GitHub-Seite.