赞
踩
RAG(检索增强生成)包括以下步骤:
这个过程通过利用从数据源检索到的额外信息,使LLM能够生成更准确和具有上下文相关性的回答。
LlamaIndex提供以下工具,可以快速搭建可投入生产的RAG系统:
RAG(检索增强生成)包括五个关键阶段:加载(Loading)、索引(Indexing)、存储(Storing)、查询(Querying)和评估(Evaluation)。
在LlamaIndex中,我们可以使用大语言模型的API接口或者本地大语言模型。例如:
使用ChatGPT
from llama_index.llms.openai import OpenAI
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
Settings.llm = OpenAI(temperature=0.2, model="gpt-4")
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
documents,
)
使用HuggingFace的大语言模型
from llama_index.core import PromptTemplate # Transform a string into input zephyr-specific input def completion_to_prompt(completion): return f"<|system|>\n</s>\n<|user|>\n{completion}</s>\n<|assistant|>\n" # Transform a list of chat messages into zephyr-specific input def messages_to_prompt(messages): prompt = "" for message in messages: if message.role == "system": prompt += f"<|system|>\n{message.content}</s>\n" elif message.role == "user": prompt += f"<|user|>\n{message.content}</s>\n" elif message.role == "assistant": prompt += f"<|assistant|>\n{message.content}</s>\n" # ensure we start with a system prompt, insert blank if needed if not prompt.startswith("<|system|>\n"): prompt = "<|system|>\n</s>\n" + prompt # add final assistant prompt prompt = prompt + "<|assistant|>\n" return prompt import torch from llama_index.llms.huggingface import HuggingFaceLLM from llama_index.core import Settings Settings.llm = HuggingFaceLLM( model_name="HuggingFaceH4/zephyr-7b-beta", tokenizer_name="HuggingFaceH4/zephyr-7b-beta", context_window=3900, max_new_tokens=256, generate_kwargs={"temperature": 0.7, "top_k": 50, "top_p": 0.95}, messages_to_prompt=messages_to_prompt, completion_to_prompt=completion_to_prompt, device_map="auto", )
使用自定义大语言模型
from typing import Optional, List, Mapping, Any from llama_index.core import SimpleDirectoryReader, SummaryIndex from llama_index.core.callbacks import CallbackManager from llama_index.core.llms import ( CustomLLM, CompletionResponse, CompletionResponseGen, LLMMetadata, ) from llama_index.core.llms.callbacks import llm_completion_callback from llama_index.core import Settings class OurLLM(CustomLLM): context_window: int = 3900 num_output: int = 256 model_name: str = "custom" dummy_response: str = "My response" @property def metadata(self) -> LLMMetadata: """Get LLM metadata.""" return LLMMetadata( context_window=self.context_window, num_output=self.num_output, model_name=self.model_name, ) @llm_completion_callback() def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse: return CompletionResponse(text=self.dummy_response) @llm_completion_callback() def stream_complete( self, prompt: str, **kwargs: Any ) -> CompletionResponseGen: response = "" for token in self.dummy_response: response += token yield CompletionResponse(text=response, delta=token) # define our LLM Settings.llm = OurLLM() # define embed model Settings.embed_model = "local:BAAI/bge-base-en-v1.5" # Load the your data documents = SimpleDirectoryReader("./data").load_data() index = SummaryIndex.from_documents(documents) # Query and print response query_engine = index.as_query_engine() response = query_engine.query("<query_text>") print(response)
使用Embedding模型的几种方法;
from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.embeddings.huggingface_optimum import OptimumEmbedding from typing import Any, List from InstructorEmbedding import INSTRUCTOR from llama_index.core.embeddings import BaseEmbedding from llama_index.core import Settings # local Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) # ONNX OptimumEmbedding.create_and_save_optimum_model( "BAAI/bge-small-en-v1.5", "./bge_onnx" ) Settings.embed_model = OptimumEmbedding(folder_name="./bge_onnx") # custom class InstructorEmbeddings(BaseEmbedding): def __init__( self, instructor_model_name: str = "hkunlp/instructor-large", instruction: str = "Represent the Computer Science documentation or question:", **kwargs: Any, ) -> None: self._model = INSTRUCTOR(instructor_model_name) self._instruction = instruction super().__init__(**kwargs) def _get_query_embedding(self, query: str) -> List[float]: embeddings = self._model.encode([[self._instruction, query]]) return embeddings[0] def _get_text_embedding(self, text: str) -> List[float]: embeddings = self._model.encode([[self._instruction, text]]) return embeddings[0] def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]: embeddings = self._model.encode( [[self._instruction, text] for text in texts] ) return embeddings async def _get_query_embedding(self, query: str) -> List[float]: return self._get_query_embedding(query) async def _get_text_embedding(self, text: str) -> List[float]: return self._get_text_embedding(text)
LlamaIndex提供了模板类,使用方法如下:
from llama_index.core import PromptTemplate template = ( "We have provided context information below. \n" "---------------------\n" "{context_str}" "\n---------------------\n" "Given this information, please answer the question: {query_str}\n" ) qa_template = PromptTemplate(template) # you can create text prompt (for completion API) prompt = qa_template.format(context_str=..., query_str=...) # or easily convert to message prompts (for chat API) messages = qa_template.format_messages(context_str=..., query_str=...)
from llama_index.core import ChatPromptTemplate from llama_index.core.llms import ChatMessage, MessageRole message_templates = [ ChatMessage(content="You are an expert system.", role=MessageRole.SYSTEM), ChatMessage( content="Generate a short story about {topic}", role=MessageRole.USER, ), ] chat_template = ChatPromptTemplate(message_templates=message_templates) # you can create message prompts (for chat API) messages = chat_template.format_messages(topic=...) # or easily convert to text prompt (for completion API) prompt = chat_template.format(topic=...)
最常用的提示是text_qa_template 和refine_template。
# shakespeare! qa_prompt_tmpl_str = ( "Context information is below.\n" "---------------------\n" "{context_str}\n" "---------------------\n" "Given the context information and not prior knowledge, " "answer the query in the style of a Shakespeare play.\n" "Query: {query_str}\n" "Answer: " ) qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str) query_engine.update_prompts( {"response_synthesizer:text_qa_template": qa_prompt_tmpl} )
query_engine = index.as_query_engine(
text_qa_template=custom_qa_prompt, refine_template=custom_refine_prompt
)
在RAG(检索增强生成)中的加载阶段涉及以下内容:
文档是任何数据源的通用容器。 例如,PDF、API 输出或从数据库检索的数据。 它们可以手动构建,也可以通过数据加载器自动创建。 默认情况下,文档存储文本以及一些其他属性。例如:
from llama_index.core import SimpleDirectoryReader
from llama_index.core import Document
# through function
documents = SimpleDirectoryReader("./data").load_data()
# load manually
text_list = [text1, text2, ...]
documents = [Document(text=t) for t in text_list]
ID:doc_id 用于实现索引中文档的高效刷新。 使用 SimpleDirectoryReader 时,可以自动将 doc_id 设置为每个文档的完整路径:
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data", filename_as_id=True).load_data()
document.doc_id = "My new document id!"
节点代表源文档的“块”,无论是文本块、图像还是更多。 它们还包含元数据以及与其他节点和索引结构的关系信息。可以选择直接定义节点及其所有属性。 还可以选择通过 NodeParser 类将源文档“解析”为节点。
from llama_index.core.node_parser import SentenceSplitter
parser = SentenceSplitter()
nodes = parser.get_nodes_from_documents(documents)
from llama_index.core.schema import TextNode, NodeRelationship, RelatedNodeInfo
node1 = TextNode(text="<text_chunk>", id_="<node_id>")
node2 = TextNode(text="<text_chunk>", id_="<node_id>")
# set relationships
node1.relationships[NodeRelationship.NEXT] = RelatedNodeInfo(
node_id=node2.node_id
)
node2.relationships[NodeRelationship.PARENT] = RelatedNodeInfo(
node_id=node1.node_id, metadata={"key": "val"}
)
nodes = [node1, node2]
每个文档/节点的元数据字典可以包含附加信息来帮助通知响应并跟踪查询响应的来源。 此信息可以是任何内容,例如文件名或类别。 此信息包含在节点中,使索引能够在查询和响应中利用它。 默认情况下,元数据会注入到嵌入和 LLM 模型调用的文本中。
文档的元数据:
# method1
document = Document(
text="text",
metadata={"filename": "<doc_file_name>", "category": "<category>"},
)
# method2
document.metadata = {"filename": "<doc_file_name>"}
# method3
from llama_index.core import SimpleDirectoryReader
filename_fn = lambda filename: {"file_name": filename}
# automatically sets the metadata of each document according to filename_fn
documents = SimpleDirectoryReader(
"./data", file_metadata=filename_fn
).load_data()
可以自定义需要排除的元数据,修改元数据的分隔符,修改元数据的模板,一个相对复杂的文档如下所示
from llama_index.core import Document from llama_index.core.schema import MetadataMode document = Document( text="This is a super-customized document", metadata={ "file_name": "super_secret_document.txt", "category": "finance", "author": "LlamaIndex", }, excluded_llm_metadata_keys=["file_name"], metadata_seperator="::", metadata_template="{key}=>{value}", text_template="Metadata: {metadata_str}\n-----\nContent: {content}", ) print( "The LLM sees this: \n", document.get_content(metadata_mode=MetadataMode.LLM), ) print( "The Embedding model sees this: \n", document.get_content(metadata_mode=MetadataMode.EMBED), )
可以使用LLM通过元数据提取器模块自动提取元数据。元数据提取器模块包括以下“特征提取器”:
from llama_index.core.extractors import ( TitleExtractor, QuestionsAnsweredExtractor, ) from llama_index.core.node_parser import TokenTextSplitter text_splitter = TokenTextSplitter( separator=" ", chunk_size=512, chunk_overlap=128 ) title_extractor = TitleExtractor(nodes=5) qa_extractor = QuestionsAnsweredExtractor(questions=3) # assume documents are defined -> extract nodes from llama_index.core.ingestion import IngestionPipeline pipeline = IngestionPipeline( transformations=[text_splitter, title_extractor, qa_extractor] ) nodes = pipeline.run( documents=documents, in_place=True, show_progress=True, )
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(
documents, transformations=[text_splitter, title_extractor, qa_extractor]
)
自定义抽取器:
from llama_index.core.extractors import BaseExtractor
class CustomExtractor(BaseExtractor):
async def aextract(self, nodes) -> List[Dict]:
metadata_list = [
{
"custom": node.metadata["document_title"]
+ "\n"
+ node.metadata["excerpt_keywords"]
}
for node in nodes
]
return metadata_list
加载数据
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
使用LlamaHub加载数据:在此示例中,LlamaIndex 使用 DatabaseReader 连接器,该连接器对 SQL 数据库运行查询并将结果的每一行作为文档返回:
from llama_index.core import download_loader
from llama_index.readers.database import DatabaseReader
reader = DatabaseReader(
scheme=os.getenv("DB_SCHEME"),
host=os.getenv("DB_HOST"),
port=os.getenv("DB_PORT"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASS"),
dbname=os.getenv("DB_NAME"),
)
query = "SELECT * FROM users"
documents = reader.load_data(query=query)
自定义文件类型读取:可以通过将文件扩展名字典作为 file_extractor 传递给 BaseReader 实例来扩展 SimpleDirectoryReader 以读取其他文件类型。 BaseReader 应该读取文件并返回文档列表。 例如,要添加对 .myfile 文件的自定义支持:
from llama_index.core import SimpleDirectoryReader from llama_index.core.readers.base import BaseReader from llama_index.core import Document class MyFileReader(BaseReader): def load_data(self, file, extra_info=None): with open(file, "r") as f: text = f.read() # load_data returns a list of Document objects return [Document(text=text + "Foobar", extra_info=extra_info or {})] reader = SimpleDirectoryReader( input_dir="./data", file_extractor={".myfile": MyFileReader()} ) documents = reader.load_data() print(documents)
自定义文件系统读取:SimpleDirectoryReader 采用可选的 fs 参数,可用于遍历远程文件系统。这可以是由 fsspec 协议实现的任何文件系统对象。 fsspec 协议具有针对各种远程文件系统的开源实现,包括 AWS S3、Azure Blob 和 DataLake、Google Drive、SFTP 等。
这是连接到 S3 的示例:
from s3fs import S3FileSystem
s3_fs = S3FileSystem(key="...", secret="...")
bucket_name = "my-document-bucket"
reader = SimpleDirectoryReader(
input_dir=bucket_name,
fs=s3_fs,
recursive=True, # recursively searches all subdirectories
)
documents = reader.load_data()
print(documents)
加载数据后需要处理和转换数据,然后再将其放入存储系统。 这些转换包括分块、提取元数据和嵌入每个块。转换输入/输出是 Node 对象(Document 是 Node 的子类)。 转换也可以堆叠和重新排序。
转换包括高级和低级 API。
高级API:index通过from_documents() 方法,它接受 Document 对象数组,并正确解析它们并将它们分块。
from llama_index.core.node_parser import SentenceSplitter
text_splitter = SentenceSplitter(chunk_size=512, chunk_overlap=10)
# global
from llama_index.core import Settings
Settings.text_splitter = text_splitter
# per-index
index = VectorStoreIndex.from_documents(
documents, transformations=[text_splitter]
)
低级API:也可以明确定义这些步骤。通过使用转换模块(文本拆分器、元数据提取器等)作为独立组件,或在声明性转换管道接口中组合它们来实现此目的。
from llama_index.core import SimpleDirectoryReader
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import TokenTextSplitter
documents = SimpleDirectoryReader("./data").load_data()
pipeline = IngestionPipeline(transformations=[TokenTextSplitter(), ...])
nodes = pipeline.run(documents=documents)
document = Document(
text="text",
metadata={"filename": "<doc_file_name>", "category": "<category>"},
)
可以解析普通文档、HTML、JSON、Markdown等
from llama_index.core.node_parser import SimpleFileNodeParser from llama_index.readers.file import FlatReader from pathlib import Path md_docs = FlatReader().load_data(Path("./test.md")) parser = SimpleFileNodeParser() md_nodes = parser.get_nodes_from_documents(md_docs) from llama_index.core.node_parser import HTMLNodeParser parser = HTMLNodeParser(tags=["p", "h1"]) # optional list of tags nodes = parser.get_nodes_from_documents(html_docs) from llama_index.core.node_parser import JSONNodeParser parser = JSONNodeParser() nodes = parser.get_nodes_from_documents(json_docs) from llama_index.core.node_parser import MarkdownNodeParser parser = MarkdownNodeParser() nodes = parser.get_nodes_from_documents(markdown_docs)
句子划分:SentenceSplitter 根据句子边界的同时分割文本。
from llama_index.core.node_parser import SentenceSplitter
splitter = SentenceSplitter(
chunk_size=1024,
chunk_overlap=20,
)
nodes = splitter.get_nodes_from_documents(documents)
句子窗口解析:将所有文档拆分为单独的句子。 生成的节点还包含元数据中每个节点周围的句子“窗口”。
import nltk
from llama_index.core.node_parser import SentenceWindowNodeParser
node_parser = SentenceWindowNodeParser.from_defaults(
# how many sentences on either side to capture
window_size=3,
# the metadata key that holds the window of surrounding sentences
window_metadata_key="window",
# the metadata key that holds the original sentence
original_text_metadata_key="original_sentence",
)
语义划分解析:语义分割器不是使用固定块大小对文本进行分块,而是使用嵌入相似性自适应地选择句子之间的断点。 这确保了“块”包含语义上彼此相关的句子。
from llama_index.core.node_parser import SemanticSplitterNodeParser
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding()
splitter = SemanticSplitterNodeParser(
buffer_size=1, breakpoint_percentile_threshold=95, embed_model=embed_model
)
token划分解析:TokenTextSplitter 尝试根据原始token计数拆分为一致的块大小。
from llama_index.core.node_parser import TokenTextSplitter
splitter = TokenTextSplitter(
chunk_size=1024,
chunk_overlap=20,
separator=" ",
)
nodes = splitter.get_nodes_from_documents(documents)
分层节点解析器:该节点解析器将节点分块为分层节点。 这意味着单个输入将被分块为多个块大小的层次结构,每个节点都包含对其父节点的引用。
当与 AutoMergingRetriever 结合使用时,能够在检索到大多数子节点时自动将检索到的节点替换为其父节点。 此过程为LLM提供了更完整的响应综合背景。
from llama_index.core.node_parser import HierarchicalNodeParser
node_parser = HierarchicalNodeParser.from_defaults(
chunk_sizes=[2048, 512, 128]
)
IngestionPipeline 使用应用于输入数据的转换概念。 这些转换将应用于输入数据,并且结果节点将被返回或插入到矢量数据库(如果给定)中。 每个节点+转换对都会被缓存,因此使用相同节点+转换组合的后续运行(如果缓存已持久)可以使用缓存的结果并节省时间。例如:
from llama_index.core import Document from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.core.node_parser import SentenceSplitter from llama_index.core.extractors import TitleExtractor from llama_index.core.ingestion import IngestionPipeline from llama_index.vector_stores.qdrant import QdrantVectorStore import qdrant_client client = qdrant_client.QdrantClient(location=":memory:") vector_store = QdrantVectorStore(client=client, collection_name="test_store") pipeline = IngestionPipeline( transformations=[ SentenceSplitter(chunk_size=25, chunk_overlap=0), TitleExtractor(), OpenAIEmbedding(), ], vector_store=vector_store, ) # Ingest directly into a vector db pipeline.run(documents=[Document.example()]) # Create your index from llama_index.core import VectorStoreIndex index = VectorStoreIndex.from_vector_store(vector_store)
在上面的示例中,嵌入是作为管道的一部分进行计算的。 如果将管道连接到向量存储,则嵌入必须是管道的一个阶段,否则稍后的索引实例化将失败。如果不连接到向量存储,即仅生成节点列表,则可以省略管道中的嵌入。
Pipeline有以下特征:
nodes = await pipeline.arun(documents=documents)
pipeline.run(documents=[...], num_workers=4)
目前,以下组件是 Transformation 对象:文本分割器、节点解析器、元数据提取器、Embeddingsmodel(查看我们支持的嵌入列表)。除了这些模块,我们可以自定义转换操作,例如
import re from llama_index.core import Document from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.core.node_parser import SentenceSplitter from llama_index.core.ingestion import IngestionPipeline from llama_index.core.schema import TransformComponent class TextCleaner(TransformComponent): def __call__(self, nodes, **kwargs): for node in nodes: node.text = re.sub(r"[^0-9A-Za-z ]", "", node.text) return nodes # use in a pipeline pipeline = IngestionPipeline( transformations=[ SentenceSplitter(chunk_size=25, chunk_overlap=0), TextCleaner(), OpenAIEmbedding(), ], ) nodes = pipeline.run(documents=[Document.example()])
在RAG(检索增强生成)中的索引阶段涉及以下内容:
LlamaIndex 提供了几种不同的索引类型。 在这里介绍几种最常见的。
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(documents)
index = VectorStoreIndex(nodes)
Summary Index:摘要索引是一种更简单的索引形式,最适合查询,目的是生成文档中文本的摘要。 摘要索引只是将节点存储为顺序链。在查询期间,如果没有指定其他查询参数,LlamaIndex 只是将列表中的所有节点加载到我们的响应合成模块中。摘要索引提供了多种查询摘要索引的方法, 包括来自基于嵌入的查询,该查询将获取前 k 个邻居,或者添加关键字过滤器
Tree index:树索引从一组节点(成为该树中的叶节点)构建层次树。查询树索引涉及从根节点向下遍历到叶节点。 默认情况下,(child_branch_factor=1),查询会在给定父节点的情况下选择一个子节点。 如果 child_branch_factor=2,则查询每个级别选择两个子节点。
其他索引方法参见官方文档:Module Guides
当搜索嵌入时,查询本身会转换为向量嵌入,然后由 VectorStoreIndex 执行数学运算,根据所有嵌入与查询在语义上的相似程度对它们进行排名。排名完成后,VectorStoreIndex 会返回最相似的嵌入作为相应的文本块。 它返回的嵌入数量称为 k,因此控制返回多少嵌入的参数称为 top_k。 由于这个原因,整个类型的搜索通常被称为“top-k 语义检索”。Top-k 检索是查询向量索引的最简单形式。
LlamaIndex 还支持可交换存储组件,包括:
可以使用LlamaIndex提供的存储方法,也可能使用外部向量数据库。
内置方法:
from llama_index.core import StorageContext, load_index_from_storage
# save the index
index.storage_context.persist(persist_dir="<persist_dir>")
# rebuild storage context
storage_context = StorageContext.from_defaults(persist_dir="<persist_dir>")
# load index
index = load_index_from_storage(storage_context)
外部向量数据库;LlamaIndex 支持很多向量数据库,这些向量存储的架构、复杂性和成本各不相同。 许多向量存储(FAISS 除外)将存储数据和索引(嵌入)。 这意味着不需要使用单独的文档存储或索引存储。在此示例中,我们将使用 Chroma,一个开源向量数据库。
使用 Chroma 存储 VectorStoreIndex 中的嵌入步骤如下:
import chromadb from llama_index.core import VectorStoreIndex, SimpleDirectoryReader from llama_index.vector_stores.chroma import ChromaVectorStore from llama_index.core import StorageContext # load some documents documents = SimpleDirectoryReader("./data").load_data() # initialize client, setting path to save data db = chromadb.PersistentClient(path="./chroma_db") # create collection chroma_collection = db.get_or_create_collection("quickstart") # assign chroma as the vector_store to the context vector_store = ChromaVectorStore(chroma_collection=chroma_collection) storage_context = StorageContext.from_defaults(vector_store=vector_store) # create your index index = VectorStoreIndex.from_documents( documents, storage_context=storage_context ) # create a query engine and query query_engine = index.as_query_engine() response = query_engine.query("What is the meaning of life?") print(response)
下面的代码是加载一个创建好的向量数据库
import chromadb from llama_index.core import VectorStoreIndex from llama_index.vector_stores.chroma import ChromaVectorStore from llama_index.core import StorageContext # initialize client db = chromadb.PersistentClient(path="./chroma_db") # get collection chroma_collection = db.get_or_create_collection("quickstart") # assign chroma as the vector_store to the context vector_store = ChromaVectorStore(chroma_collection=chroma_collection) storage_context = StorageContext.from_defaults(vector_store=vector_store) # load your index from stored vectors index = VectorStoreIndex.from_vector_store( vector_store, storage_context=storage_context ) # create a query engine query_engine = index.as_query_engine() response = query_engine.query("What is llama2?") print(response)
简单的查询只是对LLM的prompt调用:它可以是一个问题并获得答案,或者是一个总结请求。更复杂的查询可能涉及重复/链接prompt+ LLM 调用,甚至跨多个组件的推理循环。
在RAG(检索增强生成)中的查询阶段涉及以下内容:
在下面的示例中,我们自定义检索器以对 top_k 使用不同的值,并添加一个后处理步骤,该步骤要求检索到的节点达到要包含的最小相似度分数。 当有相关结果时,这将提供大量数据,但如果没有任何相关结果,则可能不会提供任何数据。
from llama_index.core import VectorStoreIndex, get_response_synthesizer from llama_index.core.retrievers import VectorIndexRetriever from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.postprocessor import SimilarityPostprocessor # build index index = VectorStoreIndex.from_documents(documents) # configure retriever retriever = VectorIndexRetriever( index=index, similarity_top_k=10, ) # configure response synthesizer response_synthesizer = get_response_synthesizer() # assemble query engine query_engine = RetrieverQueryEngine( retriever=retriever, response_synthesizer=response_synthesizer, node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.7)], ) # query response = query_engine.query("What did the author do growing up?") print(response)
节点后处理器:支持高级节点过滤和增强,可以进一步提高检索到的节点对象的相关性。 这可以帮助减少 LLM 调用的时间/次数/成本或提高响应质量。例如:
响应合成:检索器获取相关节点后,BaseSynthesizer 通过组合信息来合成最终响应。配置query_engine = RetrieverQueryEngine.from_args(retriever, response_mode=response_mode)
,目前支持以下选项:
LlamaIndex 提供了衡量生成结果质量和检索质量的关键模块。
LlamaIndex 提供基于LLM的评估模块来衡量结果的质量。 这使用“黄金”LLM(例如 GPT-4)以多种方式决定预测答案是否正确。当前的评估模块不需要真实标签。 可以通过查询、上下文、响应的某种组合来完成评估,并将这些与 LLM 调用结合起来。
这些评估模块有以下形式:
from llama_index.core import VectorStoreIndex from llama_index.llms.openai import OpenAI from llama_index.core.evaluation import FaithfulnessEvaluator # create llm llm = OpenAI(model="gpt-4", temperature=0.0) # build index ... vector_index = VectorStoreIndex(...) # define evaluator evaluator = FaithfulnessEvaluator(llm=llm) # query index query_engine = vector_index.as_query_engine() response = query_engine.query( "What battles took place in New York City in the American Revolution?" ) eval_result = evaluator.evaluate_response(response=response) print(str(eval_result.passing))
给定问题数据集和真实排名,我们可以使用平均倒数排名(MRR)、命中率、精度等排名指标来评估检索器。核心检索评估步骤围绕以下内容:
from llama_index.core.evaluation import RetrieverEvaluator
# define retriever somewhere (e.g. from index)
# retriever = index.as_retriever(similarity_top_k=2)
retriever = ...
retriever_evaluator = RetrieverEvaluator.from_metric_names(
["mrr", "hit_rate"], retriever=retriever
)
retriever_evaluator.evaluate(
query="query", expected_ids=["node_id1", "node_id2"]
)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。