🔍 📱 嘿,Chatbot爱好者!今天我们来深入解析一个超强的文档检索和问答系统框架!🚀
🧠 你是否曾经面对海量文档感到头痛?需要快速精准回答问题却不知从何下手?别担心,这个基于LlamaIndex和TruLens的框架就是你的救星!
🔍 本文将为你揭秘:
🌟 无论你是工程师、数据科学家,还是对智能问答系统感兴趣的极客,这篇文章都能让你收获满满!
🏗️ 首先是框架整体设计
索引构建 - 文档的多重变身
查询引擎 - 智能检索的核心
评估系统 - 性能监控利器
node_parser = HierarchicalNodeParser.from_defaults(chunk_sizes=chunk_sizes) nodes = node_parser.get_nodes_from_documents(documents) leaf_nodes = get_leaf_nodes(nodes)
这个解析器就像一个超级智能的剪刀✂️,它能将文档切分成不同大小的片段。想象一下,你有一本厚厚的百科全书:
为什么要这样做?因为不同的问题需要不同粒度的信息!比如"解释相对论"可能需要整个章节,而"爱因斯坦出生年份"可能一个句子就够了。
retriever = AutoMergingRetriever( base_retriever, automerging_index.storage_context, verbose=True )
这个检索器简直就是一个魔法师🧙♂️!它不仅能找到相关的文本片段,还能自动将它们合并。想象一下,你问"苹果公司的发展历程",它能自动将"乔布斯创立苹果"、"iPhone发布"、"Tim Cook接任CEO"这些片段智能地组合在一起!
automerging_index = VectorStoreIndex( leaf_nodes, storage_context=storage_context, service_context=merging_context )
这个索引就像一个超级图书管理员👨💼,它将所有文本片段转化为向量,并用一种特殊的方式存储。当你问问题时,它能以光速找到最相关的片段!
rerank = SentenceTransformerRerank( top_n=rerank_top_n, model="BAAI/bge-reranker-base" )
这个组件就像一个严格的评委,它会对初步检索的结果进行二次评判,只保留最相关的部分。这就保证了回答的精准性!
auto_merging_index_0 = build_automerging_index( documents, llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1), embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index_0", chunk_sizes=[2048,512], ) auto_merging_index_1 = build_automerging_index( documents, llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1), embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index_1", chunk_sizes=[2048,512,128], )
自动合并检索:不用担心文本片段太小,系统会自动合并相关片段!
重排序机制:通过similarity_top_k
和rerank_top_n
参数,我们可以微调检索的精度和效率。
auto_merging_engine_0 = get_automerging_query_engine( auto_merging_index_0, similarity_top_k=12, rerank_top_n=6, )
集成评估:使用TruLens进行系统评估,让你的系统性能一目了然!
tru_recorder = get_prebuilt_trulens_recorder( auto_merging_engine_0, app_id ='app_0' ) run_evals(eval_questions, tru_recorder, auto_merging_engine_0)
chunk_sizes
。长文档可能需要更多层级。similarity_top_k
和rerank_top_n
值,找到最佳平衡点。🎉 总结:
这个框架不仅强大,而且高度可定制。无论你是处理科技文档、法律合同还是文学作品,它都能帮你构建一个智能、高效的问答系统。
a) 性能优化:不同的文档类型和查询需求可能需要不同的索引结构。
b) 平衡精度和效率:层次越多,索引越精细,但计算开销也越大。
c) 适应性研究:了解哪种索引结构最适合特定的文档集和查询类型。
a) 粒度控制:不同层次提供不同粒度的文本块。
b) 上下文保留:多层结构有助于在检索时保留更多上下文信息。
c) 检索灵活性:可以根据查询复杂度选择合适层次的文本块。
假设我们有一本关于世界历史的教科书。
两层索引 [2048, 512]:
三层索引 [2048, 512, 128]:
"简述第二次世界大战的主要原因"
"希特勒在哪一年入侵波兰?"
通过比较不同层次索引的性能,我们可以找到最适合这本历史教科书的索引结构。
现在,解释这行代码:
auto_merging_engine_0 = get_automerging_query_engine( auto_merging_index_0, similarity_top_k=12, rerank_top_n=6 )
这行代码创建了一个查询引擎,具体解释如下:
auto_merging_index_0
:这是之前创建的自动合并索引。
similarity_top_k=12
:这个参数指定初始检索时要返回的最相似文本块的数量。在这里,系统会首先检索12个最相似的文本块。
rerank_top_n=6
:这个参数指定在重新排序后要保留的top相关文本块的数量。系统会对初始检索的12个文本块进行重新排序,然后只保留最相关的6个。
get_automerging_query_engine
:这个函数创建了一个查询引擎,它使用自动合并检索器和重排序后处理器。
这种方法的优点是它在广泛搜索(12个初始结果)和精确答案(6个最终结果)之间取得了平衡,有助于提高回答的相关性和准确性。
节点解析器(Node Parser)的概念、作用和工作原理:
节点解析器的概念:
节点解析器是一个工具,用于将大型文档分割成更小、更易管理的部分,这些部分被称为"节点"。在LlamaIndex中,节点是文档的基本单位,包含文本内容和元数据。
节点解析器的作用:
HierarchicalNodeParser的工作原理:
node_parser = HierarchicalNodeParser.from_defaults( chunk_sizes=[2048, 512, 128] ) nodes = node_parser.get_nodes_from_documents([document])
为什么这样做很有用:
通过使用HierarchicalNodeParser,我们可以更有效地组织和检索大型文档中的信息,既保留了文档的整体结构,又提供了灵活的检索粒度。
import os from llama_index import ( ServiceContext, StorageContext, VectorStoreIndex, load_index_from_storage, ) from llama_index.node_parser import HierarchicalNodeParser from llama_index.node_parser import get_leaf_nodes from llama_index import StorageContext, load_index_from_storage from llama_index.retrievers import AutoMergingRetriever from llama_index.indices.postprocessor import SentenceTransformerRerank from llama_index.query_engine import RetrieverQueryEngine def build_automerging_index( documents, llm, embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index", chunk_sizes=None, ): chunk_sizes = chunk_sizes or [2048, 512, 128] node_parser = HierarchicalNodeParser.from_defaults(chunk_sizes=chunk_sizes) nodes = node_parser.get_nodes_from_documents(documents) leaf_nodes = get_leaf_nodes(nodes) merging_context = ServiceContext.from_defaults( llm=llm, embed_model=embed_model, ) storage_context = StorageContext.from_defaults() storage_context.docstore.add_documents(nodes) if not os.path.exists(save_dir): automerging_index = VectorStoreIndex( leaf_nodes, storage_context=storage_context, service_context=merging_context ) automerging_index.storage_context.persist(persist_dir=save_dir) else: automerging_index = load_index_from_storage( StorageContext.from_defaults(persist_dir=save_dir), service_context=merging_context, ) return automerging_index def get_automerging_query_engine( automerging_index, similarity_top_k=12, rerank_top_n=6, ): base_retriever = automerging_index.as_retriever(similarity_top_k=similarity_top_k) retriever = AutoMergingRetriever( base_retriever, automerging_index.storage_context, verbose=True ) rerank = SentenceTransformerRerank( top_n=rerank_top_n, model="BAAI/bge-reranker-base" ) auto_merging_engine = RetrieverQueryEngine.from_args( retriever, node_postprocessors=[rerank] ) return auto_merging_engine import os from llama_index import ( ServiceContext, StorageContext, VectorStoreIndex, load_index_from_storage, ) from llama_index.node_parser import HierarchicalNodeParser from llama_index.node_parser import get_leaf_nodes from llama_index import StorageContext, load_index_from_storage from llama_index.retrievers import AutoMergingRetriever from llama_index.indices.postprocessor import SentenceTransformerRerank from llama_index.query_engine import RetrieverQueryEngine def build_automerging_index( documents, llm, embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index", chunk_sizes=None, ): chunk_sizes = chunk_sizes or [2048, 512, 128] node_parser = HierarchicalNodeParser.from_defaults(chunk_sizes=chunk_sizes) nodes = node_parser.get_nodes_from_documents(documents) leaf_nodes = get_leaf_nodes(nodes) merging_context = ServiceContext.from_defaults( llm=llm, embed_model=embed_model, ) storage_context = StorageContext.from_defaults() storage_context.docstore.add_documents(nodes) if not os.path.exists(save_dir): automerging_index = VectorStoreIndex( leaf_nodes, storage_context=storage_context, service_context=merging_context ) automerging_index.storage_context.persist(persist_dir=save_dir) else: automerging_index = load_index_from_storage( StorageContext.from_defaults(persist_dir=save_dir), service_context=merging_context, ) return automerging_index def get_automerging_query_engine( automerging_index, similarity_top_k=12, rerank_top_n=6, ): base_retriever = automerging_index.as_retriever(similarity_top_k=similarity_top_k) retriever = AutoMergingRetriever( base_retriever, automerging_index.storage_context, verbose=True ) rerank = SentenceTransformerRerank( top_n=rerank_top_n, model="BAAI/bge-reranker-base" ) auto_merging_engine = RetrieverQueryEngine.from_args( retriever, node_postprocessors=[rerank] ) return auto_merging_engine from llama_index.llms import OpenAI index = build_automerging_index( [document], llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1), save_dir="./merging_index", ) query_engine = get_automerging_query_engine(index, similarity_top_k=6) TruLens Evaluation from trulens_eval import Tru Tru().reset_database() Two layers auto_merging_index_0 = build_automerging_index( documents, llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1), embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index_0", chunk_sizes=[2048,512], ) auto_merging_engine_0 = get_automerging_query_engine( auto_merging_index_0, similarity_top_k=12, rerank_top_n=6, ) from utils import get_prebuilt_trulens_recorder tru_recorder = get_prebuilt_trulens_recorder( auto_merging_engine_0, app_id ='app_0' ) eval_questions = [] with open('generated_questions.text', 'r') as file: for line in file: # Remove newline character and convert to integer item = line.strip() eval_questions.append(item) def run_evals(eval_questions, tru_recorder, query_engine): for question in eval_questions: with tru_recorder as recording: response = query_engine.query(question) run_evals(eval_questions, tru_recorder, auto_merging_engine_0) from trulens_eval import Tru Tru().get_leaderboard(app_ids=[]) Tru().run_dashboard() Three layers auto_merging_index_1 = build_automerging_index( documents, llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1), embed_model="local:BAAI/bge-small-en-v1.5", save_dir="merging_index_1", chunk_sizes=[2048,512,128], ) auto_merging_engine_1 = get_automerging_query_engine( auto_merging_index_1, similarity_top_k=12, rerank_top_n=6, ) tru_recorder = get_prebuilt_trulens_recorder( auto_merging_engine_1, app_id ='app_1' ) run_evals(eval_questions, tru_recorder, auto_merging_engine_1) from trulens_eval import Tru Tru().get_leaderboard(app_ids=[]) Tru().run_dashboard()
🔬 核心原理解析
多层次索引:
想象你在整理一本百科全书。你可能会先分章节(2048字符),再分小节(512字符),最后到段落(128字符)。这就是多层次索引的原理!它让系统能够根据问题的具体情况,选择最合适的"阅读范围"。
自动合并检索:
这就像是一个超级阅读理解高手。它不仅能找到相关的段落,还能自动将多个段落组合起来,形成一个完整的答案。再也不用担心答非所问啦!
重排序机制:
这相当于给检索结果做最后一道筛选。通过精确的语义理解,它能将最相关的信息排在最前面,确保回答的质量。
💡 使用小贴士
根据你的文档类型调整chunk_sizes。科技文档可能需要更细的划分,而文学作品可能需要保留更多上下文。
多尝试不同的similarity_top_k和rerank_top_n值。这就像调整显微镜的焦距,找到最清晰的图像!
充分利用TruLens仪表板。它就像你的AI助手的"体检报告",告诉你哪里表现出色,哪里需要改进。
上一篇:作用域和链接属性