LangChain Decorators ✨
免责声明:`LangChain decorators` 并非由 LangChain 团队创建,也不受其支持。
LangChain decorators是 LangChain 之上的一个封装,它提供了语法糖 🍭,用于编写自定义的 langchain 提示和链有关反馈、问题、贡献,请在此处提出 issue: ju-bezdek/langchain-decorators
主要原则和优势:
- 更具
pythonic风格的代码编写方式 - 编写多行提示,而不破坏代码流程的缩进
- 利用 IDE 内置的 提示、类型检查 和 带文档的弹出窗口 功能,快速查看函数以了解提示、它使用的参数等
- 利用 🦜🔗 LangChain 生态系统的所有强大功能
- 添加对 可选参数 的支持
- 通过将参数绑定到一个类来轻松共享它们
以下是使用 LangChain Decorators ✨ 编写的代码的简单示例
@llm_prompt
def write_me_short_post(topic:str, platform:str="twitter", audience:str = "developers")->str:
"""
为我关于 {topic} 的帖子写一个短标题,目标平台是 {platform}。
受众是 {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 除了 inputs_variables 和 prompt 之外还需要更多的 init 参数……这里是隐藏在装饰器中的实现细节。 这是它的工作原理:
- 使用 全局设置:
# 为所有提示定义全局设置(如果未设置 - 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_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}
为我关于 {topic} 的帖子写一个短标题,目标平台是 {platform}。
受众是 {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"):
"""
为我关于 {topic} 的帖子写一个短标题,目标平台是 {platform}。
受众是 {audience}。
(最多 15 个词)
"""
pass
# 只是一个演示流的任意函数... 在实际世界中会是一些 WebSocket 代码
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("Stream finished ... 我们可以通过交替的颜色区分 token")
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>
为我关于 {topic} 的帖子写一个短标题,目标平台是 {platform}。
受众是 {audience}。
(最多 15 个词)
现在只有上面的代码块将被用作提示,而文档字符串的其余部分将被用作开发人员的描述。 (它还有一个很好的好处是 IDE(如 VS code)将正确显示提示(不尝试将其解析为 markdown,因此不会正确显示新行)) """ return
## Chat 消息提示
对于聊天模型,将提示定义为一组消息模板非常有用……方法如下:
```python
@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 支持 system, assistant, user)
Helo, who are you
回复:
\``` python <<- 使用 \ 转义内部代码块,该代码块应成为提示的一部分
def hello():
print("Argh... hello you pesky pirate")
\```
我们还可以使用占位符添加一些历史记录
{history}
{human_input}
现在只有上面的代码块将被用作提示,而文档字符串的其余部分将被用作开发人员的描述。 (它还有一个很好的好处是 IDE(如 VS code)将正确显示提示(不尝试将其解析为 markdown,因此不会正确显示新行)) """ pass
这里的角色是模型原生角色(chatGPT 的 assistant, user, system)
# 可选部分
- 您可以定义提示的整个部分为可选
- 如果部分中的任何输入缺失,则整个部分都不会被渲染
语法如下:
```python
@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: ",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>
You are an assistant named {assistant_name}.
Your role is to act as {assistant_role}
Introduce your self (in less than 20 words)
"""
personality = AssistantPersonality(assistant_name="John", assistant_role="a pirate")
print(personality.introduce_your_self(personality))
# 更多示例:
- 这些以及其他一些示例也可在 [colab notebook here](https://colab.research.google.com/drive/1no-8WfeP6JaLD9yUtkPgym6x0G9ZYZOG#scrollTo=N4cf__D0E2Yk) 中找到
- 包括使用纯粹的 langchain 装饰器重新实现的 [ReAct Agent](https://colab.research.google.com/drive/1no-8WfeP6JaLD9yUtkPgym6x0G9ZYZOG#scrollTo=3bID5fryE2Yp)