主题
LlamaIndex 充当你的数据与大语言模型(LLM)之间的桥梁,提供一套工具包,让你围绕数据建立起一个查询接口,用于问答、摘要等多种任务。
本教程将带你用一个 Data Agent 构建上下文增强的聊天机器人。这个由 LLM 驱动的 agent 能够对你的数据智能地执行任务。最终成果是一个聊天机器人 agent,它配备了 LlamaIndex 提供的一组扎实的数据接口工具,用来回答关于你数据的提问。
注:本教程建立在「针对 SEC 10-K 文件建立查询接口」的早期工作之上——可以看这里。
上下文
在这份指南里,我们要构建一个「10-K 聊天机器人」,它使用存放在 Dropbox 上的 UBER 原始 10-K HTML 文件。用户可以与这个聊天机器人对话,询问与 10-K 文件相关的问题。
准备工作
import os
import openai
os.environ["OPENAI_API_KEY"] = "sk-..."
openai.api_key = os.environ["OPENAI_API_KEY"]
import nest_asyncio
nest_asyncio.apply()摄取数据
先把 2019 至 2022 年的原始 10-K 文件下载下来。
# NOTE: the code examples assume you're operating within a Jupyter notebook.
# download files
!mkdir data
!wget "https://www.dropbox.com/s/948jr9cfs7fgj99/UBER.zip?dl=1" -O data/UBER.zip
!unzip data/UBER.zip -d data为把 HTML 文件解析成规整文本,我们使用 Unstructured 库。多亏 llama-index-readers-file 包,我们可以直接对接 Unstructured,把任意文本转换成 LlamaIndex 能够摄取的 Document 格式。
先安装必要的包:
!pip install llama-index-readers-file unstructured然后就可以用 UnstructuredReader 把 HTML 文件解析成一组 Document 对象。
from llama_index.readers.file import UnstructuredReader
from pathlib import Path
years = [2022, 2021, 2020, 2019]
loader = UnstructuredReader()
doc_set = {}
all_docs = []
for year in years:
year_docs = loader.load_data(
file=Path(f"./data/UBER/UBER_{year}.html"), split_documents=False
)
# insert year metadata into each year
for d in year_docs:
d.metadata = {"year": year}
doc_set[year] = year_docs
all_docs.extend(year_docs)为每一年建立向量索引
我们先为每一年各建一个向量索引。每个向量索引都能回答关于该年度 10-K 文件的问题。
逐个构建索引并保存到磁盘。
# initialize simple vector indices
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.core import Settings
Settings.chunk_size = 512
index_set = {}
for year in years:
storage_context = StorageContext.from_defaults()
cur_index = VectorStoreIndex.from_documents(
doc_set[year],
storage_context=storage_context,
)
index_set[year] = cur_index
storage_context.persist(persist_dir=f"./storage/{year}")要从磁盘加载索引,做法如下
# Load indices from disk
from llama_index.core import load_index_from_storage
index_set = {}
for year in years:
storage_context = StorageContext.from_defaults(
persist_dir=f"./storage/{year}"
)
cur_index = load_index_from_storage(
storage_context,
)
index_set[year] = cur_index建立子问题查询引擎,跨 10-K 文件合成答案
既然我们手上有四年的文档,那么除了询问某一年的 10-K 文件,我们可能还想提出需要对全部 10-K 文件做分析才能回答的问题。
为应对这种情况,我们可以使用子问题查询引擎。它把一个查询拆解成若干子查询,每个子查询由对应的单个向量索引作答,再把结果综合起来回答总查询。
LlamaIndex 为索引(及查询引擎)提供了若干包装器,让它们能被查询引擎和 agent 使用。我们先为每个向量索引定义一个 QueryEngineTool。每个工具都有名称和描述;这两样正是 LLM agent 用来决定该选哪个工具的信息。
from llama_index.core.tools import QueryEngineTool, ToolMetadata
individual_query_engine_tools = [
QueryEngineTool(
query_engine=index_set[year].as_query_engine(),
metadata=ToolMetadata(
name=f"vector_index_{year}",
description=f"useful for when you want to answer queries about the {year} SEC 10-K for Uber",
),
)
for year in years
]现在可以创建子问题查询引擎了,它让我们能够跨多份 10-K 文件合成答案。我们传入上面定义的 individual_query_engine_tools,以及用于执行子查询的 llm。
from llama_index.llms.openai import OpenAI
from llama_index.core.query_engine import SubQuestionQueryEngine
query_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=individual_query_engine_tools,
llm=OpenAI(model="gpt-3.5-turbo"),
)搭建聊天机器人 agent
我们用 LlamaIndex 的 Data Agent 来搭建外层的聊天机器人 agent,它可以访问一组工具。具体来说,我们使用 FunctionAgent,它利用 OpenAI API 的函数调用能力。我们既要把先前为每个索引(对应某一年)分别定义的工具用上,也要用上一个步骤里为子问题查询引擎定义的那个工具。
先为子问题查询引擎定义一个 QueryEngineTool:
query_engine_tool = QueryEngineTool(
query_engine=query_engine,
metadata=ToolMetadata(
name="sub_question_query_engine",
description="useful for when you want to answer queries that require analyzing multiple SEC 10-K documents for Uber",
),
)然后,把我们上面定义的各个工具合并成 agent 的一个工具列表:
tools = individual_query_engine_tools + [query_engine_tool]最后调用 FunctionAgent() 创建 agent,把上面定义的工具列表传给它。
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
agent = FunctionAgent(tools=tools, llm=OpenAI(model="gpt-4.1"))测试 agent
现在我们可以用各种提问来测试这个 agent。
如果只发一句简单的 “hello”,agent 不会动用任何工具。
response = await agent.run("hi, i am bob")
print(str(response))Hello Bob! How can I assist you today?如果问的是某一年 10-K 相关的问题,agent 会选用对应那一年的向量索引工具。
response = await agent.run(
"What were some of the biggest risk factors in 2020 for Uber?"
)
print(str(response))Some of the biggest risk factors for Uber in 2020 were:
1. The adverse impact of the COVID-19 pandemic and actions taken to mitigate it on the business.
2. The potential reclassification of drivers as employees, workers, or quasi-employees instead of independent contractors.
3. Intense competition in the mobility, delivery, and logistics industries, with low-cost alternatives and well-capitalized competitors.
4. The need to lower fares or service fees and offer driver incentives and consumer discounts to remain competitive.
5. Significant losses incurred and the uncertainty of achieving profitability.
6. The risk of not attracting or maintaining a critical mass of platform users.
7. Operational, compliance, and cultural challenges related to the workplace culture and forward-leaning approach.
8. The potential negative impact of international investments and the challenges of conducting business in foreign countries.
9. Risks associated with operational and compliance challenges, localization, laws and regulations, competition, social acceptance, technological compatibility, improper business practices, liability uncertainty, managing international operations, currency fluctuations, cash transactions, tax consequences, and payment fraud.
These risk factors highlight the challenges and uncertainties that Uber faced in 2020.最后,如果让它跨年度比较/对照风险因素,它就会动用子问题查询引擎工具。
cross_query_str = "Compare/contrast the risk factors described in the Uber 10-K across years. Give answer in bullet points."
response = await agent.run(cross_query_str)
print(str(response))Here is a comparison of the risk factors described in the Uber 10-K reports across years:
2022 Risk Factors:
- Potential adverse effect if drivers were classified as employees instead of independent contractors.
- Highly competitive nature of the mobility, delivery, and logistics industries.
- Need to lower fares or service fees to remain competitive.
- History of significant losses and expectation of increased operating expenses.
- Impact of future pandemics or disease outbreaks on the business and financial results.
- Potential harm to the business due to economic conditions and their effect on discretionary consumer spending.
2021 Risk Factors:
- Adverse impact of the COVID-19 pandemic and actions to mitigate it on the business.
- Potential reclassification of drivers as employees instead of independent contractors.
- Highly competitive nature of the mobility, delivery, and logistics industries.
- Need to lower fares or service fees and offer incentives to remain competitive.
- History of significant losses and uncertainty of achieving profitability.
- Importance of attracting and maintaining a critical mass of platform users.
2020 Risk Factors:
- Adverse impact of the COVID-19 pandemic on the business.
- Potential reclassification of drivers as employees.
- Highly competitive nature of the mobility, delivery, and logistics industries.
- Need to lower fares or service fees to remain competitive.
- History of significant losses and potential future expenses.
- Importance of attracting and maintaining a critical mass of platform users.
- Operational and cultural challenges faced by the company.
2019 Risk Factors:
- Competition with local companies.
- Differing levels of social acceptance.
- Technological compatibility issues.
- Exposure to improper business practices.
- Legal uncertainty.
- Difficulties in managing international operations.
- Fluctuations in currency exchange rates.
- Regulations governing local currencies.
- Tax consequences.
- Financial accounting burdens.
- Difficulties in implementing financial systems.
- Import and export restrictions.
- Political and economic instability.
- Public health concerns.
- Reduced protection for intellectual property rights.
- Limited influence over minority-owned affiliates.
- Regulatory complexities.
These comparisons highlight both common and unique risk factors that Uber faced in different years.搭建聊天循环
聊天机器人已经就绪,再走几步就能搭出一个基本的交互循环,和我们这个由 SEC 文件增强的聊天机器人对话了!
agent = FunctionAgent(tools=tools, llm=OpenAI(model="gpt-4.1"))
while True:
text_input = input("User: ")
if text_input == "exit":
break
response = await agent.run(text_input)
print(f"Agent: {response}")下面是这个循环运行时的一个例子:
User: What were some of the legal proceedings against Uber in 2022?
Agent: In 2022, Uber faced several legal proceedings. Some of the notable ones include:
1. Petition against Proposition 22: A petition was filed in California alleging that Proposition 22, which classifies app-based drivers as independent contractors, is unconstitutional.
2. Lawsuit by Massachusetts Attorney General: The Massachusetts Attorney General filed a lawsuit against Uber, claiming that drivers should be classified as employees and entitled to protections under wage and labor laws.
3. Allegations by New York Attorney General: The New York Attorney General made allegations against Uber regarding the misclassification of drivers and related employment violations.
4. Swiss social security rulings: Swiss social security rulings classified Uber drivers as employees, which could have implications for Uber's operations in Switzerland.
5. Class action lawsuits in Australia: Uber faced class action lawsuits in Australia, with allegations that the company conspired to harm participants in the taxi, hire-car, and limousine industries.
It's important to note that the outcomes of these legal proceedings are uncertain and may vary.
User:Notebook
可以查看我们的对应 notebook。