Skip to main content

LangChain 装饰器 ✨

免责声明:`LangChain 装饰器` 不是由 LangChain 团队创建的,也不受其支持。

LangChain 装饰器 是 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

示例

开始的好主意是查看这里的示例:

定义其他参数

在这里,我们只是将一个函数标记为提示,使用 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:
...

传递内存和/或回调:

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

这样我们只需标记哪些提示应该进行流式处理,而无需调整我们应该使用哪个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... 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"):
"""
这里是将提示写入函数文档字符串的好方法,并为开发者提供额外的文档。

它需要是一个代码块,标记为 `<prompt>` 语言
```<prompt>
Write me a short header for my post about {topic} for {platform} platform.
It should be for {audience} audience.
(Max 15 words)
```

现在只有上面的代码块将作为提示使用,文档字符串的其余部分将作为开发者的描述。
(这还有一个好处,即 IDE(如 VS code)将正确显示提示(不会尝试将其解析为 markdown,因此不会正确显示换行))
"""
return

聊天消息提示

对于聊天模型,定义提示作为一组消息模板非常有用……以下是如何做到这一点:

@llm_prompt
def simulate_conversation(human_input:str, agent_role:str="a pirate"):
"""
## 系统消息
- 注意 `<prompt:_role_>` 标签内的 `:system` 后缀


```<prompt:system>
You are a {agent_role} hacker. You mus 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.
```

# 人类消息
(我们使用 LLM 强制执行的真实角色 - GPT 支持系统、助手、用户)
``` <prompt:user>
Helo, who are you
```
回复:


``` <prompt:assistant>
\``` python <<- escaping inner code block with \ that should be part of the prompt
def hello():
print("Argh... hello you pesky pirate")
\```
```

我们还可以使用占位符添加一些历史记录
```<prompt:placeholder>
{history}
```
```<prompt:user>
{human_input}
```

现在,只有上面的代码块将用作提示,其余的文档字符串将用作开发人员的描述。
(这也有一个不错的好处,IDE(如 VS code)将正确显示提示(而不是尝试将其解析为 markdown,因此不会正确显示换行符))
"""
pass

这里的角色是模型的本地角色(助手、用户、系统,用于 chatGPT)

可选部分

  • 您可以定义整个提示的可选部分
  • 如果该部分的任何输入缺失,则整个部分将不会被渲染

其语法如下:

@llm_prompt
def prompt_with_optional_partials():
"""
这段文本将始终被渲染,但

{? 该块中的任何内容仅在所有 {value}s 参数不为空(None | "")时渲染 ?}

您也可以将其放置在单词之间
这也将被渲染{? , 但
该块仅在 {this_value} 和 {this_value}
不为空时渲染?} !
"""

输出解析器

  • llm_prompt 装饰器会根据输出类型自动尝试检测最佳输出解析器。(如果未设置,它将返回原始字符串)
  • 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)

更复杂的结构

对于 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):
"""
我们可以在提示中引用任何 {field} 或 {a_property}...并在方法中与 {function_kwarg} 结合使用
"""


@llm_prompt
def introduce_your_self(self)->str:
"""
``` <prompt:system>
你是一个名叫 {assistant_name} 的助手。
你的角色是充当 {assistant_role}
```
```<prompt:user>
介绍一下你自己(少于20个词)
```
"""



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

print(personality.introduce_your_self(personality))

更多示例:


此页面是否有帮助?


您还可以留下详细的反馈 在 GitHub 上