使用 LlamaIndex 构建智能文档查询系统

使用 LlamaIndex 构建智能文档查询系统

    • 1. 环境准备
    • 2. 初始化模型
    • 3. 加载文档
    • 4. 构建索引和查询引擎
    • 5. 生成扩展查询
    • 6. 主函数
    • 7. 总结

在现代信息检索系统中,如何高效地从大量文档中提取出有用的信息是一个重要的挑战。本文将介绍如何使用 LlamaIndex 构建一个智能文档查询系统,通过多种查询优化技术来提高检索的准确性和效率。

1. 环境准备

首先,我们需要安装必要的 Python 库,并加载环境变量。

import logging
import os

from dotenv import find_dotenv, load_dotenv
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, PromptTemplate, get_response_synthesizer
from llama_index.core.indices.query.query_transform.base import (
    StepDecomposeQueryTransform, HyDEQueryTransform
)
from llama_index.core.node_parser import (
    TokenTextSplitter, SentenceWindowNodeParser
)
from llama_index.core.postprocessor import MetadataReplacementPostProcessor
from llama_index.core.query_engine import (
    MultiStepQueryEngine, TransformQueryEngine
)
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI

# 读取本地 .env 文件
load_dotenv(find_dotenv())

# 设置日志级别为 ERROR,避免 WARNING 信息干扰
logging.basicConfig(level=logging.ERROR)

2. 初始化模型

接下来,我们初始化 OpenAI 的 LLM 和 Embedding 模型。

# 初始化 OpenAI 模型
llm_client = OpenAI(
    model="gpt-4",
    api_base=os.environ["OPENAI_BASE_URL"],
    api_key=os.environ["OPENAI_API_KEY"],
    is_chat_model=True,
    seed=42,
)

# 初始化 OpenAI Embedding 模型
embed_client = OpenAIEmbedding(
    model="text-embedding-3-large",
    api_base=os.environ["OPENAI_EMBED_BASE_URL"],
    api_key=os.environ["OPENAI_API_KEY"],
)

3. 加载文档

我们定义一个函数来加载指定目录下的文档。

# 加载文档
def load_documents(directory):
    print('=' * 50)
    print('📂 正在加载文档...')
    print('=' * 50 + '\n')
    documents = SimpleDirectoryReader(directory).load_data()
    print(f'✅ 文档加载完成。\n')
    return documents

4. 构建索引和查询引擎

我们使用不同的节点解析器来构建索引,并创建查询引擎。

# 构建索引并创建查询引擎
def build_index_and_query_engine(documents, embed_model, llm, node_parser, postprocessors=None):
    print(f"\n{'=' * 50}")
    print(f"🔍 正在使用 {node_parser.__class__.__name__} 方法进行测试...")
    print(f"{'=' * 50}\n")

    print("📑 正在处理文档...")
    nodes = node_parser.get_nodes_from_documents(documents)
    index = VectorStoreIndex(nodes, embed_model=embed_model)

    query_engine = index.as_query_engine(
        similarity_top_k=5,
        streaming=True,
        llm=llm,
        node_postprocessors=postprocessors if postprocessors else []
    )
    return query_engine, index

5. 生成扩展查询

为了增强查询的效果,我们使用 LLM 对原始查询进行扩展。

# 生成扩展查询
query_gen_str = """\
系统角色设定:
你是一个专业的问题改写助手。你的任务是将用户的原始问题扩充为一个更完整、更全面的问题。

规则:
1. 将可能的歧义、相关概念和上下文信息整合到一个完整的问题中
2. 使用括号对歧义概念进行补充说明
3. 添加关键的限定词和修饰语
4. 确保改写后的问题清晰且语义完整
5. 对于模糊概念,在括号中列举主要可能性

原始问题:
{query}

请生成一个综合的改写问题,确保:
- 包含原始问题的核心意图
- 涵盖可能的歧义解释
- 使用清晰的逻辑关系词连接不同方面
- 必要时使用括号补充说明

输出格式:
[综合改写] - 改写后的问题
"""
query_gen_prompt = PromptTemplate(query_gen_str)


def generate_queries(query: str):
    response = llm_client.predict(query_gen_prompt, query=query)
    return response

6. 主函数

在主函数中,我们加载文档并测试不同的查询方法。

# 主函数
def main():
    # 加载文档
    documents = load_documents('./docs/md')

    # 定义问题和节点解析器
    question = '张伟是哪个部门的'
    node_parsers = [
        TokenTextSplitter(chunk_size=1024, chunk_overlap=20),
        # SentenceSplitter(chunk_size=512, chunk_overlap=50),
        # SentenceWindowNodeParser.from_defaults(
        #     window_size=3,
        #     window_metadata_key="window",
        #     original_text_metadata_key="original_text"
        # ),
        # SemanticSplitterNodeParser(
        #     buffer_size=1,
        #     breakpoint_percentile_threshold=95,
        #     embed_model=embed_client
        # ),
        # MarkdownNodeParser()
    ]

    # 遍历不同的节点解析器进行测试
    for parser in node_parsers:
        if isinstance(parser, SentenceWindowNodeParser):
            postprocessors = [MetadataReplacementPostProcessor(target_metadata_key="window")]
        else:
            postprocessors = None

        query_engine, index = build_index_and_query_engine(documents, embed_client, llm_client, parser, postprocessors)

        # 方法一:使用大模型扩充用户问题
        print("\n🔍 原始问题:")
        print(f"   {question}")
        expanded_query = generate_queries(question)
        print("\n📝 扩展查询:")
        print(f"   {expanded_query}\n")

        expanded_response = query_engine.query(expanded_query)

        print("💭 AI回答:")
        print("-" * 40)
        expanded_response.print_response_stream()
        print("\n")

        # 显示参考文档
        print("\n📚 参考依据:")
        print("-" * 40)
        for i, node in enumerate(expanded_response.source_nodes, 1):
            print(f"\n文档片段 {i}:")
            print(f"相关度得分: {node.score:.4f}")
            print("-" * 30)
            print(node.text)

        # 方法二:将单一查询改写为多步骤查询
        step_decompose_transform = StepDecomposeQueryTransform(verbose=True, llm=llm_client)
        response_synthesizer = get_response_synthesizer(llm=llm_client, callback_manager=query_engine.callback_manager,
                                                        verbose=True)
        multi_step_query_engine = MultiStepQueryEngine(
            query_engine=query_engine,
            query_transform=step_decompose_transform,
            response_synthesizer=response_synthesizer,
            index_summary="公司人员信息",
        )

        print(f"❓ 用户问题: {question}\n")
        print("🤖 AI正在进行多步查询...")
        multi_step_response = multi_step_query_engine.query(question)

        print("\n📚 参考依据:")
        print("-" * 40)
        for i, node in enumerate(multi_step_response.source_nodes, 1):
            print(f"\n文档片段 {i}:")
            print("-" * 30)
            print(node.text)

        # 方法三:用假设文档来增强检索(HyDE)
        hyde = HyDEQueryTransform(include_original=True, llm=llm_client)
        hyde_query_engine = TransformQueryEngine(
            query_engine=query_engine,
            query_transform=hyde
        )

        print(f"❓ 用户问题: {question}\n")
        print("🤖 AI正在通过 HyDE 分析...")
        hyde_response = hyde_query_engine.query(question)

        print("\n💭 AI回答:")
        print("-" * 40)
        hyde_response.print_response_stream()

        # 显示参考文档
        print("\n📚 参考依据:")
        print("-" * 40)
        for i, node in enumerate(hyde_response.source_nodes, 1):
            print(f"\n文档片段 {i}:")
            print("-" * 30)
            print(node.text)

        # 显示生成的假设文档
        query_bundle = hyde(question)
        hyde_doc = query_bundle.embedding_strs[0]
        print(f"🤖 AI生成的假想文档:\n{hyde_doc}\n")


if __name__ == "__main__":
    main()

7. 总结

通过本文的介绍,我们学习了如何使用 LlamaIndex 构建一个智能文档查询系统。我们探讨了如何加载文档、构建索引、生成扩展查询以及使用多种查询优化技术来提高检索的准确性和效率。希望本文能帮助你更好地理解和应用 LlamaIndex 在实际项目中的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/948720.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【微软,模型规模】模型参数规模泄露:理解大型语言模型的参数量级

模型参数规模泄露:理解大型语言模型的参数量级 关键词: #大型语言模型 Large Language Model #参数规模 Parameter Scale #GPT-4o #GPT-4o-mini #Claude 3.5 Sonnet 具体实例与推演 近日,微软在一篇医学相关论文中意外泄露了OpenAI及Claud…

SpringBoot Maven 项目 pom 中的 plugin 插件用法整理

把 SpringBoot Maven 项目打包成 jar 文件时,我们通常用到 spring-boot-maven-plugin 插件。 前面也介绍过,在 spring-boot-starter-parent POM 和 spring-boot-starter POM 中都有插件的管理,现在我们就撸一把构建元素中插件的用法。 一、…

UE5AI感知组件

官方解释: AI感知系统为Pawn提供了一种从环境中接收数据的方式,例如噪音的来源、AI是否遭到破坏、或AI是否看到了什么。 AI感知组件(AIPerception Component)是用于实现游戏中的非玩家角色(NPC)对环境和其…

【数据仓库】hive on Tez配置

hive on Tez 搭建 前提是hive4.0hadoop3.2.2数仓已搭建完成,现在只是更换其执行引擎 为Tez。搭建可参考【数据仓库】hive hadoop数仓搭建实践文章。 Tez 下载 下载地址 https://archive.apache.org/dist/tez/ 官网地址 https://tez.apache.org/releases/apac…

finereport动态数据源插件教程2

场景: 模板中有多个数据集,只需要其中一个数据集按照不同的参数显示不同数据库的数据。 模板制作: 两个数据集ds1,ds2,ds1的绑定到参数面板的下拉框上,ds2显示到模板正文中,现在需要ds1根据不同…

Java通过谷歌邮箱Gmail直接发送邮件的三种方式

错误 Connected to the target VM, address: 127.0.0.1:52082, transport: socketException in thread "main" javax.mail.MessagingException: Got bad greeting from SMTP host: smtp.gmail.com, port: 587, response: [EOF] at com.sun.mail.smtp.SMTPTransp…

WSDM 2025 | 时间序列(time series)论文总结

AWSDM 2025于2025年3月10号到14号在德国汉诺威举行(Hannover, Germany) 本文总结了WSDM 2024有关时间序列(time series)的相关论文,如有疏漏,欢迎大家补充。(没有时空数据相关的论文&#xff0…

反直觉导致卡关-迫击炮谜题

这个谜题,在两周目中先后卡了我至少三个小时,先后缓慢装填并发射迫击炮弹尝试了数百次。 一周目卡了很久,稀里糊涂的过了,想不到二周目还会卡那么久。 研究了很多播主的攻略,但还是一头雾水, 直到分析其…

庐山派K230学习日记4 PWM控制

1 本节介绍​ 📝本节您将学习如何通过将K230开发板的GPIO引脚复用为PWM功能并输出PWM信号;实现输出PWM信号及控制板载无源蜂鸣器发出声音。 🏆学习目标 1️⃣如何将GPIO引脚配置为PWM模式,通过40Pin排针中的部分引脚来输出PWM信号…

c语言的文件操作与文件缓冲区

目录 C语言文件操作函数汇总 简单介绍文件 为什么使用文件 什么是文件 文件名 二进制文件和文本文件 流和标准流 流 标准流 文件指针 文件的打开和关闭 文件的顺序读写 顺序读写函数介绍 文件的随机读写 fseek ftell rewind 文件读取结束的判定 文件缓冲区 缓…

嵌入式linux中socket控制与实现

一、概述 1、首先网络,一看到这个词,我们就会想到IP地址和端口号,那IP地址和端口各有什么作用呢? (1)IP地址如身份证一样,是标识的电脑的,一台电脑只有一个IP地址。 (2)端口提供了一种访问通道,服务器一般都是通过知名端口号来识别某个服务。例如,对于每个TCP/IP实…

Nginx:动静分离

什么是动静分离? 动静分离 是指将网站中的静态资源(如图片、样式表、脚本等)和动态内容(如 PHP、Python、Node.js 等后端生成的内容)分开部署和处理。这样做的好处是可以利用不同的服务器或缓存策略来优化不同类型的资源。 动静分离的好处 提高性能:静态资源可以直接从…

PADS Layout 差分线设计规则及其设计规则约束的详细过程步骤

一般我们的电路板有很多的差分线,有90欧姆的差分线,也有100欧姆的差分线,90欧姆的差分线主要是针对USB的差分线,特别是对于USB HUB的板子,那么我们就要设置差分线。一般我们设置差分线,一般要切换到Router里面来设置,如下所示: 那么设置差分对,一般要对原理图和Router…

计算机网络--路由表的更新

一、方法 【计算机网络习题-RIP路由表更新-哔哩哔哩】 二、举个例子 例1 例2

概述(讲讲python基本语法和第三方库)

我是北子,这是我自己写的python教程,主要是记录自己的学习成果方便自己日后复习, 我先学了C/C,所以这套教程中可能会将很多概念和C/C去对比,所以该教程大概不适合零基础的人。 it seems that python nowadays 只在人工…

redux用法总结

redux用法总结 目录 基本概念工作原理核心概念基本使用异步操作 Redux ThunkRedux Saga React 集成Redux Toolkit最佳实践 基本概念 什么是 Redux? Redux 是一个可预测的状态容器,用于管理 JavaScript 应用的状态。它遵循三个基本原则: …

Gitee上传项目代码教程(详细)

工具必备:Git Bash 上传步骤 1.在Gitee创建项目仓库 2.进入本地项目目录 右键打开Git Bash here 3.配置用户名和邮箱 如果之前给git配置过用户名和邮箱可跳过 查看Git是否配置成功:git config --list git config --global user.name "xxx"…

ARM CCA机密计算安全模型之安全生命周期管理

安全之安全(security)博客目录导读 目录 一、固件启用的调试 二、CCA系统安全生命周期 三、重新供应 四、可信子系统与CCA HES 启用 CCA(机密计算架构)的安全系统是指 CCA 平台的实现处于可信状态。 由于多种原因,CCA 启用系统可能处于不…

计算机视觉CV期末总复习

1.计算机视觉基础 数字图像表示 二值图像 仅包含黑白两种颜色的图像,只使用1个比特为(0黑或1白)表示 彩色图像:分不同的颜色空间 gray灰度图像 每个像素只有一个采样颜色,取值范围0--255,为8比特位&a…

web安全常用靶场

这里写自定义目录标题 phpstydy2018pikachuxss-labs phpstydy2018 网盘地址 提取码: nxnw ‌phpStudy是一款专为PHP开发者设计的集成环境工具,主要用于简化PHP开发环境的搭建过程。‌ 它集成了Apache、MySQL、PHP等核心组件,用户只需进行一次性安装&a…