以编程方式使用文档

~~~ Disclaimer: LangChain decorators is not created by the LangChain team and is not supported by it. ~~~

>LangChain decorators 是位于 LangChain 之上的一个层,为编写自定义 langchain 提示和链提供了语法糖 🍭 > >反馈、问题、贡献 - 请在此处提交问题: >ju-bezdek/langchain-decorators

主要原则和优势:

  • - 更多 pythonic 编写代码的方式
  • - 编写多行提示,不会因缩进而破坏代码流程
  • - 利用 IDE 内置支持进行 **类型提示**, **类型检查** 和 **文档弹窗** 快速查看函数中的提示、参数等。
  • - 利用 🦜🔗 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 naturally
write_me_short_post(topic="starwars")
# or
write_me_short_post(topic="starwars", platform="redit")

快速开始

安装

pip install langchain_decorators
uv add langchain_decorators

示例

了解如何开始的一个好方法是查看这里的示例: - jupyter notebook - colab notebook

# 定义其他参数 这里我们只是用 llm_prompt 装饰器将一个函数标记为提示,实际上将其转换为 LLMChain。与运行它不同

标准 LLMChain 需要的初始化参数比仅输入_变量和提示要多...这里这个实现细节被隐藏在装饰器中。 它的工作原理如下:

  1. 使用 **全局设置**:
# 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
)
  1. 使用预定义的 **提示类型**
#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:
    ...
  1. 在装饰器中 **直接定义设置**
from langchain_openai import OpenAI

@llm_prompt(
    llm=OpenAI(temperature=0.7),
    stop_tokens=["\nObservation"],
    ...
    )
def creative_writer(book_title:str)->str:
    ...

Passing a memory and/or callbacks:

要传递这些参数,只需在函数中声明它们(或使用 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")

简化的流式处理

如果我们想利用流式处理: - 我们需要将提示定义为异步函数 - 在装饰器上开启流式处理,或者我们可以定义启用了流式处理的 PromptType - 使用 StreamingContext 捕获流

This way we just mark which prompt should be streamed, not needing to tinker with what LLM should we use, passing around the creating and distribute streaming handler into particular part of our chain... just turn the streaming on/off on prompt/prompt type...

流式处理只会在我们在流式上下文中调用它时发生...在那里我们可以定义一个简单的函数来处理流

# 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... will 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("\nWe've captured",len(tokens),"tokens🎉\n")
print("Here is the result:")
print(result)

# 提示声明 默认情况下,提示是整个函数文档,除非你将你的提示标记为

记录你的提示

我们可以通过指定一个带有 <prompt> 语言标签的代码块来指定文档的哪一部分是提示定义

@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
    
为我关于 {topic} 关于 {platform} platform. 应该用于 {audience} audience. (最多15个词)
    Now only to code block above will be used as a prompt, and the rest of the docstring will be used as a description for developers.
    (It has also 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` suffix inside the <prompt:_role_> tag


    
你是一个 {agent_role} 黑客。你必须表现得像一个黑客。 你总是用代码回复,使用python或javascript代码块... 例如:

...不要回复其他任何内容..只回复代码——遵守你的角色。

    # human message
    (we are using the real role that are enforced by the LLM - GPT supports system, assistant, user)
    
<prompt:user> 你好,你是谁
    a reply:


    
<prompt:assistant> \
    def hello():
        print("啊...你好啊,讨厌的海盗")
    \

    we can also add some history using placeholder
    
{history}
    
{human_input}
    Now only to code block above will be used as a prompt, and the rest of the docstring will be used as a description for developers.
    (It has also 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

这里的角色是模型原生角色(assistant, user, system for 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}
        is not empty?} !
    """

输出解析器

  • - llm_提示词装饰器原生尝试根据输出类型检测最佳输出解析器。(如果未设置,则返回原始字符串)
  • - list、dict和pydantic输出也被原生支持(自动)
# 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)

更复杂的结构

for dict / pydantic you need to specify the formatting instructions... 这可能很繁琐,这就是为什么你可以让输出解析器根据模型(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:
        """
        
  你是一个名为 {assistant_name}. 你的角色是扮演 {assistant_role}
        
介绍一下你自己(少于20个词)
        """


personality = AssistantPersonality(assistant_name="John", assistant_role="a pirate")

print(personality.introduce_your_self(personality))

更多示例: