使用 `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 库有所帮助!

相关推荐
hvinsion40 分钟前
【开源工具】超全Emoji工具箱开发实战:Python+PyQt5打造跨平台表情管理神器
python·qt·开源·emoji·表情包
x_feng_x3 小时前
Java从入门到精通 - 常用API(一)
java·开发语言·python
蹦蹦跳跳真可爱5895 小时前
Python----目标检测(《Fast R-CNN》和Fast R-CNN)
人工智能·python·深度学习·神经网络·目标检测·cnn
czliutz5 小时前
NiceGUI 是一个基于 Python 的现代 Web 应用框架
开发语言·前端·python
bluebonnet276 小时前
【agent开发】部署LLM(一)
python·llama
HHBon7 小时前
判断用户输入昵称是否存在(Python)
linux·开发语言·python
敢敢变成了憨憨8 小时前
java操作服务器文件(把解析过的文件迁移到历史文件夹地下)
java·服务器·python
敲键盘的小夜猫9 小时前
Milvus向量Search查询综合案例实战(下)
数据库·python·milvus
简简单单做算法9 小时前
基于mediapipe深度学习的虚拟画板系统python源码
人工智能·python·深度学习·mediapipe·虚拟画板
愿望会实现吧11 小时前
|从零开始的Pyside2界面编程|绘图、布局及页面切换
python