Lanchchain Decorators ist eine Schicht auf Langchain, die syntaktischen Zucker liefert? zum Schreiben von benutzerdefinierten Langchain -Eingabeaufforderungen und Ketten
Hinweis: Dies ist ein inoffizielles Addon in die Langchain -Bibliothek. Es versucht nicht zu konkurrieren, nur um es einfacher zu machen. Viele Ideen hier sind total der Meinung nach
Hier ist ein einfaches Beispiel für einen Code mit Langchain -Dekoratoren
@ 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" )Hauptprinzipien und Vorteile:
pythonic Art, Code zu schreibenSchneller Start
Sofortige Erklärungen
LLM -Funktionen (OpenAI -Funktionen)
Vereinfachtes Streaming
Automatische LLM -Auswahl
Komplexere Strukturen
Bindung der Eingabeaufforderung an ein Objekt
Definieren benutzerdefinierter Einstellungen
Debuggen
Speichern Sie einen Speicher, einen Rückruf, Stopp usw.
Andere
pip install langchain_decoratorsGute Idee, wie man anfängt, ist die Überprüfung der Beispiele hier:
Standardmäßig ist die Eingabeaufforderung die gesamten Funktionsdokumente, es sei denn, Sie markieren Ihre Eingabeaufforderung
Wir können angeben, welcher Teil unserer Dokument Sprachetikett
@ 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 Für Chat -Modelle ist sehr nützlich, um die Eingabeaufforderung als eine Reihe von Nachrichtenvorlagen zu definieren ... Hier finden Sie, wie dies zu tun ist:
@ 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))
"""
passDie Rollen hier sind modellische Rollen (Assistent, Benutzer, System für ChatGPT)
Die Syntax dafür ist wie folgt:
@ 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 )Derzeit wird nur für die neuesten OpenAI -Chat -Modelle unterstützt
Alles, was Sie tun müssen, ist Ihre Funktion mit dem Dekorator @llm_function zu kommentieren.
Dadurch wird die Beschreibung für LLM analysiert (erster kohärenter Absatz wird als Funktionsbeschreibung angesehen)
und ASO -Parameterbeschreibungen (Google, Numpy und SPIHNX -Notationen werden vorerst unterstützt)
Standardmäßig wird das DOCString -Format automatisch behoben, aber das Festlegen des Formats des DocString kann die Dinge ein wenig beschleunigen. - auto (Standard): Das Format wird automatisch aus dem DocString abgeleitet - google : Die DocString wird als Markdown analysiert (siehe Google Docstring -Format) - numpy : Die DocString wird als Markdown analysiert (siehe Numpy Docstring -Format) - sphinx : Das Dokusstring wird als Sphinx -Format (siehe Sphinx docstring Format) pariert (siehe Sphinx -Format (siehe Sphinx -Format) (siehe Sphinx -Format) (siehe Sphinx -Format) Format (siehe Sphinx -Format) -Format (siehe Sphinx -Format) -Format (siehe Sphinx -Format) -Format (siehe Sphinx -Format) -Format)
Der beste Weg, um Enum zu definieren, ist die Annotation von Typ mit Literal Annotation:
@ 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 Alternative zu buchstäblicher Annotation eines "Enum" -Scharguments können dieses "TypeScript" wie Format verwenden: ["value_a" | "value_b"] ... wenn es analysiert wird. Dieser Text ist auch Teil einer Beschreibung ... Wenn Sie ihn nicht wollen, können Sie diese Notation als Typnotation verwenden. Beispiel:
Args:
message_type (["email" | "sms"]): type of a message / channel how to send the message
Dann übergeben Sie diese Funktionen als Argumente an und @llm_prompt (das Argument muss als functions bezeichnet werden
Hier erfahren Sie, wie man es benutzt:
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 )Zusätzlich können Sie Ihrer LLM -Eingabeaufforderung ein
function_call-Argument hinzufügen, um das GPT -Verhalten zu steuern.
- Wenn Sie den Wert auf "keine" festlegen, deaktiviert er den Funktionsaufruf für den Moment, kann sie jedoch weiterhin sehen (nützlich für einige Argumentation/Planung, bevor Sie die Funktion aufrufen).
- Wenn Sie den Wert auf "Auto" festlegen - wird GPT die Verwendung oder die Verwendung der Funktionen verwenden
- Wenn Sie den Wert auf einen Namen der Funktion / oder auf die Funktion, die sich selbst (Dekorateure befinden
Wenn Sie Funktionsargument verwenden, wird der Ausgang immer 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
"""
... Wenn Sie sehen möchten, wie das Schema erstellt wurde, können Sie die Methode get_function_schema verwenden, die der Funktion vom Dekorator hinzugefügt wird:
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 ) Um das Ergebnis dem Speicher / Agent_Scratchpad hinzuzufügen, können Sie to_function_message verwenden, um eine Funktionsmessage zu generieren, die LLM als Tool / Funktionsergebnis interpretiert
Mit Funktionen können Sie den Anbieter von LLM -Funktionen dynamischer bereitstellen, beispielsweise die Liste der Funktionen - basierend auf der Eingabe. Es ermöglicht Ihnen auch, jeder Funktion einen eindeutigen Namen für diesen LLM -Lauf anzugeben. Dies kann aus zwei Gründen nützlich sein:
Funktionsschemata (und insbesondere deren Beschreibungen) sind entscheidende Werkzeuge, um LLM zu leiten. Wenn Sie die dynamische Funktionserklärung aktivieren, können Sie (wieder) dieselben Eingabeaufforderungsattribute für die Hauptaufforderung auch im Schema LLM_Function verwenden:
@ 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 , ...])Dies dient nur zur Illustration. In Code -Beispielen finden Sie hier vollständig ausführbare Beispiele
Wenn wir Streaming nutzen wollen:
Auf diese Weise markieren wir nur, welche Eingabeaufforderung gestreamt werden sollte, und müssen nicht an dem Basteln mit dem Basteln, was wir verwenden sollen, basteln, das Erstellen und verteilen Sie den Streaming -Handler in einen bestimmten Teil unserer Kette ... schalten Sie einfach das Streaming ein/aus der Eingabeaufforderung/Eingabeaufforderung an ...
Das Streaming erfolgt nur, wenn wir es im Streaming -Kontext aufrufen ... Dort können wir eine einfache Funktion definieren, um den Stream zu verarbeiten
# 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 )Im wirklichen Leben könnte es Situationen geben, in denen der Kontext über dem Fenster des Basismodells wachsen würde, das Sie verwenden (z. B. lange Chat -Geschichte) ... aber da dies nur manchmal passieren könnte, wäre es großartig, wenn es nur in diesem Szenario das (normalerweise teurere) Modell mit größerem Kontextfenster verwendet wird, und ansonsten würden wir das billigere verwenden.
Jetzt können Sie es mit LLMSelector tun
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" ))Mit dieser Klasse können Sie eine Sequenz von LLMs mit einer Regel definieren, die auf der Länge der Eingabeaufforderung basiert, und die erwartete Erzeugungslänge ... und erst nach der Übergabe des Schwellenwerts wird das teurere Modell automatisch verwendet.
Sie können es in Globalsettings definieren:
langchain_decorators . GlobalSettings . define_settings (
llm_selector = my_llm_selector # pass the selector into global settings
)Hinweis: Ab der Version V0.0.10 Sie dort befinden sich der LLMSELECTOR in den vordefinierten Standardeinstellungen. Sie können es überschreiben, indem Sie Ihnen eigene oder das Standard -Streaming -LLM einrichten oder das Standard -LLM- oder Standard -Streaming -LLM einrichten
Oder in einen bestimmten Eingabeaufforderungstyp:
from langchain_decorators import PromptTypes
class MyCustomPromptTypes ( PromptTypes ):
MY_TUBO_PROMPT = PromptTypeSettings ( llm_selector = my_llm_selector )Für Dict / Pydantic müssen Sie die Formatierungsanweisungen angeben ... Dies kann mühsam sein. Deshalb können Sie den Ausgangsparser die Anweisungen basierend auf dem Modell (pydantisch) generieren lassen.
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 )) Hier markieren wir nur eine Funktion als Eingabeaufforderung mit llm_prompt Decorator und verwandeln sie effektiv in eine LLMchain. Anstatt es auszuführen
Standard LLMChain nimmt viel mehr Init -Parameter als nur inputs_variables und ein Aufforderung an ... hier ist dieses Implementierungsdetail im Dekorateur versteckt. So funktioniert es:
Verwenden globaler Einstellungen :
# 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
)Verwenden vordefinierter Eingabeaufforderungstypen
#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 :
...Definieren Sie die Einstellungen direkt im Dekorateur
from langchain . llms import OpenAI
@ llm_prompt (
llm = OpenAI ( temperature = 0.7 ),
stop_tokens = [ " n Observation" ],
...
)
def creative_writer ( book_title : str ) -> str :
...Um eines davon zu bestehen, deklarieren Sie sie einfach in der Funktion (oder verwenden Sie KWARGs, um etwas zu bestehen).
(Sie müssen nicht unbedingt deklariert werden, aber es ist eine gute Praxis, wenn Sie sie verwenden wollen.)
@ 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" ) Es gibt mehrere Optionen, wie die in die Konsole angemeldeten Ausgänge steuern können. Am einfachsten ist es, Env Variable zu definieren: LANGCHAIN_DECORATORS_VERBOSE und auf "wahr" einstellen
Sie können dies auch programmatisch steuern, indem Sie Ihre globalen Einstellungen wie hier gezeigt definieren
Die letzte Option besteht darin, es jeweils pro Fall zu steuern, indem sie einfach im Eingabeaufforderung im ausführlichen Modus turenden:
@llm_prompt(verbose=True)
def your_prompt(param1):
...
PromptWatch IO ist eine Plattform, um Details über alles zu verfolgen und zu verfolgen, was in Langchain -Ausführungen vor sich geht. Es ermöglicht einen einzelnen Zeilenabfall in der Integration, indem Sie Ihren Einstiegspunktcode einwickeln
with PromptWatch():
run_your_code()
Weitere Informationen zu PromPTWatch finden Sie hier: www.promptwatch.io
Feedback, Beiträge und PR werden begrüßt