跳转到主要内容

热门内容

今日:


总体:


最近浏览:


Chinese, Simplified

category

关于LLM为何难以解决数学问题,以及如何使用LangChain代理、OpenAI和Chainlit解决这些限制的教程

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


“Math Wiz”的应用程序架构。作者插图。
 

为什么LLM在数学方面很吃力?


众所周知,大型语言模型(LLM)在数学和推理任务方面非常糟糕,这是许多语言模型的共同特点。这有几个不同的原因:

  • 缺乏训练数据:原因之一是他们的训练数据有限。在大量文本数据集上训练的语言模型可能缺乏足够的数学问题和解决方案。这可能导致对数字的误解,忘记重要的计算步骤,以及缺乏定量推理技能。
  • 缺乏数字表示:另一个原因是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”


应用程序流程


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


LangChain代理商解构。作者插图
我们的Math Wiz应用程序的代理将使用以下工具:

  • 维基百科工具:该工具将负责使用维基百科API从维基百科获取最新信息。虽然有付费工具和API可以集成在LangChain中,但我会使用维基百科作为应用程序的在线信息来源。
  • 计算器工具:这个工具将负责解决用户的数学查询。这包括任何涉及数值计算的内容。例如,如果用户问4的平方根是多少,这个工具是合适的。
  • 推理工具:我们应用程序设置中的最后一个工具是推理工具,负责处理基于逻辑/推理的用户查询。任何数学单词问题也应该用这个工具来处理。
    现在我们已经有了一个粗略的应用程序设计,我们可以开始考虑构建这个应用程序了。

了解LangChain智能体


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

在我们的应用程序流中,我们定义了一些不同的工具,希望用于我们的数学应用程序。根据用户输入,代理应该决定使用这些工具中的哪一个。如果不需要工具,则不应使用该工具。LangChain代理可以为我们简化这一过程。这些代理使用语言模型来选择要执行的操作序列。从本质上讲,LLM充当代理的“大脑”,指导它使用哪个工具进行特定查询,以及按哪个顺序进行查询。这与LangChain链不同,在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 impo

步骤2


接下来,我们将定义基于OpenAI的语言模型。LangChain提供了LangChain openai包,可用于定义openai模型的实例。我们将使用OpenAI的gpt-3.5涡轮结构模型。dotenv包已经在处理API密钥,因此您不需要在此处显式定义它:

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


我们将在我们的数学和推理链中使用该LLM,并作为我们的代理的决策者。

步骤3


在构建自己的代理时,您需要向它提供一个可以使用的工具列表。除了调用的实际函数外,该工具还包括一些其他参数:

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

我们现在将创建我们的三个工具。第一个将是使用维基百科API包装器的在线工具:

wikipedia = WikipediaAPIWrapper()
wikipedia_tool = Tool(name="Wikipedia",
                     func=wikipedia.run,
                  description="A useful tool for searching the Internet 
to    find information on world events, issues, dates, years, etc. Worth 
using for general topics. Use precise questions.")


在上面的代码中,我们定义了维基百科API包装器的一个实例。之后,我们将其封装在LangChain工具中,并附上名称、功能和描述。

接下来,让我们定义用于计算任何数值表达式的工具。LangChain提供LLMMathChain,它使用numexpr Python库来计算数学表达式。同样重要的是,我们要明确定义该工具的用途。该描述有助于代理决定使用一组工具中的哪一个工具进行特定的用户查询。对于基于链的工具,我们将使用Tool.from_function()方法。

problem_chain = LLMMathChain.from_llm(llm=llm)
math_tool = Tool.from_function(name="Calculator",
               func=problem_chain.run,
                description="Useful for when you need to 
                answer questions 
about math. This tool is only for math questions and
 nothing else. Only input
math expressions.")


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

word_problem_template = """You are a reasoning agent tasked with solving 
the user's logic-based questions. Logically arrive at the solution, and be 
factual. In your answers, clearly detail the steps involved and give the 
final answer. Provide the response in bullet points. 
Question  {question} Answer"""
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="Reasoning Tool",
                                      func=word_problem_chain.run,
                                      description="Useful for when 
                                      you need 
to answer logic-based/reasoning questions.",
                                   )


步骤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": "I have 3 apples and 4 oranges. I give half of my oranges 
              away and buy two dozen new ones, alongwith three packs of 
              strawberries. Each pack of strawberry has 30 strawberries. 
              How  many total pieces of fruit do I have at the end?"}))

应用程序对逻辑问题的回答
 

创建Chainlit应用程序


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

https://medium.com/@tahreemrasul/building-a-chatbot-application-with-chainlit-and-langchain-3e86da0099a6

我们将为应用程序使用两个decorator函数。这些将是@cl.on_chat_start和@cl.on_message装饰器函数@cl.on_chat_start将负责包装启动用户会话时应该执行的所有代码@cl.on_message将具有我们希望在用户发送查询时执行的代码位。

让我们用这两个decorator函数重新构造我们的chatbot.py脚本。让我们首先将chailit包导入我们的chatbot.py脚本:

import chainlit as cl


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

@cl.on_chat_start
def math_chatbot():
   llm = OpenAI(model='gpt-3.5-turbo-instruct',
                temperature=0)
 
 # prompt for reasoning based tool
   word_problem_template = """You are a reasoning agent tasked with solving 
   the user's logic-based questions. Logically arrive at the solution, and 
   be factual. In your answers, clearly detail the steps involved and give 
   the final answer. Provide the response in bullet points. Question  
   {question} Answer"""
   math_assistant_prompt = PromptTemplate(
       input_variables=["question"],
       template=word_problem_template
   )
   
 # chain for reasoning based tool
   word_problem_chain = LLMChain(llm=llm,
                                 prompt=math_assistant_prompt)
   # reasoning based tool                              
   word_problem_tool = Tool.from_function(name="Reasoning Tool",
                                          func=word_problem_chain.run,
                                          description="Useful for when 
                                          you need to answer 
                                          logic-based/reasoning questions."
                                          )
 # calculator tool for arithmetics
   problem_chain = LLMMathChain.from_llm(llm=llm)
   math_tool = Tool.from_function(name="Calculator",
                                  func=problem_chain.run,
                                  description="Useful for when you 
                                  need to answer numeric questions. 
                                  This tool is only for math questions 
                                  and nothing else. Only input math 
                                  expressions, without text",
                                  )
 
 # Wikipedia Tool
   wikipedia = WikipediaAPIWrapper()
   wikipedia_tool = Tool(
       name="Wikipedia",
       func=wikipedia.run,
       description="A useful tool for searching the Internet to 
       find information on world events, issues, dates, "
                   "years, etc. Worth using for general topics.
                    Use precise questions.",
   )
 
 # agent
   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.

让我们在我们的repo中编辑chailit.md文件。这是在运行上述命令时自动创建的。

# Welcome to Math Wiz! 🤖
Hi there! 👋 I am a reasoning tool to help you with your math 
or logic-based reasoning questions. How can I
help today?


刷新浏览器选项卡以使更改生效。


数学Wiz前端。图片(按作者)
 

Demo


该应用程序的演示可以在此处查看:

https://youtube.com/shorts/v5VZ-zL6N5U

 

测试和验证


现在让我们验证我们的机器人的性能。我们没有将任何内存集成到机器人中,所以每个查询都必须是自己的函数调用。让我们向我们的应用程序提出一些数学问题。为了进行比较,我附上了来自Chat GPT 3.5和我们的Math Wiz应用程序的同一查询的每个响应的屏幕截图。

算术题
问题1

What is the cube root of 625?
# correct answer = 8.5498

Math Wiz对以下问题的回答:“625的立方根是什么?”图片由作者提供。

ChatGPT回应:“625的立方根是什么?”图片由作者提供。
我们的Math Wiz应用程序能够正确回答问题。但是,ChatGPT的响应不正确。

问题2

what is cube root of 81? Multiply with 13.27, and subtract 5.

# correct answer = 52.4195

Math Wiz的回答是:“81的立方根是什么?乘以13.27,减去5。”图片由作者提供。

聊天GPT的回应:“什么是81的立方根?乘以13.27,减去5。”图片由作者提供。
我们的Math Wiz应用程序也能够正确回答这个问题。然而,再一次,ChatGPT的响应是不正确的。偶尔,ChatGPT可以正确回答数学问题,但这需要及时进行工程和多次输入。

推理问题
问题1

让我们向我们的应用程序提出一些推理/逻辑问题。其中一些问题有算术成分。我希望代理决定在每种情况下使用哪种工具。

I have 3 apples and 4 oranges. I give half of my oranges away and buy two 
dozen new ones, alongwith three packs of strawberries. Each pack of 
strawberry has 30 strawberries. How many total pieces of fruit do I have at 
the end?

# correct answer = 3 + 2 + 24 + 90 = 119

 

Math Wiz对总果实计算的回应。图片由作者提供。

ChatGPT对总果实计算的响应。图片由作者提供。
我们的Math Wiz应用程序能够正确回答这个问题。但是,ChatGPT的响应不正确。它不仅不必要地使推理步骤复杂化,而且未能得出正确的答案。然而,在另一个场合,ChatGPT能够正确回答这个问题。这当然是不可靠的。

问题2

Steve's sister is 10 years older than him. Steve was born when the cold war 
ended. When was Steve's sister born?

# correct answer = 1991 - 10 = 1981

#正确答案=1991-10=1981

Math Wiz对基于历史事件的年龄计算的反应。图片由作者提供。

ChatGPT对基于历史事件的年龄计算的响应。图片由作者提供。
我们的Math Wiz应用程序能够正确回答这个问题。ChatGPT的响应再次不正确。尽管它能够正确地计算出冷战结束的年份,但它把数学部分的计算搞砸了。由于比她大10岁,计算姐姐的年龄应该包括减去史蒂夫出生的年份。ChatGPT进行了加法运算,表示缺乏推理能力。

问题3

give me the year when Tom Cruise's Top Gun released raised to the power 2

# correct answer = 1987**2 = 3944196

Math Wiz根据电影上映日期对算术问题的回答。图片由作者提供。

ChatGPT根据电影上映日期对算术问题的回答。图片由作者提供。
我们的Math Wiz应用程序能够正确回答这个问题。ChatGPT的响应再次不正确。尽管它能够正确地计算出电影的上映日期,但最终的计算是错误的。

结论和下一步行动


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

如果你觉得这个教程很有帮助,可以考虑给它五十个掌声来支持它。你可以跟随我分享人工智能领域的工作演示、解释和酷炫的附带项目。来LinkedIn和X上打个招呼吧!我在那里分享指南、代码片段和其他有用的内容。

本文地址
最后修改
星期日, 五月 12, 2024 - 17:55
Article