일관된 구조에서 LLM의 출력을 생성하는 것은 본질적으로 어렵다. TypeGpt는이 프로세스를 파이썬에서 클래스를 정의하는 것만 큼 쉽게 단순화합니다.
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
여기서 파서는 LLM이 반환하면 description
구문 분석하지만 필요하지 않습니다. 기본적으로 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
요소 내부에 지침을 추가하려면 LLMOutput
대신 LLMArrayElementOutput
사용해야합니다.
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를 세 번 전화하려고 시도합니다. 그러나 온도가 0이 아닌 경우에만 사용하십시오.
prompt = ExamplePrompt (...)
output = client . chat . completions . generate_output ( model = "gpt-4" , prompt = prompt , ...)
Python의 제한된 유형 시스템으로 인해 출력 유형은 명시 적 서브 클래스 ExamplePrompt.Output
대신 유형 BaseLLMResponse
입니다. 코드에서 전체 유형 안전을 달성하려면 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>
...
"""
언어 모델의 혼동을 피하기 위해 복수의 "마우스"가 단수형 "마우스"로 자동 변환되는 방법에 주목하십시오.