向量自动生成(Embedding)+ 语义检索(kNN)完整实践文档
9.2默认向量是看不到的,但是其实已经存进去。需要指定字段查询才行。
```js
GET wenda_question_es/_search
{
"query": { "ids": { "values": ["auto-2"] } },
"_source": ["title"],
"fields": ["titleVector"]
}
```
# Elasticsearch 9.x
## 向量自动生成(Embedding)+ 语义检索(kNN)完整实践文档
> 场景:
>
> * 中文问答 / 文本语义相似
> * 自动生成向量
> * 写入时自动维护
> * 支持 kNN 相似度查询
> * 使用 **火山方舟 doubao-embedding-text-240715**
## 一、前置条件(必须满足)
### 1. ES 版本
* Elasticsearch **9.x**
* 自建或云上均可
### 2. License(关键)
* **Basic 不支持 inference**
* 需要:
* Trial(30 天)
* 或 Platinum / Enterprise
检查 license:
```http
GET /_license
```
开启 trial(如果可用):
```http
POST /_license/start_trial?acknowledge=true
```
## 二、创建 Inference Endpoint(Embedding 服务)
### 目的
让 ES 能调用 **火山方舟 embeddings API**,自动生成向量。
### 使用模型
* 模型名:`doubao-embedding-text-240715`
* 向量维度:**2560**
* 相似度:`dot_product` 或 `cosine`
### 创建 inference endpoint
```http
PUT _inference/text_embedding/ark-emb
{
"service": "custom",
"service_settings": {
"similarity": "dot_product",
"dimensions": 2560,
"url": "https://ark.cn-beijing.volces.com/api/v3/embeddings",
"headers": {
"Authorization": "Bearer ${api_key}",
"Content-Type": "application/json"
},
"request": "{\"model\":\"doubao-embedding-text-240715\",\"input\":${input}}",
"response": {
"json_parser": {
"text_embeddings": "$.data[*].embedding[*]",
"embedding_type": "float"
}
},
"secret_parameters": {
"api_key": "你的火山方舟 API Key"
}
}
}
```
### 验证 endpoint 是否可用
```http
POST _inference/text_embedding/ark-emb
{
"input": "测试一段文本"
}
```
期望:
* 返回 embedding
* 长度 = 2560
## 三、索引 Mapping:新增向量字段
### 目的
为文本存储 embedding,用于 kNN 查询。
### 新增字段(已有索引可直接加)
```http
PUT your_index_name/_mapping
{
"properties": {
"titleVector": {
"type": "dense_vector",
"dims": 2560,
"index": true,
"similarity": "dot_product"
}
}
}
```
> 注意
>
> * 只能 **新增字段**
> * `dims` 必须和模型一致(2560)
## 四、创建 Ingest Pipeline(自动写入向量)
### 目的
在写入文档时:
* 自动调用 inference
* 把 `title` → `titleVector`
### 创建 pipeline
```http
PUT _ingest/pipeline/title_embedding_pipeline
{
"processors": [
{
"inference": {
"model_id": "ark-emb",
"input_output": [
{
"input_field": "title",
"output_field": "titleVector"
}
]
}
}
]
}
```
## 五、设置索引默认 Pipeline(实现“自动”)
### 目的
以后 **所有写入都自动算向量**,不用每次带 `?pipeline=...`
```http
PUT your_index_name/_settings
{
"index.default_pipeline": "title_embedding_pipeline"
}
```
验证:
```http
GET your_index_name/_settings?filter_path=*.settings.index.default_pipeline
```
## 六、写数据(向量会自动生成)
### 正确写法(推荐)
**必须使用 index / bulk,而不是 _update**
```http
PUT your_index_name/_doc/auto-1
{
"title": "实现一个线程的方式有哪些?",
"description": "",
"tags": ["多线程"],
"createTime": "2025-10-18"
}
```
> ⚠️ `_update` **不会触发 pipeline**
> 修改 title 时建议用 **整条覆盖写入**
## 七、验证向量是否写入(关键)
### 方式 1:用 `fields` 查看(推荐)
```http
GET your_index_name/_search
{
"query": {
"ids": { "values": ["auto-1"] }
},
"_source": ["title"],
"fields": ["titleVector"]
}
```
如果返回:
```json
"fields": {
"titleVector": [ ...2560 个 float... ]
}
```
✅ 表示向量已写入
### 为什么 `_source` 看不到?
ES 9.x 默认:
* `dense_vector` **不返回在 `_source`**
* 这是正常行为(省空间)
## 八、语义相似度查询(kNN)
### 示例:查相似问题
```http
GET your_index_name/_search
{
"size": 5,
"knn": {
"field": "titleVector",
"query_vector": [2560维向量],
"k": 5,
"num_candidates": 200
}
}
```
> `query_vector` 可来自:
>
> * 新问题实时 embedding
> * 或 ES inference(如果你在应用层调用)
## 九、老数据回填向量(必做一次)
### 推荐方式:重建索引 + reindex
1️⃣ 新建索引(带 mapping + default_pipeline)
2️⃣ 回填数据:
```http
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}
```
> 因为 `new_index` 有 default_pipeline
> 👉 每条数据都会自动生成向量
3️⃣ 切 alias
## 十、以后新索引怎么复用?
你只需要做这 4 步:
1. 创建 inference endpoint(一次,全局)
2. 新索引加 `dense_vector` 字段
3. 绑定 `index.default_pipeline`
4. 用 index / bulk 写数据
## 十一、最佳实践总结(很重要)
* **不要用 `_update` 改文本**
* **向量不在 `_source` 是正常**
* **dims 必须严格一致**
* **pipeline + default_pipeline = 自动维护**
* **老数据一定要 reindex 一次**
公众号进入网站体验更佳

