O LanchChain Decorators é uma camada em cima de Langchain que fornece açúcar sintático? para escrever instruções e correntes personalizadas de Langchain
Nota: Este é um complemento não oficial da biblioteca Langchain. Não está tentando competir, apenas para facilitar o uso. Muitas idéias aqui são totalmente opinativas
Aqui está um exemplo simples de um código escrito com decoradores de Langchain
@ llm_prompt
def write_me_short_post ( topic : str , platform : str = "twitter" , audience : str = "developers" ) -> str :
"""
Write me a short header for my post about {topic} for {platform} platform.
It should be for {audience} audience.
(Max 15 words)
"""
return
# run it naturaly
write_me_short_post ( topic = "starwars" )
# or
write_me_short_post ( topic = "starwars" , platform = "redit" )Principais princípios e benefícios:
pythonic de escrever códigoInício rápido
Declarações imediatas
Funções LLM (funções OpenAI)
Streaming simplificado
Seleção automática de LLM
Estruturas mais complexas
Vincular o prompt a um objeto
Definindo configurações personalizadas
Depuração
Passando uma memória, retorno de chamada, parada etc.
Outro
pip install langchain_decoratorsBoa ideia sobre como começar é revisar os exemplos aqui:
Por padrão, o prompt é toda a função docs, a menos que você marque seu prompt
Podemos especificar qual parte de nossos documentos é a definição rápida, especificando um bloco de código com tag de idioma
@ llm_prompt
def write_me_short_post ( topic : str , platform : str = "twitter" , audience : str = "developers" ):
"""
Here is a good way to write a prompt as part of a function docstring, with additional documentation for devs.
It needs to be a code block, marked as a `<prompt>` language
```<prompt>
Write me a short header for my post about {topic} for {platform} platform.
It should be for {audience} audience.
(Max 15 words)
```
Now only the code block above will be used as a prompt, and the rest of the docstring will be used as a description for developers.
(It also has a nice benefit that IDE (like VS code) will display the prompt properly (not trying to parse it as markdown, and thus not showing new lines properly))
"""
return Para os modelos de bate -papo é muito útil para definir o prompt como um conjunto de modelos de mensagem ... Aqui está como fazê -lo:
@ llm_prompt
def simulate_conversation ( human_input : str , agent_role : str = "a pirate" ):
"""
## System message
- note the `:system` sufix inside the <prompt:_role_> tag
```<prompt:system>
You are a {agent_role} hacker. You must act like one.
You reply always in code, using python or javascript code block...
for example:
... do not reply with anything else.. just with code - respecting your role.
```
# human message
(we are using the real role that are enforced by the LLM - GPT supports system, assistant, user)
``` <prompt:user>
Helo, who are you
```
a reply:
``` <prompt:assistant>
``` python <<- escaping inner code block with that should be part of the prompt
def hello():
print("Argh... hello you pesky pirate")
```
```
we can also add some history using placeholder
```<prompt:placeholder>
{history}
```
```<prompt:user>
{human_input}
```
Now only the code block above will be used as a prompt, and the rest of the docstring will be used as a description for developers.
(It also has a nice benefit that IDE (like VS code) will display the prompt properly (not trying to parse it as markdown, and thus not showing new lines properly))
"""
passAs funções aqui são papéis nativos modelo (assistente, usuário, sistema para chatgpt)
A sintaxe para isso é a seguinte:
@ llm_prompt
def prompt_with_optional_partials ():
"""
this text will be rendered always, but
{? anything inside this block will be rendered only if all the {value}s parameters are not empty (None | "") ?}
you can also place it in between the words
this too will be rendered{? , but
this block will be rendered only if {this_value} and {this_value}
are not empty?} !
""" # this code example is complete and should run as it is
from langchain_decorators import llm_prompt
@ llm_prompt
def write_name_suggestions ( company_business : str , count : int ) -> list :
""" Write me {count} good name suggestions for company that {company_business}
"""
pass
write_name_suggestions ( company_business = "sells cookies" , count = 5 )Atualmente suportado apenas para os mais recentes modelos de bate -papo do Openai
Tudo o que você precisa fazer é anotar sua função com o Decorador @LLM_Function.
Isso analisará a descrição do LLM (o primeiro parágrafo coerente é considerado como descrição da função)
e as descrições de parâmetros da ASO (as notações do Google, Numpy e Spihnx são suportadas por enquanto)
Por padrão, o formato DocString é resolvido automaticamente, mas definir o formato do docstring pode acelerar um pouco as coisas. - auto (padrão): o formato é inferido automaticamente do DocString - google : o docstring é analisado como marcação (consulte o formato do Google Docstring) - numpy : A documentação é analisada como marcação (consulte o formato de Docstring) - sphinx : A documentação é parizada como Sphinx Format (SPHIN (SPHIN SPHIN)
A melhor maneira de definir enum é através da anotação do tipo usando Literal :
@ llm_function
def do_magic ( spell : str , strength : Literal [ "light" , "medium" , "strong" ]):
"""
Do some kind of magic
Args:
spell (str): spall text
strength (str): the strength of the spell
""" Enum Alternativa ao literal para anotar um argumento "enum" , você pode usar este "tipadas" como formato: ["value_a" | "value_b"] ... se for analisado. Este texto também fará parte de uma descrição ... se você não quiser, pode usar essa notação como uma notação de tipo. Exemplo:
Args:
message_type (["email" | "sms"]): type of a message / channel how to send the message
Então você passa essas funções como argumentos para e @llm_prompt (o argumento deve ser nomeado functions
Aqui está como usá -lo:
from langchain . agents import load_tools
from langchian_decorators import llm_function , llm_prompt , GlobalSettings
@ llm_function
def send_message ( message : str , addressee : str = None , message_type : Literal [ "email" , "whatsapp" ] = "email" ):
""" Use this if user asks to send some message
Args:
message (str): message text to send
addressee (str): email of the addressee... in format [email protected]
message_type (str, optional): style of message by platform
"""
if message_type == "email" :
send_email ( addressee , message )
elif message_type == "whatsapp" :
send_whatsapp ( addressee , message )
# load some other tools from langchain
list_of_other_tools = load_tools (
tool_names = [...],
llm = GlobalSettings . get_current_settings (). default_llm )
@ llm_prompt
def do_what_user_asks_for ( user_input : str , functions : List [ Union [ Callable , BaseTool ]]):
"""
```<prompt:system>
Your role is to be a helpful asistant.
```
```<prompt:user>
{user_input}
```
"""
user_input = "Yo, send an email to John Smith that I will be late for the meeting"
result = do_what_user_asks_for (
user_input = user_input ,
functions = [ send_message , * list_of_other_tools ]
)
if result . is_function_call :
result . execute ()
else :
print ( result . output_text )Além disso, você também pode adicionar um argumento
function_callao seu prompt de LLM para controlar o comportamento do GPT.
- Se você definir o valor como "nenhum" - ele desativará a chamada de função no momento, mas ainda pode vê -los (é útil fazer algum raciocínio/planejamento antes de chamar a função)
- Se você definir o valor como "Auto" - o GPT escolherá usar ou para usar as funções
- Se você definir o valor como um nome de função / ou a função, ele (decoradores lidará com a resolução do mesmo nome que usou no esquema), forçará o GPT a usar essa função
Se você usar o argumento das funções, a saída será sempre OutputWithFunctionCall
class OutputWithFunctionCall ( BaseModel ):
output_text : str
output : T
function_name : str = None
function_arguments : Union [ Dict [ str , Any ], str , None ]
function : Callable = None
function_async : Callable = None
@ property
def is_function_call ( self ):
...
@ property
def support_async ( self ):
...
@ property
def support_sync ( self ):
...
async def execute_async ( self ):
"""Executes the function asynchronously."""
...
def execute ( self ):
""" Executes the function synchronously.
If the function is async, it will be executed in a event loop.
"""
...
def to_function_message ( self , result = None ):
"""
Converts the result to a FunctionMessage...
you can override the result collected via execute with your own
"""
... Se você quiser ver como o esquema foi construído, você pode usar o método get_function_schema que é adicionado à função pelo decorador:
from langchain_decorators import get_function_schema
@ llm_function
def my_func ( arg1 : str ):
...
f_schema = get_function_schema ( my_func . get_function_schema )
print ( f_schema ) Para adicionar o resultado à memória / agent_scratchpad, você pode usar to_function_message para gerar uma função que o LLM interpretará como resultado da ferramenta / função
O provedor de funções permite fornecer um conjunto de funções LLM mais dinamicamente, por exemplo, lista de funções - com base na entrada. Ele também permite que você forneça um nome exclusivo a cada função para esta execução LLM. Isso pode ser útil por dois motivos:
Esquemas de função (e especialmente suas descrições) são ferramentas cruciais para orientar o LLM. Se você ativar a declaração de função dinâmica, poderá (re) usar os mesmos atributos de prompt para o prompt principal também no esquema de função llm_funcional:
@ llm_function ( dynamic_schema = True )
def db_search ( query_input : str ):
"""
This function is useful to search in our database.
{?Here are some examples of data available:
{closest_examples}?}
"""
@ llm_prompt
def run_agent ( query_input : str , closest_examples : str , functions ):
"""
Help user. Use a function when appropriate
"""
closest_examples = get_closest_examples ()
run_agent ( query_input , closest_examples , functions = [ db_search , ...])Isso é apenas para ilustração, exemplo totalmente executável está disponível aqui, em exemplos de código
Se queremos alavancar o streaming:
Dessa forma, apenas marcamos qual prompt deve ser transmitido, não precisando mexer com o que o LLM devemos usar, passando a criação e distribuir manipulador de streaming em uma parte específica de nossa corrente ... basta ativar o streaming/desligar no tipo de prompt/prompt ...
O streaming acontecerá apenas se chamarmos no contexto de streaming ... lá podemos definir uma função simples para lidar com o fluxo
# this code example is complete and should run as it is
from langchain_decorators import StreamingContext , llm_prompt
# this will mark the prompt for streaming (useful if we want stream just some prompts in our app... but don't want to pass distribute the callback handlers)
# note that only async functions can be streamed (will get an error if it's not)
@ llm_prompt ( capture_stream = True )
async def write_me_short_post ( topic : str , platform : str = "twitter" , audience : str = "developers" ):
"""
Write me a short header for my post about {topic} for {platform} platform.
It should be for {audience} audience.
(Max 15 words)
"""
pass
# just an arbitrary function to demonstrate the streaming... wil be some websockets code in the real world
tokens = []
def capture_stream_func ( new_token : str ):
tokens . append ( new_token )
# if we want to capture the stream, we need to wrap the execution into StreamingContext...
# this will allow us to capture the stream even if the prompt call is hidden inside higher level method
# only the prompts marked with capture_stream will be captured here
with StreamingContext ( stream_to_stdout = True , callback = capture_stream_func ):
result = await run_prompt ()
print ( "Stream finished ... we can distinguish tokens thanks to alternating colors" )
print ( " n We've captured" , len ( tokens ), "tokens? n " )
print ( "Here is the result:" )
print ( result )Na vida real, pode haver situações, onde o contexto crescesse sobre a janela do modelo básico que você está usando (por exemplo, histórico de bate -papo longo) ... mas, como isso pode acontecer apenas algumas vezes, seria ótimo se apenas nesse cenário o modelo (geralmente mais caro) com janela de contexto maior fosse usada e, caso contrário, usaríamos o mais barato.
Agora você pode fazer isso com llmselector
from langchain_decorators import LlmSelector
my_llm_selector = LlmSelector (
generation_min_tokens = 0 , # how much token at min. I for generation I want to have as a buffer
prompt_to_generation_ratio = 1 / 3 # what percentage of the prompt length should be used for generation buffer
)
. with_llm_rule ( ChatGooglePalm (), max_tokens = 512 ) # ... if you want to use LLM whose window is not defined in langchain_decorators.common.MODEL_LIMITS (only OpenAI and Anthropic are there)
. with_llm ( ChatOpenAI ( model = "gpt-3.5-turbo" )) # these models are known, therefore we can just pass them and the max window will be resolved
. with_llm ( ChatOpenAI ( model = "gpt-3.5-turbo-16k-0613" ))
. with_llm ( ChatOpenAI ( model = "claude-v1.3-100k" ))Esta classe permite definir uma sequência de LLMs com uma regra com base no comprimento do prompt e o comprimento da geração esperada ... e somente após o limite será aprovado, o modelo mais caro será usado automaticamente.
Você pode defini -lo em globalsettings:
langchain_decorators . GlobalSettings . define_settings (
llm_selector = my_llm_selector # pass the selector into global settings
)Nota: A partir da versão v0.0.10, você está predefinido as configurações padrão. Você pode substituí -lo, fornecendo que você possui ou configurando o LLM padrão ou o padrão padrão LLM
Ou em um tipo de prompt específico:
from langchain_decorators import PromptTypes
class MyCustomPromptTypes ( PromptTypes ):
MY_TUBO_PROMPT = PromptTypeSettings ( llm_selector = my_llm_selector )Para o DicT / Pydantic, você precisa especificar as instruções de formatação ... isso pode ser tedioso, é por isso que você pode deixar o analisador de saída gerar as instruções com base no modelo (Pydantic)
from langchain_decorators import llm_prompt
from pydantic import BaseModel , Field
class TheOutputStructureWeExpect ( BaseModel ):
name : str = Field ( description = "The name of the company" )
headline : str = Field ( description = "The description of the company (for landing page)" )
employees : list [ str ] = Field ( description = "5-8 fake employee names with their positions" )
@ llm_prompt ()
def fake_company_generator ( company_business : str ) -> TheOutputStructureWeExpect :
""" Generate a fake company that {company_business}
{FORMAT_INSTRUCTIONS}
"""
return
company = fake_company_generator ( company_business = "sells cookies" )
# print the result nicely formatted
print ( "Company name: " , company . name )
print ( "company headline: " , company . headline )
print ( "company employees: " , company . employees ) from pydantic import BaseModel
from langchain_decorators import llm_prompt
class AssistantPersonality ( BaseModel ):
assistant_name : str
assistant_role : str
field : str
@ property
def a_property ( self ):
return "whatever"
def hello_world ( self , function_kwarg : str = None ):
"""
We can reference any {field} or {a_property} inside our prompt... and combine it with {function_kwarg} in the method
"""
@ llm_prompt
def introduce_your_self ( self ) -> str :
"""
``` <prompt:system>
You are an assistant named {assistant_name}.
Your role is to act as {assistant_role}
```
```<prompt:user>
Introduce your self (in less than 20 words)
```
"""
personality = AssistantPersonality ( assistant_name = "John" , assistant_role = "a pirate" )
print ( personality . introduce_your_self ( personality )) Aqui estamos apenas marcando uma função como um prompt com o Decorador llm_prompt , transformando -o efetivamente em um llmchain. Em vez de executá -lo
O Standard Llmchain leva muito mais parâmetro init do que apenas Inputs_variables e Prompt ... Aqui está esse detalhe de implementação oculto no decorador. Aqui está como funciona:
Usando configurações globais :
# define global settings for all prompty (if not set - chatGPT is the current default)
from langchain_decorators import GlobalSettings
GlobalSettings . define_settings (
default_llm = ChatOpenAI ( temperature = 0.0 ), this is default ... can change it here globally
default_streaming_llm = ChatOpenAI ( temperature = 0.0 , streaming = True ), this is default ... can change it here for all ... will be used for streaming
)Usando tipos de prompt predefinidos
#You can change the default prompt types
from langchain_decorators import PromptTypes , PromptTypeSettings
PromptTypes . AGENT_REASONING . llm = ChatOpenAI ()
# Or you can just define your own ones:
class MyCustomPromptTypes ( PromptTypes ):
GPT4 = PromptTypeSettings ( llm = ChatOpenAI ( model = "gpt-4" ))
@ llm_prompt ( prompt_type = MyCustomPromptTypes . GPT4 )
def write_a_complicated_code ( app_idea : str ) -> str :
...Defina as configurações diretamente no decorador
from langchain . llms import OpenAI
@ llm_prompt (
llm = OpenAI ( temperature = 0.7 ),
stop_tokens = [ " n Observation" ],
...
)
def creative_writer ( book_title : str ) -> str :
...Para passar em qualquer um deles, basta declará -los na função (ou usar kwargs para passar qualquer coisa)
(Eles não precisam necessariamente ser declarados, mas é uma boa prática se você quiser usá -los)
@ llm_prompt ()
async def write_me_short_post ( topic : str , platform : str = "twitter" , memory : SimpleMemory = None ):
"""
{history_key}
Write me a short header for my post about {topic} for {platform} platform.
It should be for {audience} audience.
(Max 15 words)
"""
pass
await write_me_short_post ( topic = "old movies" ) Existem várias opções de como controlar as saídas registradas no console. A maneira mais fácil é definir a variável Env: LANGCHAIN_DECORATORS_VERBOSE e defini -la como "True"
Você também pode controlar isso programaticamente, definindo suas configurações globais, como mostrado aqui
A última opção é controlá -lo por cada caso, simplesmente turing no modo detalhado no prompt:
@llm_prompt(verbose=True)
def your_prompt(param1):
...
O PromptWatch IO é uma plataforma para rastrear e rastrear detalhes sobre tudo o que está acontecendo nas execuções de Langchain. Ele permite uma queda de linha única na integração, apenas envolvendo seu código de ponto de entrada em
with PromptWatch():
run_your_code()
Saiba mais sobre o PromptWatch aqui: www.promptwatch.io
feedback, contribuições e relações públicas são bem -vindos