为什么我们不再使用LangChain来构建我们的人工智能代理

为什么我们不再使用LangChain来构建我们的人工智能代理

当抽象弊大于利时:在生产中使用LangChain的经验教训以及我们应该做些什么

在Octomind,我们使用具有多个LLM的AI代理来自动创建和修复端到端测试。直到几个月前,我们还用语言链框架.

在这篇文章中,我将分享我们与LangChain的斗争,以及为什么用模块化构建块替换它的僵化的高级抽象简化了我们的代码库,并使我们的团队更加快乐和高效。

背景故事

我们在生产中使用LangChain超过12个月,从2023年初开始,然后在2024年将其移除。

LangChain似乎是我们在2023年的最佳选择。它有一个令人印象深刻的组件和工具列表,它的受欢迎程度飙升。它答应”使开发人员能够在一个下午内从一个想法到工作代码。“但随着我们的要求变得更加复杂,问题开始浮出水面,将LangChain变成了一个摩擦源,而不是生产力。

当它的不灵活性开始显现时,我们很快发现自己已经进入了LangChain内部,以改进我们系统的底层行为。但是因为LangChain有意地从您那里抽象出如此多的细节,所以编写我们需要的底层代码通常是不容易或者不可能的。

早期框架的危险

人工智能和LLMs是快速变化的领域,每周都有新概念和新想法涌现。因此,当一个像LangChain这样的框架围绕多种新兴技术创建时,设计经得起时间考验的抽象是非常困难的。

我确信,如果我在他们那样做的时候尝试构建一个像LangChain这样的框架,我也不会做得更好。事后来看,错误很容易被指出,这篇文章的意图不是不公平地批评LangChain的核心开发人员或他们的贡献者。每个人都在尽力而为。

精心设计抽象很难即使需求已经被很好的理解了。但是当你在这样一个不断变化的状态中建模组件时(例如代理),只对较低层次的构件使用抽象是一个更安全的选择。

LangChain抽象的问题

当我们的简单需求与它的使用假设一致时,LangChain最初是有帮助的。但是它的高层次抽象很快使我们的代码变得更加难以理解和难以维护。当我们的团队开始花和构建功能一样多的时间来理解和调试LangChain时,这并不是一个好现象。

LangChain的抽象方法的问题可以通过一个将英语单词翻译成意大利语的小例子来说明。

下面是一个仅使用OpenAI包的Python示例:

from openai import OpenAI

client = OpenAI(api_key="<your_api_key>")
text = "hello!"
language = "Italian"

messages = [
    {"role": "system", "content": "You are an expert translator"},
    {"role": "user", "content": f"Translate the following from English into {language}"},
    {"role": "user", "content": f"{text}"},
]

response = client.chat.completions.create(model="gpt-4o", messages=messages)
result = response.choices[0].message.content

这是易于理解的简单代码,包含一个类和一个函数调用。剩下的就是标准Python了。

让我们对比一下LangChain的版本:

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

os.environ["OPENAI_API_KEY"] = "<your_api_key>"
text = "hello!"
language = "Italian"


prompt_template = ChatPromptTemplate.from_messages(
    [("system", "You are an expert translator"),
     ("user", "Translate the following from English into {language}"),
     ("user", "{text}")]
)

parser = StrOutputParser()
chain = prompt_template | model | parser
result = chain.invoke({"language": language, "text": text})

代码大致做了同样的事情,但相似之处也就到此为止了。

我们现在有三个类和四个函数调用。但最令人担忧的是引入了三个新的抽象概念:

提示模板:向LLM提供提示
输出解析器:处理来自LLM的输出
Chains: LangChain的“LCEL语法”覆盖了Python的|操作符

LangChain所做的只是增加了代码的复杂性,却没有任何明显的好处。

这段代码对于早期原型来说可能是不错的。但是对于生产用途,必须合理理解每个组件所以在真实世界的使用条件下,它不会出乎意料地在你身上爆炸。您必须遵循给定的数据结构,并围绕这些抽象来设计您的应用程序。

让我们看看Python中的另一个抽象比较,这一次是从API获取JSON。

使用内置的http包:

import http.client
import json

conn = http.client.HTTPSConnection("api.example.com")
conn.request("GET", "/data")
response = conn.getresponse()
data = json.loads(response.read().decode())
conn.close()
使用请求包:

import requests

response = requests.get("/data")
data = response.json()

赢家显而易见。这就是好的抽象的感觉。

诚然,这些都是微不足道的例子。但是我的观点是,好的抽象简化了你的代码,减少了理解它所需的认知负荷。

LangChain试图通过以下方式用更少的代码做更多的事情,让您的生活更轻松对你隐瞒细节。但是当这以简单性和灵活性为代价时,‌abstractions就失去了它们的价值。

LangChain也有使用的习惯其他抽象之上的抽象,所以你经常被迫从嵌套抽象的角度来理解如何正确使用API。这不可避免地导致理解大量的堆栈跟踪和调试您没有编写的内部框架代码,而不是实现新的功能。

LangChain对我们开发团队的影响
我们的应用程序大量使用AI代理来执行不同类型的任务,例如测试用例发现、剧作家测试生成和自动修复。

当我们想从具有单个顺序代理的架构转移到更复杂的架构时,LangChain是一个限制因素。例如,生成子代理,并让它们与原始代理进行交互。或者多个相互作用的专业代理。

在另一个实例中,我们需要根据业务逻辑和LLM的输出,动态地改变代理可以访问的工具的可用性。但是LangChain ‌not提供了一种从外部观察代理状态的方法,导致我们缩小了实现的范围,以适应lang chain代理可用的有限功能。

一旦我们删除了它,我们就不再需要将我们的需求转化为LangChain合适的解决方案。我们可以编码。

那么,如果不是LangChain,您应该使用什么框架呢?也许你根本不需要框架。

构建AI应用需要框架吗?

LangChain在早期通过提供LLM特性帮助了我们,因此我们可以专注于构建我们的应用程序。但事后看来,如果没有一个框架,我们的长期发展会更好。

LangChain的一长串组件给人的印象是构建一个LLM驱动的应用程序很复杂。但是大多数应用程序需要的核心组件通常是:

用于LLM通信的客户端
用于函数调用的函数/工具
面向RAG的矢量数据库
用于跟踪、评估等的可观察性平台。

其余的要么是这些组件的助手(例如向量数据库的分块和嵌入),要么是常规的应用程序任务,例如通过数据持久化和缓存来管理文件和应用程序状态。

如果你在没有框架的情况下开始你的人工智能开发之旅,是的,将你自己的工具箱放在一起需要更长的时间,并且需要更多的前期学习和研究。但这是值得花的时间,是对你和你的应用程序的未来的有价值的投资,因为你正在学习你将要操作的领域的基础知识。

在大多数情况下,您对LLM的使用将简单明了。您将主要编写顺序代码,迭代提示,并提高输出的质量和可预测性。大多数任务都可以通过简单的代码和相对较少的外部软件包来完成。

即使使用代理,也不太可能超出简单的代理与代理之间的通信,在预先确定的顺序流中使用业务逻辑来处理代理状态及其响应。你不需要一个框架来实现这个。

虽然代理空间正随着激动人心的可能性和有趣的用例而快速发展,但我们建议在代理使用模式固化的同时保持简单。

借助构建模块保持快速和精简

假设你没有将垃圾代码投入生产,团队创新和迭代的速度是衡量成功的最重要的标准。人工智能领域的许多发展都是由实验和原型开发推动的。

但是框架通常是为加强基于既定使用模式的结构LLM驱动的应用程序还不具备的功能。必须将新想法转化为特定于框架的代码,这限制了您的迭代速度。

构建块方法更喜欢简单的低级代码和精心选择的外部包,保持您的架构精简,以便开发人员可以将注意力集中到他们试图解决的问题上。

积木是简单的东西,你觉得它被全面理解,不太可能改变。例如,向量数据库。这是一种已知的具有一组基本功能的模块化组件,因此可以轻松更换被替换掉了。您的代码库需要精简且适应性强,以最大化您的学习速度和您从每个迭代周期中获得的价值。

.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值