SubQuestionQueryEngine

在实际应用中,我们经常会遇到需要跨越多个文档来回答复杂查询的场景。

在本 Notebook 中,我们将深入探讨如何通过将复杂查询分解为更简单的子查询,并利用 SubQuestionQueryEngine 来生成答案,从而解决跨越多个文档的复杂查询问题。

安装

!pip install llama-index
!pip install llama-index-llms-anthropic
!pip install llama-index-embeddings-huggingface

设置 API 密钥

import os
os.environ['ANTHROPIC_API_KEY'] = 'YOUR ANTHROPIC API KEY'

设置 LLM 和 Embedding 模型

我们将使用 Anthropic 最新发布的 Claude-3 Opus LLM。

from llama_index.llms.anthropic import Anthropic
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
llm = Anthropic(temperature=0.0, model='claude-3-opus-20240229')
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-base-en-v1.5")
from llama_index.core import Settings
Settings.llm = llm
Settings.embed_model = embed_model
Settings.chunk_size = 512

设置日志

# 注意:这仅在 jupyter notebook 中是必需的。
# 详情:Jupyter 在后台运行一个事件循环。
#          这会导致我们在启动事件循环以进行异步查询时出现嵌套事件循环。
#          这通常是不允许的,我们使用 nest_asyncio 来方便地允许它。
import nest_asyncio

nest_asyncio.apply()

import logging
import sys

# 设置根记录器
logger = logging.getLogger()
logger.setLevel(logging.INFO)  # 将记录器级别设置为 INFO

# 清除所有现有的处理程序
logger.handlers = []

# 设置 StreamHandler 以输出到 sys.stdout (Colab 的输出)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)  # 将处理程序级别设置为 INFO

# 将处理程序添加到记录器
logger.addHandler(handler)

from IPython.display import display, HTML

下载数据

我们将使用 Uber 和 Lyft 2021 年的 10K SEC 文件。

!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O './uber_2021.pdf'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/lyft_2021.pdf' -O './lyft_2021.pdf'
--2024-03-08 07:07:32--  https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/uber_2021.pdf
正在解析 raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.108.133, 185.199.110.133, ...
正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... 已连接。
已发送 HTTP 请求,等待响应... 200 OK
文件大小:1880483 (1.8M) [application/octet-stream]
正在保存到 ‘./uber_2021.pdf’

./uber_2021.pdf     100%[===================>]   1.79M  --.-KB/s    在 0.02s 内

2024-03-08 07:07:32 (87.4 MB/s) - ‘./uber_2021.pdf’ 已保存 [1880483/1880483]

--2024-03-08 07:07:33--  https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/lyft_2021.pdf
正在解析 raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... 已连接。
已发送 HTTP 请求,等待响应... 200 OK
文件大小:1440303 (1.4M) [application/octet-stream]
正在保存到 ‘./lyft_2021.pdf’

./lyft_2021.pdf     100%[===================>]   1.37M  --.-KB/s    在 0.02s 内

2024-03-08 07:07:33 (74.9 MB/s) - ‘./lyft_2021.pdf’ 已保存 [1440303/1440303]

加载数据

from llama_index.core import SimpleDirectoryReader
lyft_docs = SimpleDirectoryReader(input_files=["lyft_2021.pdf"]).load_data()
uber_docs = SimpleDirectoryReader(input_files=["uber_2021.pdf"]).load_data()
print(f'已加载 Lyft 10-K 文件,包含 {len(lyft_docs)} 页')
print(f'已加载 Uber 10-K 文件,包含 {len(uber_docs)} 页')
已加载 Lyft 10-K 文件,包含 238 页
已加载 Uber 10-K 文件,包含 307 页

索引数据

from llama_index.core import VectorStoreIndex
lyft_index = VectorStoreIndex.from_documents(lyft_docs[:100])
uber_index = VectorStoreIndex.from_documents(uber_docs[:100])

创建查询引擎

lyft_engine = lyft_index.as_query_engine(similarity_top_k=5)
uber_engine = uber_index.as_query_engine(similarity_top_k=5)

查询

response = await lyft_engine.aquery('What is the revenue of Lyft in 2021? Answer in millions with page reference')
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"

根据第 79 页的合并运营报表,Lyft 在截至 2021 年 12 月 31 日的财年总收入为 32.083 亿美元。

response = await uber_engine.aquery('What is the revenue of Uber in 2021? Answer in millions, with page reference')
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"

根据第 77 页的合并运营报表,Uber 在截至 2021 年 12 月 31 日的财年收入为 174.55 亿美元。

创建工具

from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.core.query_engine import SubQuestionQueryEngine

query_engine_tools = [
    QueryEngineTool(
        query_engine=lyft_engine,
        metadata=ToolMetadata(name='lyft_10k', description='提供 2021 年 Lyft 财务信息')
    ),
    QueryEngineTool(
        query_engine=uber_engine,
        metadata=ToolMetadata(name='uber_10k', description='提供 2021 年 Uber 财务信息')
    ),
]

创建 SubQuestionQueryEngine

sub_question_query_engine = SubQuestionQueryEngine.from_defaults(query_engine_tools=query_engine_tools)

查询

response = await sub_question_query_engine.aquery('Compare revenue growth of Uber and Lyft from 2020 to 2021')
HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
生成了 4 个子问题。
 [1;3;38;2;237;90;200m[uber_10k] Q: What was Uber's revenue in 2020?
 [0m [1;3;38;2;90;149;237m[uber_10k] Q: What was Uber's revenue in 2021?
 [0m [1;3;38;2;11;159;203m[lyft_10k] Q: What was Lyft's revenue in 2020?
 [0m [1;3;38;2;155;135;227m[lyft_10k] Q: What was Lyft's revenue in 2021?
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;11;159;203m[lyft_10k] A: 根据 Lyft 的合并运营报表数据,Lyft 在 2020 年的总收入为 2,364,681,000 美元。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;90;149;237m[uber_10k] A: 根据 Uber 的合并运营报表,Uber 在 2021 年的收入为 174.55 亿美元。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;237;90;200m[uber_10k] A: 根据 Uber 的合并运营报表,Uber 在 2020 年的收入为 111.39 亿美元。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;155;135;227m[lyft_10k] A: 根据 Lyft 的合并运营报表,Lyft 在截至 2021 年 12 月 31 日的财年总收入为 3,208,323,000 美元。这包括:

- 根据 ASC 606 的客户合同收入为 2,957,979,000 美元
- 根据 ASC 842 的租赁收入为 250,344,000 美元

因此,Lyft 在截至 2021 年 12 月 31 日的财年总收入为 3,208,323,000 美元。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))

从 2020 年到 2021 年,Uber 和 Lyft 的收入都实现了显著增长: Uber 的收入从 2020 年的 111.39 亿美元增长到 2021 年的 174.55 亿美元,同比增长约 56.7%。 Lyft 的总收入从 2020 年的 2,364,681,000 美元增长到 2021 年的 3,208,323,000 美元,增长了约 35.7%。 因此,虽然 Lyft 的收入基数较低,但 Uber 在 2020 年至 2021 年间的收入增长百分比更高(分别为 56.7% 和 35.7%)。 由于 2020 年新冠疫情对两家公司的叫车业务产生了重大影响,两家公司在 2021 年都出现了强劲反弹。但 Uber 的收入增长速度更快,达到了 175 亿美元,而 Lyft 为 32 亿美元。

response = await sub_question_query_engine.aquery('Compare the investments made by Uber and Lyft')
HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
生成了 4 个子问题。
 [1;3;38;2;237;90;200m[uber_10k] Q: What investments did Uber make in 2021
 [0m [1;3;38;2;90;149;237m[uber_10k] Q: What was the total amount invested by Uber in 2021
 [0m [1;3;38;2;11;159;203m[lyft_10k] Q: What investments did Lyft make in 2021
 [0m [1;3;38;2;155;135;227m[lyft_10k] Q: What was the total amount invested by Lyft in 2021
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;90;149;237m[uber_10k] A: 根据提供的内容,Uber 在 2021 年进行了以下投资:

- 收购业务 23 亿美元(净额,扣除已收购现金)
- 购买可交易证券 11 亿美元
- 购买非可交易股权证券 9.82 亿美元
- 购买应收票据 2.97 亿美元
- 购买物业和设备 2.98 亿美元

因此,Uber 在 2021 年的总投资约为 50 亿美元,涵盖了业务收购、可交易和非可交易证券、应收票据以及物业和设备购买。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;11;159;203m[lyft_10k] A: 根据提供的内容,Lyft 在 2021 年投资于可交易证券和定期存款:

- Lyft 在 2021 年购买了价值 38 亿美元的可交易证券。这些可交易证券包括投资级别的可供出售的债务证券。

- Lyft 在 2021 年还投资了价值 5 亿美元的定期存款。这些定期存款按成本计价,接近公允价值。

截至 2021 年 12 月 31 日,Lyft 的投资组合的加权平均剩余期限不到一年。Lyft 的投资政策旨在最大限度地降低信用损失风险。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;237;90;200m[uber_10k] A: 根据提供的内容,Uber 在 2021 年的投资旨在:

- 通过激励措施、折扣和促销活动,增加使用其平台的司机、消费者、商户、托运人和运营商的数量
- 在现有市场和新市场进行扩张
- 增加研发支出
- 扩大营销渠道和运营
- 雇佣更多员工
- 在其平台增加新产品和新服务

内容表明,Uber 预计由于持续进行此类投资而导致运营费用大幅增加,因此在短期内会产生亏损。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
 [1;3;38;2;155;135;227m[lyft_10k] A: 根据上下文中提供的财务信息,现金流量表显示 Lyft 在截至 2021 年 12 月 31 日的财年,投资活动产生的净现金为 2.67 亿美元。这主要包括:

- 可交易证券的销售和到期所得款项 38 亿美元
- 定期存款到期 6.755 亿美元
- 部分被购买可交易证券 38 亿美元和定期存款 5 亿美元所抵消

因此,虽然销售/到期所得款项总额约为 45 亿美元,但 Lyft 将大部分资金进行了再投资,2021 年的净新增投资约为 2.67 亿美元。
 [0mHTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))

2021 年,Uber 和 Lyft 的投资类型有所不同: Uber 的总投资约为 50 亿美元,其中包括: - 23 亿美元用于收购业务 - 11 亿美元用于可交易证券 - 9.82 亿美元用于非可交易股权证券 - 2.97 亿美元用于应收票据 - 2.98 亿美元用于物业和设备 Lyft 的净投资约为 2.67 亿美元,主要用于: - 可交易证券,购买额为 38 亿美元,但销售和到期额也为 38 亿美元 - 定期存款 5 亿美元,但到期额为 6.755 亿美元 因此,Uber 大量投资于收购业务、证券、票据和物业,而 Lyft 则专注于短期可交易证券和定期存款,并将大部分收益进行了再投资。与 Lyft 的净投资相比,Uber 的总投资额要大得多。