为了账号安全,请及时绑定邮箱和手机立即绑定

使用LangChain Agents构建一个数学应用程序

为什么大语言模型在数学上遇到困难,以及如何使用LangChain Agents、OpenAI和Chainlit解决这些限制的教程

在本教程中,我将演示如何使用LangChain代理来创建一个自定义的数学应用程序,该应用程序利用了OpenAI的GPT3.5模型。对于应用程序的前端,我将使用Chainlit,一个易于使用的开源Python框架。这个生成式数学应用程序,我们称之为“Math Wiz”,旨在帮助用户解决数学或推理/逻辑问题。

“Math Wiz”应用架构图。作者绘制。

为什么大语言模型在数学上会遇到困难?

大型语言模型(LLMs)在数学和推理任务方面表现较差,这是许多语言模型的共同特点。造成这种情况的原因有以下几点:

  • 训练数据不足: 一个原因是他们的训练数据有限。语言模型在庞大的文本数据集上训练,可能缺乏足够的数学问题和解决方案。这可能导致对数字的误解,忘记重要的计算步骤,并缺乏定量推理能力。
  • 缺乏数值表示: 另一个原因是LLM被设计成理解和生成文本,处理的是标记而不是数值。大多数基于文本的任务可以有多个合理的答案。然而,数学问题通常只有一个正确的答案。
  • 生成性质: 由于这些语言模型的生成性质,生成一致的准确和精确的答案对数学问题来说对LLM来说可能具有挑战性。

这使得“数学问题”成为利用LangChain代理系统的理想候选者。代理系统使用语言模型与其他工具交互,以分解复杂问题(稍后会详细介绍)。本教程的代码可在我的GitHub上找到。

环境设置

我们可以从创建一个新的 conda 环境开始,使用 python=3.11

    conda create -n math_assistant python=3.11

激活环境:

    conda activate math_assistant

接下来,让我们安装所有必要的库:

    pip install -r requirements.txt

OpenAI 注册并获取自己的密钥,开始调用 gpt 模型。获取密钥后,在你的仓库中创建一个 .env 文件并存储 OpenAI 密钥:

    OPENAI_API_KEY="your_openai_api_key"
应用流程

数学大师的应用流程如下图所示。我们管道中的代理将拥有一组可以用来回答用户查询的工具。大型语言模型(LLM)充当代理的“大脑”,指导其决策。当用户提交一个问题时,代理会使用LLM来选择最合适的工具或工具组合来提供答案。如果代理确定需要多个工具,它还将指定这些工具的使用顺序。

LangChain Agents 分解图。作者绘制

我们的 Math Wiz 应用程序代理将使用以下工具:

  1. 维基百科工具: 这个工具将负责使用维基百科API从维基百科获取最新信息。虽然有一些付费工具和API可以集成到LangChain中,但我将使用维基百科作为应用程序的在线信息来源。
  2. 计算器工具: 这个工具将负责解决用户的数学问题。这包括任何涉及数值计算的问题。例如,如果用户询问4的平方根是多少,这个工具就适用。
  3. 推理工具: 我们应用程序中的最后一个工具是一个推理工具,负责处理基于逻辑或推理的用户问题。任何数学应用题也应由这个工具处理。

现在我们有了一个大致的应用设计,我们可以开始考虑构建这个应用了。

理解LangChain代理

LangChain 代理旨在通过提供更复杂和交互性任务的接口来增强与语言模型的交互。我们可以将代理视为用户与大型语言模型之间的中介。代理试图将看似复杂的用户查询分解为更简单、可操作的步骤,这些步骤是我们的大语言模型可能无法单独处理的。

在我们的应用流程中,我们定义了一些不同的工具,希望在我们的数学应用中使用。根据用户的输入,代理应该决定使用这些工具中的哪一个。如果某个工具不需要使用,则不应使用。LangChain代理可以简化这一过程。这些代理使用语言模型来选择要执行的动作序列。基本上,LLM充当代理的“大脑”,指导它在特定查询中使用哪个工具以及使用顺序。这与LangChain链不同,在链中动作的顺序是硬编码在代码中的。LangChain提供了一套广泛的工具,可以与代理集成。这些工具包括但不限于在线搜索工具、基于API的工具、基于链的工具等。有关LangChain代理及其类型的更多信息,请参阅此链接

每步实施
步骤 1

创建一个 chatbot.py 脚本并导入所需的依赖项:

    from langchain_openai import OpenAI  
    from langchain.chains import LLMMathChain, LLMChain  
    from langchain.prompts import PromptTemplate  
    from langchain_community.utilities import WikipediaAPIWrapper  
    from langchain.agents.agent_types import AgentType  
    from langchain.agents import Tool, initialize_agent  
    from dotenv import load_dotenv  

    load_dotenv()
步骤2

接下来,我们将定义基于OpenAI的语言模型。LangChain提供了langchain-openai包,可以用来定义OpenAI模型的实例。我们将使用OpenAI的gpt-3.5-turbo-instruct模型。dotenv包已经处理了API密钥,因此你不需要在这里显式地定义它:

    llm = OpenAI(model='gpt-3.5-turbo-instruct',  
                 temperature=0)

我们将在这个数学和推理链中使用这个LLM,并且让它成为我们代理的决策者。

步骤3

当构建你自己的代理时,你需要为其提供一个它可以使用的工具列表。除了实际调用的功能外,Tool 还包含一些其他参数:

  • name (str),是必需的,并且在提供给代理的一组工具中必须是唯一的。
  • description (str),是可选的,但推荐使用,因为它用于代理确定工具的使用。

我们现在将创建三个工具。第一个工具将使用Wikipedia API封装器:

    wikipedia = WikipediaAPIWrapper()  
    wikipedia_tool = Tool(name="Wikipedia",  
                          func=wikipedia.run,  
                          description="一个有用的工具,用于在互联网上查找有关世界事件、问题、日期、年份等的信息。对于一般主题来说非常有用。请使用精确的问题。")

在上面的代码中,我们定义了一个Wikipedia API的实例。之后,我们将它包装成了一个LangChain Tool,并指定了名称、功能和描述。

接下来,让我们定义将用于计算任何数值表达式的工具。LangChain 提供了 LLMMathChain,它使用 numexpr Python 库来计算数学表达式。同样重要的是,我们需要明确地定义这个工具的用途。描述对于代理在一组工具中选择合适的工具来回答特定用户查询非常有帮助。对于我们的链式工具,我们将使用 Tool.from_function() 方法。

    problem_chain = LLMMathChain.from_llm(llm=llm)  
    math_tool = Tool.from_function(name="计算器",  
                    func=problem_chain.run,  
                     description="当你需要回答数学问题时,这个工具非常有用。这个工具仅用于回答数学问题,不用于其他任何问题。只能输入数学表达式。")

最后,我们将定义一个基于逻辑/推理的查询工具。首先,我们将创建一个提示,用于指导模型执行特定任务。然后,我们将创建一个简单的LLMChain,并将LLM和提示传递给它。

    word_problem_template = """你是一个推理代理,任务是解决用户的逻辑问题。通过逻辑推理得出答案,并保持事实性。在回答中,清晰地详细说明所涉及的步骤,并给出最终答案。以项目符号的形式提供响应。
    问题:{question} 回答"""

    math_assistant_prompt = PromptTemplate(input_variables=["question"],  
                                           template=word_problem_template  
                                           )  
    word_problem_chain = LLMChain(llm=llm,  
                                  prompt=math_assistant_prompt)  
    word_problem_tool = Tool.from_function(name="推理工具",  
                                           func=word_problem_chain.run,  
                                           description="当你需要回答逻辑/推理问题时,这个工具非常有用。",  
                                        )
步骤4

我们现在将用上面创建的工具初始化我们的代理。我们还将指定LLM来帮助它选择使用哪些工具以及使用顺序:

    agent = initialize_agent(  
        tools=[wikipedia_tool, math_tool, word_problem_tool],  
        llm=llm,  
        agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,  
        verbose=False,  
        handle_parsing_errors=True  
    )  

    print(agent.invoke(  
        {"input": "我有3个苹果和4个橙子。我把一半的橙子送人了,并买了两打新的橙子,还买了三包草莓。每包草莓有30颗。最后我总共有多少水果?"}))

应用对逻辑问题的回复

创建Chainlit应用

我们将使用 Chainlit,一个开源的 Python 框架,来构建我们的应用程序。使用 Chainlit,你可以用几行简单的代码构建对话式 AI 应用程序。要深入了解 Chainlit 的功能以及应用程序的设置,你可以查看我在这里的文章:

使用 Chainlit 和 LangChain 构建聊天机器人应用 在这篇文章中,我们将使用 Chainlit 框架为我们的自定义聊天机器人 Scoopsie 开发一个应用程序界面。

我们将为我们的应用使用两个装饰器函数。这两个函数分别是 @cl.on_chat_start@cl.on_message@cl.on_chat_start 负责包裹所有在用户会话启动时应执行的代码。@cl.on_message 则包含在用户发送查询时应执行的代码片段。

让我们用这两个装饰器函数重构我们的 chatbot.py 脚本。首先,我们从 chatbot.py 脚本中导入 chainlit 包:

    import chainlit as cl

接下来,我们将编写一个围绕 @cl.on_chat_start 装饰器函数的包装函数。我们将在此函数中添加我们的 LLM、工具和代理初始化代码。我们将把 agent 存储在用户会话的变量中,以便在用户发送消息时检索。

    @cl.on_chat_start  
    def math_chatbot():  
        llm = OpenAI(model='gpt-3.5-turbo-instruct',  
                     temperature=0)  

        # 基于推理的工具提示
        word_problem_template = """你是一个推理代理,负责解决用户提出的逻辑问题。通过逻辑推理得出答案,并保持事实性。在回答中,清晰地详细说明所涉及的步骤并给出最终答案。用项目符号提供响应。问题 {question} 回答"""  

        math_assistant_prompt = PromptTemplate(  
            input_variables=["question"],  
            template=word_problem_template  
        )  

        # 基于推理的工具链
        word_problem_chain = LLMChain(llm=llm,  
                                      prompt=math_assistant_prompt)  
        # 基于推理的工具
        word_problem_tool = Tool.from_function(name="推理工具",  
                                               func=word_problem_chain.run,  
                                               description="当你需要回答逻辑/推理问题时,这个工具很有用。"  
                                               )  

        # 计算工具用于算术
        problem_chain = LLMMathChain.from_llm(llm=llm)  
        math_tool = Tool.from_function(name="计算器",  
                                       func=problem_chain.run,  
                                       description="当你需要回答数值问题时,这个工具很有用。此工具仅用于数学问题,不用于其他问题。只输入数学表达式,不要输入文本",  
                                       )  

        # 维基百科工具
        wikipedia = WikipediaAPIWrapper()  
        wikipedia_tool = Tool(  
            name="维基百科",  
            func=wikipedia.run,  
            description="一个有用的工具,用于搜索互联网以查找有关世界事件、问题、日期、年份等的信息。适用于一般主题。使用精确的问题。",  
        )  

        # 代理
        agent = initialize_agent(  
            tools=[wikipedia_tool, math_tool, word_problem_tool],  
            llm=llm,  
            agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,  
            verbose=False,  
            handle_parsing_errors=True  
        )  

        cl.user_session.set("agent", agent)

接下来,让我们定义用于 @cl.on_message 装饰器的包装函数。这个函数将包含当用户向我们的应用发送查询时的代码。我们将首先获取在会话开始时设置的代理,然后异步调用该代理并传入用户的查询。

    @cl.on_message  
    async def process_user_query(message: cl.Message):  
        agent = cl.user_session.get("agent")  

        response = await agent.acall(message.content,  
                                     callbacks=[cl.AsyncLangchainCallbackHandler()])  

        await cl.Message(response["output"]).send()

我们可以使用以下命令运行我们的应用程序:

    chainlit run chatbot.py

该应用程序应在 https://localhost:8000 可用。

让我们也编辑我们仓库中的 chainlit.md 文件。当你运行上述命令时,此文件会自动生成。

    # 欢迎来到 Math Wiz! 🤖  

    你好!👋 我是一个推理工具,可以帮助你解决数学或基于逻辑的推理问题。今天我如何帮助你?

刷新浏览器标签页以使更改生效。

数学奇境前端。图片由作者提供

演示

该应用的演示可以在这里查看:

MathWizDemo youtube.com
测试和验证

现在让我们验证一下我们机器人的性能。我们没有为机器人集成任何记忆功能,所以每个查询都需要作为一个独立的功能调用来处理。让我们问应用程序一些数学问题。为了对比,我将附上每个查询在 Chat GPT 3.5 和我们的 Math Wiz 应用程序中的响应截图。

算术问题

问题1

625的立方根是多少?  

# 正确答案 = 8.5498

数学高手对“625的立方根是什么?”的回应。图片由作者提供。

ChatGPT 对“625 的立方根是什么?”的回复:图片由作者提供。

我们的数学小助手应用回答正确了。然而,ChatGPT的回答是错误的。

问题2

81的立方根是多少?然后乘以13.27,并减去5。

# 正确答案 = 52.4195

数学高手对:“81的立方根是多少?乘以13.27,然后减去5。”图片由作者提供。

Chat GPT 对“81的立方根是多少?乘以13.27,然后减去5。”的回复。图片由作者提供。

我们的数学小助手应用也能正确回答这个问题。然而,ChatGPT的回答仍然不正确。偶尔,ChatGPT也能正确回答数学问题,但这取决于提示工程和多次输入。

推理问题

问题1

让我们问我们的应用程序一些推理/逻辑问题。其中一些问题包含算术部分。我希望代理能够决定在每种情况下使用哪种工具。

    我有3个苹果和4个橙子。我把一半的橙子送人了,并买了两打新的橙子,同时还买了三包草莓。每包草莓有30颗草莓。最后我总共有多少件水果?

    # 正确答案 = 3 + 2 + 24 + 90 = 119

数学高手对总水果计算的回复。作者供图.

ChatGPT 对水果总数的回复。_图片由作者提供。

我们的数学小助手应用能够正确回答这个问题。然而,ChatGPT的回答是错误的。它不仅无谓地复杂化了推理步骤,还未能得出正确的答案。不过,在另一个场合,ChatGPT也曾正确回答过这个问题。这种情况显然是不可靠的。

问题2

史蒂夫的姐姐比他大10岁。史蒂夫出生时冷战刚刚结束。史蒂夫的姐姐是什么时候出生的?

# 正确答案 = 1991 - 10 = 1981

数学高手对基于历史事件的年龄计算的回复。_图片由作者提供。

ChatGPT 对基于历史事件的年龄计算的回复。Image by Author.

我们的数学小助手应用正确回答了这个问题。而ChatGPT的回答再次出现了错误。尽管它正确地推断出了冷战结束的年份,但在数学计算部分却出了错。由于Steve的姐姐比他大10岁,计算她年龄时应该从Steve的出生年份中减去,但ChatGPT却进行了加法运算,这表明它缺乏推理能力。

问题3

     给我汤姆·克鲁斯主演的《壮志凌云》上映的年份的平方值  

    # 正确答案 = 1987² = 3944196

数学高手对基于电影上映日期的算术问题的回答。Image by Author.

ChatGPT 对基于电影上映日期的算术问题的回答。Image by Author.

我们的Math Wiz应用正确回答了这个问题。而ChatGPT的回答再次错误。尽管它正确地计算出了电影的上映日期,但最终的计算结果还是错误的。

结论与下一步计划

在本教程中,我们使用了LangChain代理和工具来创建一个数学解题器,该解题器还可以回答用户的问题,包括推理和逻辑问题。我们看到,我们的Math Wiz应用正确回答了所有问题,然而,ChatGPT给出的大多数答案都是错误的。这是构建该工具的一个很好的第一步。然而,LLMMathChain可能会根据我们提供的输入失败,如果输入包含基于字符串的文本。这可以通过几种不同的方式来解决,例如为您的代码创建错误处理工具,为LLMMathChain添加后处理逻辑,以及使用自定义提示。您还可以通过包含搜索工具来提高工具的有效性,以获得更复杂和准确的结果,因为维基百科有时可能没有更新的信息。您可以在我的GitHub上找到本教程的代码。

如果你觉得这篇教程对你有帮助,不妨给它点五十个赞支持一下。你可以跟随我的分享,查看我在 这里 发布的工作演示、解释和一些有趣的AI项目。来我的 LinkedInX 上打个招呼吧!我在那里分享一些指南、代码片段和其他有用的内容。👋

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消