Oracle AI向量搜索专为人工智能(AI)工作负载而设计,允许您基于语义而不是关键字查询数据。 Oracle AI向量搜索的最大优势之一是,可以将非结构化数据上的语义搜索与业务数据上的关系搜索结合在一个单一系统中。 这不仅强大,而且更加有效,因为您无需添加专门的向量数据库,消除了在多个系统之间的数据碎片化带来的痛苦。
此外,您的向量可以受益于Oracle Database的所有最强大的功能,例如:
本指南演示了如何使用Langchain将Oracle AI向量搜索用于提供端到端的RAG管道。本指南通过以下示例进行了说明:
如果您刚开始使用Oracle数据库,请考虑探索免费的Oracle 23 AI,它提供了一个很好的介绍,帮助您设置数据库环境。在使用数据库时,通常建议不要默认使用系统用户;相反,您可以创建自己的用户以提高安全性和定制性。有关用户创建的详细步骤,请参考我们的端到端指南,该指南还展示了如何在Oracle中设置用户。此外,了解用户权限对于有效管理数据库安全至关重要。您可以在官方的Oracle指南中了解更多关于管理用户账户和安全性的内容。
请安装Oracle Python Client驱动程序,以便在Oracle AI向量搜索中使用Langchain。
# 安装 oracledb 模块
!pip install oracledb
首先,创建一个具有所有必要权限的演示用户。
import sys
import oracledb
# 请更新为您的用户名、密码、主机名和服务名
# 请确保此用户具有执行以下所有操作的足够权限
username = ""
password = ""
dsn = ""
try:
conn = oracledb.connect(user=username, password=password, dsn=dsn) # 连接数据库
print("Connection successful!") # 打印连接成功信息
cursor = conn.cursor() # 创建游标
cursor.execute(
"""
begin
-- 删除用户
begin
execute immediate 'drop user testuser cascade'; -- 执行立即语句删除用户testuser及其所有对象
exception
when others then
dbms_output.put_line('Error setting up user.'); -- 如果出现异常,打印错误信息
end;
execute immediate 'create user testuser identified by testuser'; -- 执行立即语句创建用户testuser
execute immediate 'grant connect, unlimited tablespace, create credential, create procedure, create any index to testuser'; -- 给用户testuser授予权限
execute immediate 'create or replace directory DEMO_PY_DIR as ''/scratch/hroy/view_storage/hroy_devstorage/demo/orachain'''; -- 创建或替换目录DEMO_PY_DIR
execute immediate 'grant read, write on directory DEMO_PY_DIR to public'; -- 给public授予目录DEMO_PY_DIR的读写权限
execute immediate 'grant create mining model to testuser'; -- 给用户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("User setup done!") # 打印用户设置完成信息
cursor.close() # 关闭游标
conn.close() # 关闭连接
except Exception as e:
print("User setup failed!") # 打印用户设置失败信息
cursor.close() # 关闭游标
conn.close() # 关闭连接
sys.exit(1) # 退出程序,返回状态码1
Connection successful! User setup done!
考虑以下情景:用户拥有存储在Oracle数据库或文件系统中的文档,并打算利用Oracle AI Vector Search(由Langchain提供支持)对这些数据进行处理。
为了准备文档进行分析,需要进行全面的预处理工作流程。首先,必须检索文档,根据需要进行摘要(如果需要),并根据需要对文档进行分块。随后的步骤涉及为这些分块生成嵌入,并将其集成到Oracle AI Vector Store中。用户随后可以对这些数据进行语义搜索。
Oracle AI Vector Search Langchain库包含一套文档处理工具,可促进文档加载、分块、摘要生成和嵌入创建。
在接下来的章节中,我们将详细介绍如何利用Oracle AI Langchain API有效地实现这些过程。
import oracledb
# 请更新为您的用户名、密码、主机名和服务名
username = ""
password = ""
dsn = ""
try:
# 连接到Oracle数据库
conn = oracledb.connect(user=username, password=password, dsn=dsn)
print("连接成功!")
except Exception as e:
print("连接失败!")
sys.exit(1)
Connection successful!
创建一个演示表并插入一些示例文档。
try:
cursor = conn.cursor() # 创建游标对象
drop_table_sql = """drop table demo_tab""" # 删除表的SQL语句
cursor.execute(drop_table_sql) # 执行删除表的操作
create_table_sql = """create table demo_tab (id number, data clob)""" # 创建表的SQL语句
cursor.execute(create_table_sql) # 执行创建表的操作
insert_row_sql = """insert into demo_tab values (:1, :2)""" # 插入行的SQL语句
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) # 执行批量插入行的操作
conn.commit() # 提交事务
print("表已创建并填充。")
cursor.close() # 关闭游标
except Exception as e:
print("表创建失败。")
cursor.close() # 关闭游标
conn.close() # 关闭连接
sys.exit(1) # 退出程序
Table created and populated.
在包含演示用户和填充的示例表格的基础上,剩下的配置工作涉及设置嵌入和摘要功能。用户可以选择多个提供者选项,包括本地数据库解决方案和第三方服务,如Ocigenai、Hugging Face和OpenAI。如果用户选择第三方提供者,则需要建立包含必要身份验证详细信息的凭据。相反,如果选择数据库作为嵌入提供者,则需要将ONNX模型上传到Oracle数据库中。使用数据库选项时,不需要进行其他设置以启用摘要功能。
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)
ONNX model loaded.
在选择第三方提供商生成嵌入时,用户需要建立凭证以安全访问提供商的端点。
*重要提示:* 选择“数据库”提供商生成嵌入时不需要凭证。然而,如果用户决定使用第三方提供商,则必须创建特定于所选提供商的凭证。
以下是一个示例说明:
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("Credentials created.")
except Exception as ex:
cursor.close()
raise
用户可以通过适当配置加载器参数,从Oracle数据库、文件系统或两者同时加载文档。有关这些参数的详细信息,请参阅Oracle AI Vector Search Guide。
利用OracleDocLoader的一个重要优点是其能够处理超过150种不同的文件格式,从而消除了为不同文档类型使用多个加载器的需求。有关支持的格式的完整列表,请参阅Oracle Text Supported Document Formats。
以下是一个演示如何使用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"文档-0: {docs[0].page_content}") # 内容
Number of docs loaded: 3
现在用户已经加载了文档,他们可能希望为每个文档生成一个摘要。Oracle AI Vector Search Langchain库提供了一套用于文档摘要的API。它支持多个摘要提供者,例如Database、OCIGENAI、HuggingFace等,允许用户选择最符合他们需求的提供者。要使用这些功能,用户必须按照指定的摘要参数进行配置。有关这些参数的详细信息,请参阅Oracle AI Vector Search指南。
*注意:* 用户可能需要设置代理,如果他们想要使用一些第三方摘要生成提供商,而不是使用 Oracle 自家的默认提供商 'database'。如果您没有代理,请在实例化 OracleSummary 时移除代理参数。
# 当我们实例化summary和embedder对象时要使用的代理
proxy = ""
以下示例代码将展示如何生成摘要:
from langchain_community.utilities.oracleai import OracleSummary # 导入OracleSummary类
from langchain_core.documents import Document # 导入Document类
# 使用'database'提供程序
summary_params = {
"provider": "database", # 提供程序为数据库
"glevel": "S", # 摘要级别为S
"numParagraphs": 1, # 摘要段落数为1
"language": "english", # 语言为英语
}
# 获取摘要实例
# 如果不需要代理,请删除代理
summ = OracleSummary(conn=conn, params=summary_params, proxy=proxy) # 实例化OracleSummary类
list_summary = []
for doc in docs:
summary = summ.get_summary(doc.page_content) # 获取文档内容的摘要
list_summary.append(summary) # 将摘要添加到列表中
""" 验证 """
print(f"Number of Summaries: {len(list_summary)}") # 打印摘要数量
# print(f"Summary-0: {list_summary[0]}") #content # 打印第一个摘要内容
Number of Summaries: 3
文档的大小可能不同,从小到非常大不等。用户通常喜欢将他们的文档分成较小的部分,以便生成嵌入。针对这种分割过程,提供了广泛的定制选项。有关这些参数的详细信息,请参阅Oracle AI矢量搜索指南。
以下是一个示例代码,演示了如何实现这一点:
from langchain_community.document_loaders.oracleai import OracleTextSplitter # 导入OracleTextSplitter类
from langchain_core.documents import Document # 导入Document类
# 使用默认参数进行分割
splitter_params = {"normalize": "all"}
""" 获取分割器实例 """
splitter = OracleTextSplitter(conn=conn, params=splitter_params) # 创建OracleTextSplitter实例
list_chunks = [] # 创建空列表用于存储分割后的文本块
for doc in docs: # 遍历文档列表
chunks = splitter.split_text(doc.page_content) # 使用分割器对文档内容进行分割
list_chunks.extend(chunks) # 将分割后的文本块添加到列表中
""" 验证 """
print(f"Number of Chunks: {len(list_chunks)}") # 打印分割后的文本块数量
# print(f"Chunk-0: {list_chunks[0]}") # content # 打印第一个文本块的内容
Number of Chunks: 3
现在文档已根据要求进行了分块,用户可能希望为这些块生成嵌入。Oracle AI Vector Search 提供了多种方法来生成嵌入,可以利用本地托管的ONNX模型或第三方API。有关配置这些替代方案的详细说明,请参阅Oracle AI Vector Search指南。
*注意:* 目前,OracleEmbeddings通过单独调用每个请求的REST端点来处理每个嵌入生成请求,而不是批量处理。这种方法可能导致超过某些提供商设置的每分钟最大请求配额。然而,我们正在积极努力改进这个过程,通过实现请求批处理,将多个嵌入请求合并为较少的API调用,从而优化我们对提供商资源的使用,并遵守他们的请求限制。预计很快将推出此更新,消除当前的限制。
*注意:* 用户可能需要配置代理来使用第三方嵌入生成提供商,但不包括使用ONNX模型的“database”提供商。
# 用于实例化summary和embedder对象时使用的代理
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"嵌入-0: {embeddings[0]}") # 内容
Number of embeddings: 3
现在您已经了解如何使用 Oracle AI Langchain 库的 API 单独处理文档,让我们展示如何与 Oracle AI 向量存储集成,以便进行语义搜索。
首先,让我们导入所有的依赖项。
import sys
import oracledb # 导入oracledb模块
from langchain_community.document_loaders.oracleai import ( # 导入langchain_community.document_loaders.oracleai模块中的OracleDocLoader和OracleTextSplitter类
OracleDocLoader,
OracleTextSplitter,
)
from langchain_community.embeddings.oracleai import OracleEmbeddings # 导入langchain_community.embeddings.oracleai模块中的OracleEmbeddings类
from langchain_community.utilities.oracleai import OracleSummary # 导入langchain_community.utilities.oracleai模块中的OracleSummary类
from langchain_community.vectorstores import oraclevs # 导入langchain_community.vectorstores模块中的oraclevs模块
from langchain_community.vectorstores.oraclevs import OracleVS # 导入langchain_community.vectorstores.oraclevs模块中的OracleVS类
from langchain_community.vectorstores.utils import DistanceStrategy # 导入langchain_community.vectorstores.utils模块中的DistanceStrategy类
from langchain_core.documents import Document # 导入langchain_core.documents模块中的Document类
接下来,让我们将所有文档处理阶段结合在一起。以下是示例代码:
'''在这个示例中,我们将使用“数据库”提供程序来进行摘要和嵌入。因此,我们不需要执行以下操作:
- 为第三方提供程序设置代理
- 为第三方提供程序创建凭据
如果您选择使用第三方提供程序,请按照代理和凭据的必要步骤进行操作。
'''
# 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、summary、splitter和embedder
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)}")
Connection successful! ONNX model loaded. Number of total chunks with metadata: 3
到目前为止,我们已经处理了文档并生成了带有元数据的块。接下来,我们将使用这些块创建Oracle AI向量存储。
以下是如何执行此操作的示例代码:
# 创建Oracle AI向量存储
vectorstore = OracleVS.from_documents(
chunks_with_mdata, # 使用包含元数据的块列表作为输入
embedder, # 使用的嵌入器
client=conn, # Oracle数据库连接客户端
table_name="oravs", # 存储向量的表名
distance_strategy=DistanceStrategy.DOT_PRODUCT, # 距离计算策略
)
""" 验证 """
print(f"Vector Store Table: {vectorstore.table_name}") # 打印向量存储表名
Vector Store Table: oravs
所提供的示例说明了使用DOT_PRODUCT距离策略创建向量存储的过程。用户可以根据我们的综合指南中详细介绍的内容,灵活选择使用不同的距离策略来使用Oracle AI向量存储。
现在将嵌入存储在向量存储中,建议在查询执行期间建立索引以提高语义搜索性能。
*注意* 如果遇到“内存不足”错误,请建议在数据库配置中增加 *vector_memory_size*
以下是创建索引的示例代码片段:
# 创建索引
oraclevs.create_index(
conn, vectorstore, params={"idx_name": "hnsw_oravs", "idx_type": "HNSW"}
)
# 打印索引创建成功的消息
print("Index created.")
这个示例演示了在'oravs'表中创建一个默认的HNSW索引的过程。用户可以根据自己的具体需求调整各种参数。有关这些参数的详细信息,请参考Oracle AI向量搜索指南。
此外,可以创建各种类型的向量索引以满足不同的需求。更多详细信息请参阅我们的综合指南。
query = "什么是Oracle AI Vector Store?"
filter = {"document_id": ["1"]}
# 无过滤器的相似度搜索
print(vectorstore.similarity_search(query, 1))
# 带有过滤器的相似度搜索
print(vectorstore.similarity_search(query, 1, filter=filter))
# 带有相关性分数的相似度搜索
print(vectorstore.similarity_search_with_score(query, 1))
# 带有相关性分数和过滤器的相似度搜索
print(vectorstore.similarity_search_with_score(query, 1, filter=filter))
# 最大边际相关性搜索
print(vectorstore.max_marginal_relevance_search(query, 1, fetch_k=20, lambda_mult=0.5))
# 带有过滤器的最大边际相关性搜索
print(
vectorstore.max_marginal_relevance_search(
query, 1, fetch_k=20, lambda_mult=0.5, filter=filter)
)
[Document(page_content='The database stores LOBs differently from other data types. Creating a LOB column implicitly creates a LOB segment and a LOB index. The tablespace containing the LOB segment and LOB index, which are always stored together, may be different from the tablespace containing the table. Sometimes the database can store small amounts of LOB data in the table itself rather than in a separate LOB segment.', metadata={'_oid': '662f2f257677f3c2311a8ff999fd34e5', '_rowid': 'AAAR/xAAEAAAAAnAAC', 'id': '662f2f257677f3c2311a8ff999fd34e5$3$1', 'document_id': '3', 'document_summary': 'Sometimes the database can store small amounts of LOB data in the table itself rather than in a separate LOB segment.\n\n'})] [] [(Document(page_content='The database stores LOBs differently from other data types. Creating a LOB column implicitly creates a LOB segment and a LOB index. The tablespace containing the LOB segment and LOB index, which are always stored together, may be different from the tablespace containing the table. Sometimes the database can store small amounts of LOB data in the table itself rather than in a separate LOB segment.', metadata={'_oid': '662f2f257677f3c2311a8ff999fd34e5', '_rowid': 'AAAR/xAAEAAAAAnAAC', 'id': '662f2f257677f3c2311a8ff999fd34e5$3$1', 'document_id': '3', 'document_summary': 'Sometimes the database can store small amounts of LOB data in the table itself rather than in a separate LOB segment.\n\n'}), 0.055675752460956573)] [] [Document(page_content='If the answer to any preceding questions is yes, then the database stops the search and allocates space from the specified tablespace; otherwise, space is allocated from the database default shared temporary tablespace.', metadata={'_oid': '662f2f253acf96b33b430b88699490a2', '_rowid': 'AAAR/xAAEAAAAAnAAA', 'id': '662f2f253acf96b33b430b88699490a2$1$1', 'document_id': '1', 'document_summary': 'If the answer to any preceding questions is yes, then the database stops the search and allocates space from the specified tablespace; otherwise, space is allocated from the database default shared temporary tablespace.\n\n'})] [Document(page_content='If the answer to any preceding questions is yes, then the database stops the search and allocates space from the specified tablespace; otherwise, space is allocated from the database default shared temporary tablespace.', metadata={'_oid': '662f2f253acf96b33b430b88699490a2', '_rowid': 'AAAR/xAAEAAAAAnAAA', 'id': '662f2f253acf96b33b430b88699490a2$1$1', 'document_id': '1', 'document_summary': 'If the answer to any preceding questions is yes, then the database stops the search and allocates space from the specified tablespace; otherwise, space is allocated from the database default shared temporary tablespace.\n\n'})]