why-use-LCEL
outline: deep
为什么用 LCEL
我们建议首先阅读 LCEL 入门部分。
LCEL 可以轻松地从基本组件构建复杂的链。它通过提供以下功能来实现此目的:
- 统一的接口:每个 LCEL 对象都实现 Runnable 接口,该接口定义一组通用的调用方法(invoke、batch、stream、ainvoke 等)。这使得 LCEL 对象链也可以自动支持这些调用。也就是说,每个 LCEL 对象链本身就是一个 LCEL 对象。
- 组合原语:LCEL 提供了许多原语,可以轻松组合链、并行化组件、添加后备、动态配置链内部等。
为了更好地理解 LCEL 的价值,了解它的实际应用并思考如何在没有它的情况下重新创建类似的功能会很有帮助。在本演练中,我们将使用入门部分中的基本示例来实现这一点。我们将采用简单的提示+模型链(它在幕后已经定义了很多功能),并看看如何重新创建所有这些功能。
%pip install –upgrade –quiet langchain-core langchain-openai langchain-anthropic
调用
在最简单的情况下,我们只想传入一个主题字符串并返回一个笑话字符串:
- 未用LCEL
- 使用LCEL
from typing import List
import openai
prompt_template = "Tell me a short joke about {topic}"
client = openai.OpenAI()
def call_chat_model(messages: List[dict]) -> str:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
)
return response.choices[0].message.content
def invoke_chain(topic: str) -> str:
prompt_value = prompt_template.format(topic=topic)
messages = [{"role": "user", "content": prompt_value}]
return call_chat_model(messages)
invoke_chain("ice cream")
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
prompt = ChatPromptTemplate.from_template(
"Tell me a short joke about {topic}"
)
output_parser = StrOutputParser()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain = (
{"topic": RunnablePassthrough()}
| prompt
| model
| output_parser
)
chain.invoke("ice cream")
流
如果我们想流式传输结果,我们需要更改我们的函数:
- 未用LCEL
- 使用LCEL
from typing import Iterator
def stream_chat_model(messages: List[dict]) -> Iterator[str]:
stream = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
stream=True,
)
for response in stream:
content = response.choices[0].delta.content
if content is not None:
yield content
def stream_chain(topic: str) -> Iterator[str]:
prompt_value = prompt.format(topic=topic)
return stream_chat_model([{"role": "user", "content": prompt_value}])
for chunk in stream_chain("ice cream"):
print(chunk, end="", flush=True)
for chunk in chain.stream("ice cream"):
print(chunk, end="", flush=True)
批处理
如果我们想并行运行一批输入,我们将再次需要一个新函数:
- 未用LCEL
- 使用LCEL
from concurrent.futures import ThreadPoolExecutor
def batch_chain(topics: list) -> list:
with ThreadPoolExecutor(max_workers=5) as executor:
return list(executor.map(invoke_chain, topics))
batch_chain(["ice cream", "spaghetti", "dumplings"])
chain.batch(["ice cream", "spaghetti", "dumplings"])
异步
如果我们需要异步版本
- 未用LCEL
- 使用LCEL
async_client = openai.AsyncOpenAI()
async def acall_chat_model(messages: List[dict]) -> str:
response = await async_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
)
return response.choices[0].message.content
async def ainvoke_chain(topic: str) -> str:
prompt_value = prompt_template.format(topic=topic)
messages = [{"role": "user", "content": prompt_value}]
return await acall_chat_model(messages)
await ainvoke_chain("ice cream")
chain.ainvoke("ice cream")
LLM代替聊天模式
如果我们想使用完成端点而不是聊天端点:
- 未用LCEL
- 使用LCEL
def call_llm(prompt_value: str) -> str:
response = client.completions.create(
model="gpt-3.5-turbo-instruct",
prompt=prompt_value,
)
return response.choices[0].text
def invoke_llm_chain(topic: str) -> str:
prompt_value = prompt_template.format(topic=topic)
return call_llm(prompt_value)
invoke_llm_chain("ice cream")
from langchain_openai import OpenAI
llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm_chain = (
{"topic": RunnablePassthrough()}
| prompt
| llm
| output_parser
)
llm_chain.invoke("ice cream")