從固有的結構中產生LLM的輸出本質上很難。 Typegpt簡化了此過程,就像在Python中定義類一樣容易。
為我們自己的項目提供動力,例如Spexia
pip install typegpt
將您的提示和所需的輸出模式定義為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 ]
如果您將OpenAI用作LLM提供商,只需將OpenAI客戶端類名稱替換為子類TypeOpenAI
(為異步使用AsyncTypeOpenAI
,或用於Azure使用TypeAzureOpenAI
/ AsyncTypeAzureOpenAI
)即可使其安全。您仍然可以像以前一樣使用它,但是現在也可以調用這樣的聊天完成的generate_output
函數以生成輸出對象:
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 )
您會得到這樣的不錯的輸出:
Output ( num_sentences = 1 , adjectives = [ 'young' , 'exceptional' ], nouns = [ 'athlete' , 'skill' , 'agility' , 'field' ], verbs = [ 'demonstrated' ])
您的輸出類型可以包含字符串,整數,浮點,布爾值或其中的列表。也可以將元素標記為可選。也可以提供默認值。
class Output ( BaseLLMResponse ):
title : str = "My Recipe"
description : str | None
num_ingredients : int
ingredients : list [ int ]
estimated_time : float
is_oven_required : bool
在這裡,解析器將解析description
如果LLM返回,但不需要它。默認情況下None
。 title
也具有相同的範圍,因為它具有默認值。
您還可以定義更多限製或為LLM提供有關某些元素的更多信息:
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
默認情況下,庫總是期望每個元素只有一個行響應。您可以通過在LLMOutput
中設置multiline=True
來覆蓋此信息:
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 )
您可以嵌套響應類型。請注意,您需要將BaseLLMArrayElement
用於要嵌套在列表中的類。要在BaseLLMArrayElement
元素中添加說明,您必須使用LLMArrayElementOutput
而不是LLMOutput
。
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
您可能會有一個提示,該提示由於潛在的大依賴性而使用大量令牌。為了確保您的提示始終適合LLM的令牌限制,您可以在提示類中實現功能reduce_if_possible
:
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
在reduce_if_possible
功能中,您應該以小步驟減小提示的大小,並在成功減少的情況下返回True
。該功能被重複調用,直到提示擬合為止。調用OpenAI generate_output
函數時,這會自動確保提示適合給定模型。此外,您可以指定具有相同效果的自定義輸入令牌限制以節省成本: client.chat.completions.generate_output(..., max_input_tokens=2000)
。
在某些情況下,GPT仍可能返回無法正確遵循模式的輸出。發生這種情況時,OpenAI客戶端將拋出LLMParseException
。要在輸出不符合您的模式時自動重試,您可以將retry_on_parse_error
設置為要允許的重試次數:
out = client . chat . completions . generate_output ( "gpt-3.5-turbo" , prompt = prompt , ..., retry_on_parse_error = 3 )
現在,圖書館將嘗試在提出錯誤之前嘗試三次致電GPT。但是,請確保僅在溫度不為零時使用它。
prompt = ExamplePrompt (...)
output = client . chat . completions . generate_output ( model = "gpt-4" , prompt = prompt , ...)
由於Python的有限類型系統,輸出類型是類型的BaseLLMResponse
而不是顯式subclass ExamplePrompt.Output
。要在代碼中獲得完整類型的安全性,只需添加參數output_type=ExamplePrompt.Output
:
prompt = ExamplePrompt (...)
output = client . chat . completions . generate_output ( model = "gpt-4" , prompt = prompt , output_type = ExamplePrompt . Output , ...)
此參數不僅是類型的裝飾器。它也可以用來覆蓋GPT試圖預測的實際輸出類型。
給模型舉例說明很難解釋的任務:
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 :
...
在生成輸出時,請確保將AzureChatModel
用作模型,該輸出由Deployment_ID和相應的基本模型組成(如果需要,則用於自動減少提示)。
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 )
任何具有系統和用戶提示概念的LLM都可以使用此庫。生成系統和用戶消息(包括架構提示)這樣:
messages = prompt . generate_messages (
token_limit = max_prompt_length , token_counter = lambda messages : num_tokens_from_messages ( messages )
)
其中max_prompt_length
是允許使用提示的最大令牌數量,而num_tokens_from_messages
需要是一個函數,該函數計算給定消息列表的預測令牌用法。如果您不想自動減小提示的大小,請在此處返回0
。
使用生成的消息調用您的LLM。解析您收到的完整字符串,例如這樣的輸出類:
out = ExamplePrompt . Output . parse_response ( completion )
該庫自動從定義的輸出類中生成LLM兼容的模式,並將指令添加到系統提示的末端以遵守此模式。例如,對於以下抽象提示:
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 ]
將生成以下系統提示:
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>
...
"""
請注意,複數“小鼠”如何自動轉換為單數“鼠標”,以避免混淆語言模型。