主题
Elasticsearch
Maven 依赖
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-elasticsearch</artifactId>
<version>1.22.0-beta32</version>
</dependency>1
2
3
4
5
2
3
4
5
概述
langchain4j-elasticsearch 模块提供了与 Elasticsearch 的集成,作为嵌入存储与内容检索器。
它带有两个主要类:
ElasticsearchEmbeddingStore:一个EmbeddingStore接口的实现,用 Elasticsearch 存储与检索嵌入。ElasticsearchContentRetriever:一个ContentRetriever接口的实现,用 Elasticsearch 基于向量相似度搜索检索相关文档。
两个类都需要一个 Elasticsearch 客户端 来连接到 Elasticsearch 服务器。
java
String apiKey = "VnVhQ2ZHY0JDZGJrU...";
ElasticsearchClient client = ElasticsearchClient.of(ec -> ec
.host("https://localhost:9200")
.apiKey(apiKey));1
2
3
4
2
3
4
注:
关于如何创建一个 ElasticsearchClient 实例,参见 Elasticsearch 文档。
ElasticsearchEmbeddingStore
要创建 ElasticsearchEmbeddingStore 实例,你需要提供一个 ElasticsearchClient:
java
ElasticsearchEmbeddingStore store = ElasticsearchEmbeddingStore.builder()
.client(client)
.build();1
2
3
2
3
它带有以下选项:
indexName:要使用的 Elasticsearch 索引名。默认为default。configuration:要使用的ElasticsearchConfiguration。默认为ElasticsearchConfigurationKnn。refresh:由 ID 写入或移除的文档何时对搜索可见。默认为Refresh.False。
上面的代码等价于:
java
ElasticsearchEmbeddingStore store = ElasticsearchEmbeddingStore.builder()
.client(client)
.configuration(ElasticsearchConfigurationKnn.builder().build())
.indexName("default")
.build();1
2
3
4
5
2
3
4
5
刷新策略
Elasticsearch 不会在写请求返回的那一刻就让文档可被搜索。默认情况下,文档会在下一次周期性索引刷新时(每秒一次,除非索引另有设定)对搜索可见。在此之前,文档被安全存储、可按 ID 取回,但不会出现在搜索中。
当你添加文档后紧接着做某种会搜索它们的动作时,这一点就有影响。refresh 选项让你可以改为等待搜索可见性:
java
import co.elastic.clients.elasticsearch._types.Refresh;
ElasticsearchEmbeddingStore store = ElasticsearchEmbeddingStore.builder()
.client(client)
.refresh(Refresh.WaitFor)
.build();1
2
3
4
5
6
2
3
4
5
6
这三个值是:
Refresh.False(默认)在文档一被存储就返回,并把刷新留给 Elasticsearch。这是最快的选项。Refresh.WaitFor在文档对搜索可见后返回。它不强制额外刷新,因此当你需要可见性时,它通常是正确的选择。注意,若索引关闭了自动刷新(index.refresh_interval: -1),该调用会一直等到别的东西触发一次刷新。Refresh.True立即强制一次刷新。它在不等待的情况下给出可见性,但每次请求都创建一个新段会降低索引吞吐,所以在写密集的索引上应避免它。
该选项适用于 add、addAll 与 removeAll(Collection<String> ids)。它不改变搜索,也不会让你与别人并发做出的写入相隔离。同样的选项在 ElasticsearchContentRetriever.builder() 上也可用。
按过滤条件移除(removeAll(Filter))是一次 delete-by-query,因此它也只匹配那些已对搜索可见的文档:片刻前添加的嵌入可能在它之后仍存活。若你添加嵌入后又立即按过滤条件移除它们,请配置 Refresh.WaitFor,好让写入在移除运行前就可被搜索。
细节参见 Elasticsearch 刷新参数文档。
不带嵌入地存储文档
除了常规的 add(Embedding, TextSegment) 方法,该存储也能为纯文本建索引,而不为它计算嵌入:
java
store.add("Printer troubleshooting guide"); // generates an id
store.add("my-id", "Printer troubleshooting guide"); // with your own id
store.addAllText(List.of("First guide", "Second guide")); // several at once1
2
3
2
3
由于这些文档没有向量,向量搜索永不返回它们,无论用 ElasticsearchConfigurationKnn 还是 ElasticsearchConfigurationScript。它们仍会被全文搜索找到,因此单个索引可同时容纳带嵌入与纯文本的文档,你可以用 ElasticsearchConfigurationFullText 或 ElasticsearchConfigurationHybrid 以两种方式搜索它。
ElasticsearchContentRetriever
一个 ContentRetriever 需要一个嵌入模型:
java
EmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel();1
要创建一个 ElasticsearchContentRetriever 实例,你需要提供 ElasticsearchClient 与 EmbeddingModel:
java
ElasticsearchContentRetriever contentRetriever = ElasticsearchContentRetriever.builder()
.client(client)
.embeddingModel(embeddingModel)
.build();1
2
3
4
2
3
4
它带有以下选项:
configuration:要使用的ElasticsearchConfiguration(见下文)。默认为ElasticsearchConfigurationKnn。indexName:要使用的 Elasticsearch 索引名。默认为default。若不存在,索引会被自动创建。maxResults:要检索的最大结果数。默认为3。minScore:检索结果的最低分数阈值。默认为0.0。filter:检索期间要应用的Filter(若有)。默认为null。
上面的代码等价于:
java
ElasticsearchContentRetriever contentRetriever = ElasticsearchContentRetriever.builder()
.client(client)
.embeddingModel(embeddingModel)
.configuration(ElasticsearchConfigurationKnn.builder().build())
.indexName("default")
.maxResults(3)
.minScore(0.0)
.filter(null)
.build();1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
ElasticsearchConfiguration
一个 ElasticsearchConfiguration 定义了嵌入存储或内容检索器将如何与 Elasticsearch 服务器交互。你可以通过实现 ElasticsearchConfiguration 接口创建你自己的配置,或使用以下提供的某个实现:
ElasticsearchConfigurationKnn:使用近似 kNN 查询(默认)。ElasticsearchConfigurationScript:使用 scriptScore 查询。请注意该实现使用余弦相似度。ElasticsearchConfigurationFullText:使用全文搜索(仅用于内容检索器)。ElasticsearchConfigurationHybrid:使用混合搜索(仅用于内容检索器,需要付费许可)。它把一个 kNN 向量查询与一个全文查询结合。
要创建一个配置实例,你可以使用每个实现提供的 builder。例如:
java
ElasticsearchConfiguration configuration = ElasticsearchConfigurationKnn.builder().build();1
ElasticsearchConfigurationKnn
ElasticsearchConfigurationKnn 使用近似 kNN 查询来执行向量相似度搜索。
它是 ElasticsearchEmbeddingStore 与 ElasticsearchContentRetriever 都使用的默认配置。
要创建一个实例,你可以使用 builder:
java
ElasticsearchConfiguration configuration = ElasticsearchConfigurationKnn.builder().build();1
它带有以下选项:
numCandidates:搜索期间要考虑的候选邻居数。默认为null,即使用 Elasticsearch 的默认值。includeVectorResponse:是否在搜索响应中包含向量字段。默认为false。
注: 自 elasticsearch 服务器 9.2 版本起,向量字段默认会从响应中排除。要在响应中包含向量字段(不推荐),请在 builder 中设置
includeVectorResponse:javaElasticsearchConfigurationKnn configuration = ElasticsearchConfigurationKnn.builder() .includeVectorResponse(true) .build();1
2
3
ElasticsearchConfigurationScript
ElasticsearchConfigurationScript 使用 scriptScore 查询来执行向量相似度搜索。请注意该实现使用余弦相似度。
它对 ElasticsearchEmbeddingStore 与 ElasticsearchContentRetriever 都可用。
要创建一个实例,你可以使用 builder:
java
ElasticsearchConfiguration configuration = ElasticsearchConfigurationScript.builder().build();1
它带有以下选项:
includeVectorResponse:是否在搜索响应中包含向量字段。默认为false。
注: 自 elasticsearch 服务器 9.2 版本起,向量字段默认会从响应中排除。要在响应中包含向量字段(不推荐),请在 builder 中设置
includeVectorResponse:javaElasticsearchConfiguration configuration = ElasticsearchConfigurationScript.builder() .includeVectorResponse(true) .build();1
2
3
ElasticsearchConfigurationFullText
ElasticsearchConfigurationFullText 使用全文搜索来检索相关文档。
它仅对 ElasticsearchContentRetriever 可用。
要创建一个实例,你可以使用 builder:
java
ElasticsearchConfiguration configuration = ElasticsearchConfigurationFullText.builder().build();1
ElasticsearchConfigurationHybrid
ElasticsearchConfigurationHybrid 使用混合搜索,把一个 kNN 向量查询与一个全文查询结合。请注意混合搜索需要 elasticsearch 企业许可或试用。
它仅对 ElasticsearchContentRetriever 可用。
要创建一个实例,你可以使用 builder:
java
ElasticsearchConfiguration configuration = ElasticsearchConfigurationHybrid.builder().build();1
它带有以下选项:
numCandidates:搜索期间要考虑的候选邻居数。默认为null,即使用 Elasticsearch 的默认值。includeVectorResponse:是否在搜索响应中包含向量字段。默认为false。
注: 自 elasticsearch 服务器 9.2 版本起,向量字段默认会从响应中排除。要在响应中包含向量字段(不推荐),请在 builder 中设置
includeVectorResponse:javaElasticsearchConfiguration configuration = ElasticsearchConfigurationHybrid.builder() .includeVectorResponse(true) .build();1
2
3
创建自定义配置
你可以通过实现 ElasticsearchConfiguration 接口创建你自己的 Elasticsearch 配置。例如:
java
public class MyElasticsearchConfiguration implements ElasticsearchConfiguration {
@Override
SearchResponse<Document> vectorSearch(
ElasticsearchClient client,
String indexName,
EmbeddingSearchRequest embeddingSearchRequest) {
// Your optional custom vector search implementation here
}
@Override
SearchResponse<Document> fullTextSearch(
ElasticsearchClient client,
String indexName,
FullTextSearchRequest request) {
// Your optional custom full text search implementation here
}
@Override
SearchResponse<Document> hybridSearch(
ElasticsearchClient client,
String indexName,
EmbeddingSearchRequest embeddingSearchRequest,
String textQuery) {
// Your optional custom hybrid search implementation here
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
请注意,你可以只实现与你用例相关的那些方法:
vectorSearch用于向量相似度搜索(由ElasticsearchEmbeddingStore与ElasticsearchContentRetriever共同使用)。fullTextSearch用于全文搜索(仅由ElasticsearchContentRetriever使用)。hybridSearch用于混合搜索(仅由ElasticsearchContentRetriever使用)。
FullTextSearchRequest 携带要搜索的 textQuery,连同在 ElasticsearchContentRetriever 上配置的 maxResults、minScore 与 filter。你的实现负责应用它们,否则不匹配该过滤条件的文档可能被返回。
注: 还有一个已弃用的
fullTextSearch(ElasticsearchClient client, String indexName, String textQuery)方法。只实现那一个方法的配置仍能继续工作,但检索器的maxResults、minScore与filter会被忽略,并会记录一条警告。请改实现那个接收FullTextSearchRequest的方法。