【Langchain4j-Java AI开发】08-向量嵌入与向量数据库

LangChain4j 向量嵌入与向量数据库

概述

向量嵌入(Embedding)是将文本转换为数值向量的技术,是 RAG 和语义搜索的基础。本教程将介绍如何使用 LangChain4j 的 Embedding 功能和向量数据库。

什么是 Embedding?

文本到向量的转换

java 复制代码
EmbeddingModel model = new BgeSmallEnV15QuantizedEmbeddingModel();

// 将文本转换为向量
Embedding embedding = model.embed("人工智能").content();

System.out.println("向量维度: " + embedding.dimension());  // 384
System.out.println("向量值: " + Arrays.toString(embedding.vector()));
// 输出: [0.123, -0.456, 0.789, ...]

语义相似度

相似的文本会产生相似的向量:

java 复制代码
Embedding e1 = model.embed("猫").content();
Embedding e2 = model.embed("小猫").content();
Embedding e3 = model.embed("汽车").content();

// 计算余弦相似度
double similarity12 = CosineSimilarity.between(e1, e2);  // 0.85(高相似度)
double similarity13 = CosineSimilarity.between(e1, e3);  // 0.12(低相似度)

Embedding 模型

1. 本地模型(推荐用于开发)

BGE Small (英文)
java 复制代码
import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;

EmbeddingModel model = new BgeSmallEnV15QuantizedEmbeddingModel();

// 嵌入单个文本
Embedding embedding = model.embed("Hello World").content();

// 批量嵌入
List<TextSegment> segments = List.of(
        TextSegment.from("第一段文本"),
        TextSegment.from("第二段文本"),
        TextSegment.from("第三段文本")
);
List<Embedding> embeddings = model.embedAll(segments).content();

特点

  • ✅ 完全本地运行,无需 API
  • ✅ 免费,无使用限制
  • ✅ 快速(量化版本)
  • ❌ 主要适用于英文
  • 维度:384
All-MiniLM-L6-v2 (多语言)
java 复制代码
import dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel;

EmbeddingModel model = new AllMiniLmL6V2QuantizedEmbeddingModel();

特点

  • 支持多语言
  • 维度:384
  • 速度快

2. OpenAI Embedding(质量最高)

java 复制代码
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;

EmbeddingModel model = OpenAiEmbeddingModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("text-embedding-3-small")  // 或 text-embedding-3-large
        .build();

Embedding embedding = model.embed("人工智能正在改变世界").content();

模型对比

模型 维度 价格 适用场景
text-embedding-3-small 1536 通用场景
text-embedding-3-large 3072 高精度需求
text-embedding-ada-002 1536 旧版(不推荐)

3. 其他云端模型

Cohere
java 复制代码
import dev.langchain4j.model.cohere.CohereEmbeddingModel;

EmbeddingModel model = CohereEmbeddingModel.builder()
        .apiKey(System.getenv("COHERE_API_KEY"))
        .modelName("embed-multilingual-v3.0")  // 多语言模型
        .build();
Azure OpenAI
java 复制代码
import dev.langchain4j.model.azure.AzureOpenAiEmbeddingModel;

EmbeddingModel model = AzureOpenAiEmbeddingModel.builder()
        .apiKey(System.getenv("AZURE_OPENAI_KEY"))
        .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
        .deploymentName("text-embedding-ada-002")
        .build();
HuggingFace
java 复制代码
import dev.langchain4j.model.huggingface.HuggingFaceEmbeddingModel;

EmbeddingModel model = HuggingFaceEmbeddingModel.builder()
        .apiKey(System.getenv("HUGGINGFACE_API_KEY"))
        .modelId("sentence-transformers/all-MiniLM-L6-v2")
        .build();

向量数据库

1. InMemoryEmbeddingStore(开发测试)

java 复制代码
package dev.langchain4j.example;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.util.List;

public class InMemoryStoreExample {

    public static void main(String[] args) {

        // 1. 创建 Embedding 模型
        EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();

        // 2. 创建内存存储
        EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();

        // 3. 准备文档
        List<TextSegment> segments = List.of(
                TextSegment.from("Java 是一种面向对象的编程语言"),
                TextSegment.from("Python 是一种简洁易学的编程语言"),
                TextSegment.from("JavaScript 主要用于 Web 开发"),
                TextSegment.from("机器学习是人工智能的一个分支")
        );

        // 4. 嵌入并存储
        for (TextSegment segment : segments) {
            Embedding embedding = embeddingModel.embed(segment).content();
            embeddingStore.add(embedding, segment);
        }

        // 5. 搜索
        String query = "什么编程语言适合 Web?";
        Embedding queryEmbedding = embeddingModel.embed(query).content();

        List<EmbeddingMatch<TextSegment>> matches = embeddingStore.findRelevant(
                queryEmbedding,
                3,    // 返回前 3 个结果
                0.5   // 最低相似度
        );

        // 6. 显示结果
        System.out.println("查询: " + query);
        System.out.println("\n最相关的文档:");
        for (EmbeddingMatch<TextSegment> match : matches) {
            System.out.printf("相似度: %.3f - %s\n",
                    match.score(),
                    match.embedded().text());
        }
    }
}

输出示例

复制代码
查询: 什么编程语言适合 Web?

最相关的文档:
相似度: 0.856 - JavaScript 主要用于 Web 开发
相似度: 0.678 - Python 是一种简洁易学的编程语言
相似度: 0.621 - Java 是一种面向对象的编程语言

2. Chroma(本地持久化)

java 复制代码
import dev.langchain4j.store.embedding.chroma.ChromaEmbeddingStore;

// 启动 Chroma 服务: docker run -p 8000:8000 chromadb/chroma

EmbeddingStore<TextSegment> chromaStore = ChromaEmbeddingStore.builder()
        .baseUrl("http://localhost:8000")
        .collectionName("my-documents")
        .build();

// 添加数据
chromaStore.add(embedding, segment);

// 搜索
List<EmbeddingMatch<TextSegment>> results = chromaStore.findRelevant(
        queryEmbedding,
        5
);

Docker 启动

bash 复制代码
docker run -d -p 8000:8000 chromadb/chroma

3. Pinecone(云端生产)

java 复制代码
import dev.langchain4j.store.embedding.pinecone.PineconeEmbeddingStore;

EmbeddingStore<TextSegment> pineconeStore = PineconeEmbeddingStore.builder()
        .apiKey(System.getenv("PINECONE_API_KEY"))
        .environment("us-east-1-aws")
        .projectId("my-project")
        .index("my-index")
        .nameSpace("production")  // 可选
        .build();

特点

  • ✅ 完全托管,无需维护
  • ✅ 高性能,低延迟
  • ✅ 自动扩展
  • ❌ 付费服务

4. PostgreSQL (pgvector)

java 复制代码
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;

EmbeddingStore<TextSegment> pgStore = PgVectorEmbeddingStore.builder()
        .host("localhost")
        .port(5432)
        .database("vectordb")
        .user("postgres")
        .password("password")
        .table("embeddings")
        .dimension(384)  // 根据 embedding 模型调整
        .createTable(true)  // 自动创建表
        .build();

PostgreSQL 设置

sql 复制代码
-- 安装 pgvector 扩展
CREATE EXTENSION vector;

-- 创建表(如果不使用 createTable=true)
CREATE TABLE embeddings (
    id UUID PRIMARY KEY,
    embedding VECTOR(384),
    text TEXT,
    metadata JSONB
);

-- 创建索引
CREATE INDEX ON embeddings USING ivfflat (embedding vector_cosine_ops);

5. Elasticsearch

java 复制代码
import dev.langchain4j.store.embedding.elasticsearch.ElasticsearchEmbeddingStore;

EmbeddingStore<TextSegment> esStore = ElasticsearchEmbeddingStore.builder()
        .serverUrl("http://localhost:9200")
        .indexName("embeddings")
        .dimension(384)
        .build();

6. Redis

java 复制代码
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;

EmbeddingStore<TextSegment> redisStore = RedisEmbeddingStore.builder()
        .host("localhost")
        .port(6379)
        .dimension(384)
        .indexName("embeddings-idx")
        .build();

7. Qdrant

java 复制代码
import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;

EmbeddingStore<TextSegment> qdrantStore = QdrantEmbeddingStore.builder()
        .host("localhost")
        .port(6334)
        .collectionName("my-collection")
        .build();

Docker 启动

bash 复制代码
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant

向量数据库选择指南

数据库 适用场景 优点 缺点
InMemory 开发测试 简单快速 不持久化
Chroma 小型项目 本地部署,易用 性能有限
pgvector 已有 PostgreSQL 无需额外服务 性能较专业向量DB差
Pinecone 生产环境 高性能,免维护 付费
Qdrant 中大型项目 开源,高性能 需自行部署
Elasticsearch 已有 ES 集群 综合搜索能力 向量搜索非专长
Redis 需要缓存 速度极快 内存占用大

元数据与过滤

添加元数据

java 复制代码
import dev.langchain4j.data.document.Metadata;

// 创建带元数据的文本段
TextSegment segment = TextSegment.from(
        "Java 是一种面向对象的编程语言",
        Metadata.from(
                "category", "编程语言",
                "difficulty", "中等",
                "year", 1995,
                "author", "James Gosling"
        )
);

// 嵌入并存储
Embedding embedding = embeddingModel.embed(segment).content();
embeddingStore.add(embedding, segment);

使用过滤器搜索

java 复制代码
import dev.langchain4j.store.embedding.filter.Filter;

// 创建过滤器
Filter filter = Filter.metadataKey("category").isEqualTo("编程语言");

// 过滤搜索
List<EmbeddingMatch<TextSegment>> matches = embeddingStore.findRelevant(
        queryEmbedding,
        5,
        0.7,
        filter  // 只返回编程语言类别的文档
);

复杂过滤条件

java 复制代码
import static dev.langchain4j.store.embedding.filter.Filter.*;

// AND 条件
Filter andFilter = and(
        metadataKey("category").isEqualTo("编程语言"),
        metadataKey("year").isGreaterThan(2000)
);

// OR 条件
Filter orFilter = or(
        metadataKey("difficulty").isEqualTo("简单"),
        metadataKey("difficulty").isEqualTo("中等")
);

// 组合条件
Filter complexFilter = and(
        metadataKey("category").isEqualTo("编程语言"),
        or(
                metadataKey("difficulty").isEqualTo("简单"),
                metadataKey("year").isGreaterThan(2010)
        )
);

实战示例

构建语义搜索引擎

java 复制代码
package dev.langchain4j.example;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Path;
import java.util.List;

import static dev.langchain4j.data.document.loader.FileSystemDocumentLoader.loadDocuments;

public class SemanticSearchEngine {

    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;

    public SemanticSearchEngine() {
        this.embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();
        this.embeddingStore = new InMemoryEmbeddingStore<>();
    }

    // 索引文档
    public void indexDocuments(String directoryPath) {
        // 1. 加载文档
        List<Document> documents = loadDocuments(Path.of(directoryPath), "*.txt");
        System.out.println("加载了 " + documents.size() + " 个文档");

        // 2. 分割文档
        DocumentSplitter splitter = DocumentSplitters.recursive(300, 50);

        for (Document document : documents) {
            List<TextSegment> segments = splitter.split(document);

            // 3. 嵌入并存储每个片段
            for (TextSegment segment : segments) {
                Embedding embedding = embeddingModel.embed(segment).content();
                embeddingStore.add(embedding, segment);
            }
        }

        System.out.println("索引完成");
    }

    // 搜索
    public List<SearchResult> search(String query, int maxResults) {
        // 1. 嵌入查询
        Embedding queryEmbedding = embeddingModel.embed(query).content();

        // 2. 搜索
        List<EmbeddingMatch<TextSegment>> matches = embeddingStore.findRelevant(
                queryEmbedding,
                maxResults,
                0.6
        );

        // 3. 转换结果
        return matches.stream()
                .map(match -> new SearchResult(
                        match.embedded().text(),
                        match.score(),
                        match.embedded().metadata()
                ))
                .toList();
    }

    public static void main(String[] args) {
        SemanticSearchEngine engine = new SemanticSearchEngine();

        // 索引文档
        engine.indexDocuments("documents/");

        // 搜索
        List<SearchResult> results = engine.search("如何学习 Java?", 3);

        System.out.println("\n搜索结果:");
        for (int i = 0; i < results.size(); i++) {
            SearchResult result = results.get(i);
            System.out.printf("%d. (相似度: %.3f)\n%s\n\n",
                    i + 1,
                    result.score(),
                    result.text());
        }
    }

    record SearchResult(String text, double score, Metadata metadata) {}
}

性能优化

1. 批量嵌入

java 复制代码
// ❌ 不好:逐个嵌入
for (TextSegment segment : segments) {
    Embedding embedding = embeddingModel.embed(segment).content();
    embeddingStore.add(embedding, segment);
}

// ✅ 好:批量嵌入
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
embeddingStore.addAll(embeddings, segments);

2. 使用量化模型

java 复制代码
// 使用量化模型(更快,体积更小)
EmbeddingModel model = new BgeSmallEnV15QuantizedEmbeddingModel();
// 而不是完整精度模型

3. 调整向量维度(如果支持)

java 复制代码
// OpenAI 支持降维
EmbeddingModel model = OpenAiEmbeddingModel.builder()
        .apiKey(apiKey)
        .modelName("text-embedding-3-large")
        .dimensions(512)  // 降至 512 维(默认 3072)
        .build();

4. 创建索引(向量数据库)

sql 复制代码
-- PostgreSQL pgvector
CREATE INDEX ON embeddings USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

-- 或使用 HNSW 索引(更快)
CREATE INDEX ON embeddings USING hnsw (embedding vector_cosine_ops);

最佳实践

1. 选择合适的模型

java 复制代码
// 开发/测试:本地模型
EmbeddingModel devModel = new BgeSmallEnV15QuantizedEmbeddingModel();

// 生产/高质量:OpenAI
EmbeddingModel prodModel = OpenAiEmbeddingModel.builder()
        .apiKey(apiKey)
        .modelName("text-embedding-3-small")
        .build();

2. 文本预处理

java 复制代码
public String preprocessText(String text) {
    return text
            .toLowerCase()                    // 转小写
            .replaceAll("\\s+", " ")          // 规范化空白
            .replaceAll("[^\\p{L}\\p{N}\\s]", "")  // 移除标点
            .trim();
}

3. 监控嵌入质量

java 复制代码
// 检查嵌入分布
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();

double avgNorm = embeddings.stream()
        .mapToDouble(e -> {
            double[] vector = e.vector();
            return Math.sqrt(Arrays.stream(vector).map(v -> v * v).sum());
        })
        .average()
        .orElse(0);

System.out.println("平均向量范数: " + avgNorm);

常见问题

Q1: 不同的 Embedding 模型可以混用吗?

A: 不可以。同一个向量存储中的所有向量必须使用相同的模型和维度。

Q2: 如何选择向量维度?

A:

  • 小维度(384): 速度快,存储小,精度稍低
  • 中维度(768-1536): 平衡选择(推荐)
  • 大维度(3072): 精度高,但慢且占用大

Q3: 向量数据库的数据可以持久化吗?

A:

  • InMemoryEmbeddingStore: 不持久化
  • 其他数据库: 都支持持久化

下一步学习

参考资料

相关推荐
5Gcamera1 小时前
4G body camera BC310/BC310D user manual
人工智能·边缘计算·智能安全帽·执法记录仪·smarteye
小冷coding1 小时前
【MySQL】MySQL 插入一条数据的完整流程(InnoDB 引擎)
数据库·mysql
Elias不吃糖2 小时前
Java Lambda 表达式
java·开发语言·学习
爱喝可乐的老王2 小时前
机器学习中常用交叉验证总结
人工智能·机器学习
情缘晓梦.2 小时前
C语言指针进阶
java·开发语言·算法
鲨莎分不晴2 小时前
Redis 基本指令与命令详解
数据库·redis·缓存
专注echarts研发20年2 小时前
工业级 Qt 业务窗体标杆实现・ResearchForm 类深度解析
数据库·qt·系统架构
公链开发3 小时前
2026 Web3机构级风口:RWA Tokenization + ZK隐私系统定制开发全解析
人工智能·web3·区块链
wyw00003 小时前
目标检测之YOLO
人工智能·yolo·目标检测
发哥来了3 小时前
AI视频生成企业级方案选型指南:2025年核心能力与成本维度深度对比
大数据·人工智能