在大语言模型(LLM)应用开发领域,LangChain表达式语言(LCEL)和AgentExecutor一直是开发者的得力助手。然而,随着应用场景的复杂化,这些工具的局限性也日益凸显。本文将深入探讨LCEL和AgentExecutor的不足,并引入一个新的解决方案。
LCEL链表达式的局限性
LangChain表达式语言(LCEL)提供了一种便捷的方式来创建链式应用,将知识库、LLM、prompt、工具调用和输出解析器等组件串联成一个有向无环图。尽管LCEL极大地降低了LLM应用的开发难度,但在处理复杂、动态的对话流程时,它仍然存在一些明显的局限性:
-
线性流程: LCEL链通常是线性的,只能按照预定义的顺序执行步骤。这种线性结构限制了在对话中进行动态路由和条件分支的能力,使得处理复杂对话场景变得困难。
-
状态管理复杂: 在处理多轮对话时,LCEL的状态管理变得非常复杂。每次调用链时,都需要手动传递和更新状态,这不仅增加了代码的复杂性,也提高了出错的可能性。
-
工具集成不直观: 虽然LCEL链可以调用外部工具,但在链的内部结构中集成和协调多个工具的使用并不直观,尤其是在需要根据对话上下文动态选择工具时。
举个例子,在构建一个问题分解策略的并行子问题优化链时,LCEL表达式的复杂性就会显现出来:
# 分解问题链 decomposition_chain = ({"question": RunnablePassthrough()}| decomposition_prompt| ChatOpenAI(model="gpt-4o-mini", temperature=0)| StrOutputParser()| (lambda x: x.strip().split("\n")) )# 子问题答案生成链 sub_question_chain = ({"context": retriever, "question": RunnablePassthrough()}| sub_question_prompt| ChatOpenAI(model="gpt-4o-mini")| StrOutputParser() )# 组装链 chain = ({"question": RunnablePassthrough(),"context": decomposition_chain | {"questions": RunnablePassthrough(),"answers": sub_question_chain.map()} | RunnableLambda(format_qa_pairs)} | prompt | llm_output_str )
这个例子展示了当工具嵌套层级稍微多一些时,LCEL链的构建就会变得相当复杂。
AgentExecutor的局限性
AgentExecutor的出现在一定程度上解决了LCEL的部分缺陷,它允许智能体根据输入动态选择工具和操作。然而,AgentExecutor也存在一些值得注意的局限性:
-
复杂性: AgentExecutor的配置和使用相对复杂,特别是在处理复杂的对话流程和多轮对话时。需要手动管理智能体的状态和工具调用,这增加了开发的难度。
-
动态路由能力有限: 尽管AgentExecutor支持动态选择工具,但在处理复杂的条件分支和动态路由时仍然不够灵活。缺乏一种直观的方式来定义和执行复杂的对话流程。
-
状态持久性不足: AgentExecutor在处理长时间运行的对话时,缺乏内置的状态持久性机制。每次对话重启时,都需要从头开始,无法恢复之前的对话状态。
-
过度封装: AgentExecutor要求被包装的Agent必须符合特定的要求才能使用,例如输入变量固定、输入prompt固定、解析器固定等。这使得二次开发AgentExecutor变得困难。
-
黑盒不可控: 在构建复杂Agent时,无法修改工具的使用顺序,也无法在执行过程中添加人机交互。
引入新概念: 图和状态机
面对LCEL和AgentExecutor的局限性,我们需要一个更灵活、更强大的框架来构建复杂的智能体应用。在介绍这个新框架之前,让我们先通过一个简单的概念来帮助大家认识"图"和"状态机"。
想象一下"带娃状态图"这个场景:
- 状态: 婴儿的行为状态,包括饥饿程度、睡眠状态、体感温度等。
- 事件: 哄睡、喂奶、换尿布等行为。
- 节点: 妈妈(决策者)、奶奶/外婆(检查者)、爸爸(执行者)。
我们可以将这个场景构建成一个状态图:
在这个状态图中:
- 妈妈负责决定需要执行什么事件
- 老人负责判断妈妈的决定是否合理
- 爸爸负责执行具体的事件
- 执行完特定事件后,更新婴儿状态,然后由妈妈再次检测状态继续做决定
这个"带娃状态图"实际上就是一个简化版的图结构和状态机模型。将这个概念应用到LLM/Agent应用开发中,我们可以得到一个更灵活、更强大的框架。
结语
通过分析LCEL和AgentExecutor的局限性,我们认识到在构建复杂LLM应用时需要一个更灵活的框架。图结构和状态机的概念为我们提供了新的思路。在下一篇文章中,我们将介绍一个基于这些概念的新框架——LangGraph,它如何解决现有技术的问题,以及如何使用它来构建更强大的LLM应用。