LangChain装饰器 ✨ (LangChain Decorators ✨)
LangChain装饰器是在LangChain之上的一层,为编写自定义LangChain提示和链提供了语法糖🍭。
反馈、问题、贡献请在这里提出: ju-bezdek/langchain-decorators
主要原则和优势:
- 更符合Python风格的编写代码方式
- 编写多行提示,不会因缩进而破坏代码流程
- 利用IDE内置的提示、类型检查和弹出文档的支持,快速查看函数的提示、参数等信息
- 充分利用🦜🔗 LangChain生态系统的所有功能
- 添加对可选参数的支持
- 通过将参数绑定到一个类,轻松共享提示之间的参数
这是一个使用LangChain装饰器 ✨编写的简单示例代码:
@llm_prompt
def write_me_short_post(topic:str, platform:str="twitter", audience:str = "developers")->str:
"""
为{platform}平台上关于{topic}的帖子写一个简短的标题。
它应该面向{audience}受众。
(最多15个单词)
"""
return
# 自然运行
write_me_short_post(topic="starwars")
# 或者
write_me_short_post(topic="starwars", platform="redit")
快速入门
安装
pip install langchain_decorators
示例
开始的好方法是查看这里的示例:
定义其他参数
在这里,我们只是将函数标记为一个带有llm_prompt
装饰器的提示,将其有效地转换为LLMChain。而不是运行它...
标准的LLMChain需要比仅输入变量和提示更多的初始化参数... 这个实现细节在装饰器中隐藏起来。 它的工作原理如下:
- 使用全局设置:
# 为所有提示定义全局设置(如果未设置,则chatGPT是当前默认设置)
from langchain_decorators import GlobalSettings
GlobalSettings.define_settings(
default_llm=ChatOpenAI(temperature=0.0), 这是默认设置...可以在这里全局更改
default_streaming_llm=ChatOpenAI(temperature=0.0,streaming=True), 这是默认设置...可以在这里全局更改,将用于流式处理
)
- 使用预定义的提示类型
# 您可以更改默认的提示类型
from langchain_decorators import PromptTypes, PromptTypeSettings
PromptTypes.AGENT_REASONING.llm = ChatOpenAI()
# 或者您可以定义自己的提示类型:
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=["\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}
为{platform}平台上关于{topic}的帖子写一个简短的标题。
它应该面向{audience}受众。
(最多15个单词)
"""
pass
await write_me_short_post(topic="old movies")
简化的流式处理
如果我们想利用流式处理:
- 我们需要将提示定义为异步函数
- 在装饰器上打开流式处理,或者我们可以定义带有流式处理的PromptType
- 使用StreamingContext捕获流
这样,我们只需标记哪个提示应该进行流式处理,而不需要调整我们要使用的LLM,将创建和分发流处理程序传递到我们链的特定部分... 只需在提示/提示类型上打开/关闭流式处理...
只有在流式处理上下文中调用它时,流式处理才会发生... 在那里,我们可以定义一个简单的函数来处理流
# 这个代码示例是完整的,应该可以直接运行
from langchain_decorators import StreamingContext, llm_prompt
# 这将标记提示进行流式处理(如果我们只想在应用程序中流式处理一些提示,但不想传递分发的回调处理程序)
# 请注意,只有异步函数可以进行流式处理(如果不是异步函数,将会出错)
@llm_prompt(capture_stream=True)
async def write_me_short_post(topic:str, platform:str="twitter", audience:str = "developers"):
"""
为{platform}平台上关于{topic}的帖子写一个简短的标题。
它应该面向{audience}受众。
(最多15个单词)
"""
pass
# 只是一个任意的函数,用于演示流式处理... 在真实世界中,将是一些Websockets代码
tokens=[]
def capture_stream_func(new_token:str):
tokens.append(new_token)
# 如果我们想捕获流,我们需要将执行包装在StreamingContext中...
# 这将允许我们捕获流,即使提示调用在更高级别的方法中隐藏
# 只有标记为capture_stream的提示才会在这里被捕获
with StreamingContext(stream_to_stdout=True, callback=capture_stream_func):
result = await run_prompt()
print("流处理完成...我们可以通过交替的颜色区分标记")
print("\n我们捕获了",len(tokens),"个标记🎉\n")
print("这是结果:")
print(result)
提示声明
默认情况下,提示是整个函数文档,除非您标记了您的提示
记录您的提示
我们可以指定我们的文档的哪个部分是提示定义,通过使用带有<prompt>
语言标签的代码块
@llm_prompt
def write_me_short_post(topic:str, platform:str="twitter", audience:str = "developers"):
"""
这是一种很好的方法,将提示作为函数文档的一部分编写,同时为开发人员提供额外的文档。
它需要是一个代码块,标记为`<prompt>`语言
```<prompt>
为{platform}平台上关于{topic}的帖子写一个简短的标题。
它应该面向{audience}受众。
(最多15个单词)
```
现在只有上面的代码块将被用作提示,其余的文档字符串将被用作开发人员的描述。
(它还有一个好处,即IDE(如VS code)将正确显示提示(不尝试将其解析为markdown,因此无法正确显示换行符))
"""
return
聊天消息提示
对于聊天模型来说,将提示定义为一组消息模板非常有用... 这是如何做到的:
@llm_prompt
def simulate_conversation(human_input:str, agent_role:str="a pirate"):
"""
## 系统消息
- 注意在<prompt:_role_>标签中的`:system`后缀
```<prompt:system>
你是一个{agent_role}黑客。你必须像一个黑客一样行动。
你只能用代码回复,使用Python或JavaScript代码块...
例如:
... 不要用其他任何东西回复... 只用代码 - 尊重你的角色。
```
# 人类消息
(我们使用LLM强制执行的真实角色,GPT支持system、assistant、user)
``` <prompt:user>
你好,你是谁
```
一个回复:
``` <prompt:assistant>
\``` python <<- 用\转义内部代码块,它应该是提示的一部分
def hello():
print("Argh... 你好,你这个讨厌的海盗")
\```
```
我们还可以使用占位符添加一些历史记录
```<prompt:placeholder>
{history}
```
```<prompt:user>
{human_input}
```
现在只有上面的代码块将被用作提示,其余的文档字符串将被用作开发人员的描述。
(它还有一个好处,即IDE(如VS code)将正确显示提示(不尝试将其解析为markdown,因此无法正确显示换行符))
"""
pass
这里的角色是模型的本机角色(assistant、user、system用于chatGPT)
可选部分
- 您可以定义整个提示的部分,如果所有的{value}参数都为空(None | ""),则不会渲染整个部分
这个语法如下:
@llm_prompt
def prompt_with_optional_partials():
"""
这个文本总是会被渲染,但是
{? 这个块中的任何内容只有在所有的{value}参数都不为空(None | "")时才会被渲染 ?}
你也可以将它放在单词之间
这也会被渲染{? ,但是
只有当{this_value}和{this_value}都不为空时,这个块才会被渲染?}!
"""
输出解析器
- llm_prompt装饰器会尝试根据输出类型自动检测最佳的输出解析器(如果未设置,则返回原始字符串)
- 列表、字典和pydantic输出也可以原生支持(自动)
# 这个代码示例是完整的,应该可以直接运行
from langchain_decorators import llm_prompt
@llm_prompt
def write_name_suggestions(company_business:str, count:int)->list:
""" 为{company_business}的公司提供{count}个好的名称建议
"""
pass
write_name_suggestions(company_business="sells cookies", count=5)
更复杂的结构
对于字典/Pydantic,您需要指定格式化说明... 这可能很繁琐,这就是为什么您可以让输出解析器根据模型(pydantic)为您生成说明的原因
from langchain_decorators import llm_prompt
from pydantic import BaseModel, Field
class TheOutputStructureWeExpect(BaseModel):
name:str = Field (description="公司的名称")
headline:str = Field( description="公司的描述(用于首页)")
employees:list[str] = Field(description="5-8个虚假员工姓名及其职位")
@llm_prompt()
def fake_company_generator(company_business:str)->TheOutputStructureWeExpect:
""" 生成一个{company_business}的虚假公司
{FORMAT_INSTRUCTIONS}
"""
return
company = fake_company_generator(company_business="sells cookies")
# 以漂亮的格式打印结果
print("公司名称:",company.name)
print("公司描述:",company.headline)
print("公司员工:",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))
更多示例:
- 这些和更多示例也可以在这里的colab笔记本中找到
- 包括使用纯LangChain装饰器重新实现的ReAct Agent