Lanchchain Decorators - это слой на вершине Langchain, который обеспечивает синтаксический сахар? Для написания пользовательских подсказок и цепочек Langchain
Примечание: это неофициальный дополнение к библиотеке Лангчейна. Это не пытается конкурировать, просто чтобы облегчить его. Много идей здесь полностью самоуверенные
Вот простой пример кода, написанного с декораторами 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" )Основные принципы и преимущества:
pythonic способ написания кодаБыстрый старт
Быстрое объявления
Функции LLM (OpenAI функции)
Упрощенная потоковая передача
Автоматический выбор LLM
Более сложные структуры
Привязывание подсказки с объектом
Определение пользовательских настроек
Отладка
Прохождение памяти, обратный вызов, остановка и т. Д.
Другой
pip install langchain_decoratorsХорошая идея о том, как начать, - это просмотреть примеры здесь:
По умолчанию подсказка - это все документы функции, если вы не отметите свою подсказку
Мы можем указать, какая часть наших документов является оперативным определением, указав блок кода с языковой тег
@ 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 Для моделей чата очень полезно для определения подсказки как набор шаблонов сообщений ... вот как это сделать:
@ 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))
"""
passРоли здесь являются модельными нативными ролями (помощник, пользователь, система для CHATGPT)
Синтаксис для этого заключается в следующем:
@ 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 )В настоящее время поддерживается только для последних моделей чата OpenAI
Все, что вам нужно сделать, это аннотировать вашу функцию с помощью декоратора @llm_function.
Это проанализирует описание для LLM (первый согласованный абзац рассматривается как описание функции)
и описания параметров ASO (на данный момент поддерживаются записи Google, Numpy и Spihnx)
По умолчанию формат DocString автоматически разрешается, но установка формата DocString может немного ускорить ситуацию. - auto (по умолчанию): формат автоматически выводится из DocString - google : DocString анализируется как отметка (см. Формат Docstring Google) - numpy : Docstring анализируется как отметка (см. Numpy Docstring Format) - sphinx : Docstring Parseds Pomformat (см.
Лучший способ определить Enum - это аннотация типа с использованием 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 Альтернатива буквальному, чтобы аннотировать аргумент «перечисления» , вы можете использовать этот «Типовой», подобный формату: ["value_a" | "value_b"] ... если будут разыграны. Этот текст тоже будет частью описания ... если вы не хотите, вы можете использовать эту нотацию в качестве нотации типа. Пример:
Args:
message_type (["email" | "sms"]): type of a message / channel how to send the message
Затем вы передаете эти функции в качестве аргументов и @llm_prompt (аргумент должен быть назван functions
Вот как его использовать:
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 )Кроме того, вы также можете добавить аргумент
function_callв свою подсказку LLM для управления поведением GPT.
- Если вы установите значение на «нет» - он отключит вызов функции на данный момент, но он все еще может их увидеть (полезно сделать какое -то рассуждение/планирование, прежде чем вызовать функцию)
- Если вы установите значение на «Auto» - GPT выберет использовать или использовать функции
- Если вы установите значение на имя функции / или функции, которую он сам (декораторы будут обрабатывать разрешение того же имени, что и в схеме), он заставит GPT использовать эту функцию
Если вы используете аргумент функций, вывод всегда будет 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
"""
... Если вы хотите увидеть, как была построена схема, вы можете использовать метод get_function_schema , который добавляется к функции декоратором:
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 ) Чтобы добавить результат в память / agent_scratchpad, вы можете использовать to_function_message для генерации функции, который LLM будет интерпретировать как результат инструмента / функции
Поставщик функций позволяет вам более динамически предоставлять набор функций LLM, например, список функций - на основе ввода. Это также позволяет вам дать уникальное имя для каждой функции для этого LLM. Это может быть полезно по двум причинам:
Функциональные схемы (и особенно их описания) являются важными инструментами для руководства LLM. Если вы включите объявление динамической функции, вы можете (повторно) использовать те же атрибуты приглашения для основной подсказки также в схеме 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 , ...])Это только для иллюстрации, полностью исполняемый пример доступен здесь, в примерах кода
Если мы хотим использовать потоковую передачу:
Таким образом, мы просто отмечаем, какое подсказку следует транслировать, не нуждаясь в том, чтобы возиться с тем, что мы должны использовать LLM, передавая обработчик создания и распределения потоковой передачи в определенную часть нашей цепи ... просто включите/выключить потоковую передачу на подсказке/подсказку ...
Потоковая передача произойдет только в том случае, если мы будем его в контексте потоковой передачи ... Там мы можем определить простую функцию для обработки потока
# 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 )В реальной жизни могут быть ситуации, когда контекст будет расти над окном базовой модели, которую вы используете (например, история длинного чата) ... но, поскольку это может произойти только несколько раз, было бы здорово, если бы только в этом сценарии (обычно более дорогой) модель с большим окном контекста будет использоваться, и в противном случае мы использовали бы более дешевую.
Теперь вы можете сделать это с 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" ))Этот класс позволяет вам определить последовательность LLMS с правилом, основанным на длине подсказки и ожидаемой длине генерации ... и только после того, как пороговое значение будет передано, более дорогой модель будет использоваться автоматически.
Вы можете определить его в GlobalSettings:
langchain_decorators . GlobalSettings . define_settings (
llm_selector = my_llm_selector # pass the selector into global settings
)Примечание. Начиная с версией v0.0.10 вы там, что LLMSelector находится в предварительно определенных настройках по умолчанию. Вы можете переопределить его, предоставив вам собственные или настроив LLM по умолчанию или потоковую передачу по умолчанию LLM
Или в конкретный тип подсказки:
from langchain_decorators import PromptTypes
class MyCustomPromptTypes ( PromptTypes ):
MY_TUBO_PROMPT = PromptTypeSettings ( llm_selector = my_llm_selector )Для DICT / Pydantic вам нужно указать инструкции по форматированию ... это может быть утомительно, поэтому вы можете позволить выходному парсеру генерировать вам инструкции на основе модели (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 )) Здесь мы просто отмечаем функцию как подсказку с декоратором llm_prompt , эффективно превращая ее в LLMCHAIN. Вместо того, чтобы запускать это
Стандартный LLMCHAIN занимает гораздо больше параметров init, чем просто inputs_variables и resment ... Вот эта деталь реализации скрыта в декораторе. Вот как это работает:
Используя глобальные настройки :
# 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
)Использование предопределенных типов
#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 :
...Определите настройки непосредственно в декораторе
from langchain . llms import OpenAI
@ llm_prompt (
llm = OpenAI ( temperature = 0.7 ),
stop_tokens = [ " n Observation" ],
...
)
def creative_writer ( book_title : str ) -> str :
...Чтобы пройти любой из них, просто объявите их в функции (или используйте Kwargs, чтобы пройти что -либо)
(Их не обязательно нужно объявить, но это хорошая практика, если вы собираетесь их использовать)
@ 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" ) Есть несколько параметров, как управлять выходами, вошли в консоль. Самый простой способ - определить переменную env: LANGCHAIN_DECORATORS_VERBOSE и установить ее на «true»
Вы также можете управлять этим программно, определив свои глобальные настройки, как показано здесь
Последний вариант - контролировать его по каждому случаю, просто позаботившись в режиме Verbose в приглашении:
@llm_prompt(verbose=True)
def your_prompt(param1):
...
PromptWatch IO - это платформа для отслеживания и отслеживания подробностей обо всем, что происходит в казнях Langchain. Это позволяет одну строку сбросить интеграцию, просто обернув код точки входа в
with PromptWatch():
run_your_code()
Узнайте больше о recavlewatch здесь: www.promptwatch.io
Отзывы, взносы и пиар приветствуются