使用 `llama_index` 构建智能问答系统:多种文档切片方法的评估

使用 `llama_index` 构建智能问答系统:多种文档切片方法的评估

    • 代码优化与解析
      • [1. **代码结构优化**](#1. 代码结构优化)
      • [2. **日志管理**](#2. 日志管理)
      • [3. **环境变量管理**](#3. 环境变量管理)
      • [4. **模型初始化**](#4. 模型初始化)
      • [5. **提示模板更新**](#5. 提示模板更新)
      • [6. **问答函数优化**](#6. 问答函数优化)
      • [7. **索引构建与查询引擎**](#7. 索引构建与查询引擎)
      • [8. **节点解析器测试**](#8. 节点解析器测试)
    • 总结

在现代自然语言处理(NLP)应用中,构建一个高效的问答系统是一个常见的需求。llama_index 是一个强大的工具,可以帮助我们快速构建基于文档的问答系统。本文将介绍如何优化和解析一个基于 llama_index 的问答系统代码,并逐步解析其核心功能。


代码优化与解析

1. 代码结构优化

我们将代码拆分为多个函数,使得代码结构更清晰,便于维护和扩展。以下是优化后的代码结构:

  • update_prompt_template:用于动态更新查询引擎的提示模板。
  • ask_question:向查询引擎提问并输出结果。
  • load_documents:加载指定目录下的文档。
  • build_index_and_query_engine:构建索引并创建查询引擎。
  • main:主函数,负责程序的整体逻辑。

这种模块化的设计使得代码更易于理解和扩展。


2. 日志管理

为了避免不必要的警告信息干扰,我们使用 logging.basicConfig(level=logging.ERROR) 来设置日志级别为 ERROR。这样可以确保只有重要的错误信息被输出。

python 复制代码
import logging
logging.basicConfig(level=logging.ERROR)

3. 环境变量管理

我们使用 dotenv 库加载 .env 文件中的环境变量,确保敏感信息(如 API Key)不会硬编码在代码中。

python 复制代码
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())

4. 模型初始化

我们初始化 OpenAI 的 LLM 和 Embedding 模型,确保模型配置一致且易于修改。

python 复制代码
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,
)

embed_client = OpenAIEmbedding(
    model="text-embedding-3-large",
    api_base=os.environ["OPENAI_EMBED_BASE_URL"],
    api_key=os.environ["OPENAI_API_KEY"],
)

5. 提示模板更新

update_prompt_template 函数用于动态更新查询引擎的提示模板,确保问答系统能够根据需求调整回答风格。

python 复制代码
def update_prompt_template(query_engine, qa_prompt_tmpl_str=None):
    if qa_prompt_tmpl_str is None:
        qa_prompt_tmpl_str = (
            "你叫公司小蜜,是公司的答疑机器人。你需要仔细阅读参考信息,然后回答大家提出的问题。"
            "注意事项:\n"
            "1. 根据上下文信息而非先验知识来回答问题。\n"
            "2. 如果是工具咨询类问题,请务必给出下载地址链接。\n"
            "3. 如果员工部门查询问题,请务必注意有同名员工的情况,可能有2个、3个甚至更多同名的人\n"
            "以下是参考信息。"
            "---------------------\n"
            "{context_str}\n"
            "---------------------\n"
            "问题:{query_str}\n。"
            "回答:"
        )
    qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)
    query_engine.update_prompts(
        {"response_synthesizer:text_qa_template": qa_prompt_tmpl}
    )
    return query_engine

6. 问答函数优化

ask_question 函数负责处理用户的问题,输出问题和回答,并展示参考文档。通过检查 response 对象是否有 print_response_stream 方法,确保兼容不同的响应类型。

python 复制代码
def ask_question(question, query_engine):
    update_prompt_template(query_engine)

    print('=' * 50)
    print(f'🤔 问题:{question}')
    print('=' * 50 + '\n')

    response = query_engine.query(question)

    print('🤖 回答:')
    if hasattr(response, 'print_response_stream') and callable(response.print_response_stream):
        response.print_response_stream()
    else:
        print(str(response))

    print('\n' + '-' * 50)
    print('📚 参考文档:\n')
    for i, source_node in enumerate(response.source_nodes, start=1):
        print(f'文档 {i}:')
        print(source_node)
        print()

    print('-' * 50)
    return response

7. 索引构建与查询引擎

build_index_and_query_engine 函数负责构建索引并创建查询引擎。根据不同的节点解析器(如 TokenTextSplitterSentenceSplitter 等),生成不同的查询引擎。

python 复制代码
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

8. 节点解析器测试

我们使用不同的节点解析器(如 TokenTextSplitterSentenceSplitter 等)对文档进行处理,并测试其效果。对于 SentenceWindowNodeParser,还需要使用 MetadataReplacementPostProcessor 进行后处理。

python 复制代码
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 = build_index_and_query_engine(documents, embed_client, llm_client, parser, postprocessors)
    ask_question(question, query_engine)

总结

通过优化代码结构、模块化处理、日志管理和环境变量管理,代码的可读性和可维护性得到了显著提升。同时,通过不同的节点解析器对文档进行处理,可以更好地理解不同解析器的效果和适用场景。希望这篇博客对你理解和使用 llama_index 库有所帮助!

相关推荐
HsuHeinrich17 分钟前
流程图(四)利用python绘制漏斗图
python·数据可视化
码农丁丁2 小时前
[python3]Excel解析库-xlwt
python·excel·xlwt
reasonsummer2 小时前
【办公类-47-02】20250103 课题资料快速打印(单个docx转PDF,多个pdf合并一个PDF 打印)
python·pdf
io_T_T4 小时前
python SQLAlchemy ORM——从零开始学习 01 安装库
python
香菜的开发日记4 小时前
快速学习 pytest 基础知识
自动化测试·python·pytest
背太阳的牧羊人4 小时前
grouped.get_group((‘B‘, ‘A‘))选择分组
python·pandas
穆姬姗5 小时前
【Python】论文长截图、页面分割、水印去除、整合PDF
开发语言·python·pdf
chusheng18406 小时前
基于 Python Django 的西西家居全屋定制系统(源码+部署+文档)
开发语言·python·django·家具定制系统·python 全屋家具定制系统·python 家居定制
走在考研路上6 小时前
力扣896
python·算法·leetcode
wshzd6 小时前
LLM之RAG实战(五十一)| 使用python和Cypher解析PDF数据,并加载到Neo4j数据库
数据库·python·pdf