向量自动生成(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 一次**