Skip to main content
Oracle AI 数据库支持 AI 工作负载,您可以通过含义(语义)而不仅仅是关键词来查询数据。它将非结构化内容的语义搜索业务数据的关系型过滤结合在一个系统中——因此您可以构建检索工作流(如 RAG),而无需引入单独的向量数据库或将数据分散在多个平台上。 本指南演示如何使用 OracleVS(Oracle AI 向量搜索的 LangChain 向量存储集成)来:
  • 将文档和嵌入摄取到 Oracle 中
  • 运行相似性搜索
  • 创建 HNSW 和 IVF 索引
  • 应用元数据过滤以进行高级检索
  • 在 Oracle Database 26ai 中启用混合搜索(关键词 + 语义)
  • 使用 Oracle Text 运行全文搜索

先决条件

安装 langchain-oracledbpython-oracledb 驱动程序将作为依赖项自动安装。
pip install -qU langchain-oracledb

连接到 Oracle 数据库

以下示例代码将展示如何连接到 Oracle 数据库。默认情况下,python-oracledb 在“Thin”模式下运行,该模式直接连接到 Oracle 数据库。此模式不需要 Oracle 客户端库。但是,当 python-oracledb 使用它们时,会提供一些额外的功能。当使用 Oracle 客户端库时,python-oracledb 被称为“Thick”模式。两种模式都全面支持 Python 数据库 API v2.0 规范。请参阅以下指南,了解每种模式支持的功能。如果您无法使用 Thin 模式,您可能需要切换到 Thick 模式。
import oracledb

# 请使用您的用户名、密码、主机名、端口和服务名称进行更新
username = "<username>"
password = "<password>"
dsn = "<hostname>:<port>/<service_name>"

connection = oracledb.connect(user=username, password=password, dsn=dsn)
print("Connection successful!")

导入所需的依赖项

from langchain_oracledb.vectorstores import oraclevs
from langchain_oracledb.vectorstores.oraclevs import OracleVS
from langchain_community.vectorstores.utils import DistanceStrategy
from langchain_core.documents import Document
from langchain_huggingface import HuggingFaceEmbeddings

加载文档

# 定义文档列表(以下示例是 Oracle 概念手册中的 5 个随机文档)

documents_json_list = [
    {
        "id": "cncpt_15.5.3.2.2_P4",
        "text": "如果任何先前问题的答案为是,则数据库停止搜索并从指定的表空间分配空间;否则,从数据库默认的共享临时表空间分配空间。",
        "link": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/logical-storage-structures.html#GUID-5387D7B2-C0CA-4C1E-811B-C7EB9B636442",
    },
    {
        "id": "cncpt_15.5.5_P1",
        "text": "表空间可以在数据库打开时在线(可访问)或离线(不可访问)。\n表空间通常在线,以便其数据可供用户使用。SYSTEM 表空间和临时表空间不能离线。",
        "link": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/logical-storage-structures.html#GUID-D02B2220-E6F5-40D9-AFB5-BC69BCEF6CD4",
    },
    {
        "id": "cncpt_22.3.4.3.1_P2",
        "text": "数据库存储 LOB 的方式与其他数据类型不同。创建 LOB 列会隐式创建 LOB 段和 LOB 索引。包含 LOB 段和 LOB 索引的表空间(它们始终存储在一起)可能与包含表的表空间不同。\n有时数据库可以将少量 LOB 数据存储在表本身中,而不是单独的 LOB 段中。",
        "link": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/concepts-for-database-developers.html#GUID-3C50EAB8-FC39-4BB3-B680-4EACCE49E866",
    },
    {
        "id": "cncpt_22.3.4.3.1_P3",
        "text": "LOB 段以称为块的片段存储数据。块是数据块的逻辑连续集合,是 LOB 分配的最小单位。表中的一行存储一个称为 LOB 定位器的指针,该指针指向 LOB 索引。当查询表时,数据库使用 LOB 索引快速定位 LOB 块。",
        "link": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/concepts-for-database-developers.html#GUID-3C50EAB8-FC39-4BB3-B680-4EACCE49E866",
    },
]
# 创建 LangChain 文档

documents_langchain = []

for doc in documents_json_list:
    metadata = {"id": doc["id"], "link": doc["link"]}
    doc_langchain = Document(page_content=doc["text"], metadata=metadata)
    documents_langchain.append(doc_langchain)

使用不同的距离度量创建向量存储

首先,我们将创建三个具有不同距离函数的向量存储。由于我们尚未在其中创建索引,它们目前只会创建表。稍后我们将使用这些向量存储来创建 HNSW 索引。要了解有关 Oracle AI 向量搜索支持的不同索引类型的更多信息,请参阅以下指南 您可以手动连接到 Oracle 数据库,并将看到三个表: Documents_DOTDocuments_COSINEDocuments_EUCLIDEAN 然后,我们将创建三个额外的表 Documents_DOT_IVFDocuments_COSINE_IVFDocuments_EUCLIDEAN_IVF,这些表将用于在表上创建 IVF 索引,而不是 HNSW 索引。
# 使用不同的距离策略将文档摄取到 Oracle 向量存储中

# 使用我们的 API 调用时,首先通过 from_documents() 使用文档子集初始化向量存储,
# 然后使用 add_texts() 逐步添加更多文档。
# 这种方法可以防止系统过载并确保高效的文档处理。

model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")

vector_store_dot = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_DOT",
    distance_strategy=DistanceStrategy.DOT_PRODUCT,
)
vector_store_max = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_COSINE",
    distance_strategy=DistanceStrategy.COSINE,
)
vector_store_euclidean = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_EUCLIDEAN",
    distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE,
)

# 使用不同的距离策略将文档摄取到 Oracle 向量存储中
vector_store_dot_ivf = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_DOT_IVF",
    distance_strategy=DistanceStrategy.DOT_PRODUCT,
)
vector_store_max_ivf = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_COSINE_IVF",
    distance_strategy=DistanceStrategy.COSINE,
)
vector_store_euclidean_ivf = OracleVS.from_documents(
    documents_langchain,
    model,
    client=connection,
    table_name="Documents_EUCLIDEAN_IVF",
    distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE,
)

文本的添加和删除操作以及基本相似性搜索

def manage_texts(vector_stores):
    """
    向每个向量存储添加文本,演示重复添加的错误处理,并执行文本删除。
    展示相似性搜索和为每个向量存储创建索引。

    Args:
    - vector_stores (list): OracleVS 实例列表。
    """
    texts = ["Rohan", "Shailendra"]
    metadata = [
        {"id": "100", "link": "Document Example Test 1"},
        {"id": "101", "link": "Document Example Test 2"},
    ]

    for i, vs in enumerate(vector_stores, start=1):
        # 添加文本
        try:
            vs.add_texts(texts, metadata)
            print(f"\n\n\n向量存储 {i} 的文本添加完成\n\n\n")
        except Exception as ex:
            print(f"\n\n\n向量存储 {i} 的预期重复添加错误\n\n\n")

        # 使用 'id' 的值删除文本
        vs.delete([metadata[0]["id"]])
        print(f"\n\n\n向量存储 {i} 的文本删除完成\n\n\n")

        # 相似性搜索
        results = vs.similarity_search("How are LOBS stored in Oracle Database", 2)
        print(f"\n\n\n向量存储 {i} 的相似性搜索结果: {results}\n\n\n")


vector_store_list = [
    vector_store_dot,
    vector_store_max,
    vector_store_euclidean,
    vector_store_dot_ivf,
    vector_store_max_ivf,
    vector_store_euclidean_ivf,
]
manage_texts(vector_store_list)

使用特定参数创建索引

def create_search_indices(connection):
    """
    为向量存储创建搜索索引,每个索引都针对其距离策略进行了特定参数调整。
    """
    # DOT_PRODUCT 策略的索引
    # 注意我们正在使用默认参数创建 HNSW 索引
    # 这将默认创建具有 8 个并行工作线程的 HNSW 索引,并使用 Oracle AI 向量搜索的默认精度
    oraclevs.create_index(
        connection,
        vector_store_dot,
        params={"idx_name": "hnsw_idx1", "idx_type": "HNSW"},
    )

    # COSINE 策略的索引,具有特定参数
    # 注意我们正在创建具有并行 16 和目标精度规范为 97% 的 HNSW 索引
    oraclevs.create_index(
        connection,
        vector_store_max,
        params={
            "idx_name": "hnsw_idx2",
            "idx_type": "HNSW",
            "accuracy": 97,
            "parallel": 16,
        },
    )

    # EUCLIDEAN_DISTANCE 策略的索引,具有特定参数
    # 注意我们正在通过指定高级用户参数(neighbors = 64 和 efConstruction = 100)创建 HNSW 索引
    oraclevs.create_index(
        connection,
        vector_store_euclidean,
        params={
            "idx_name": "hnsw_idx3",
            "idx_type": "HNSW",
            "neighbors": 64,
            "efConstruction": 100,
        },
    )

    # DOT_PRODUCT 策略的索引,具有特定参数
    # 注意我们正在使用默认参数创建 IVF 索引
    # 这将默认创建具有 8 个并行工作线程的 IVF 索引,并使用 Oracle AI 向量搜索的默认精度
    oraclevs.create_index(
        connection,
        vector_store_dot_ivf,
        params={
            "idx_name": "ivf_idx1",
            "idx_type": "IVF",
        },
    )

    # COSINE 策略的索引,具有特定参数
    # 注意我们正在创建具有并行 32 和目标精度规范为 90% 的 IVF 索引
    oraclevs.create_index(
        connection,
        vector_store_max_ivf,
        params={
            "idx_name": "ivf_idx2",
            "idx_type": "IVF",
            "accuracy": 90,
            "parallel": 32,
        },
    )

    # EUCLIDEAN_DISTANCE 策略的索引,具有特定参数
    # 注意我们正在通过指定高级用户参数(neighbor_part = 64)创建 IVF 索引
    oraclevs.create_index(
        connection,
        vector_store_euclidean_ivf,
        params={"idx_name": "ivf_idx3", "idx_type": "IVF", "neighbor_part": 64},
    )

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


create_search_indices(connection)

高级搜索

Oracle Database 23ai 支持预过滤、过滤中和后过滤,以增强 AI 向量搜索功能。这些过滤机制允许用户在执行向量相似性搜索之前、期间和之后应用约束,从而提高搜索性能和准确性。 Oracle 23ai 中过滤的关键点:
  1. 预过滤 应用传统 SQL 过滤器,在执行向量相似性搜索之前减少数据集。 通过限制 AI 算法处理的数据量来帮助提高效率。
  2. 过滤中 利用 AI 向量搜索直接在向量嵌入上执行相似性搜索,使用优化的索引和算法。 基于向量相似性高效过滤结果,而无需扫描整个数据集。
  3. 后过滤 在向量相似性搜索之后应用额外的 SQL 过滤以优化结果。 允许根据业务逻辑或其他元数据条件进行进一步优化。
为什么这很重要?
  • 性能优化:预过滤显著减少了查询执行时间,使在海量数据集上的搜索更加高效。
  • 准确性增强:过滤中确保向量搜索在语义上有意义,提高搜索结果的质量。

过滤详情

OracleVS 支持一组可以使用 filter 参数应用于 metadata 字段的过滤器。这些过滤器允许您根据各种条件选择和优化数据。 可用的过滤器操作符:
操作符描述
\$exists字段存在。
\$eq字段值等于操作数值 (=)。
\$ne字段存在且值不等于操作数值 (!=)。
\$gt字段值大于操作数值 (>)。
\$lt字段值小于操作数值 (<)。
\$gte字段值大于或等于操作数值 (>=)。
\$lte字段值小于或等于操作数值 (<=)。
\$between字段值在操作数数组中的两个值之间(或等于)。
\$startsWith字段值以操作数值开头。
\$hasSubstring字段值包含操作数作为子字符串。
\$instr字段值包含操作数作为子字符串。
\$regex字段值匹配给定的正则表达式模式。
\$like字段值匹配操作数模式(使用类似 SQL 的语法)。
\$in字段值等于操作数数组中的至少一个值。
\$nin字段存在,但其值不等于操作数数组中的任何值,或者字段不存在。
\$all字段值是一个数组,包含操作数数组中的所有项,或者与单个操作数匹配的标量。
  • 您可以使用逻辑操作符组合这些过滤器:
逻辑操作符描述
\$and逻辑 AND
\$or逻辑 OR
\$nor逻辑 NOR
示例过滤器:
{
  "age": 65,
  "name": {"$regex": "*rk"},
  "$or": [
    {
      "$and": [
        {"name": "Jason"},
        {"drinks": {"$in": ["tea", "soda"]}}
      ]
    },
    {
      "$nor": [
        {"age": {"$lt": 65}},
        {"name": "Jason"}
      ]
    }
  ]
}
其他使用技巧:
  • 当对象中的所有过滤器都必须满足时,可以省略 $and。这两者是等效的:
{ "$and": [
    { "name": { "$startsWith": "Fred" } },
    { "salary": { "$gt": 10000, "$lte": 20000 } }
]}
{
  "name": { "$startsWith": "Fred" },
  "salary": { "$gt": 10000, "$lte": 20000 }
}
  • $not 子句可以否定比较操作符:
{ "address.zip": { "$not": { "$eq": "90001" } } }
  • 使用 field: scalar 等同于 field: { "$eq": scalar }
{ "animal": "cat" }
有关更多过滤器示例,请参阅测试规范
# 创建索引后进行高级搜索
def conduct_advanced_searches(vector_stores):
    query = "How are LOBS stored in Oracle Database"
    # 构建一个用于直接与文档元数据进行比较的过滤器
    # 此过滤器旨在包含元数据 'id' 恰好为 '2' 的文档
    db_filter = {
        "$and": [
            {"id": "101"},  # FilterCondition
            {
                "$or": [  # FilterGroup
                    {"status": "approved"},
                    {"link": "Document Example Test 2"},
                    {
                        "$and": [  # Nested FilterGroup
                            {"status": "approved"},
                            {"link": "Document Example Test 2"},
                        ]
                    },
                ]
            },
        ]
    }

    for i, vs in enumerate(vector_stores, start=1):
        print(f"\n--- 向量存储 {i} 高级搜索 ---")
        # 不带过滤器的相似性搜索
        print("\n不带过滤器的相似性搜索结果:")
        print(vs.similarity_search(query, 2))

        # 带过滤器的相似性搜索
        print("\n带过滤器的相似性搜索结果:")
        print(vs.similarity_search(query, 2, filter=db_filter))

        # 带相关性分数的相似性搜索
        print("\n带相关性分数的相似性搜索:")
        print(vs.similarity_search_with_score(query, 2))

        # 带过滤器的带相关性分数的相似性搜索
        print("\n带过滤器的带相关性分数的相似性搜索:")
        print(vs.similarity_search_with_score(query, 2, filter=db_filter))

        # 最大边际相关性搜索
        print("\n最大边际相关性搜索结果:")
        print(vs.max_marginal_relevance_search(query, 2, fetch_k=20, lambda_mult=0.5))

        # 带过滤器的最大边际相关性搜索
        print("\n带过滤器的最大边际相关性搜索结果:")
        print(
            vs.max_marginal_relevance_search(
                query, 2, fetch_k=20, lambda_mult=0.5, filter=db_filter
            )
        )


conduct_advanced_searches(vector_store_list)

混合搜索

Oracle Database 26ai 支持混合搜索,将关键词(全文)和语义(向量)搜索结合到单一的检索功能中。langchain-oracledb 集成公开了:
  • OracleVectorizerPreference:创建由混合索引使用的数据库端向量化器首选项。
  • create_hybrid_index / acreate_hybrid_index:创建 HYBRID VECTOR INDEX。
  • OracleHybridSearchRetriever:在 OracleVS 表上执行关键词、语义或混合检索。

先决条件和模型配置

使用混合搜索时,请使用 OracleEmbeddings 配置您的 OracleVS,以便向量化器首选项与嵌入配置完全匹配。您可以通过 OracleVectorizerPreference 提供额外参数来进一步调整混合向量索引。有关详细信息,请参阅文档
from langchain_core.documents import Document
from langchain_oracledb.embeddings import OracleEmbeddings
from langchain_oracledb.vectorstores.oraclevs import OracleVS
from langchain_oracledb.retrievers.hybrid_search import (
    OracleVectorizerPreference,
    create_hybrid_index,
    OracleHybridSearchRetriever,
)

# 使用 OracleEmbeddings(显示数据库驻留模型)
embeddings = OracleEmbeddings(conn=connection, params={"provider": "database", "model": "DB_MODEL"})

# 创建/加载您的向量存储
vs = OracleVS(connection, table_name="DOCS", embedding_function=embeddings)

# 创建向量化器首选项
pref = OracleVectorizerPreference.create_preference(
    vector_store=vs, preference_name="PREF_DOCS"
)

# 创建 HYBRID VECTOR INDEX
create_hybrid_index(
    connection,
    idx_name="IDX_DOCS_HYB",
    vectorizer_preference=pref
)

# 构建检索器并搜索
retriever = OracleHybridSearchRetriever(
    vector_store=vs,
    idx_name="IDX_DOCS_HYB",
    search_mode="hybrid",     # "hybrid" | "keyword" | "semantic"
    k=5,
    return_scores=True,       # 在元数据中包含 score、text_score、vector_score
)

docs = retriever.invoke("refund policy for premium plan")
for d in docs:
    print(d.page_content, d.metadata.get("score"), d.metadata.get("text_score"), d.metadata.get("vector_score"))

# 完成首选项后的可选清理:
# pref.drop_preference()
替代方案 不使用显式首选项创建索引:
  • 如果您不想管理命名首选项,请传递 vector_store。该函数将创建临时首选项,构建索引,然后自动删除首选项。
create_hybrid_index(
    connection,
    idx_name="IDX_DOCS_HYB2",
    vector_store=vs,          # 与 vectorizer_preference 互斥
    params={"parallel": 8},
)
注意事项和技巧:
  • search_mode 决定使用哪些信号:
    • “keyword”:仅关键词
    • “semantic”:仅向量
    • “hybrid”(默认):两者结合
  • 通过检索器的 params 参数传递 DBMS_HYBRID_VECTOR 参数。
  • return_scores=True 将总体分数以及组件 text_score 和 vector_score 添加到 Document.metadata。
  • 通过 acreate_hybrid_indexOracleHybridSearchRetriever.ainvoke 支持异步用法。
更多信息:

全文搜索(Oracle Text)

您可以使用 Oracle Text 直接在 Oracle 数据库上运行高质量的关键词搜索。langchain-oracledb 集成公开了:
  • create_text_index / acreate_text_index:在列上创建 Oracle Text SEARCH INDEX
  • OracleTextSearchRetriever:运行 CONTAINS 查询并返回 LangChain Document 对象。
索引选项:
  • 如果您有 OracleVS 向量存储,您可以索引其内置的 “text” 列。
  • 您也可以通过直接提供 table_name + column_name 来索引任何其他表/列。
from langchain_oracledb.retrievers.text_search import create_text_index, OracleTextSearchRetriever

# 使用 OracleVS 表(索引 'text' 列)
retriever_text = None
create_text_index(
    connection,
    idx_name="IDX_DOCS_TEXT",
    vector_store=vs,
)

# 构建检索器。对于 OracleVS,returned_columns 默认为 ["metadata"]。
retriever_text = OracleTextSearchRetriever(
    vector_store=vs,
    k=5,
    fuzzy=True,          # 当 operator_search=False 时,对每个令牌应用 Oracle Text FUZZY
    return_scores=True,  # 将 SCORE(1) 添加为 metadata["score"]
)
docs = retriever_text.invoke("refund policy")
for d in docs:
    print(d.page_content, d.metadata.get("score"))

# 或者,索引任意表/列:
# create_text_index(connection, idx_name="IDX_MYDOCS_TEXT", table_name="MYDOCS", column_name="CONTENT")
# retriever_text = OracleTextSearchRetriever(client=connection, table_name="MYDOCS", column_name="CONTENT", k=5)
操作符模式和高级查询:
  • 默认行为 (operator_search=False):
    • 输入被视为字面文本,在非单词字符上进行标记化,并重写为 ACCUM 表达式。
    • fuzzy=True 时,每个令牌被包装为 FUZZY("token") 以匹配拼写错误。
  • 操作符模式 (operator_search=True):
    • 传递 Oracle Text 表达式不变(NEARABOUTANDORNOTWITHIN 等)。在此模式下,fuzzy 被忽略。
返回的列:
  • 当定位原始表时,通过 returned_columns 在结果中包含额外列;它们附加到 Document.metadata
  • 对于 OracleVSreturned_columns 默认为 [“metadata”]。
# 操作符模式示例
retriever_text_ops = OracleTextSearchRetriever(
    vector_store=vs,
    operator_search=True,   # 直接传递 Oracle Text 表达式
    return_scores=True,
)
docs = retriever_text_ops.invoke('NEAR((policy, refund), 2, TRUE)')
注意事项和技巧:
  • 当使用 operator_search=True 时,fuzzy 被忽略(按设计)。
  • 通过 acreate_text_indexOracleTextSearchRetriever.ainvoke 支持异步用法。
更多信息:

端到端演示

请参阅我们的完整演示指南 Oracle AI 向量搜索端到端演示指南,以借助 Oracle AI 向量搜索构建端到端 RAG 管道。