Bootstrap

构建LangChain应用程序的示例代码:62、如何使用Oracle AI向量搜索和Langchain构建端到端的RAG(检索增强生成)pipeline

Oracle AI 向量搜索与文档处理

Oracle AI向量搜索专为人工智能(AI)工作负载设计,允许您基于语义而非关键词来查询数据。
Oracle AI向量搜索的最大优势之一是可以在单一系统中结合对非结构化数据的语义搜索和对业务数据的关系搜索。
这不仅功能强大,而且显著更有效,因为您不需要添加专门的向量数据库,从而消除了多个系统之间数据碎片化的痛点。

此外,您的向量可以受益于Oracle数据库所有最强大的功能,如以下这些:

本指南演示了如何使用Oracle AI向量搜索与Langchain来服务端到端的RAG pipeline。本指南通过示例展示了:

  • 使用OracleDocLoader从各种来源加载文档
  • 使用OracleSummary在数据库内/外对它们进行摘要
  • 使用OracleEmbeddings在数据库内/外为它们生成嵌入
  • 使用OracleTextSplitter的高级Oracle功能根据不同需求对它们进行分块
  • 在向量存储中存储和索引它们,并在OracleVS中为查询查询它们

如果您刚开始使用Oracle数据库,可以考虑探索免费的Oracle 23 AI,它为设置您的数据库环境提供了很好的介绍。在使用数据库时,通常建议避免默认使用系统用户;相反,您可以创建自己的用户以增强安全性和自定义性。有关用户创建的详细步骤,请参阅我们的端到端指南,它还展示了如何在Oracle中设置用户。此外,了解用户权限对于有效管理数据库安全至关重要。您可以在官方Oracle指南中了解更多关于管理用户账户和安全性的信息。

先决条件

请安装Oracle Python客户端驱动程序以使用Langchain和Oracle AI向量搜索。

# pip install oracledb
# 安装Oracle Python客户端驱动程序

创建演示用户

首先,创建一个具有所有必需权限的演示用户。

import sys
import oracledb

# 请更新您的用户名、密码、主机名和服务名
# 请确保此用户具有执行以下所有操作的足够权限
username = ""
password = ""
dsn = ""

try:
    conn = oracledb.connect(user=username, password=password, dsn=dsn)
    print("连接成功!")

    cursor = conn.cursor()
    cursor.execute(
        """
    begin
        -- 删除用户
        begin
            execute immediate 'drop user testuser cascade';
        exception
            when others then
                dbms_output.put_line('设置用户时出错。');
        end;
        execute immediate 'create user testuser identified by testuser';
        execute immediate 'grant connect, unlimited tablespace, create credential, create procedure, create any index to testuser';
        execute immediate 'create or replace directory DEMO_PY_DIR as ''/scratch/hroy/view_storage/hroy_devstorage/demo/orachain''';
        execute immediate 'grant read, write on directory DEMO_PY_DIR to public';
        execute immediate 'grant create mining model to testuser';

        -- 网络访问
        begin
            DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
                host => '*',
                ace => xs$ace_type(privilege_list => xs$name_list('connect'),
                                principal_name => 'testuser',
                                principal_type => xs_acl.ptype_db));
        end;
    end;
    """
    )
    print("用户设置完成!")
    cursor.close()
    conn.close()
except Exception as e:
    print("用户设置失败!")
    cursor.close()
    conn.close()
    sys.exit(1)

使用Oracle AI处理文档

考虑以下场景:用户拥有存储在Oracle数据库或文件系统中的文档,并打算将这些数据与由Langchain支持的Oracle AI向量搜索一起使用。

为了准备文档进行分析,需要一个全面的预处理工作流程。首先,必须检索文档,根据需要进行摘要,并按需分块。随后的步骤涉及为这些块生成嵌入并将它们集成到Oracle AI向量存储中。然后,用户可以对这些数据进行语义搜索。

Oracle AI向量搜索Langchain库包含一套文档处理工具,可以促进文档加载、分块、摘要生成和嵌入创建。

在接下来的部分中,我们将详细说明如何使用Oracle AI Langchain API有效地实现这些过程中的每一个。

连接到演示用户

以下示例代码将展示如何连接到Oracle数据库。默认情况下,python-oracledb 以"瘦"模式运行,直接连接到Oracle数据库。这种模式不需要Oracle客户端库。然而,当python-oracledb使用它们时,一些额外的功能是可用的。当使用Oracle客户端库时,python-oracledb被称为处于"厚"模式。两种模式都有全面的功能,支持Python数据库API v2.0规范。参见以下指南,其中讨论了每种模式支持的功能。如果您无法使用瘦模式,可能需要切换到厚模式。

import sys
import oracledb

# 请更新您的用户名、密码、主机名和服务名
username = ""
password = ""
dsn = ""

try:
    conn = oracledb.connect(user=username, password=password, dsn=dsn)
    print("连接成功!")
except Exception as e:
    print("连接失败!")
    sys.exit(1)

填充演示表

创建一个演示表并插入一些示例文档。

try:
    cursor = conn.cursor()

    drop_table_sql = """drop table demo_tab"""
    cursor.execute(drop_table_sql)
    # 删除已存在的demo_tab表

    create_table_sql = """create table demo_tab (id number, data clob)"""
    cursor.execute(create_table_sql)
    # 创建新的demo_tab表,包含id和data两列

    insert_row_sql = """insert into demo_tab values (:1, :2)"""
    rows_to_insert = [
        (
            1,
            "如果前面任何问题的答案是肯定的,那么数据库将停止搜索并从指定的表空间分配空间;否则,将从数据库默认共享临时表空间分配空间。",
        ),
        (
            2,
            "当数据库打开时,表空间可以是在线(可访问)或离线(不可访问)。\n表空间通常在线,以便用户可以访问其数据。SYSTEM表空间和临时表空间不能离线。",
        ),
        (
            3,
            "数据库存储LOB的方式与其他数据类型不同。创建LOB列隐式创建LOB段和LOB索引。包含LOB段和LOB索引(它们总是存储在一起)的表空间可能与包含表的表空间不同。\n有时,数据库可以将少量LOB数据存储在表本身中,而不是在单独的LOB段中。",
        ),
    ]
    cursor.executemany(insert_row_sql, rows_to_insert)
    # 向demo_tab表中插入示例数据

    conn.commit()

    print("表创建并填充完成。")
    cursor.close()
except Exception as e:
    print("表创建失败。")
    cursor.close()
    conn.close()
    sys.exit(1)

随着演示用户和填充的示例表的包含,剩余的配置涉及设置嵌入和摘要功能。用户有多个提供者选项,包括本地数据库解决方案和第三方服务,如Ocigenai、Hugging Face和OpenAI。如果用户选择第三方提供者,他们需要建立包含必要认证详细信息的凭据。相反,如果选择数据库作为嵌入的提供者,则需要将ONNX模型上传到Oracle数据库。使用数据库选项时,摘要功能不需要额外设置。

加载ONNX模型

Oracle支持各种嵌入提供者,使用户能够在专有数据库解决方案和第三方服务(如OCIGENAI和HuggingFace)之间进行选择。这个选择决定了生成和管理嵌入的方法。

重要提示:如果用户选择数据库选项,他们必须将ONNX模型上传到Oracle数据库。相反,如果选择第三方提供者进行嵌入生成,则不需要将ONNX模型上传到Oracle数据库。

直接在Oracle中使用ONNX模型的一个显著优势是它通过消除向外部方传输数据的需求来提供增强的安全性和性能。此外,这种方法避免了通常与网络或REST API调用相关的延迟。

以下是将ONNX模型上传到Oracle数据库的示例代码:

from langchain_community.embeddings.oracleai import OracleEmbeddings

# 请更新您的相关信息
# 确保系统中有onnx文件
onnx_dir = "DEMO_PY_DIR"
onnx_file = "tinybert.onnx"
model_name = "demo_model"

try:
    OracleEmbeddings.load_onnx_model(conn, onnx_dir, onnx_file, model_name)
    print("ONNX模型加载完成。")
except Exception as e:
    print("ONNX模型加载失败!")
    sys.exit(1)

创建凭据

当选择第三方提供者生成嵌入时,用户需要建立凭据以安全地访问提供者的端点。

重要提示: 当选择"database"提供者生成嵌入时不需要凭据。但是,如果用户决定使用第三方提供者,他们必须创建特定于所选提供者的凭据。

以下是一个说明性示例:

try:
    cursor = conn.cursor()
    cursor.execute(
        """
       declare
           jo json_object_t;
       begin
           -- HuggingFace
           dbms_vector_chain.drop_credential(credential_name  => 'HF_CRED');
           jo := json_object_t();
           jo.put('access_token', '<access_token>');
           dbms_vector_chain.create_credential(
               credential_name   =>  'HF_CRED',
               params            => json(jo.to_string));

           -- OCIGENAI
           dbms_vector_chain.drop_credential(credential_name  => 'OCI_CRED');
           jo := json_object_t();
           jo.put('user_ocid','<user_ocid>');
           jo.put('tenancy_ocid','<tenancy_ocid>');
           jo.put('compartment_ocid','<compartment_ocid>');
           jo.put('private_key','<private_key>');
           jo.put('fingerprint','<fingerprint>');
           dbms_vector_chain.create_credential(
               credential_name   => 'OCI_CRED',
               params            => json(jo.to_string));
       end;
       """
    )
    cursor.close()
    print("凭据创建完成。")
except Exception as ex:
    cursor.close()
    raise

加载文档

用户可以通过适当配置加载器参数,灵活地从Oracle数据库、文件系统或两者加载文档。有关这些参数的详细信息,请参阅Oracle AI向量搜索指南

使用OracleDocLoader的一个显著优势是其处理超过150种不同文件格式的能力,消除了对不同文档类型使用多个加载器的需求。有关支持的格式的完整列表,请参阅Oracle Text支持的文档格式

以下是展示如何使用OracleDocLoader的示例代码片段

from langchain_community.document_loaders.oracleai import OracleDocLoader
from langchain_core.documents import Document

# 从Oracle数据库表加载
# 确保您有具有此规格的表
loader_params = {}
loader_params = {
    "owner": "testuser",
    "tablename": "demo_tab",
    "colname": "data",
}

""" 加载文档 """
loader = OracleDocLoader(conn=conn, params=loader_params)
docs = loader.load()

""" 验证 """
print(f"加载的文档数量: {len(docs)}")
# print(f"Document-0: {docs[0].page_content}") # 内容

生成摘要

现在用户已经加载了文档,他们可能希望为每个文档生成摘要。Oracle AI向量搜索Langchain库提供了一套用于文档摘要的API。它支持多个摘要提供者,如数据库、OCIGENAI、HuggingFace等,允许用户选择最适合他们需求的提供者。要利用这些功能,用户必须按照指定配置摘要参数。有关这些参数的详细信息,请参阅Oracle AI向量搜索指南

注意: 如果用户想使用除Oracle内置和默认提供者"database"之外的第三方摘要生成提供者,可能需要设置代理。如果您没有代理,请在实例化OracleSummary时删除代理参数。

# 在实例化摘要和嵌入对象时使用的代理
proxy = ""

以下示例代码将展示如何生成摘要:

from langchain_community.utilities.oracleai import OracleSummary
from langchain_core.documents import Document

# 使用 'database' 提供者
summary_params = {
    "provider": "database",
    "glevel": "S",
    "numParagraphs": 1,
    "language": "english",
}

# 获取摘要实例
# 如果不需要,请移除代理
summ = OracleSummary(conn=conn, params=summary_params, proxy=proxy)

list_summary = []
for doc in docs:
    summary = summ.get_summary(doc.page_content)
    list_summary.append(summary)

""" 验证 """
print(f"摘要数量: {len(list_summary)}")
# print(f"Summary-0: {list_summary[0]}") #内容

分割文档

文档的大小可能从小到非常大不等。用户通常倾向于将他们的文档分割成更小的部分以便于生成嵌入。这个分割过程有广泛的自定义选项。有关这些参数的详细信息,请参阅Oracle AI向量搜索指南

以下是说明如何实现这一点的示例代码:

from langchain_community.document_loaders.oracleai import OracleTextSplitter
from langchain_core.documents import Document

# 使用默认参数进行分割
splitter_params = {"normalize": "all"}

""" 获取分割器实例 """
splitter = OracleTextSplitter(conn=conn, params=splitter_params)

list_chunks = []
for doc in docs:
    chunks = splitter.split_text(doc.page_content)
    list_chunks.extend(chunks)

""" 验证 """
print(f"块的数量: {len(list_chunks)}")
# print(f"Chunk-0: {list_chunks[0]}") # 内容

生成嵌入

现在文档已按要求分块,用户可能希望为这些块生成嵌入。Oracle AI向量搜索提供了多种生成嵌入的方法,使用本地托管的ONNX模型或第三方API。有关配置这些替代方案的全面说明,请参阅Oracle AI向量搜索指南

注意: 目前,OracleEmbeddings通过单独调用REST端点来单独处理每个嵌入生成请求,而不进行批处理。这种方法可能导致超过某些提供者设置的每分钟最大请求配额。然而,我们正在积极工作,通过实现请求批处理来增强这个过程,这将允许将多个嵌入请求组合成更少的API调用,从而优化我们对提供者资源的使用并遵守他们的请求限制。预计这个更新很快就会推出,消除当前的限制。

注意: 用户可能需要配置代理以使用第三方嵌入生成提供者,不包括使用ONNX模型的’database’提供者。

# 在实例化摘要和嵌入对象时使用的代理
proxy = ""

以下示例代码将展示如何生成嵌入:

from langchain_community.embeddings.oracleai import OracleEmbeddings
from langchain_core.documents import Document

# 使用加载到Oracle数据库的ONNX模型
embedder_params = {"provider": "database", "model": "demo_model"}

# 获取嵌入实例
# 如果不需要,请移除代理
embedder = OracleEmbeddings(conn=conn, params=embedder_params, proxy=proxy)

embeddings = []
for doc in docs:
    chunks = splitter.split_text(doc.page_content)
    for chunk in chunks:
        embed = embedder.embed_query(chunk)
        embeddings.append(embed)

""" 验证 """
print(f"嵌入数量: {len(embeddings)}")
# print(f"Embedding-0: {embeddings[0]}") # 内容

创建Oracle AI向量存储

现在您已经了解如何单独使用Oracle AI Langchain库API来处理文档,让我们展示如何与Oracle AI向量存储集成以促进语义搜索。

首先,让我们导入所有依赖项。

import sys

import oracledb
from langchain_community.document_loaders.oracleai import (
    OracleDocLoader,
    OracleTextSplitter,
)
from langchain_community.embeddings.oracleai import OracleEmbeddings
from langchain_community.utilities.oracleai import OracleSummary
from langchain_community.vectorstores import oraclevs
from langchain_community.vectorstores.oraclevs import OracleVS
from langchain_community.vectorstores.utils import DistanceStrategy
from langchain_core.documents import Document

接下来,让我们将所有文档处理阶段组合在一起。以下是示例代码:

"""
在这个示例中,我们将对摘要和嵌入使用'database'提供者。
因此,我们不需要执行以下操作:
    - 为第三方提供者设置代理
    - 为第三方提供者创建凭据

如果您选择使用第三方提供者,
请按照代理和凭据的必要步骤操作。
"""

# oracle连接
# 请更新您的用户名、密码、主机名和服务名
username = ""
password = ""
dsn = ""

try:
    conn = oracledb.connect(user=username, password=password, dsn=dsn)
    print("连接成功!")
except Exception as e:
    print("连接失败!")
    sys.exit(1)


# 加载onnx模型
# 请用您的相关信息更新
onnx_dir = "DEMO_PY_DIR"
onnx_file = "tinybert.onnx"
model_name = "demo_model"
try:
    OracleEmbeddings.load_onnx_model(conn, onnx_dir, onnx_file, model_name)
    print("ONNX模型加载完成。")
except Exception as e:
    print("ONNX模型加载失败!")
    sys.exit(1)


# 参数
# 请用相关信息更新必要的字段
loader_params = {
    "owner": "testuser",
    "tablename": "demo_tab",
    "colname": "data",
}
summary_params = {
    "provider": "database",
    "glevel": "S",
    "numParagraphs": 1,
    "language": "english",
}
splitter_params = {"normalize": "all"}
embedder_params = {"provider": "database", "model": "demo_model"}

# 实例化加载器、摘要、分割器和嵌入器
loader = OracleDocLoader(conn=conn, params=loader_params)
summary = OracleSummary(conn=conn, params=summary_params)
splitter = OracleTextSplitter(conn=conn, params=splitter_params)
embedder = OracleEmbeddings(conn=conn, params=embedder_params)

# 处理文档
chunks_with_mdata = []
for id, doc in enumerate(docs, start=1):
    summ = summary.get_summary(doc.page_content)
    chunks = splitter.split_text(doc.page_content)
    for ic, chunk in enumerate(chunks, start=1):
        chunk_metadata = doc.metadata.copy()
        chunk_metadata["id"] = chunk_metadata["_oid"] + "$" + str(id) + "$" + str(ic)
        chunk_metadata["document_id"] = str(id)
        chunk_metadata["document_summary"] = str(summ[0])
        chunks_with_mdata.append(
            Document(page_content=str(chunk), metadata=chunk_metadata)
        )

""" 验证 """
print(f"带有元数据的总块数: {len(chunks_with_mdata)}")

此时,我们已经处理了文档并生成了带有元数据的块。接下来,我们将使用这些块创建Oracle AI向量存储。

以下是如何实现的示例代码:

# 创建Oracle AI向量存储
vectorstore = OracleVS.from_documents(
    chunks_with_mdata,
    embedder,
    client=conn,
    table_name="oravs",
    distance_strategy=DistanceStrategy.DOT_PRODUCT,
)

""" 验证 """
print(f"向量存储表: {vectorstore.table_name}")

提供的示例演示了使用DOT_PRODUCT距离策略创建向量存储。用户可以灵活地使用Oracle AI向量存储的各种距离策略,详情请参阅我们的全面指南

现在嵌入已存储在向量存储中,建议建立一个索引以增强查询执行期间的语义搜索性能。

注意 如果遇到"内存不足"错误,建议在数据库配置中增加 vector_memory_size

以下是创建索引的示例代码片段:

oraclevs.create_index(
    conn, vectorstore, params={"idx_name": "hnsw_oravs", "idx_type": "HNSW"}
)

print("索引创建完成。")

这个例子演示了在’oravs’表中的嵌入上创建默认HNSW索引。用户可以根据他们的特定需求调整各种参数。有关这些参数的详细信息,请参阅Oracle AI向量搜索指南

此外,可以创建各种类型的向量索引以满足不同的需求。更多详情可以在我们的全面指南中找到。

执行语义搜索

一切就绪!

我们已经成功处理了文档并将它们存储在向量存储中,然后创建了索引以增强查询性能。我们现在准备进行语义搜索。

以下是这个过程的示例代码:

query = "What is Oracle AI Vector Store?"
filter = {"document_id": ["1"]}

# Similarity search without a filter
print(vectorstore.similarity_search(query, 1))

# Similarity search with a filter
print(vectorstore.similarity_search(query, 1, filter=filter))

# Similarity search with relevance score
print(vectorstore.similarity_search_with_score(query, 1))

# Similarity search with relevance score with filter
print(vectorstore.similarity_search_with_score(query, 1, filter=filter))

# Max marginal relevance search
print(vectorstore.max_marginal_relevance_search(query, 1, fetch_k=20, lambda_mult=0.5))

# Max marginal relevance search with filter
print(
    vectorstore.max_marginal_relevance_search(
        query, 1, fetch_k=20, lambda_mult=0.5, filter=filter
    )
)

总结

本文档详细介绍了如何使用Oracle AI向量搜索和Langchain构建端到端的RAG(检索增强生成)pipeline。主要内容包括:

  1. 环境设置和用户创建
  2. 文档处理流程:加载、摘要生成、分块和嵌入生成
  3. Oracle AI向量存储的创建和索引建立
  4. 语义搜索的实现和示例

本指南突出了Oracle AI向量搜索的强大功能,包括与关系型数据的结合、广泛的文档格式支持、灵活的嵌入和摘要生成选项,以及优化的搜索性能。

;