Datawhale AI夏令营 RAG入门 task3~~~

配置模型

这一步已经比较套路了,参照之前的几个任务。
主要是加载API、设置我们要调用的模型、配置我们所需要的嵌入模型
我这里和教程不太一样的是,模型用的是智谱的glm-4-air

import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()
# 从环境变量中读取api_key
api_key = os.getenv('ZHIPU_API_KEY')
base_url = "https://open.bigmodel.cn/api/paas/v4/"
chat_model = "glm-4-air"
emb_model = "embedding-2"

# 配置对话模型
from llama_index.llms.zhipuai import ZhipuAI
llm = ZhipuAI(
    api_key = api_key,
    model = chat_model,
)

# 配置嵌入模型
from llama_index.embeddings.zhipuai import ZhipuAIEmbedding
embedding = ZhipuAIEmbedding(
    api_key = api_key,
    model = emb_model,
)
emb = embedding.get_text_embedding("测试测试测试")

文本准备

我这里选取“强化学习入门指南.txt“作为示例文本
这里注意文本路径名的设置
下面三种写法有什么不一样?

'docs/强化学习入门指南.txt'
'/docs/强化学习入门指南.txt'
'./docs/强化学习入门指南.txt'

'docs/强化学习入门指南.txt'
最常见的相对路径写法。Python 会从当前工作目录(也就是你运行 Python 脚本的那个目录)开始查找 docs 文件夹,然后在其下寻找 强化学习入门指南.txt 文件。

'/docs/强化学习入门指南.txt'
一种绝对路径的写法。开头的斜杠 / 表示从根目录开始查找。在类 Unix 系统(如 Linux 和 macOS)中,这代表了文件系统的顶层。在 Windows 系统中,这通常会解析到当前驱动器的根目录(例如 C:\)
显然,在该项目中,我们要的不是绝对路径的写法

'./docs/强化学习入门指南.txt'
也是一种相对路径写法,大多数情况下和第一种写法等价

# 读取本地的文本
from llama_index.core import SimpleDirectoryReader,Document
documents = SimpleDirectoryReader(input_files=['../docs/强化学习入门指南.txt']).load_data()

索引的构建

方法一:最简方法 - 从文档直接构建索引

使用LlamaIndex的 VectorStoreIndex.from_documents()函数来帮助我们完成

  1. 自动对传入的documents进行切块(默认使用SentenceSplitter)。
  2. 调用指定的embed_model将每个文本块转换成向量。
  3. 使用一个默认的、内存中的简单向量存储库来存放这些向量。
  4. index.as_query_engine() 会自动配置好检索器和回答合成器。
    不过,简单的方法通常意味着它可能不太灵活
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(documents,embed_model=embedding,show_progress=True)

方法二:构建节点,然后构建索引(使用Faiss)

首先解析文档为节点

# 构建节点
from llama_index.core.node_parser import SentenceSplitter
transformations = [SentenceSplitter(chunk_size = 512)]

from llama_index.core.ingestion.pipeline import run_transformations
nodes = run_transformations(documents, transformations=transformations)

接下来,根据节点来进行索引的构建
执行完毕后,index 对象就创建完成了。它是一个包含了所有节点信息和向量信息,并使用Faiss作为底层向量引擎的完整索引。

from llama_index.vector_stores.faiss import FaissVectorStore
import faiss
from llama_index.core import StorageContext, VectorStoreIndex

dimensions = len(emb)
vector_store = FaissVectorStore(faiss_index=faiss.IndexFlatL2(dimensions))
storage_context = StorageContext.from_defaults(vector_store=vector_store)

index = VectorStoreIndex(
    nodes = nodes,
    storage_context=storage_context,
    embed_model = embedding,
)

接下来,我们可以进行索引的持久化,即调用index.storage_context.persist(persist_dir)方法,把索引存储到硬盘

persist_dir = "./storage"
index.storage_context.persist(persist_dir)

如何读取之前保存到硬盘的索引呢?如下所示:

from llama_index.vector_stores.faiss import FaissVectorStore
import faiss
from llama_index.core import StorageContext, load_index_from_storage
vector_store = FaissVectorStore.from_persist_dir(persist_dir)
storage_context = StorageContext.from_defaults(
    vector_store=vector_store, persist_dir=persist_dir
)
index = load_index_from_storage(storage_context=storage_context,embed_model = embedding)

让我们来测试一下:

query_engine = index.as_query_engine(llm=llm)
# 回答提问
response = query_engine.query("怎么入门强化学习?")
response

方法三:检索器 + 合成器

# 构建检索器
from llama_index.core.retrievers import VectorIndexRetriever
kwargs = {'similarity_top_k': 5, 'index': index, 'dimensions': dimensions} # 必要参数
retriever = VectorIndexRetriever(**kwargs)

# 构建合成器
from llama_index.core.response_synthesizers  import get_response_synthesizer
response_synthesizer = get_response_synthesizer(llm=llm)

# 构建问答引擎
from llama_index.core.query_engine import RetrieverQueryEngine
engine = RetrieverQueryEngine(
      retriever=retriever,
      response_synthesizer=response_synthesizer
        )

我们来测试一下,来个提问

# 提问
question = "请问商标注册需要提供哪些文件?"
answer = engine.query(question)
print(answer.response)

方法四:利用Qdrant向量库

先安装需要的一些组件和库

%pip install qdrant-client
%pip install llama-index-vector-stores-qdrant
%pip install llama-index-readers-file

这一步就来加载文档

import qdrant_client
from llama_index.core import SimpleDirectoryReader

# load documents
documents = SimpleDirectoryReader(
    input_files=['./docs/问答手册.txt']
).load_data()

print("Document ID:", documents[0].doc_id)

使用 Qdrant 构建标准 RAG 流程

先来构建索引

from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.vector_stores.qdrant import QdrantVectorStore

qclient = qdrant_client.QdrantClient(path="qdrant")
vector_store = QdrantVectorStore(client=qclient, collection_name="wenda")
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(
    documents,
    storage_context=storage_context,
    embed_model = embedding
)

接下来,让我们构建问答引擎

# 构建检索器
retriever = VectorIndexRetriever(**kwargs)
# 构建合成器
response_synthesizer = get_response_synthesizer(llm=llm)
# 构建问答引擎
engine = RetrieverQueryEngine(
      retriever=retriever,
      response_synthesizer=response_synthesizer,
        )

接下来我们来提示问问看

# 提问
question = "What are the applications of Agent AI systems ?"
answer = engine.query(question)
print(answer.response)

Qdrant支持metadata filter的
这一步是用来给每个文档添加metadata。在查询的时候,指定metadata filter。

from llama_index.core.schema import TextNode

nodes = [
    TextNode(
        text="The Shawshank Redemption",
        metadata={
            "author": "Stephen King",
            "theme": "Friendship",
            "year": 1994,
        },
    ),
    TextNode(
        text="The Godfather",
        metadata={
            "director": "Francis Ford Coppola",
            "theme": "Mafia",
            "year": 1972,
        },
    ),
    TextNode(
        text="Inception",
        metadata={
            "director": "Christopher Nolan",
            "theme": "Fiction",
            "year": 2010,
        },
    ),
    TextNode(
        text="To Kill a Mockingbird",
        metadata={
            "author": "Harper Lee",
            "theme": "Mafia",
            "year": 1960,
        },
    ),
    TextNode(
        text="1984",
        metadata={
            "author": "George Orwell",
            "theme": "Totalitarianism",
            "year": 1949,
        },
    ),
    TextNode(
        text="The Great Gatsby",
        metadata={
            "author": "F. Scott Fitzgerald",
            "theme": "The American Dream",
            "year": 1925,
        },
    ),
    TextNode(
        text="Harry Potter and the Sorcerer's Stone",
        metadata={
            "author": "J.K. Rowling",
            "theme": "Fiction",
            "year": 1997,
        },
    ),
]

接下来,构建支持过滤的索引
这里创建了一个新的 Qdrant 集合 collection_name="filter",并将上面带有元数据的 nodes 存入其中。

vector_store = QdrantVectorStore(client=qclient, collection_name="filter")
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex(
    nodes,
    storage_context=storage_context,
    embed_model = embedding
)

构建metadata filter,使用LlamaIndex 标准过滤器

from llama_index.core.vector_stores import (
    MetadataFilter,
    MetadataFilters,
    FilterOperator,
)

filters = MetadataFilters(
    filters=[
        MetadataFilter(key="theme", operator=FilterOperator.EQ, value="Mafia"),
    ]
)

进行检索,检察一下结果

retriever = index.as_retriever(filters=filters, llm=llm)
retriever.retrieve("What is inception about?")

接下来是组合多个filter方法

from llama_index.core.vector_stores import FilterOperator, FilterCondition

filters = MetadataFilters(
    filters=[
        MetadataFilter(key="theme", value="Fiction"),
        MetadataFilter(key="year", value=1997, operator=FilterOperator.GT),
    ],
    condition=FilterCondition.AND,
)

retriever = index.as_retriever(filters=filters, llm=llm)
retriever.retrieve("Harry Potter?")

直接把filter的字典作为参数,构建retriever。构建一个更复杂的filter

retriever = index.as_retriever(
    vector_store_kwargs={"filter": {"theme": "Mafia"}},
    llm=llm
)
retriever.retrieve("What is inception about?")

也可以利用Qdrant自带的检索能力

nodes = [
    TextNode(
        text="りんごとは",
        metadata={"author": "Tanaka", "fruit": "apple", "city": "Tokyo"},
    ),
    TextNode(
        text="Was ist Apfel?",
        metadata={"author": "David", "fruit": "apple", "city": "Berlin"},
    ),
    TextNode(
        text="Orange like the sun",
        metadata={"author": "Jane", "fruit": "orange", "city": "Hong Kong"},
    ),
    TextNode(
        text="Grape is...",
        metadata={"author": "Jane", "fruit": "grape", "city": "Hong Kong"},
    ),
    TextNode(
        text="T-dot > G-dot",
        metadata={"author": "George", "fruit": "grape", "city": "Toronto"},
    ),
    TextNode(
        text="6ix Watermelons",
        metadata={
            "author": "George",
            "fruit": "watermelon",
            "city": "Toronto",
        },
    ),
]
vector_store = QdrantVectorStore(client=qclient, collection_name="default")
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex(
    nodes,
    storage_context=storage_context,
    embed_model = embedding
)

这里的语法完全是 Qdrant 的

from qdrant_client.http.models import Filter, FieldCondition, MatchValue
filters = Filter(
    should=[
        Filter(
            must=[
                FieldCondition(
                    key="fruit",
                    match=MatchValue(value="apple"),
                ),
                FieldCondition(
                    key="city",
                    match=MatchValue(value="Tokyo"),
                ),
            ]
        ),
        Filter(
            must=[
                FieldCondition(
                    key="fruit",
                    match=MatchValue(value="grape"),
                ),
                FieldCondition(
                    key="city",
                    match=MatchValue(value="Toronto"),
                ),
            ]
        ),
    ]
)
retriever = index.as_retriever(
    vector_store_kwargs={"qdrant_filters": filters},
    llm=llm
)
response = retriever.retrieve("Who makes grapes?")
for node in response:
    print("node", node.score)
    print("node", node.text)
    print("node", node.metadata)
posted @ 2025-07-20 02:22  语冰morni  阅读(28)  评论(0)    收藏  举报