尧图精选

基于 Oracle AI Vector Search 的 LangChain4j 向量存储与聊天记忆实战指南

🕒 发布时间:2026/9/15 12:13:36 📁 来源:尧图网络
基于 Oracle AI Vector Search 的 LangChain4j 向量存储与聊天记忆实战指南【免费下载链接】langchain4jLangChain4j is an idiomatic, open-source Java library for building LLM-powered applications on the JVM. It offers a unified API over popular LLM providers and vector stores, and makes implementing tool calling (including MCP support), agents and RAG easy. It integrates seamlessly with enterprise Java frameworks like Quarkus and Spring Boot.项目地址: https://gitcode.com/GitHub_Trending/la/langchain4j本篇指南围绕 LangChain4j 仓库中的langchain4j-oracle模块展开系统讲解如何利用 Oracle Database 23.4 的原生 AI Vector Search 能力在 JVM 应用中构建生产级的EmbeddingStore向量存储与ChatMemoryStore聊天记忆持久化。读完本文你将掌握OracleEmbeddingStore的完整配置方式含表结构、建表策略、IVF 向量索引、JSON 元数据索引、OracleChatMemoryStore的接入方法以及该模块集成测试的运行方式。模块概览langchain4j-oracle是 LangChain4j 官方集成模块之一位于仓库的 langchain4j-oracle 目录pom.xml中将其描述为 LangChain4j :: Integration :: Oracle。它实现了两大类能力OracleEmbeddingStore实现 LangChain4j 核心的EmbeddingStore接口使用 Oracle Database 的AI Vector Search特性存储与检索向量并支持元数据过滤Filter与数据删除RemovalOracleChatMemoryStore实现ChatMemoryStore接口为多轮对话提供基于 Oracle 表的持久化聊天记忆。从源码结构看OracleEmbeddingStore.java该存储直接基于 JDBCojdbc8驱动与javax.sql.DataSource工作不引入额外的客户端组件。pom.xml显示模块依赖langchain4j-core与com.oracle.database.jdbc:ojdbc8版本 23.5.0.24.07编译目标为 Java 17。环境要求使用该模块的前提条件非常明确Oracle Database 23.4 或更新版本AI Vector Search 能力VECTOR数据类型、VECTOR_DISTANCE函数、向量索引等从该版本开始提供JDBC 兼容的DataSource模块本身不创建连接所有数据库访问都通过你提供的DataSource完成。因此无论你的 Oracle 数据库部署在本地、云上如 OCI Autonomous Database还是通过 Docker 运行只要版本满足要求即可使用。安装依赖在 Maven 项目中引入如下依赖dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-oracle/artifactId version0.1.0/version /dependency需要说明的是当前仓库 langchain4j-oracle/pom.xml 中的版本为1.21.0-beta31-SNAPSHOT跟随langchain4j-parentREADME 中给出的0.1.0为独立发布版本号。建议在实际项目中以你正在使用的 LangChain4j 主版本所对应的langchain4j-oracle发布版本为准并将langchain4j-core与langchain4j-oracle的版本保持一致避免接口不兼容。快速开始构建 OracleEmbeddingStoreOracleEmbeddingStore的实例通过 Builder 模式创建。Builder 的必填项有两个DataSource与嵌入表embedding table这一点在 OracleEmbeddingStore.java 的build()方法中有强制校验缺失任一配置都会抛出IllegalArgumentException。EmbeddingStore embeddingStore OracleEmbeddingStore.builder() .dataSource(myDataSource) .embeddingTable(my_embedding_table) .build();向量之间的相似度通过cosine similarity余弦相似度计算即两个向量夹角的余弦值。在底层search方法使用VECTOR_DISTANCE(embedding_column, ?, COSINE)计算距离详见 OracleEmbeddingStore.java。关于 DataSource 与连接池强烈建议为DataSource配置连接池例如 Oracle 的Universal Connection PoolUCP或HikariCP。连接池可以避免频繁创建数据库连接带来的延迟开销。这一点在 OracleEmbeddingStore.java 的类级 JavaDoc 中同样被明确强调模块测试也使用了 UCP 作为 DataSource 实现见 pom.xml 中com.oracle.database.jdbc:ucp依赖。从源码实现看add、search、removeAll等方法均采用 try-with-resources 模式每次操作从dataSource.getConnection()获取连接并在结束时释放——这正是连接池能显著提升性能的原因所在。建表策略CreateOption如果数据库中的嵌入表已经存在直接提供表名即可默认不会尝试建表如果表不存在则可以通过CreateOption让模块在build()时自动建表EmbeddingStore embeddingStore OracleEmbeddingStore.builder() .dataSource(myDataSource) .embeddingTable(my_embedding_table, CreateOption.CREATE_IF_NOT_EXISTS) .build();CreateOption枚举定义在 CreateOption.java共三个取值取值语义CREATE_NONE不尝试创建任何 schema 对象默认值CREATE_IF_NOT_EXISTS已存在则复用否则创建CREATE_OR_REPLACE删除已存在的对象并重建建表逻辑位于 EmbeddingTable.javaCREATE_OR_REPLACE会先执行DROP TABLE IF EXISTS再执行CREATE TABLE IF NOT EXISTS两条语句以 batch 方式提交。默认表结构默认情况下嵌入表包含以下四列列名类型说明idVARCHAR(36)主键存放由EmbeddingStore.add(...)生成的 UUID 字符串embeddingVECTOR(*, FLOAT32)存放向量永不存储 NULLtextCLOB存放文本片段TextSegment的文本仅调用add(Embedding)时为 NULLmetadataJSON存放元数据仅调用add(Embedding)时为 NULL实际的建表 SQL 在 EmbeddingTable.java 中可以看到向量列声明为VECTOR(*, FLOAT32)维度不限、32 位浮点元数据列使用 Oracle 原生JSON类型。在数据写入层面addAll使用PreparedStatement的批量插入并通过OracleType.VECTOR_FLOAT32绑定向量参数元数据则被转换为OSONOracle 二进制 JSON后写入见 OracleEmbeddingStore.java。数值类型的元数据Integer/Long/Float/Double会保留其数值语义其余对象含 UUID以字符串形式存储读取时再按需还原。自定义嵌入表EmbeddingTable Builder如果你的现有表列名与上述默认命名不一致或希望使用不同的列名可以使用EmbeddingTable的 Builder 进行完整定制OracleEmbeddingStore embeddingStore OracleEmbeddingStore.builder() .dataSource(myDataSource) .embeddingTable(EmbeddingTable.builder() .createOption(CREATE_OR_REPLACE) // 表已存在时请改用 NONE .name(my_embedding_table) .idColumn(id_column_name) .embeddingColumn(embedding_column_name) .textColumn(text_column_name) .metadataColumn(metadata_column_name) .build()) .build();EmbeddingTable.Builder提供的可配置项见 EmbeddingTable.java方法默认值说明name(String)无必填表名没有默认值缺失时build()直接抛异常createOption(CreateOption)CREATE_NONE建表策略idColumn(String)id主键列名embeddingColumn(String)embedding向量列名textColumn(String)text文本列名metadataColumn(String)metadata元数据列名OracleEmbeddingStore.Builder也提供了embeddingTable(String)、embeddingTable(String, CreateOption)、embeddingTable(EmbeddingTable)三种重载分别适用于表已存在需要建表和完全自定义三种场景。为检索提速创建向量索引与元数据索引Builder 允许通过Index实例在嵌入表的 embedding 列和 metadata 列上创建索引。Index由两种 Builder 创建IVFIndexBuilder与JSONIndexBuilder。需要注意默认情况下不会创建任何索引IndexBuilder的默认createOption为CREATE_NONE。IVFIndexBuilderembedding 列的 IVF 向量索引IVFIndexBuilder用于在 embedding 列上配置IVFInverted File Flat索引。IVF 是 Oracle AI Vector Search 目前支持的邻居分区Neighbor Partition类向量索引它按分区组织向量在高检索质量与合理速度之间取得平衡。OracleEmbeddingStore embeddingStore OracleEmbeddingStore.builder() .dataSource(myDataSource) .embeddingTable(EmbeddingTable.builder() .createOption(CreateOption.CREATE_OR_REPLACE) // 表已存在时请改用 NONE .name(my_embedding_table) .idColumn(id_column_name) .embeddingColumn(embedding_column_name) .textColumn(text_column_name) .metadataColumn(metadata_column_name) .build()) .index(Index.ivfIndexBuilder().createOption(CreateOption.CREATE_OR_REPLACE).build()) .build();IVFIndexBuilder的完整可配置参数定义在 IVFIndexBuilder.java方法取值范围说明targetAccuracy(int)0100百分比目标准确率用于指导索引构建degreeOfParallelism(int) 0索引构建的并行度neighborPartitions(int)110,000,000质心分区数量IVF 特有参数samplePerPartition(int)≥ 1传入聚类算法的向量总数每个分区的采样数 × 分区数不建议传全部向量否则会显著拉长建索引时间minVectorsPerPartition(int) 0每个分区目标最小向量数用于修剪过小的分区参考值 100生成的建索引语句形如CREATE VECTOR INDEX ... ON table(embedding) ORGANIZATION NEIGHBOR PARTITIONS WITH DISTANCE COSINE WITH TARGET ACCURACY n PARALLEL n PARAMETERS (TYPE IVF, NEIGHBOR PARTITIONS n, SAMPLES_PER_PARTITION n, MIN_VECTORS_PER_PARTITION n)索引名称默认由表名拼接_VECTOR_INDEX后缀生成如my_embedding_table_VECTOR_INDEX超过 128 字符上限时会自动截断见 IndexBuilder.java你也可以通过name(String)显式指定索引名。提示OracleEmbeddingStore.Builder还提供了一个便捷方法vectorIndex(CreateOption)等价于index(Index.ivfIndexBuilder().createOption(createOption).build())适合只需要默认 IVF 索引的场景。JSONIndexBuildermetadata 列的 JSON 函数索引JSONIndexBuilder用于在 metadata 列的**键key**上创建基于函数的索引。它的巧妙之处在于索引使用的函数表达式与存储执行元数据过滤时使用的函数完全一致即JSON_VALUE(metadata, $.key RETURNING ... NULL ON ERROR)因此过滤查询可以直接命中索引。OracleEmbeddingStore.builder() .dataSource(myDataSource) .embeddingTable(EmbeddingTable.builder() .createOption(CreateOption.CREATE_OR_REPLACE) // 表已存在时请改用 NONE .name(my_embedding_table) .idColumn(id_column_name) .embeddingColumn(embedding_column_name) .textColumn(text_column_name) .metadataColumn(metadata_column_name) .build()) .index(Index.jsonIndexBuilder() .createOption(CreateOption.CREATE_OR_REPLACE) .key(name, String.class, JSONIndexBuilder.Order.ASC) .key(year, Integer.class, JSONIndexBuilder.Order.DESC) .build()) .build();JSONIndexBuilder的配置项见 JSONIndexBuilder.java方法说明key(String key, Class? keyType, Order order)为某个元数据键创建索引表达式可调用多次索引多个键keyType为键值的 Java 类型如String.class、Integer.classorder为ASC/DESCisUnique(boolean)是否创建UNIQUE索引不能与isBitmap同时指定isBitmap(boolean)是否创建BITMAP索引不能与isUnique同时指定createOption(CreateOption)建索引策略默认值为CREATE_IF_NOT_EXISTS与IndexBuilder基类的默认值不同name(String)显式指定索引名元数据索引的默认命名规则为表名_METADATA_键名1_键名2键名转大写下划线连接。元数据过滤与数据删除OracleEmbeddingStore完整支持 LangChain4j 的Filter机制search(EmbeddingSearchRequest)与removeAll(Filter)都会将过滤器转换为 SQL 的WHERE子句。转换的核心在SQLFilters/SQLFilter类中元数据键通过EmbeddingTable.mapMetadataKey(...)映射为JSON_VALUE(metadata, $.key RETURNING 类型 NULL ON ERROR)表达式见 EmbeddingTable.javaNULL ON ERROR确保 JSON 中不存在该键时返回 NULL 而非报错。这一能力在测试中得到了充分验证例如 SQLFilterIT.java 验证了IsIn/IsNotIn过滤值包含不同类型对象Integer、Long、Float、Double、String 混合时SQLFilters会把 IN/NOT IN 拆分为等价的 OR 条件避免类型转换错误此外仓库中还包含 OracleEmbeddingStoreWithFilteringIT.java、MetadataIndexStoreWithFilteringIT.java 等一批过滤相关集成测试。数据删除方面removeAll(CollectionString ids)按主键批量DELETEremoveAll()直接执行TRUNCATE TABLE见 OracleEmbeddingStore.java接口契约由 OracleEmbeddingStoreContractTest.java 继承核心模块的EmbeddingStoreRemoveAllContract进行验证。检索原理与精确/近似搜索search是向量存储的核心其实现细节OracleEmbeddingStore.java值得深入理解查询 SQL 形如SELECT VECTOR_DISTANCE(embedding, ?, COSINE) distance, id, embedding, text, metadata FROM table [WHERE ...] ORDER BY distance FETCH [APPROXIMATE] FIRST n ROWS ONLY通过 Builder 的exactSearch(boolean)控制检索模式false默认使用近似检索approximatetrue使用精确检索exact。二者都依赖向量索引来加速距离到分数的换算cosine 距离的取值范围是 02代码用score 1 - distance / 2将其转换为 01 的相似度分数分数越高越相似minScore 过滤在本地进行由于 Oracle 23.4 中距离列出现在WHERE子句会触发ORA-06553错误minScore过滤没有放进 SQL而是在结果集上按距离升序遍历、遇到低于阈值的记录即中断因为结果已按距离排序后续记录必然更低通过defineColumnType(...)与setLobPrefetchSize(Integer.MAX_VALUE)预先声明列类型和 LOB 预取大小减少网络往返针对 Oracle JDBC 的一个已知 bug 做了规避处理。Chat Memory Store对话记忆持久化除了向量存储该模块还提供OracleChatMemoryStore——一个基于 Oracle 表的ChatMemoryStore简单持久化实现用于把多轮对话的历史消息存入数据库。创建表首先创建存储表CREATE TABLE chat_memory ( memory_id VARCHAR2(255) PRIMARY KEY, content CLOB NOT NULL );接入对话记忆ChatMemoryStore store OracleChatMemoryStore.builder() .dataSource(myDataSource) .tableName(chat_memory) .build(); ChatMemory chatMemory MessageWindowChatMemory.builder() .id(conversation-1) .maxMessages(10) .chatMemoryStore(store) .build();存储模型与实现要点OracleChatMemoryStore的存储模型为每个 memory id 一行该会话的全部消息以 JSON 数组序列化后存放在content列中见 OracleChatMemoryStore.java。写入采用MERGE语句WHEN MATCHED THEN UPDATE / WHEN NOT MATCHED THEN INSERT天然支持存在即更新、不存在即插入的语义消息序列化/反序列化复用ChatMessageSerializer/ChatMessageDeserializerBuilder支持自定义tableName(String)、memoryIdColumnName(String)、contentColumnName(String)默认值分别为CHAT_MEMORY、MEMORY_ID、CONTENT出于 SQL 注入防护考虑表名/列名会通过标识符正则校验仅允许[A-Za-z_][A-Za-z0-9_]*或带引号的合法标识符非法输入直接抛IllegalArgumentException。注意只有当MessageWindowChatMemory等记忆组件配置了chatMemoryStore时对话历史才会被持久化到 Oracle 表中。运行集成测试该模块自带完整的集成测试套件*IT.java结尾的类默认运行方式为通过 TestContainers 启动 Oracle Database 的 Docker 镜像对应org.testcontainers:oracle-free依赖见 pom.xml。如果不想依赖 Docker也可以让测试连接现有的 Oracle 数据库只需配置以下环境变量环境变量说明ORACLE_JDBC_URL设置为 Oracle JDBC URL例如jdbc:oracle:thinexample:1521/serviceNameORACLE_JDBC_USER数据库用户名可选ORACLE_JDBC_PASSWORD数据库用户密码可选测试运行时需要使用 Docker 守护进程TestContainers 默认依赖它因此请确保运行环境满足 Docker 前提否则建议配置ORACLE_JDBC_*三个环境变量指向可用的 Oracle 实例。测试基类 OracleContainerTestBase.java 与 CommonTestOperations.java 中可以看到测试数据库的构建方式。小结langchain4j-oracle为 Java 生态提供了一条零额外中间件的 RAG 落地方案直接利用 Oracle Database 23.4 的 AI Vector Search 原生能力通过 Builder 式 API 即可完成建表、建索引、向量写入与相似度检索同时配套了基于表的聊天记忆持久化实现。关键实践要点回顾版本硬性前提Oracle Database ≥ 23.4务必使用连接池 DataSourceUCP 或 HikariCP避免高频建连开销按场景选择建表策略表已存在用CREATE_NONE需要自动建表用CREATE_IF_NOT_EXISTS开发/演示环境可用CREATE_OR_REPLACE数据量大时务必创建索引embedding 列用IVFIndexBuilder高频过滤的元数据键用JSONIndexBuilder默认CREATE_IF_NOT_EXISTS近似检索是默认行为追求精确结果时显式调用exactSearch(true)对话记忆按 memory id 一行 JSON 存储多个会话共用一张chat_memory表即可。相关源码与测试均可继续在仓库的 langchain4j-oracle 目录下深入阅读。【免费下载链接】langchain4jLangChain4j is an idiomatic, open-source Java library for building LLM-powered applications on the JVM. It offers a unified API over popular LLM providers and vector stores, and makes implementing tool calling (including MCP support), agents and RAG easy. It integrates seamlessly with enterprise Java frameworks like Quarkus and Spring Boot.项目地址: https://gitcode.com/GitHub_Trending/la/langchain4j创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →