在本文中,我将演示如何使用LangChain代理利用 OpenAI 的 GPT3.5 模型创建自定义数学应用程序。对于应用程序前端,我将使用Chainlit,一个易于使用的开源 Python 框架。这个生成数学应用程序,我们称之为“Math Wiz”,旨在帮助用户解决数学或推理/逻辑问题。
环境设置
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 代理
LangChain 代理旨在通过为更复杂的交互式任务提供接口来增强与语言模型的交互。我们可以将代理视为用户和大型语言模型之间的中介。代理旨在将看似复杂的用户查询(我们的 LLM 可能无法独立完成)分解成更简单、可操作的步骤。
在我们的应用流程中,我们定义了几种不同的数学应用工具。根据用户输入,代理应决定使用其中哪些工具。如果某个工具不是必需的,则不应使用。LangChain 代理可以简化我们的工作。这些代理使用语言模型来选择要采取的一系列行动。从本质上讲,LLM 就像代理的 "大脑",指导代理针对特定查询使用哪种工具,以及使用的顺序。这与 LangChain 链不同,后者的操作顺序是硬编码在代码中的。LangChain 提供了大量可与代理集成的工具。这些工具包括但不限于在线搜索工具、基于 API 的工具、基于链的工具等。
实施步骤
第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步
在构建自己的代理时,你需要为它提供一个可以使用的工具列表。除了实际调用的函数外,工具还包括其他一些参数:
现在我们将创建三个工具。第一个是使用维基百科 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.")
最后,我们将定义基于逻辑/推理的查询工具。首先,我们将创建一个提示,指示模型执行特定任务。然后,我们将为该工具创建一个简单的 LLMC 链,并将 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 应用程序
我们将使用开源 Python 框架 Chainlit 来构建应用程序。使用 Chainlit,你只需几行简单的代码就能构建对话式人工智能应用程序。
我们将在应用程序中使用两个装饰器函数。它们分别是 @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、工具和代理初始化代码。我们将在用户会话中的一个变量中存储我们的代理,以便在用户发送消息时检索。
@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 t he 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 上找到。
我们还要编辑软件仓库中的 chainlit.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?
刷新浏览器标签页,以使更改生效。
演示
测试和验证
现在让我们来验证机器人的性能。我们没有在机器人中集成任何内存,因此每次查询都必须调用自己的函数。让我们向应用程序提出几个数学问题。为了便于比较,我附上了 Chat GPT 3.5 和我们的 Math Wiz 应用程序对相同查询的响应截图。
算术问题
问题 1
What is the cube root of 625?
# correct answer = 8.5498
我们的 Math Wiz 应用程序能够正确回答。但是,ChatGPT 的回答是错误的。
问题 2
what is cube root of 81? Multiply with 13.27, and subtract 5.
# correct answer = 52.4195
我们的 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 的回答却不正确。它不仅不必要地复杂了推理步骤,而且也没有得出正确答案。然而,在另一个场合,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
我们的 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 的回答再次出错。尽管它能正确计算出电影的上映日期,但最后的计算结果却不正确。
结论
在本文中,我们使用 LangChain 代理和工具创建了一个数学求解器,它还可以解决用户的推理/逻辑问题。我们看到,我们的 "Math Wiz "应用程序能正确回答所有问题,但 ChatGPT 给出的大多数答案都是错误的。这是构建工具的第一步。不过,如果我们提供的输入包含基于字符串的文本,LLMMathChain 可能会失效。这可以通过几种不同的方式来解决,例如为代码创建错误处理工具、为 LLMMathChain 添加后处理逻辑以及使用自定义提示。由于维基百科有时可能没有更新信息,你还可以通过添加搜索工具来获得更复杂、更准确的结果,从而提高工具的功效。