Es ist von Natur aus schwierig, Ausgänge aus LLMs in einer konsistenten Struktur zu produzieren. Taptpt vereinfacht diesen Prozess so einfach wie die Definition einer Klasse in Python.
Wir führen unsere eigenen Projekte wie Spexia an
pip install typegpt
Definieren Sie Ihr promptes und gewünschtes Ausgangsschema als Unterklasse in Python:
from typegpt import BaseLLMResponse , PromptTemplate
class ExamplePrompt ( PromptTemplate ):
def __init__ ( self , sentence : str ):
self . sentence = sentence
def system_prompt ( self ) -> str :
return "Given a sentence, extract sentence parts."
def user_prompt ( self ) -> str :
return self . sentence
class Output ( BaseLLMResponse ):
num_sentences : int
adjectives : list [ str ]
nouns : list [ str ]
verbs : list [ str ]
Wenn Sie OpenAI als LLM -Anbieter verwenden, ersetzen Sie einfach den Namen OpenAI -Client -Klasse durch die Unterklasse TypeOpenAI
(für Async verwenden Sie AsyncTypeOpenAI
oder für Azure TypeAzureOpenAI
/ AsyncTypeAzureOpenAI
), um es sicher zu machen. Sie können es immer noch wie zuvor verwenden, aber Sie können jetzt auch die Funktion generate_output
für Chat -Abschlüsse wie diese aufrufen, um das Ausgabebobjekt zu generieren:
from typegpt . openai import TypeOpenAI
prompt = ExamplePrompt ( "The young athlete demonstrated exceptional skill and agility on the field." )
client = TypeOpenAI ( api_key = "<your api key>" ) # subclass of `OpenAI`
output = client . chat . completions . generate_output ( model = "gpt-3.5-turbo" , prompt = prompt , max_output_tokens = 1000 )
Und Sie erhalten eine schöne Ausgabe wie diese:
Output ( num_sentences = 1 , adjectives = [ 'young' , 'exceptional' ], nouns = [ 'athlete' , 'skill' , 'agility' , 'field' ], verbs = [ 'demonstrated' ])
Ihr Ausgangstyp kann Zeichenfolge, Ganzzahl, Float, Boolesche oder Listen davon enthalten. Es ist auch möglich, Elemente als optional zu markieren. Standardwerte können ebenfalls bereitgestellt werden.
class Output ( BaseLLMResponse ):
title : str = "My Recipe"
description : str | None
num_ingredients : int
ingredients : list [ int ]
estimated_time : float
is_oven_required : bool
Hier analysiert der Parser description
, wenn das LLM ihn zurückgibt, es aber nicht benötigt. Es ist standardmäßig None
. Gleiches gilt für title
, da es einen Standardwert hat.
Sie können auch mehr Einschränkungen definieren oder dem LLM mehr Informationen für einige Elemente geben:
class Output ( BaseLLMResponse ):
title : str = LLMOutput ( instruction = "The title for the recipe." )
description : str | None = LLMOutput ( instruction = "An optional description for the recipe." )
num_ingredients : int
ingredients : list [ int ] = LLMArrayOutput ( expected_count = ( 1 , 5 ), instruction = lambda pos : f"The id of the { pos . ordinal } ingredient" ) # between 1 and 5 ingredients expected (and required at parse time)
estimated_time : float = LLMOutput ( instruction = "The estimated time to cook" )
is_oven_required : bool
Standardmäßig erwartet die Bibliothek immer nur eine Zeilenantwort pro Element. Sie können dies überschreiben, indem Sie multiline=True
in LLMOutput
einstellen:
class Output ( BaseLLMResponse ):
description : str = LLMOutput ( instruction = "A description for the recipe." , multiline = True )
items : list [ str ] = LLMArrayOutput ( expected_count = 5 , instruction = lambda pos : f"The { pos . ordinal } item in the list" , multiline = True )
Sie können Antworttypen nisten. Beachten Sie, dass Sie BaseLLMArrayElement
für Klassen verwenden müssen, die Sie in einer Liste nisten möchten. Um Anweisungen in einem Element von BaseLLMArrayElement
hinzuzufügen, müssen Sie LLMArrayElementOutput
anstelle von LLMOutput
verwenden.
class Output ( BaseLLMResponse ):
class Item ( BaseLLMArrayElement ):
class Description ( BaseLLMResponse ):
short : str | None
long : str
title : str
description : Description
price : float = LLMArrayElementOutput ( instruction = lambda pos : f"The price of the { pos . ordinal } item" )
items : list [ Item ]
count : int
Möglicherweise haben Sie eine Eingabeaufforderung, die aufgrund potenziell großer Abhängigkeiten eine unvorhersehbar große Anzahl von Token verwendet. Um sicherzustellen, dass Ihre Eingabeaufforderung immer in die Token -Grenze des LLM passt, können Sie die Funktion reduce_if_possible
in Ihrer Eingabeaufforderungsklasse implementieren:
class SummaryPrompt ( PromptTemplate ):
def __init__ ( self , article : str ):
self . article = article
def system_prompt ( self ) -> str :
return "Summarize the given news article"
def user_prompt ( self ) -> str :
return f"ARTICLE: { self . article } "
def reduce_if_possible ( self ) -> bool :
if len ( self . article ) > 100 :
# remove last 100 characters at a time
self . article = self . article [: - 100 ]
return True
return False
class Output ( BaseLLMResponse ):
summary : str
In der Funktion reduce_if_possible
sollten Sie die Größe Ihrer Eingabeaufforderung in kleinen Schritten reduzieren und True
zurückkehren, wenn Sie erfolgreich reduziert werden. Die Funktion wird wiederholt aufgerufen, bis die Eingabeaufforderung passt. Wenn Sie die Funktion openai generate_output
aufrufen, stellt dies automatisch sicher, dass die Eingabeaufforderung für die angegebenen Modelle geeignet ist. Zusätzlich können Sie eine benutzerdefinierte Eingangs -Token -Grenze mit demselben Effekt angeben, um die Kosten zu sparen: client.chat.completions.generate_output(..., max_input_tokens=2000)
.
In einigen Fällen kann GPT immer noch eine Ausgabe zurückgeben, die dem Schema nicht korrekt folgt. In diesem Fall wirft der OpenAI -Client eine LLMParseException
aus. Um automatisch wieder zu werden, wenn die Ausgabe Ihr Schema nicht erfüllt, können Sie retry_on_parse_error
auf die Anzahl der Wiederholungen einstellen, die Sie zulassen möchten:
out = client . chat . completions . generate_output ( "gpt-3.5-turbo" , prompt = prompt , ..., retry_on_parse_error = 3 )
Jetzt wird die Bibliothek versuchen, GPT dreimal anzurufen, bevor er einen Fehler werfen. Stellen Sie jedoch sicher, dass Sie dies nur dann verwenden, wenn die Temperatur nicht Null ist.
prompt = ExamplePrompt (...)
output = client . chat . completions . generate_output ( model = "gpt-4" , prompt = prompt , ...)
Aufgrund des begrenzten Types von Python ist der Ausgangstyp vom Typ BaseLLMResponse
anstelle der expliziten Unterklasse ExamplePrompt.Output
. Fügen Sie einfach den Parameter output_type=ExamplePrompt.Output
hinzu:
prompt = ExamplePrompt (...)
output = client . chat . completions . generate_output ( model = "gpt-4" , prompt = prompt , output_type = ExamplePrompt . Output , ...)
Dieser Parameter ist nicht nur ein Typdekorator. Es kann auch verwendet werden, um den tatsächlichen Ausgangstyp zu überschreiben, den GPT vorhersagen soll.
Geben Sie dem Modell Beispiele, um Aufgaben zu erklären, die schwer zu erklären sind:
class ExamplePrompt ( PromptTemplate ):
class Output ( BaseLLMResponse ):
class Ingredient ( BaseLLMResponse ):
name : str
quantity : int
ingredients : list [ Ingredient ]
def system_prompt ( self ) -> str :
return "Given a recipe, extract the ingredients."
def few_shot_examples ( self ) -> list [ FewShotExample [ Output ]]:
return [
FewShotExample (
input = "Let's take two apples, three bananas, and four oranges." ,
output = self . Output ( ingredients = [
self . Output . Ingredient ( name = "apple" , quantity = 2 ),
self . Output . Ingredient ( name = "banana" , quantity = 3 ),
self . Output . Ingredient ( name = "orange" , quantity = 4 ),
])
),
FewShotExample (
input = "My recipe requires five eggs and two cups of flour." ,
output = self . Output ( ingredients = [
self . Output . Ingredient ( name = "egg" , quantity = 5 ),
self . Output . Ingredient ( name = "flour cups" , quantity = 2 ),
])
)
]
def user_prompt ( self ) -> str :
...
Stellen Sie sicher, dass Sie das AzureChatModel
als Modell verwenden, um die Ausgabe zu generieren, die aus der Deployment_ID und dem entsprechenden Basismodell besteht (dies wird bei Bedarf zur automatischen Reduzierung von Eingabeaufforderungen verwendet).
from typegpt . openai import AzureChatModel , TypeAzureOpenAI
client = TypeAzureOpenAI (
azure_endpoint = "<your azure endpoint>" ,
api_key = "<your api key>" ,
api_version = "2023-05-15" ,
)
out = client . chat . completions . generate_output ( model = AzureChatModel ( deployment_id = "gpt-35-turbo" , base_model = "gpt-3.5-turbo" ), prompt = prompt , max_output_tokens = 1000 )
Jeder LLM, der einen Vorstellung von System- und Benutzeranforderungen hat, kann diese Bibliothek verwenden. Generieren Sie das System- und Benutzernachrichten (einschließlich der Eingabeaufforderung des Schemas) wie folgt:
messages = prompt . generate_messages (
token_limit = max_prompt_length , token_counter = lambda messages : num_tokens_from_messages ( messages )
)
wobei max_prompt_length
die maximale Anzahl von Token ist, die die Eingabeaufforderung verwenden darf, und num_tokens_from_messages
muss eine Funktion sein, die die vorhergesagte Token -Verwendung für eine bestimmte Liste von Nachrichten zählt. Geben Sie hier 0
zurück, wenn Sie die Größe einer Eingabeaufforderung nicht automatisch reduzieren möchten.
Verwenden Sie die generierten Nachrichten, um Ihre LLM aufzurufen. Analysieren Sie die Fertigstellung, die Sie erhalten, in die gewünschte Ausgabeklasse wie folgt zurück:
out = ExamplePrompt . Output . parse_response ( completion )
Diese Bibliothek generiert automatisch ein LLM-kompatibles Schema aus Ihrer definierten Ausgabeklasse und fügt dem Ende der Systemaufforderung Anweisungen hinzu, um dieses Schema einzuhalten. Zum Beispiel für die folgende abstrakte Eingabeaufforderung:
class DemoPrompt ( PromptTemplate ):
def system_prompt ( self ) -> str :
return "This is a system prompt"
def user_prompt ( self ) -> str :
return "This is a user prompt"
class Output ( BaseLLMResponse ):
title : str
description : str = LLMOutput ( "Custom instruction" )
mice : list [ str ]
Die folgende Systemaufforderung wird generiert:
This is a system prompt
Always return the answer in the following format:
"""
TITLE: <Put the title here>
DESCRIPTION: <Custom instruction>
MOUSE 1: <Put the first mouse here>
MOUSE 2: <Put the second mouse here>
...
"""
Beachten Sie, wie die Plural -Mäuse automatisch in die einzigartige "Maus" umgewandelt werden, um das Sprachmodell zu vermeiden.