赞
踩
- 作者:Mariya Mansurova翻译:欧阳锦
- 校对:赵茹萱
-
-
- 本文约4000字,建议阅读10分钟
- 本文通过两个使用了OpenAI API去构建LLM以解决简单任务的两个具体用例来讨论LLM对分析师未来工作的影响和启发。
第1 部分:使用工具赋能 ChatGPT
图源DALL-E 3
我想我们每个人在过去的一年中都至少想过一次ChatGPT是否(或者更确切地说何时)能够取代你的角色。我也不例外。
我们有一个共识,即生成式人工智能最近的突破将极大地影响我们的个人生活和工作。然而,目前还不清楚我们的角色将如何随着时间的推移而变化。
花大量时间思考未来可能出现的不同场景及其概率可能很有趣,但我建议采取一种完全不同的方法——尝试自己构建原型。首先,它相当具有挑战性和乐趣。其次,它将帮助我们以更有条理的方式看待我们的工作。第三,这将使我们有机会在实践中尝试最前沿的方法之一,大型语言模型(LLM)代理。
在本文中,我们将从简单的开始,了解LLM如何利用工具并完成简单的任务。但在接下来的文章中,我们将更深入地探讨LLM代理的不同方法和最佳实践。
那么,让旅程开始吧!
在继续LLM之前,让我们尝试定义什么是分析以及我们作为分析师要做什么任务。
我的座右铭是,分析团队的目标是帮助产品团队在可用时间内根据数据做出正确的决策。这是一个很好的使命,但为了定义LLM分析师的范围,我们应该进一步分解分析工作。
我喜欢Gartner 提出的框架。它确定了四种不同的数据和分析技术:
描述性分析 —— 回答诸如“发生了什么?”之类的问题。例如,12月份的收入是多少?此方法包括报告任务和使用BI工具。
诊断分析 —— 更进一步,提出诸如“为什么会发生某些事情?”之类的问题。例如,为什么收入比上年下降10%?这种技术需要对数据进行更多的深入挖掘和切片。
预测分析 —— 使我们能够获得诸如“会发生什么?”之类的问题的答案。这种方法的两个基石是预测(预测一切照常情况下的未来)和模拟(对不同的可能结果进行建模)。
规范性分析 —— 会影响最终决策。常见的问题是“我们应该关注什么?”或“我们怎样才能将体积增加10%?”。
通常,公司会逐步经历所有这些阶段。如果您的公司尚未掌握描述性分析(您没有数据仓库、BI工具或指标定义),那么几乎不可能开始查看预测和不同的场景分析。所以,这个框架也能体现出公司的数据成熟度。
同样,当分析师从初级晋升为高级时,他们可能会经历所有这些阶段,从明确的报告任务开始,逐步发展到模糊的战略问题。因此,这个框架对于个人层面也具有相关性。
如果我们回到LLM支持的分析师,我们应该专注于描述性分析和报告任务。最好从基础开始。因此,我们将重点学习LLM以了解有关数据的基本问题。
我们已经确定了第一个原型的重点。因此,我们准备好继续讨论技术问题并讨论LLM代理和工具的概念。
我们之前在使用LLM的时候(比如这里做主题建模),我们自己在代码中描述了具体的步骤。 例如,让我们看一下下面的逻辑链。 首先,我们要求模型确定客户评论的情绪。 然后,根据情绪,从评论中提取文本中提到的优点或缺点。
图源作者
在这个例子中,我们清楚地定义了LLM的行为,LLM很好地解决了这个任务。然而,如果我们构建更高层次和更模糊的东西,比如由LLM驱动的分析师,那这种方法就行不通。
如果你曾经担任分析师或与分析师一起工作过至少一天,就会知道分析师会收到大量不同的问题和询问,从基本问题开始(例如“昨天我们的网站上有多少客户?”或“您能为我们明天的董事会会议制作一个图表吗?”)到非常高级的问题(例如,“主要的客户痛点是什么?”或“我们下一步应该推出哪个市场?”)。不用说,描述所有可能的场景是不可行的。
然而,有一种方法可以帮助我们——代理。代理的核心思想是使用LLM作为推理引擎,可以选择下一步做什么以及何时将最终答案返回给客户。这听起来非常接近我们的行为:我们得到一个任务,定义所需的工具,使用它们,然后在准备好时返回最终答案。
与代理相关的基本概念(我上面已经提到过)是工具。工具是LLM 可以调用来获取缺失信息的函数(例如,执行 SQL、使用计算器或调用搜索引擎)。工具至关重要,因为它们可以让您将LLM提升到一个新的水平并与世界互动。在本文中,我们将主要关注作为工具的 OpenAI 功能。
OpenAI拥有经过微调的模型,能够与函数配合使用,以便:
你可以将带有描述的函数列表传递给模型;
如果它与你的查询相关,模型将返回一个函数调用——函数名称和调用它的输入参数。
可以在文档中找到更多信息以及支持功能的模型的最新列表。
将函数与LLM一起使用有两个突出的用例:
标记和提取——在这些情况下,函数用于确保模型的输出格式。你将得到结构化函数调用,而不是通常的内容输出。
工具和路由——这是一个更令人兴奋的用例,允许你创建代理
让我们从更简单的提取用例开始,了解如何使用OpenAI函数。
您可能想知道标记和提取之间有什么区别。这些术语非常接近。唯一的区别在于模型是提取文本中呈现的信息,还是为文本添加标签以提供新信息(即定义语言或情感)。
图源作者
由于我们决定专注于描述性分析和报告任务,因此让我们使用以下这种方法来构建传入的数据请求并提取以下组件:指标(metrics)、维度(dimensions)、过滤器(filters)、周期(period)和所需的输出(output)。
图源作者
这将是一个提取的示例,因为我们只需要文本中存在的信息。
首先,我们需要定义函数。OpenAI需要JSON格式的函数描述。该JSON将传递给LLM,因此我们需要告诉它所有上下文:该函数的作用以及如何使用它。
以下是JSON函数的示例。 我们已指定:
函数本身的名称(name)和描述(description);
每个参数的类型(type)和描述(description);
函数所需输入参数的列表。
extraction_functions = [ { "name": "extract_information", "description": "extracts information", "parameters": { "type": "object", "properties": { "metric": { "type": "string", "description": "main metric we need to calculate, for example, 'number of users' or 'number of sessions'", }, "filters": { "type": "string", "description": "filters to apply to the calculation (do not include filters on dates here)", }, "dimensions": { "type": "string", "description": "parameters to split your metric by", }, "period_start": { "type": "string", "description": "the start day of the period for a report", }, "period_end": { "type": "string", "description": "the end day of the period for a report", }, "output_type": { "type": "string", "description": "the desired output", "enum": ["number", "visualisation"] } }, "required": ["metric"], }, } ]
在此用例中无需实现该函数本身,因为我们不会使用它。我们仅以函数调用的结构化方式获得LLM响应。
现在,我们可以使用标准的OpenAI Chat Completion API来调用该函数。我们传递给API调用:
模型:我使用了可以使用函数的ChatGPT 3.5 Turbo;
消息列表:用于设置上下文和用户请求的一条系统消息;
我们之前定义的函数列表。
import openai messages = [ { "role": "system", "content": "Extract the relevant information from the provided request." }, { "role": "user", "content": "How did number of iOS users change over time?" } ] response = openai.ChatCompletion.create( model = "gpt-3.5-turbo-1106", messages = messages, functions = extraction_functions ) print(response)
结果,我们得到了以下JSON:
{ "id": "chatcmpl-8TqGWvGAXZ7L43gYjPyxsWdOTD2n2", "object": "chat.completion", "created": 1702123112, "model": "gpt-3.5-turbo-1106", "choices": [ { "index": 0, "message": { "role": "assistant", "content": null, "function_call": { "name": "extract_information", "arguments": "{\"metric\":\"number of users\",\"filters\":\"platform='iOS'\",\"dimensions\":\"date\",\"period_start\":\"2021-01-01\",\"period_end\":\"2021-12-31\",\"output_type\":\"visualisation\"}" } }, "finish_reason": "function_call" } ], "usage": { "prompt_tokens": 159, "completion_tokens": 53, "total_tokens": 212 }, "system_fingerprint": "fp_eeff13170a" }
请记住,函数和函数调用将计入令牌限制并且会产生费用。
该模型返回了一个函数调用而不是一个常见的响应:我们可以看到内容为空,并且finish_reason等于function_call。在响应中,还有函数调用的输入参数:
metric = "number of users",
filters = "platform = 'iOS'",
dimensions = "date",
period_start = "2021-01-01",
period_start = "2021-12-31",
output_type = "visualisation".
该模型做得非常好。唯一的问题是它毫无根据地假定了这个时期(period)。我们可以通过向系统消息添加更明确的指导来修复此问题,例如,“从提供的请求中提取相关信息。仅提取初始请求中提供的信息;不要添加任何其他东西。如果缺少某些内容,请返回部分信息。”
默认情况下,模型决定是否独立使用函数(function_call = 'auto')。我们可以要求它每次都返回特定的函数调用,或者根本不使用函数。
# always calling extract_information function response = openai.ChatCompletion.create( model = "gpt-3.5-turbo-1106", messages = messages, functions = extraction_functions, function_call = {"name": "extract_information"} ) # no function calls response = openai.ChatCompletion.create( model = "gpt-3.5-turbo-1106", messages = messages, functions = extraction_functions, function_call = "none" )
我们已经有了第一个使用LLM 函数的工作程序。棒极了!然而,用JSON来描述函数并不是很方便。接下来让我们讨论一下如何更轻松地做到这一点。
为了更方便地定义函数,我们可以用Pydantic。Pydantic是最流行的Python数据验证库。
我们已经使用 Pydantic来定义LangChain输出解析器。
首先,我们需要创建一个继承自BaseModel类的类并定义所有字段(函数的参数)。
- from pydantic import BaseModel, Field
- from typing import Optional
-
-
- class RequestStructure(BaseModel):
- """extracts information"""
- metric: str = Field(description = "main metric we need to calculate, for example, 'number of users' or 'number of sessions'")
- filters: Optional[str] = Field(description = "filters to apply to the calculation (do not include filters on dates here)")
- dimensions: Optional[str] = Field(description = "parameters to split your metric by")
- period_start: Optional[str] = Field(description = "the start day of the period for a report")
- period_end: Optional[str] = Field(description = "the end day of the period for a report")
- output_type: Optional[str] = Field(description = "the desired output", enum = ["number", "visualisation"])
然后,我们可以使用LangChain将Pydantic类转换为OpenAI函数。
- from langchain.utils.openai_functions import convert_pydantic_to_openai_function
- extract_info_function = convert_pydantic_to_openai_function(RequestStructure,
- name = 'extract_information')
LangChain验证了我们提供的类。例如,它确保指定功能描述,因为LLM需要它才能使用该工具。
结果,我们得到了相同的JSON来传递给LLM,但现在我们将其表示为Pydantic类。
- {'name': 'extract_information',
- 'description': 'extracts information',
- 'parameters': {'title': 'RequestStructure',
- 'description': 'extracts information',
- 'type': 'object',
- 'properties': {'metric': {'title': 'Metric',
- 'description': "main metric we need to calculate, for example, 'number of users' or 'number of sessions'",
- 'type': 'string'},
- 'filters': {'title': 'Filters',
- 'description': 'filters to apply to the calculation (do not include filters on dates here)',
- 'type': 'string'},
- 'dimensions': {'title': 'Dimensions',
- 'description': 'parameters to split your metric by',
- 'type': 'string'},
- 'period_start': {'title': 'Period Start',
- 'description': 'the start day of the period for a report',
- 'type': 'string'},
- 'period_end': {'title': 'Period End',
- 'description': 'the end day of the period for a report',
- 'type': 'string'},
- 'output_type': {'title': 'Output Type',
- 'description': 'the desired output',
- 'enum': ['number', 'visualisation'],
- 'type': 'string'}},
- 'required': ['metric']}}
现在,我们可以在对OpenAI的调用中使用它。让我们从OpenAI API切换到LangChain,使我们的API调用更加模块化。
让我们定义一个链来从请求中提取所需的信息。我们将使用LangChain,因为它是LLM最流行的框架。如果您以前没有使用过它,我建议你在我之前的一篇文章中学习一些基础知识。
我们的链条很简单。它由OpenAI模型和带有一个变量请求(request)(用户消息)的提示组成。
我们还使用绑定(bind)函数将函数(functions)参数传递给模型。 绑定(bind)函数允许我们为模型指定不属于输入的常量参数(例如函数(functions)或温度(temperature))。
- from langchain.prompts import ChatPromptTemplate
- from langchain.chat_models import ChatOpenAI
-
-
- model = ChatOpenAI(temperature=0.1, model = 'gpt-3.5-turbo-1106')\
- .bind(functions = [extract_info_function])
-
-
- prompt = ChatPromptTemplate.from_messages([
- ("system", "Extract the relevant information from the provided request. \
- Extract ONLY the information presented in the initial request. \
- Don't add anything else. \
- Return partial information if something is missing."),
- ("human", "{request}")
- ])
-
-
- extraction_chain = prompt | model
现在是时候尝试我们的功能了。我们需要使用调用方法并传递请求(request)。
extraction_chain.invoke({'request': "How many customers visited our site on iOS in April 2023 from different countries?"})
在输出中,我们得到了没有任何内容但带有函数调用的AIMessage。
- AIMessage(
- content='',
- additional_kwargs={
- 'function_call': {
- 'name': 'extract_information',
- 'arguments': '''{
- "metric":"number of customers", "filters":"device = 'iOS'",
- "dimensions":"country", "period_start":"2023-04-01",
- "period_end":"2023-04-30", "output_type":"number"}
- '''}
- }
- )
至此,我们学会了如何使用LangChain中的OpenAI函数来获得结构化输出。
下篇中,我们将继续讨论更有趣的用例——工具和路由。敬请期待!
本文灵感来自DeepLearning.AI 的“Functions, Tools and Agents with LangChain”课程。
原文标题:Can LLMs Replace Data Analysts? Building An LLM-Powered Analyst
原文链接:https://towardsdatascience.com/can-llms-replace-data-analysts-building-an-llm-powered-analyst-851578fa10ce
编辑:黄继彦
校对:林亦霖
译者简介
欧阳锦,一名在埃因霍温理工大学就读的硕士生。喜欢数据科学和人工智能相关方向。欢迎不同观点和想法的交流与碰撞,对未知充满好奇,对热爱充满坚持。
翻译组招募信息
工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。
你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。
其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。
点击文末“阅读原文”加入数据派团队~
转载须知
如需转载,请在开篇显著位置注明作者和出处(转自:数据派ID:DatapiTHU),并在文章结尾放置数据派醒目二维码。有原创标识文章,请发送【文章名称-待授权公众号名称及ID】至联系邮箱,申请白名单授权并按要求编辑。
发布后请将链接反馈至联系邮箱(见下方)。未经许可的转载以及改编者,我们将依法追究其法律责任。
点击“阅读原文”拥抱组织
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。