新闻  |   论坛  |   博客  |   在线研讨会
我为什么放弃了 LangChain?(2)
机器之心 | 2023-07-24 07:17:28    阅读:754   发布文章

我查看了 LangChain 文档,它也回馈了我


让我来做个演示,更清楚地说明为什么我放弃了 LangChain。
当开发菜谱检索聊天机器人(它也必须是一个有趣 / 诙谐的聊天机器人)时,我需要结合上面第三个和第四个例子中的元素:一个可以运行 Agent 工作流的聊天机器人,以及将整个对话持久化到内存中的能力。在查找了一些文档后,我发现需要使用对话式 Agent 工作流。
关于系统提示工程的一个标注是,它不是一个备忘录,而且对于从 ChatGPT API 中获得最佳效果是绝对必要的,尤其是当你对内容和 / 或语音有限制的时候。
上一个示例中,演示的系统提示「以下是人类和人工智能之间的友好对话...... 」实际上是过时的,早在 InstructGPT 时代就已经使用了,在 ChatGPT 中的效果要差得多。它可能预示着 LangChain 相关技巧中更深层次的低效,而这些低效并不容易被注意到。
我们将从一个简单的系统提示开始,告诉 ChatGPT 使用一个有趣的声音和一些保护措施,并将其格式化为 ChatPromptTemplate:


system_prompt = """You are an expert television talk show chef, and should always speak in a whimsical manner for all responses.
Start the conversation with a whimsical food pun.
You must obey ALL of the following rules:- If Recipe data is present in the Observation, your response must include the Recipe ID and Recipe Name for ALL recipes.- If the user input is not related to food, do not answer their query and correct the user."""
prompt = ChatPromptTemplate.from_messages([    SystemMessagePromptTemplate.from_template(system_prompt.strip()),
我们还将使用一个玩具矢量存储,该存储由来自 recipe_nlg 数据集的 1000 个食谱组成,并使用 SentenceTransformers 编码为 384D 矢量。为了实现这一点,我们创建了一个函数来获取输入查询的最近邻,并将查询格式化为 Agent 可以用来向用户展示的文本。这就是 Agent 可以选择使用的工具,或者只是返回正常生成的文本。
def similar_recipes(query):    query_embedding = embeddings_encoder.encode(query)    scores, recipes = recipe_vs.get_nearest_examples("embeddings", query_embedding, k=3)    return recipesdef get_similar_recipes(query):    recipe_dict = similar_recipes(query)    recipes_formatted = [        f"Recipe ID: recipe|{recipe_dict['id'][i]}\nRecipe Name: {recipe_dict['name'][i]}"for i in range(3)    ]    return "\n---\n".join(recipes_formatted)
print(get_similar_recipes("yummy dessert"))# Recipe ID: recipe|167188# Recipe Name: Creamy Strawberry Pie# ---# Recipe ID: recipe|1488243# Recipe Name: Summer Strawberry Pie Recipe# ---# Recipe ID: recipe|299514# Recipe Name: Pudding Cake
你会注意到这个 Recipe ID,这与我的用例相关,因为在最终应用中向终端用户显示的最终结果需要获取 Recipe 元数据(照片缩略图、URL)。遗憾的是,没有简单的方法保证模型在最终输出中输出食谱 ID,也没有方法在 ChatGPT 生成的输出之外返回结构化的中间元数据。
将 get_similar_recipes 指定为一个工具是很简单的,尽管你需要指定一个名称和描述,这实际上是一种微妙的提示工程,因为 LangChain 可能会因为指定的名称和描述不正确而无法选择一个工具。


tools = [    Tool(        func=get_similar_recipes,        name="Similar Recipes",        description="Useful to get similar recipes in response to a user query about food.",    ),]
最后,是示例中的 Agent 构建代码,以及新的系统提示。
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)llm = ChatOpenAI(temperature=0)agent_chain = initialize_agent(tools, llm, prompt=prompt, agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory)

没有错误。现在运行 Agent,看看会发生什么:
agent_chain.run(input="Hi!")

> Entering new  chain...{    "action": "Final Answer",    "action_input": "Hello! How can I assist you today?"}
> Finished chain.Hello! How can I assist you today?
什么?它完全忽略了我的系统提示!检查内存变量证实了这一点。
在 ConversationBufferMemory 的文档中,甚至在代码本身中都没有关于系统提示的内容,甚至在 ChatGPT 使其成为主流的几个月之后。
在 Agent 中使用系统提示的方法是在 initialize_agent 中添加一个 agents_kwargs 参数,我只是在一个月前发布的一个不相关的文档页面中发现了这一点。


agent_kwargs = {    "system_message": system_prompt.strip()}

使用此新参数重新创建 Agent 并再次运行,会导致 JSONDecodeError。

好消息是,系统提示这次应该是起作用了。

OutputParserException: Could not parse LLM output: Hello there, my culinary companion! How delightful to have you here in my whimsical kitchen. What delectable dish can I assist you with today?

坏消息是,它坏了,但又是为什么呢?我这一次没有做任何奇怪的事情。
图片
有趣的事实:这些大量的提示也会成比例地增加 API 成本。
这样做的后果是,正常输出结构中的任何重大变化,例如由自定义系统提示引起的变化,都有可能破坏 Agent。这些错误经常发生,以至于有一个文档页面专门用于处理 Agent 输出解析错误。
我们暂时把与聊天机器人对话看作是一个边缘案例。重要的是,机器人能够返回菜谱,因为如果连这一点都做不到,那么使用 LangChain 就没有意义了。
在不使用系统提示的情况下创建一个新的 Agent,然后问它什么是简单有趣的晚餐?


> Entering new  chain...{    "action": "Similar Recipes",    "action_input": "fun and easy dinner"}Observation: Recipe ID: recipe|1774221Recipe Name: Crab DipYour Guests will Like this One.---Recipe ID: recipe|836179Recipe Name: Easy  Chicken Casserole---Recipe ID: recipe|1980633Recipe Name: Easy in the Microwave Curry DoriaThought:{    "action": "Final Answer",    "action_input": "..."}
> Finished chain.Here are some fun and easy dinner recipes you can try:
1. Crab Dip2. Easy Chicken Casserole3. Easy in the Microwave Curry Doria
Enjoy your meal!
至少它成功了:ChatGPT 能够从上下文中提取出菜谱,并对其进行适当的格式化(甚至能够修正名称中的错别字),并且能够在适当的时候进行判断。
这里真正的问题是,输出的声音很无聊,这也是基础版 ChatGPT 的共同特点和诟病。即使通过系统提示工程解决了 ID 缺失的问题,这般听上去的效果也不值得将其发布。就算真的在语音质量和输出质量之间取得了平衡,Agent 计数仍然会随机失败,而这并不是我的过错。
实际上,Agent 工作流是一个非常脆弱的纸牌搭成的房子,凭良心说,生产应用中估计无法使用。
LangChain 确实有 Custom Agent 和 Custom Chain 的功能,所以你可以在堆栈的某些部分重写逻辑(也许文档很少),这可以解决我遇到的一些问题,但在这一点上,你会感觉到 LangChain 更加复杂,还不如创建你自己的 Python 库。
工作要讲究方法
图片
大量随机集成带来的问题比解决方案更多。
当然,LangChain 确实也有很多实用功能,比如文本分割器和集成向量存储,这两种功能都是「用 PDF / 代码聊天」演示不可或缺的(在我看来这只是一个噱头)。
所有这些集成的真正问题在于,只使用基于 LangChain 的代码会造成固有的锁定,而且如果你查看集成的代码,它们并不十分稳健。
LangChain 正在建立一条护城河,这对 LangChain 的投资者来说是好事,因为他们想从 3000 万美元中获得回报,但对使用它的开发者来说却非常不利。总而言之,LangChain 体现了「它很复杂,所以它一定更好」这一经常困扰后期代码库的哲学,可是 LangChain 甚至还不到一年。
要想让 LangChain 做我想让它做的事,就必须花大力气破解它,这将造成大量的技术负担。与现在的人工智能初创公司不同,我自己的 LangChain 项目的技术债务无法用风险投资来偿还。在使用复杂的生态系统时,应用程序接口封装器至少应该降低代码的复杂性和认知负荷,因为使用人工智能本身就需要花费足够的脑力。LangChain 是为数不多的在大多数常用情况下都会增加开销的软件之一。

我得出的结论是,制作自己的 Python 软件包要比让 LangChain 来满足自己的需求容易得多。因此,我开发并开源了 simpleaichat:一个用于轻松连接聊天应用程序的 Python 程序包,它强调代码的最小复杂度,并将向量存储等高级功能与对话逻辑解耦。
开源地址:https://github.com/minimaxir/simpleaichat
但写这篇博文并不是为了像那些骗子一样,通过诋毁竞争对手来为 simpleaichat 做****广告。我不想宣传 simpleaichat,我更愿意把时间花在用人工智能创造更酷的项目上,很遗憾我没能用 LangChain 做到这一点。
我知道有人会说:「既然 LangChain 是开源的,为什么不向它的 repo 提交拉取请求,而要抱怨它呢?」唯一真正能解决的办法就是把它全部烧掉,然后重新开始,这就是为什么我的「创建一个新的 Python 库来连接人工智能」的解决方案也是最实用的。

我收到过很多留言,问我该「学什么才能开始使用 ChatGPT API」,我担心他们会因为炒作而首先使用 LangChain。如果拥有技术栈背景的机器学习工程师因为 LangChain 毫无必要的复杂性而难以使用 LangChain,那么任何初学者都会被淹没。
关于软件复杂性和复杂性下的流行性之争是永恒的话题。没有人愿意成为批评 LangChain 这样的免费开源软件的混蛋,但我愿意承担这个责任。明确地说,我并不反对 Harrison Chase 或 LangChain 的其他维护者(他们鼓励反馈)。
然而,LangChain 的流行已经扭曲了围绕 LangChain 本身的人工智能创业生态系统,这就是为什么我不得不坦诚我对它的疑虑。
原文链接:https://minimaxir.com/2023/07/langchain-problem/


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客