通过如下设计实现,LCEL 提供了一系列功能,使从基本组件构建复杂链变得容易便捷。
统一接口 :每一个 LCEL对象都实现了 Runnable 接口,该接口定义了一组公共调用方法(invoke
, batch
, stream
, ainvoke
, …)。这使得 LCEL 链本身也可以支持这种调用,即每个 LCEL 对象链本身也是一个 LCEL 对象。
组合原语 :LCEL 提供了一系列原语,可以很容易支持组合链,并行组件,添加回退,动态配置链内部运行机制等等。
为了更好理解 LCEL 的价值,看看使用了 LCEL 之前、之后的案例是很有帮助的。下面,我们将选用入门部分的部分基本案例,进行两者之间的对比。
Invoke 在这个最简单的案例中,我们只需传入一个 topic 字符串,并获得一个相关的笑话字符串:
无 LCEL 的实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from typing import List import openaiprompt_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" )
LCEL实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughprompt = 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 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from typing import Iteratordef 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 )
LCEL 实现 1 2 for chunk in chain.stream("ice cream" ): print (chunk, end="" , flush=True )
批处理 如果需要批量并行运行一批输入,我们还需要新建一个新的函数:
无 LCEL 实现 1 2 3 4 5 6 7 8 from concurrent.futures import ThreadPoolExecutordef 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" ])
LCEL 实现 1 chain.batch(["ice cream" , "spaghetti" , "dumplings" ])
异步 异步版本:
无 LCEL 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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" )
LCEL 实现 1 chain.ainvoke("ice cream" )