路由器充当专门的模块,用于处理用户查询并从一组预定义选项中进行选择,每个选项都由特定的元数据定义。
核心路由器模块主要有两种类型
LLM 选择器:这些模块将可用选项呈现为文本提示,并使用 LLM 文本完成端点来做出决策。
Pydantic 选择器:这些模块将选项格式化为 Pydantic 模式,并将其传递给函数调用端点,将结果作为 Pydantic 对象返回。
安装¶
In [ ]
!pip install llama-index
# NOTE: This is ONLY necessary in jupyter notebook.
# Details: Jupyter runs an event-loop behind the scenes.
# This results in nested event-loops when we start an event-loop to make async queries.
# This is normally not allowed, we use nest_asyncio to allow it for convenience.
import nest_asyncio
nest_asyncio.apply()
import logging
import sys
# Set up the root logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # Set logger level to INFO
# Clear out any existing handlers
logger.handlers = []
# Set up the StreamHandler to output to sys.stdout (Colab's output)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO) # Set handler level to INFO
# Add the handler to the logger
logger.addHandler(handler)
from llama_index.core import (
VectorStoreIndex,
SummaryIndex,
SimpleDirectoryReader,
ServiceContext,
StorageContext,
)
import openai
import os
from IPython.display import display, HTML
# Setup openai api key
os.environ["OPENAI_API_KEY"] = "sk-..."
NumExpr defaulting to 2 threads.
!mkdir -p 'data/paul_graham/' !wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'
!mkdir -p 'data/paul_graham/'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'
--2024-05-16 05:27:42-- https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.110.133, 185.199.111.133, ... Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 75042 (73K) [text/plain] Saving to: ‘data/paul_graham/paul_graham_essay.txt’ data/paul 0%[ ] 0 --.-KB/s data/paul_graham/pa 100%[===================>] 73.28K --.-KB/s in 0.002s 2024-05-16 05:27:42 (46.3 MB/s) - ‘data/paul_graham/paul_graham_essay.txt’ saved [75042/75042]
# load documents documents = SimpleDirectoryReader("data/paul_graham").load_data()
# load documents
documents = SimpleDirectoryReader("data/paul_graham").load_data()
from llama_index.core.text_splitter import SentenceSplitter # create parser and parse document into nodes parser = SentenceSplitter(chunk_size=1024, chunk_overlap=100) nodes = parser(documents)
from llama_index.core.text_splitter import SentenceSplitter
# create parser and parse document into nodes
parser = SentenceSplitter(chunk_size=1024, chunk_overlap=100)
nodes = parser(documents)
# Summary Index for summarization questions summary_index = SummaryIndex(nodes) # Vector Index for answering specific context questions vector_index = VectorStoreIndex(nodes)
# Summary Index for summarization questions
summary_index = SummaryIndex(nodes)
# Vector Index for answering specific context questions
vector_index = VectorStoreIndex(nodes)
HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
摘要索引查询引擎。
- 向量索引查询引擎。
- # Summary Index Query Engine summary_query_engine = summary_index.as_query_engine( response_mode="tree_summarize", use_async=True, ) # Vector Index Query Engine vector_query_engine = vector_index.as_query_engine()
# Summary Index Query Engine
summary_query_engine = summary_index.as_query_engine(
response_mode="tree_summarize",
use_async=True,
)
# Vector Index Query Engine
vector_query_engine = vector_index.as_query_engine()
from llama_index.core.tools.query_engine import QueryEngineTool, ToolMetadata # Summary Index tool summary_tool = QueryEngineTool.from_defaults( query_engine=summary_query_engine, description="用于回答关于 Paul Graham 随笔《我做了什么》的摘要问题。", ) # Vector Index tool vector_tool = QueryEngineTool.from_defaults( query_engine=vector_query_engine, description="用于从 Paul Graham 随笔《我做了什么》中检索特定上下文。", )
from llama_index.core.tools.query_engine import QueryEngineTool, ToolMetadata
# Summary Index tool
summary_tool = QueryEngineTool.from_defaults(
query_engine=summary_query_engine,
description="Useful for summarization questions related to Paul Graham eassy on What I Worked On.",
)
# Vector Index tool
vector_tool = QueryEngineTool.from_defaults(
query_engine=vector_query_engine,
description="Useful for retrieving specific context from Paul Graham essay on What I Worked On.",
)
您可以使用各种选择器,每种都提供独特的特性。
Pydantic 选择器,仅受 gpt-4 和默认 gpt-3.5-turbo 支持,利用 OpenAI Function Call API。它们不解析原始 JSON,而是生成 pydantic 选择对象。
另一方面,LLM 选择器利用 LLM 生成 JSON 输出,然后解析该输出以查询相关索引。
对于这两种选择器类型,您可以选择路由到单个索引或多个索引。
PydanticSingleSelector¶
使用 OpenAI Function API 在底层生成/解析 pydantic 对象,供路由器选择器使用。
from llama_index.core.query_engine.router_query_engine import RouterQueryEngine from llama_index.core.selectors.llm_selectors import ( LLMSingleSelector, LLMMultiSelector, ) from llama_index.core.selectors.pydantic_selectors import ( PydanticMultiSelector, PydanticSingleSelector, ) # Create Router Query Engine query_engine = RouterQueryEngine( selector=PydanticSingleSelector.from_defaults(), query_engine_tools=[ summary_tool, vector_tool, ], )
from llama_index.core.query_engine.router_query_engine import RouterQueryEngine
from llama_index.core.selectors.llm_selectors import (
LLMSingleSelector,
LLMMultiSelector,
)
from llama_index.core.selectors.pydantic_selectors import (
PydanticMultiSelector,
PydanticSingleSelector,
)
# Create Router Query Engine
query_engine = RouterQueryEngine(
selector=PydanticSingleSelector.from_defaults(),
query_engine_tools=[
summary_tool,
vector_tool,
],
)
response = query_engine.query("What is the summary of the document?")
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Selecting query engine 0: The choice is specifically related to summarization questions about Paul Graham's essay on What I Worked On.. HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))LLMSingleSelector¶
利用 OpenAI(或其他 LLM)在内部解释生成的 JSON 并确定用于路由的子索引。
# Create Router Query Engine query_engine = RouterQueryEngine( selector=LLMSingleSelector.from_defaults(), query_engine_tools=[ summary_tool, vector_tool, ], )
# Create Router Query Engine
query_engine = RouterQueryEngine(
selector=LLMSingleSelector.from_defaults(),
query_engine_tools=[
summary_tool,
vector_tool,
],
)
response = query_engine.query("What is the summary of the document?")
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Selecting query engine 0: This choice indicates that the summary is related to summarization questions specifically about Paul Graham's essay on What I Worked On.. HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))response = query_engine.query("What did Paul Graham do after RICS?")
response = query_engine.query("What did Paul Graham do after RICS?")
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Selecting query engine 1: This choice is more relevant as it focuses on retrieving specific context from Paul Graham's essay on What I Worked On, which would likely provide information on what he did after RICS.. HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))PydanticMultiSelector¶
如果您预期查询会被导向多个索引,建议使用多选择器。此选择器将查询分派给各种子索引,然后通过摘要索引聚合响应,以提供全面答案。
让我们创建一个简单的关键词表索引和相应的工具。¶
from llama_index.core import SimpleKeywordTableIndex keyword_index = SimpleKeywordTableIndex(nodes) keyword_query_engine = keyword_index.as_query_engine() keyword_tool = QueryEngineTool.from_defaults( query_engine=keyword_query_engine, description="用于使用关键词从 Paul Graham 随笔《我做了什么》中检索特定上下文。", )
from llama_index.core import SimpleKeywordTableIndex
keyword_index = SimpleKeywordTableIndex(nodes)
keyword_query_engine = keyword_index.as_query_engine()
keyword_tool = QueryEngineTool.from_defaults(
query_engine=keyword_query_engine,
description="Useful for retrieving specific context using keywords from Paul Graham essay on What I Worked On.",
)
query_engine = RouterQueryEngine( selector=PydanticMultiSelector.from_defaults(), query_engine_tools=[vector_tool, keyword_tool, summary_tool], )
query_engine = RouterQueryEngine(
selector=PydanticMultiSelector.from_defaults(),
query_engine_tools=[vector_tool, keyword_tool, summary_tool],
)
# This query could use either a keyword or vector query engine, so it will combine responses from both
response = query_engine.query(
"What were noteable events and people from the authors time at Interleaf and YC?"
)
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Selecting query engine 0: Retrieving specific context from Paul Graham essay on What I Worked On can provide detailed information about noteable events and people from the author's time at Interleaf and YC.. HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Selecting query engine 1: Retrieving specific context using keywords from Paul Graham essay on What I Worked On can help identify key events and people related to the author's time at Interleaf and YC.. > Starting query: What were noteable events and people from the authors time at Interleaf and YC? query keywords: ['noteable', 'authors', 'time', 'interleaf', 'yc', 'events', 'people'] > Extracted keywords: ['time', 'interleaf', 'yc', 'people'] HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Combining responses from multiple query engines. HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))子问题查询引擎¶
在这里,我们将演示如何使用子问题查询引擎来解决使用多个数据源回答复杂查询的挑战。
子问题查询引擎首先将复杂查询分解为针对每个相关数据源的子问题,然后收集所有中间响应并合成最终响应。
!mkdir -p 'data/10k/' !wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf' !wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/lyft_2021.pdf' -O 'data/10k/lyft_2021.pdf'
!mkdir -p 'data/paul_graham/' !wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'
!mkdir -p 'data/10k/'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/lyft_2021.pdf' -O 'data/10k/lyft_2021.pdf'
--2024-05-16 05:36:06-- https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/uber_2021.pdf Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ... Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 1880483 (1.8M) [application/octet-stream] Saving to: ‘data/10k/uber_2021.pdf’ data/10k/uber_2021. 0%[ ] 0 --.-KB/s data/10k/uber_2021. 100%[===================>] 1.79M --.-KB/s in 0.01s 2024-05-16 05:36:06 (184 MB/s) - ‘data/10k/uber_2021.pdf’ saved [1880483/1880483] --2024-05-16 05:36:06-- https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/lyft_2021.pdf Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ... Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 1440303 (1.4M) [application/octet-stream] Saving to: ‘data/10k/lyft_2021.pdf’ data/10k/lyft_2021. 100%[===================>] 1.37M --.-KB/s in 0.01s 2024-05-16 05:36:06 (120 MB/s) - ‘data/10k/lyft_2021.pdf’ saved [1440303/1440303]
# load documents documents = SimpleDirectoryReader("data/paul_graham").load_data()
lyft_docs = SimpleDirectoryReader(
input_files=["./data/10k/lyft_2021.pdf"]
).load_data()
uber_docs = SimpleDirectoryReader(
input_files=["./data/10k/uber_2021.pdf"]
).load_data()
print(f"Loaded lyft 10-K with {len(lyft_docs)} pages")
print(f"Loaded Uber 10-K with {len(uber_docs)} pages")
Loaded lyft 10-K with 238 pages Loaded Uber 10-K with 307 pages
lyft_index = VectorStoreIndex.from_documents(lyft_docs) uber_index = VectorStoreIndex.from_documents(uber_docs)
lyft_index = VectorStoreIndex.from_documents(lyft_docs)
uber_index = VectorStoreIndex.from_documents(uber_docs)
HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
lyft_engine = lyft_index.as_query_engine(similarity_top_k=3)
lyft_engine = lyft_index.as_query_engine(similarity_top_k=3)
uber_engine = uber_index.as_query_engine(similarity_top_k=3)
response = await lyft_engine.aquery(
"What is the revenue of Lyft in 2021? Answer in millions with page reference"
)
HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))response = await uber_engine.aquery( "What is the revenue of Uber in 2021? Answer in millions, with page reference" )
response = await uber_engine.aquery(
"What is the revenue of Uber in 2021? Answer in millions, with page reference"
)
HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))定义查询引擎工具¶
query_engine_tools = [ QueryEngineTool( query_engine=lyft_engine, metadata=ToolMetadata( name="lyft_10k", description="提供 Lyft 在 2021 年的财务信息", ), ), QueryEngineTool( query_engine=uber_engine, metadata=ToolMetadata( name="uber_10k", description="提供 Uber 在 2021 年的财务信息", ), ), ]
query_engine_tools = [
QueryEngineTool(
query_engine=lyft_engine,
metadata=ToolMetadata(
name="lyft_10k",
description="Provides information about Lyft financials for year 2021",
),
),
QueryEngineTool(
query_engine=uber_engine,
metadata=ToolMetadata(
name="uber_10k",
description="Provides information about Uber financials for year 2021",
),
),
]
from llama_index.core.query_engine.sub_question_query_engine import ( SubQuestionQueryEngine, ) sub_question_query_engine = SubQuestionQueryEngine.from_defaults( query_engine_tools=query_engine_tools )
from llama_index.core.query_engine.sub_question_query_engine import (
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" )
response = await sub_question_query_engine.aquery(
"Compare revenue growth of Uber and Lyft from 2020 to 2021"
)
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Generated 4 sub questions. [uber_10k] Q: What was the revenue of Uber in 2020? [uber_10k] Q: What was the revenue of Uber in 2021? [lyft_10k] Q: What was the revenue of Lyft in 2020? [lyft_10k] Q: What was the revenue of Lyft in 2021? HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK" HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" [lyft_10k] A: $3,208,323 HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" [lyft_10k] A: $2,364,681 HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" [uber_10k] A: $11,139 million HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" [uber_10k] A: $17,455 HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
display(HTML(f'<p style="font-size:20px">{response.response}</p>'))
该文档记述了 Paul Graham 经历各种事业的历程,包括他在写作、编程以及创立 Viaweb 和 Y Combinator 等软件公司方面的经历。它讨论了他对绘画的探索,个人挑战如他母亲的疾病,以及他决定退出 Y Combinator 转而专注于绘画,之后又通过开发一种名为 Bel 的新方言回到了 Lisp 编程。叙述还包括 Graham 对他工作选择的反思,Y Combinator 领导权移交给 Sam Altman,以及他对未来项目和习俗在不断演变领域中影响的思考。
'))返回顶部