个人随笔
目录
Agent学习(11):LlamaIndex的简单使用
2026-02-08 15:25:57

LlamaIndex(曾用名 GPT Index)是一款专为大语言模型(LLM)打造的检索增强生成(RAG)开发框架,简单说:它是连接「你的私有数据(文档 / 数据库 / API)」和「通用 LLM(如 GPT-4 / 通义千问 / Llama)」的 “桥梁”,让 LLM 能精准回答基于你私有数据的问题,而不是只依赖模型自身的训练数据。

1. 大语言模型开发框架的价值是什么?

SDK:Software Development Kit,它是一组软件工具和资源的集合,旨在帮助开发者创建、测试、部署和维护应用程序或软件。

所有开发框架(SDK)的核心价值,都是降低开发、维护成本。

大语言模型开发框架的价值,是让开发者可以更方便地开发基于大语言模型的应用。主要提供两类帮助:

  1. 第三方能力抽象。比如 LLM、向量数据库、搜索接口等
  2. 常用工具、方案封装
  3. 底层实现封装。比如流式接口、超时重连、异步与并行等

好的开发框架,需要具备以下特点:

  1. 可靠性、鲁棒性高
  2. 可维护性高
  3. 可扩展性高
  4. 学习成本低

举些通俗的例子:

  • 与外部功能解依赖
    • 比如可以随意更换 LLM 而不用大量重构代码
    • 更换三方工具也同理
  • 经常变的部分要在外部维护而不是放在代码里
    • 比如 Prompt 模板
  • 各种环境下都适用
    • 比如线程安全
  • 方便调试和测试
    • 至少要能感觉到用了比不用方便吧
    • 合法的输入不会引发框架内部的报错

划重点:选对了框架,事半功倍;反之,事倍功半。

2、LlamaIndex的简单使用

LlamaIndex 是一个为开发「知识增强」的大语言模型应用的框架(也就是 SDK)。知识增强,泛指任何在私有或特定领域数据基础上应用大语言模型的情况。

1、几行代码用LlamaIndex实现一个简单的RPG

先用conda建个环境

  1. conda create -n llama-index python=3.11
  2. conda activate llama-index

简单的GPG
运行前需要安装依赖

  1. pip install --upgrade llama-index llama-index-llms-dashscope llama-index-llms-openai-like llama-index-embeddings-dashscope
  1. import os
  2. from llama_index.core import Settings
  3. from llama_index.llms.openai_like import OpenAILike
  4. from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
  5. from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
  6. from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
  7. # LlamaIndex默认使用的大模型被替换为百炼
  8. # Settings.llm = OpenAILike(
  9. # model="qwen-max",
  10. # api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
  11. # api_key=os.getenv("DASHSCOPE_API_KEY"),
  12. # is_chat_model=True
  13. # )
  14. Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, api_key=os.getenv("DASHSCOPE_API_KEY"))
  15. # LlamaIndex默认使用的Embedding模型被替换为百炼的Embedding模型
  16. Settings.embed_model = DashScopeEmbedding(
  17. # model_name="text-embedding-v1"
  18. model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V1,
  19. # api_key=os.getenv("DASHSCOPE_API_KEY")
  20. )
  21. documents = SimpleDirectoryReader("G:\\AI\\workspace\\data").load_data()
  22. index = VectorStoreIndex.from_documents(documents)
  23. query_engine = index.as_query_engine()
  24. response = query_engine.query("deepseek v3有多少参数?")
  25. print(response)

3、LlamaIndex的相关组件使用

3.1、加载本地数据

SimpleDirectoryReader 是一个简单的本地文件加载器。它会遍历指定目录,并根据文件扩展名自动加载文件(文本内容)。
支持的文件类型:

  • .csv - comma-separated values
  • .docx - Microsoft Word
  • .epub - EPUB ebook format
  • .hwp - Hangul Word Processor
  • .ipynb - Jupyter Notebook
  • .jpeg, .jpg - JPEG image
  • .mbox - MBOX email archive
  • .md - Markdown
  • .mp3, .mp4 - audio and video
  • .pdf - Portable Document Format
  • .png - Portable Network Graphics
  • .ppt, .pptm, .pptx - Microsoft PowerPoint
  1. import json
  2. from pydantic.v1 import BaseModel
  3. from llama_index.core import SimpleDirectoryReader
  4. from llama_cloud_services import LlamaParse
  5. from llama_index.core import SimpleDirectoryReader
  6. import nest_asyncio
  7. def show_json(data):
  8. """用于展示json数据"""
  9. if isinstance(data, str):
  10. obj = json.loads(data)
  11. print(json.dumps(obj, indent=4, ensure_ascii=False))
  12. elif isinstance(data, dict) or isinstance(data, list):
  13. print(json.dumps(data, indent=4, ensure_ascii=False))
  14. elif issubclass(type(data), BaseModel):
  15. print(json.dumps(data.dict(), indent=4, ensure_ascii=False))
  16. def show_list_obj(data):
  17. """用于展示一组对象"""
  18. if isinstance(data, list):
  19. for item in data:
  20. show_json(item)
  21. else:
  22. raise ValueError("Input is not a list")
  23. def queryInfo1():
  24. reader = SimpleDirectoryReader(
  25. input_dir="G:\\AI\\workspace\\data", # 目标目录
  26. recursive=False, # 是否递归遍历子目录
  27. required_exts=[".pdf"] # (可选)只读取指定后缀的文件
  28. )
  29. documents = reader.load_data()
  30. print(documents[0].text)
  31. show_json(documents[0].json())
  32. def queryInfo2():
  33. # set up parser
  34. parser = LlamaParse(
  35. result_type="markdown" # "markdown" and "text" are available
  36. )
  37. file_extractor = {".pdf": parser}
  38. documents = SimpleDirectoryReader(input_dir="G:\\AI\\workspace\\data", required_exts=[".pdf"], file_extractor=file_extractor).load_data()
  39. print(documents[0].text)
  40. # 程序入口:只有直接运行这个文件时,才执行游戏
  41. if __name__ == "__main__":
  42. queryInfo2();

上面的queryInfo2用了LlamaParse,这个对pdf的加载比较友好,执行queryInfo2()需要去https://cloud.llamaindex.ai ↗ 注册并获取 api-key ,然后设置环境变量LLAMA_CLOUD_API_KEY,这里设置完环境变量后记得重启vscode才有效,然后再安装依赖pip install llama-cloud-services,最终运行结果如下,pdf中的表格会读取为较为标准的格式。

3.2、加载远程数据

用于处理更丰富的数据类型,并将其读取为 Document 的形式。
例如:直接读取网页

  1. from llama_index.readers.web import SimpleWebPageReader
  2. def queryInfo3():
  3. documents = SimpleWebPageReader(html_to_text=True).load_data(
  4. ["https://www.suibibkc.com"]
  5. )
  6. print(documents[0].text)
  7. # 程序入口:只有直接运行这个文件时,才执行游戏
  8. if __name__ == "__main__":
  9. queryInfo3();

更多 Data Connectors


3.3、使用 TextSplitters 对文本做切分

例如:TokenTextSplitter 按指定 token 数切分文本

  1. from llama_index.core import Document
  2. from llama_index.core.node_parser import TokenTextSplitter
  3. node_parser = TokenTextSplitter(
  4. chunk_size=512, # 每个 chunk 的最大长度
  5. chunk_overlap=200 # chunk 之间重叠长度
  6. )
  7. nodes = node_parser.get_nodes_from_documents(
  8. documents, show_progress=False
  9. )
  10. show_json(nodes[1].json())
  11. show_json(nodes[2].json())

LlamaIndex 提供了丰富的 TextSplitter,例如:

  • SentenceSplitter:在切分指定长度的 chunk 同时尽量保证句子边界不被切断;
  • CodeSplitter:根据 AST(编译器的抽象句法树)切分代码,保证代码功能片段完整;
  • SemanticSplitterNodeParser:根据语义相关性对将文本切分为片段。

3.4、使用 NodeParsers 对有结构的文档做解析

例如:HTMLNodeParser解析 HTML 文档

  1. from llama_index.core.node_parser import HTMLNodeParser
  2. from llama_index.readers.web import SimpleWebPageReader
  3. documents = SimpleWebPageReader(html_to_text=False).load_data(
  4. ["https://www.suibibk.com/"]
  5. )
  6. # 默认解析 ["p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "b", "i", "u", "section"]
  7. parser = HTMLNodeParser(tags=["span"]) # 可以自定义解析哪些标签
  8. nodes = parser.get_nodes_from_documents(documents)
  9. for node in nodes:
  10. print(node.text+"\n")

更多的 NodeParser 包括 MarkdownNodeParserJSONNodeParser等等。

3.5、向量检索

  1. VectorStoreIndex 直接在内存中构建一个 Vector Store 并建索引
    基础概念:在「检索」相关的上下文中,「索引」即index, 通常是指为了实现快速检索而设计的特定「数据结构」。

索引的具体原理与实现不是本课程的教学重点,感兴趣的同学可以参考:传统索引向量索引

  1. from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
  2. from llama_index.core.node_parser import TokenTextSplitter, SentenceSplitter
  3. # 加载 pdf 文档
  4. documents = SimpleDirectoryReader(
  5. "./data",
  6. required_exts=[".pdf"],
  7. ).load_data()
  8. # 定义 Node Parser
  9. node_parser = TokenTextSplitter(chunk_size=512, chunk_overlap=200)
  10. # 切分文档
  11. nodes = node_parser.get_nodes_from_documents(documents)
  12. # 构建 index,默认是在内存中
  13. index = VectorStoreIndex(nodes)
  14. # 另外一种实现方式
  15. # index = VectorStoreIndex.from_documents(documents=documents, transformations=[SentenceSplitter(chunk_size=512)])
  16. # 写入本地文件
  17. # index.storage_context.persist(persist_dir="./doc_emb")
  18. # 获取 retriever
  19. vector_retriever = index.as_retriever(
  20. similarity_top_k=2 # 返回2个结果
  21. )
  22. # 检索
  23. results = vector_retriever.retrieve("deepseek v3数学能力怎么样?")
  24. print(results[0].text)

使用自定义的 Vector Store,以 Qdrant 为例:

  1. pip install llama-index-vector-stores-qdrant
  1. from llama_index.core.indices.vector_store.base import VectorStoreIndex
  2. from llama_index.vector_stores.qdrant import QdrantVectorStore
  3. from llama_index.core import StorageContext
  4. from qdrant_client import QdrantClient
  5. from qdrant_client.models import VectorParams, Distance
  6. client = QdrantClient(location=":memory:")
  7. collection_name = "demo"
  8. collection = client.create_collection(
  9. collection_name=collection_name,
  10. vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
  11. )
  12. vector_store = QdrantVectorStore(client=client, collection_name=collection_name)
  13. # storage: 指定存储空间
  14. storage_context = StorageContext.from_defaults(vector_store=vector_store)
  15. # 创建 index:通过 Storage Context 关联到自定义的 Vector Store
  16. index = VectorStoreIndex(nodes, storage_context=storage_context)
  17. # 获取 retriever
  18. vector_retriever = index.as_retriever(similarity_top_k=1)
  19. # 检索
  20. results = vector_retriever.retrieve("deepseek v3数学能力怎么样")
  21. print(results[0])

更多索引与检索方式

LlamaIndex 内置了丰富的检索机制,例如:

3.6、检索后处理

LlamaIndex 的 Node Postprocessors 提供了一系列检索后处理模块。

例如:我们可以用不同模型对检索后的 Nodes 做重排序

  1. # 获取 retriever
  2. vector_retriever = index.as_retriever(similarity_top_k=5)
  3. # 检索
  4. nodes = vector_retriever.retrieve("deepseek v3有多少参数?")
  5. for i, node in enumerate(nodes):
  6. print(f"[{i}] {node.text}\n")

重排序

  1. from llama_index.core.postprocessor import LLMRerank
  2. postprocessor = LLMRerank(top_n=2)
  3. nodes = postprocessor.postprocess_nodes(nodes, query_str="deepseek v3有多少参数?")
  4. for i, node in enumerate(nodes):
  5. print(f"[{i}] {node.text}")

更多的 Rerank 及其它后处理方法,参考官方文档:Node Postprocessor Modules

3.7. 生成回复(QA & Chat)

  1. import os
  2. from llama_index.core import Settings
  3. from llama_index.llms.openai_like import OpenAILike
  4. from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
  5. from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
  6. from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
  7. from llama_index.core.postprocessor import LLMRerank
  8. # LlamaIndex默认使用的大模型被替换为百炼
  9. # Settings.llm = OpenAILike(
  10. # model="qwen-max",
  11. # api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
  12. # api_key=os.getenv("DASHSCOPE_API_KEY"),
  13. # is_chat_model=True
  14. # )
  15. Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, api_key=os.getenv("DASHSCOPE_API_KEY"))
  16. # LlamaIndex默认使用的Embedding模型被替换为百炼的Embedding模型
  17. Settings.embed_model = DashScopeEmbedding(
  18. # model_name="text-embedding-v1"
  19. model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V1,
  20. # api_key=os.getenv("DASHSCOPE_API_KEY")
  21. )
  22. documents = SimpleDirectoryReader("G:\\AI\\workspace\\data").load_data()
  23. index = VectorStoreIndex.from_documents(documents)
  24. '''
  25. # 获取 retriever
  26. vector_retriever = index.as_retriever(similarity_top_k=5)
  27. # 检索
  28. nodes = vector_retriever.retrieve("deepseek v3有多少参数?")
  29. for i, node in enumerate(nodes):
  30. print(f"[{i}] {node.text}\n")
  31. #对检索结果进行重排序
  32. postprocessor = LLMRerank(top_n=2)
  33. nodes = postprocessor.postprocess_nodes(nodes, query_str="deepseek v3有多少参数?")
  34. for i, node in enumerate(nodes):
  35. print(f"[{i}] {node.text}\n")
  36. #流式输出
  37. qa_engine = index.as_query_engine(streaming=True)
  38. response = qa_engine.query("deepseek v3数学能力怎么样?")
  39. response.print_response_stream()
  40. #多轮对话
  41. chat_engine = index.as_chat_engine()
  42. response = chat_engine.chat("deepseek v3数学能力怎么样?")
  43. print(response)
  44. #会记住上下文
  45. response = chat_engine.chat("代码能力呢?")
  46. print(response)
  47. '''
  48. #多轮对话流式输出
  49. chat_engine = index.as_chat_engine()
  50. streaming_response = chat_engine.stream_chat("deepseek v3数学能力怎么样?")
  51. # streaming_response.print_response_stream()
  52. for token in streaming_response.response_gen:
  53. print(token, end="", flush=True)

3.8. Prompt

  1. from llama_index.core import PromptTemplate
  2. from llama_index.core.llms import ChatMessage, MessageRole
  3. from llama_index.core import ChatPromptTemplate
  4. import os
  5. from llama_index.core import Settings
  6. from llama_index.llms.openai_like import OpenAILike
  7. from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
  8. from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
  9. from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
  10. from llama_index.core.postprocessor import LLMRerank
  11. from llama_index.llms.deepseek import DeepSeek
  12. def queryInfo1():
  13. prompt = PromptTemplate("写一个关于{topic}的笑话")
  14. print(prompt.format(topic="小明"))
  15. def queryInfo2():
  16. chat_text_qa_msgs = [
  17. ChatMessage(
  18. role=MessageRole.SYSTEM,
  19. content="你叫{name},你必须根据用户提供的上下文回答问题。",
  20. ),
  21. ChatMessage(
  22. role=MessageRole.USER,
  23. content=(
  24. "已知上下文:\n" \
  25. "{context}\n\n" \
  26. "问题:{question}"
  27. )
  28. ),
  29. ]
  30. text_qa_template = ChatPromptTemplate(chat_text_qa_msgs)
  31. print(
  32. text_qa_template.format(
  33. name="小明",
  34. context="这是一个测试",
  35. question="这是什么"
  36. )
  37. )
  38. def queryInfo3():
  39. Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, api_key=os.getenv("DASHSCOPE_API_KEY"))
  40. chat_text_qa_msgs = [
  41. ChatMessage(
  42. role=MessageRole.SYSTEM,
  43. content="你叫{name},你必须根据用户提供的上下文回答问题。",
  44. ),
  45. ChatMessage(
  46. role=MessageRole.USER,
  47. content=(
  48. "已知上下文:\n" \
  49. "{context}\n\n" \
  50. "问题:{question}"
  51. )
  52. ),
  53. ]
  54. text_qa_template = ChatPromptTemplate(chat_text_qa_msgs)
  55. response = Settings.llm.complete(
  56. text_qa_template.format(
  57. name="小明",
  58. context="这是一个测试",
  59. question="你是谁,我们在干嘛"
  60. )
  61. )
  62. print(response.text)
  63. if __name__ == "__main__":
  64. queryInfo3();

4. 基于 LlamaIndex 实现一个功能较完整的 RAG 系统

  1. pip install qdrant-client
  2. pip install llama-index llama-index-vector-stores-qdrant

例子

  1. import os
  2. from qdrant_client import QdrantClient
  3. from qdrant_client.models import VectorParams, Distance
  4. from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, get_response_synthesizer
  5. from llama_index.vector_stores.qdrant import QdrantVectorStore
  6. from llama_index.core.node_parser import SentenceSplitter
  7. from llama_index.core.response_synthesizers import ResponseMode
  8. from llama_index.core.ingestion import IngestionPipeline
  9. from llama_index.core import Settings
  10. from llama_index.core import StorageContext
  11. from llama_index.core.postprocessor import LLMRerank, SimilarityPostprocessor
  12. from llama_index.core.retrievers import QueryFusionRetriever
  13. from llama_index.core.query_engine import RetrieverQueryEngine
  14. from llama_index.core.chat_engine import CondenseQuestionChatEngine
  15. from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
  16. from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
  17. EMBEDDING_DIM = 1536
  18. COLLECTION_NAME = "full_demo"
  19. PATH = "./qdrant_db"
  20. client = QdrantClient(path=PATH)
  21. # 1. 指定全局llm与embedding模型
  22. Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX,api_key=os.getenv("DASHSCOPE_API_KEY"))
  23. Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V1)
  24. # 2. 指定全局文档处理的 Ingestion Pipeline
  25. Settings.transformations = [SentenceSplitter(chunk_size=512, chunk_overlap=200)]
  26. # 3. 加载本地文档
  27. documents = SimpleDirectoryReader("G:\\AI\\workspace\\data").load_data()
  28. if client.collection_exists(collection_name=COLLECTION_NAME):
  29. client.delete_collection(collection_name=COLLECTION_NAME)
  30. # 4. 创建 collection
  31. client.create_collection(
  32. collection_name=COLLECTION_NAME,
  33. vectors_config=VectorParams(size=EMBEDDING_DIM, distance=Distance.COSINE)
  34. )
  35. # 5. 创建 Vector Store
  36. vector_store = QdrantVectorStore(client=client, collection_name=COLLECTION_NAME)
  37. # 6. 指定 Vector Store 的 Storage 用于 index
  38. storage_context = StorageContext.from_defaults(vector_store=vector_store)
  39. index = VectorStoreIndex.from_documents(
  40. documents, storage_context=storage_context
  41. )
  42. # 最终打分低于0.6的文档被过滤掉
  43. sp = SimilarityPostprocessor(similarity_cutoff=0.6)
  44. # 7. 定义检索后排序模型
  45. reranker = LLMRerank(top_n=2)
  46. # 8. 定义 RAG Fusion 检索器
  47. fusion_retriever = QueryFusionRetriever(
  48. [index.as_retriever()],
  49. similarity_top_k=5, # 检索召回 top k 结果
  50. num_queries=3, # 生成 query 数
  51. use_async=False,
  52. # query_gen_prompt="", # 可以自定义 query 生成的 prompt 模板
  53. )
  54. # 9. 构建单轮 query engine
  55. query_engine = RetrieverQueryEngine.from_args(
  56. fusion_retriever,
  57. # 处理器执行顺序:先过滤低相似度文档,再做LLM重排
  58. node_postprocessors=[sp,reranker],
  59. response_synthesizer=get_response_synthesizer(
  60. response_mode = ResponseMode.REFINE
  61. )
  62. )
  63. # 10. 对话引擎
  64. chat_engine = CondenseQuestionChatEngine.from_defaults(
  65. query_engine=query_engine,
  66. # condense_question_prompt="" # 可以自定义 chat message prompt 模板
  67. )
  68. # 测试多轮对话
  69. # User: deepseek v3有多少参数
  70. # User: 每次激活多少
  71. while True:
  72. question=input("User:")
  73. if question.strip() == "":
  74. break
  75. response = chat_engine.chat(question)
  76. print(f"AI: {response}")

5、基于LlamaIndex实现一个中医临床诊疗的例子

  1. import re
  2. import logging
  3. import sys
  4. import os
  5. from llama_index.core import PromptTemplate, Settings, SimpleDirectoryReader, VectorStoreIndex, load_index_from_storage, StorageContext, QueryBundle
  6. from llama_index.core.schema import MetadataMode
  7. from llama_index.core.node_parser import SentenceSplitter
  8. from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
  9. from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
  10. from llama_index.core.callbacks import LlamaDebugHandler, CallbackManager
  11. from llama_index.core.indices.vector_store import VectorIndexRetriever
  12. from llama_index.core.query_engine import RetrieverQueryEngine
  13. from llama_index.core.response_synthesizers import ResponseMode
  14. from llama_index.core import get_response_synthesizer
  15. from llama_index.core.postprocessor import SimilarityPostprocessor
  16. # 定义日志配置
  17. logging.basicConfig(stream=sys.stdout, level=logging.INFO)
  18. logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
  19. #指定全局llm与embedding模型
  20. Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX,api_key=os.getenv("DASHSCOPE_API_KEY"))
  21. Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V1)
  22. def remove_english(input_file, output_file):
  23. """
  24. 去除文件中所有英文字符并生成新文件
  25. :param input_file: 输入文件路径
  26. :param output_file: 输出文件路径
  27. """
  28. try:
  29. with open(input_file, 'r', encoding='utf-8') as f_in:
  30. content = f_in.read()
  31. # 使用正则表达式移除所有英文字母
  32. filtered_content = re.sub('[A-Za-z/]', '', content)
  33. with open(output_file, 'w', encoding='utf-8') as f_out:
  34. f_out.write(filtered_content)
  35. print(f"处理完成,已生成新文件:{output_file}")
  36. except Exception as e:
  37. print(f"处理出错:{str(e)}")
  38. def createEmbedding():
  39. # 读取文档
  40. documents = SimpleDirectoryReader("G:\\AI\workspace\\data\\", required_exts=[".txt"]).load_data()
  41. #对文档进行切分,将切分后的片段转化为embedding向量,构建向量索引
  42. index = VectorStoreIndex.from_documents(documents, transformations=[SentenceSplitter(chunk_size=256)])
  43. # 将embedding向量和向量索引存储到文件中
  44. # ./doc_emb 是存储路径
  45. index.storage_context.persist(persist_dir='G:\\AI\\workspace\\vscode\\db\\doc_emb')
  46. print("构建向量数据库完成")
  47. def queryInfo(query:str):
  48. # 使用LlamaDebugHandler构建事件回溯器,以追踪LlamaIndex执行过程中发生的事件
  49. llama_debug = LlamaDebugHandler(print_trace_on_end=True)
  50. callback_manager = CallbackManager([llama_debug])
  51. Settings.callback_manager = callback_manager
  52. # 从存储文件中读取embedding向量和向量索引
  53. storage_context = StorageContext.from_defaults(persist_dir="G:\\AI\\workspace\\vscode\\db\\doc_emb")
  54. # 根据存储的embedding向量和向量索引重新构建检索索引
  55. index = load_index_from_storage(storage_context)
  56. #构建查询引擎
  57. # streaming 流式输出
  58. # similarity_top_k 检索结果的数量
  59. query_engine = index.as_query_engine(streaming=True, similarity_top_k=5)
  60. #追踪哪些文档片段被检索
  61. # 获取我们抽取出的相似度 top 5 的片段
  62. contexts = query_engine.retrieve(QueryBundle(query))
  63. print('-' * 10 + 'ref' + '-' * 10)
  64. for i, context in enumerate(contexts):
  65. print('#' * 10 + f'chunk {i} start' + '#' * 10)
  66. content = context.node.get_content(metadata_mode=MetadataMode.LLM)
  67. print(content)
  68. print('#' * 10 + f'chunk {i} end' + '#' * 10)
  69. print('-' * 10 + 'ref' + '-' * 10)
  70. #生成答案
  71. response = query_engine.query(query)
  72. #流式输出
  73. #response.print_response_stream()
  74. print(response)
  75. # get_llm_inputs_outputs 返回每个LLM调用的开始/结束事件
  76. event_pairs = llama_debug.get_llm_inputs_outputs()
  77. print("------------------")
  78. #打印详细的执行过程
  79. #print(event_pairs)
  80. #自定义retriever和一种response synthesizer
  81. def queryInfo2(query:str):
  82. # 从存储文件中读取embedding向量和向量索引
  83. storage_context = StorageContext.from_defaults(persist_dir="G:\\AI\\workspace\\vscode\\db\\doc_emb")
  84. # 根据存储的embedding向量和向量索引重新构建检索索引
  85. index = load_index_from_storage(storage_context)
  86. # 构建retriever
  87. retriever = VectorIndexRetriever(
  88. index = index,
  89. similarity_top_k = 5,
  90. )
  91. # 构建response synthesizer
  92. response_synthesizer = get_response_synthesizer(
  93. response_mode = ResponseMode.REFINE
  94. )
  95. # 构建查询引擎
  96. query_engine = RetrieverQueryEngine(
  97. retriever = retriever,
  98. response_synthesizer = response_synthesizer,
  99. node_postprocessors = [SimilarityPostprocessor(similarity_cutoff=0.6)]
  100. )
  101. #生成答案
  102. response = query_engine.query(query)
  103. #流式输出
  104. #response.print_response_stream()
  105. print(response)
  106. #自定义 Prompt
  107. def queryInfo3(query:str):
  108. # 定义qa prompt
  109. qa_prompt_tmpl_str = (
  110. "上下文信息如下。\n"
  111. "---------------------\n"
  112. "{context_str}\n"
  113. "---------------------\n"
  114. "请根据上下文信息而不是先验知识来回答以下的查询。"
  115. "作为一个医疗人工智能助手,你的回答要尽可能严谨。\n"
  116. "Query: {query_str}\n"
  117. "Answer: "
  118. )
  119. qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)
  120. # 定义refine prompt
  121. refine_prompt_tmpl_str = (
  122. "原始查询如下:{query_str}"
  123. "我们提供了现有答案:{existing_answer}"
  124. "我们有机会通过下面的更多上下文来完善现有答案(仅在需要时)。"
  125. "------------"
  126. "{context_msg}"
  127. "------------"
  128. "考虑到新的上下文,优化原始答案以更好地回答查询。 如果上下文没有用,请返回原始答案。"
  129. "Refined Answer:"
  130. )
  131. refine_prompt_tmpl = PromptTemplate(refine_prompt_tmpl_str)
  132. # 从存储文件中读取embedding向量和向量索引
  133. storage_context = StorageContext.from_defaults(persist_dir="G:\\AI\\workspace\\vscode\\db\\doc_emb")
  134. # 根据存储的embedding向量和向量索引重新构建检索索引
  135. index = load_index_from_storage(storage_context)
  136. # 构建查询引擎
  137. query_engine = index.as_query_engine(similarity_top_k=5)
  138. # 更新查询引擎中的prompt template
  139. query_engine.update_prompts(
  140. {
  141. "response_synthesizer:text_qa_template": qa_prompt_tmpl,
  142. "response_synthesizer:refine_template": refine_prompt_tmpl
  143. }
  144. )
  145. #生成答案
  146. response = query_engine.query(query)
  147. #流式输出
  148. #response.print_response_stream()
  149. print(response)
  150. # 使用示例
  151. if __name__ == "__main__":
  152. #remove_english('G:\\AI\workspace\\data\\demo.txt', 'G:\\AI\workspace\\data\\demo-1.txt')
  153. #createEmbedding()
  154. queryInfo("不耐疲劳,口燥、咽干可能是哪些证候?");
  155. #queryInfo2("不耐疲劳,口燥、咽干可能是哪些证候?");
  156. #queryInfo3("不耐疲劳,口燥、咽干可能是哪些证候?");

上面需要补充说明的知识点如下

SentenceSplitter 参数详细设置

预设会以 1024 个 token 为界切割片段, 每个片段的开头重叠上一个片段的 200 个 token 的内容。

  1. chunk_size = 1024, # 切片 token 数限制
  2. chunk_overlap = 200, # 切片开头与前一片段尾端的重复 token 数
  3. paragraph_separator = '\n\n\n', # 段落的分界
  4. secondary_chunking_regex = '[^,.;。?!]+[,.;。?!]?' # 单一句子的样式
  5. separator = ' ', # 最小切割的分界字元

追踪检索片段

追踪检索片段,调整chunk_size的值,可以让embedding模型切分出的片段更合理,提高RAG系统的表现。
如果想追踪更多的检索片段,可以提高 similarity_top_k 的值。
如果想追踪片段具体的相似度得分(Similarity Score)的值,可以将log中的level设置为DEBUG级别。

Query 过程分析

  1. **********
  2. Trace: query
  3. |_CBEventType.QUERY -> 0.866733 seconds
  4. |_CBEventType.RETRIEVE -> 0.866733 seconds
  5. |_CBEventType.EMBEDDING -> 0.766721 seconds
  6. |_CBEventType.SYNTHESIZE -> 0.0 seconds
  7. |_CBEventType.TEMPLATING -> 0.0 seconds
  8. |_CBEventType.LLM -> 0.0 seconds
  9. **********

貌似打印结果不符合预期,我明明等了二十秒

Retrieve 检索进阶

抽取(retrieve)阶段的retrievers模块规定了针对查询从知识库获取相关上下文的技术。我们之前使用的都是默认的方法,其实LlamaIndex官方为我们提供了一些其他常用的方法:

  • SimilarityPostprocessor: 使用similarity_cutoff设置阈值。移除低于某个相似度分数的节点。
  • KeywordNodePostprocessor: 使用required_keywords和exclude_keywords。根据关键字包含或排除过滤节点。
  • MetadataReplacementPostProcessor: 用其元数据中的数据替换节点内容。
  • LongContextReorder: 重新排序节点,这有利于需要大量顶级结果的情况,可以解决模型在扩展上下文中的困难。
  • SentenceEmbeddingOptimizer: 选择percentile_cutoff或threshold_cutoff作为相关性。基于嵌入删除不相关的句子。
  • CohereRerank: 使用coherence ReRank对节点重新排序,返回前N个结果。
  • SentenceTransformerRerank: 使用SentenceTransformer交叉编码器对节点重新排序,产生前N个节点。
  • LLMRerank: 使用LLM对节点重新排序,为每个节点提供相关性评分。
  • FixedRecencyPostprocessor: 返回按日期排序的节点。
  • EmbeddingRecencyPostprocessor: 按日期对节点进行排序,但也会根据嵌入相似度删除较旧的相似节点。
  • TimeWeightedPostprocessor: 对节点重新排序,偏向于最近未返回的信息。
  • PIINodePostprocessor(β): 可以利用本地LLM或NER模型删除个人身份信息。
  • PrevNextNodePostprocessor(β): 根据节点关系,按顺序检索在节点之前、之后或两者同时出现的节点

响应合成器 response synthesizer

合成(synthesize)阶段的响应合成器(response synthesizer)会引导LLM生成响应,将用户查询与检索到的文本块混合在一起,并给出一个精心设计的答案。

LlamaIndex官方为我们提供了多种响应合成器:

  • Refine: 这种方法遍历每一段文本,一点一点地精炼答案。
  • Compact: 是Refine的精简版。它将文本集中在一起,因此需要处理的步骤更少。
  • Tree Summarize: 想象一下,把许多小的答案结合起来,再总结,直到你得到一个主要的答案。
  • Simple Summarize: 只是把文本片段剪短,然后给出一个快速的总结。
  • No Text: 这个问题不会给你答案,但会告诉你它会使用哪些文本。
  • Accumulate: 为每一篇文章找一堆小答案,然后把它们粘在一起。
  • Compact Accumulate: 是“Compact”和“Accumulate”的合成词。

现在,我们选择一种retriever和一种response synthesizer。retriever选择SimilarityPostprocessor,response synthesizer选择Refine。

RAG 系统评估

评估的例子我们可以把文档给大模型,让大模型给我们自动生成一批问答对

 6

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2