Lanchchain Decorators es una capa encima de Langchain que proporciona azúcar sintáctica? para escribir indicaciones y cadenas de langchain personalizadas
Nota: Este es un complemento no oficial a la biblioteca Langchain. No está tratando de competir, solo para facilitarlo. Muchas ideas aquí son totalmente obstinadas
Aquí hay un ejemplo simple de un código escrito con 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" )Principios y beneficios principales:
pythonicComienzo rápido
Declaraciones rápidas
Funciones LLM (funciones de OpenAI)
Transmisión simplificada
Selección automática de LLM
Estructuras más complejas
Vinculando la solicitud a un objeto
Definición de la configuración personalizada
Depuración
Pasar un recuerdo, devolución de llamada, parar, etc.
Otro
pip install langchain_decoratorsLa buena idea sobre cómo comenzar es revisar los ejemplos aquí:
Por defecto, el mensaje es la función completa de los documentos, a menos que marque su aviso
Podemos especificar qué parte de nuestros documentos es la definición rápida, especificando un bloque de código con etiqueta de idiomas
@ 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 los modelos de chat es muy útil para definir el indicador como un conjunto de plantillas de mensajes ... aquí es cómo hacerlo:
@ 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))
"""
passLos roles aquí son roles nativos modelo (asistente, usuario, sistema para chatgpt)
La sintaxis para esto es la siguiente:
@ 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 )Actualmente compatible solo para los últimos modelos de chat de Operai
Todo lo que necesita hacer es anotar su función con el decorador @llm_function.
Esto analizará la descripción de LLM (el primer párrafo coherente se considera como descripción de la función)
y descripciones de parámetros ASO (las anotaciones de Google, Numpy y Spihnx son compatibles por ahora)
Por defecto, el formato de documentos se resuelve automáticamente, pero establecer el formato de la documentación puede acelerar un poco las cosas. - auto (predeterminado): el formato se infiere automáticamente de Docstring - google : el Docstring se analiza como Markdown (consulte el formato de Docstring de Google) - numpy : el Docstring se analiza como Markdown (consulte el formato Numpy sphinx )
La mejor manera de definir Enum es a través de la anotación de 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
""" Alternativa de enum a Literal para anotar un argumento como "enum" , puede usar este formato de "mecanografiado": ["value_a" | "value_b"] ... si se analizará. Este texto también será parte de una descripción ... si no lo desea, puede usar esta notación como notación de tipo. Ejemplo:
Args:
message_type (["email" | "sms"]): type of a message / channel how to send the message
Luego, pasa estas funciones como argumentos a y @llm_prompt (el argumento debe ser nombrado functions
Aquí está cómo usarlo:
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 )Además, también puede agregar un argumento
function_calla su solicitud de LLM para controlar el comportamiento GPT.
- Si establece el valor en "Ninguno", deshabilitará la llamada de función para el momento, pero aún puede verlos (útil hacer algún razonamiento/planificación antes de llamar a la función)
- Si establece el valor en "Auto", GPT elegirá usar o usar las funciones
- Si establece el valor en un nombre de función / o la función que sí mismo (los decoradores manejarán la resolución del mismo nombre que se usa en el esquema) obligará a GPT a usar esa función
Si usa el argumento de las funciones, la salida siempre se 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
"""
... Si desea ver cómo se ha construido el esquema, puede usar el método get_function_schema que el decorador agrega a la función:
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 agregar el resultado a Memory / Agent_ScratchPad, puede usar to_function_message para generar un FunctionMessage que LLM interpretará como un resultado de la herramienta / función
El proveedor de funciones le permite proporcionar un conjunto de funciones LLM de manera más dinámica, por ejemplo, la lista de funciones, en función de la entrada. También le permite dar un nombre único a cada función para esta ejecución LLM. Esto podría ser útil por dos razones:
Los esquemas de funciones (y especialmente sus descripciones) son herramientas cruciales para guiar a LLM. Si habilita la declaración de función dinámica, puede (re) usar los mismos atributos de solicitud para el indicador principal también en el esquema LLM_Function:
@ 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 , ...])Esto es solo para ilustración, el ejemplo completamente ejecutable está disponible aquí, en ejemplos de código
Si queremos aprovechar la transmisión:
De esta manera, solo marcamos qué aviso se debe transmitir, no necesitar jugar con lo que LLM deberíamos usar, pasando la creación y distribución del controlador de transmisión en una parte particular de nuestra cadena ... simplemente encienda/desactive la transmisión en el tipo de solicitud/indicador ...
La transmisión sucederá solo si lo llamamos en el contexto de la transmisión ... allí podemos definir una función simple para manejar la transmisión
# 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 )En la vida real puede haber situaciones, en las que el contexto crecería sobre la ventana del modelo base que está utilizando (por ejemplo, un largo historial de chat) ... pero dado que esto podría suceder solo algunas veces, sería genial, si solo en este escenario se usara el modelo (generalmente más caro) con una ventana de contexto más grande, y de lo contrario usaríamos el más barato.
Ahora puedes hacerlo con 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 clase le permite definir una secuencia de LLM con una regla basada en la longitud del aviso, y la longitud de generación esperada ... y solo después de que se aprobará el umbral, el modelo más caro se usará automáticamente.
Puedes definirlo en Globalsettings:
langchain_decorators . GlobalSettings . define_settings (
llm_selector = my_llm_selector # pass the selector into global settings
)Nota: A partir de la versión V0.0.10, usted allí, el LLMSelector está en la configuración predeterminada predefinida. Puede anularlo proporcionando su posee o configurando el LLM predeterminado o la transmisión predeterminada LLM
O en un tipo de inmediato específico:
from langchain_decorators import PromptTypes
class MyCustomPromptTypes ( PromptTypes ):
MY_TUBO_PROMPT = PromptTypeSettings ( llm_selector = my_llm_selector )Para DICT / Pydantic, debe especificar las instrucciones de formato ... Esto puede ser tedioso, por eso puede dejar que el analizador de salida le genere las instrucciones basadas en el 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 )) Aquí solo estamos marcando una función como un aviso con el decorador llm_prompt , convirtiéndola efectivamente en una LLMCHAIN. En lugar de ejecutarlo
Standard LLMChain toma mucho más parámetro init que solo entradas_variables y indicadores ... Aquí está este detalle de implementación oculto en el decorador. Así es como funciona:
Uso de la configuración global :
# 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 solicitud 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 la configuración directamente en el 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 pasar cualquiera de estos, simplemente declararlos en la función (o usar kwargs para pasar cualquier cosa)
(No necesariamente necesitan ser declarados, pero es una buena práctica si los va a usar)
@ 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" ) Hay varias opciones sobre cómo controlar las salidas registradas en la consola. La forma más fácil es definir la variable env: LANGCHAIN_DECORATORS_VERBOSE y configurarlo en "verdadero"
También puede controlar esto programáticamente definiendo su configuración global como se muestra aquí.
La última opción es controlarlo por cada caso, simplemente turando en el modo detallado en la solicitud:
@llm_prompt(verbose=True)
def your_prompt(param1):
...
PromptWatch IO es una plataforma para rastrear y rastrear detalles sobre todo lo que está sucediendo en las ejecuciones de Langchain. Permite una sola línea de integración, simplemente envolviendo su código de punto de entrada en
with PromptWatch():
run_your_code()
Obtenga más información sobre PromptWatch aquí: www.prompptwatch.io
Se bienvenidos comentarios, contribuciones y relaciones públicas