Repository: alibaba/higress
Pull request #1844: feat: support elasticsearch hybrid search
Author: login: cr7258
Created: 2025-03-02T14:53:44Z | Merged: 2025-03-11T03:25:59Z

<!-- Please make sure you have read and understood the contributing guidelines -->

### Ⅰ. Describe what this PR did

Elasticsearch 支持混合搜索模式，提高搜索的准确性：
https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text-hybrid-search.html

### Ⅱ. Does this pull request fix one issue?
<!-- If that, add "fixes #xxx" below in the next line, for example, fixes #97. -->


### Ⅲ. Why don't you add test cases (unit test/integration test)? 


### Ⅳ. Describe how to verify it

#### 部署 Elasticsearch 和 Kibana

`docker-compose.yaml` 文件。

```bash
services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          echo -ne \
          "instances:\n"\
          "  - name: es01\n"\
          "    dns:\n"\
          "      - es01\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!";
      '
    healthcheck:
      test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
      interval: 1s
      timeout: 5s
      retries: 120

  es01:
    depends_on:
      setup:
        condition: service_healthy
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.http.ssl.verification_mode=certificate
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
      - cluster.routing.allocation.disk.watermark.low=2gb
      - cluster.routing.allocation.disk.watermark.high=1gb
      - cluster.routing.allocation.disk.watermark.flood_stage=512mb
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  kibana:
    depends_on:
      es01:
        condition: service_healthy
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s

volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  kibanadata:
    driver: local
```

`.env` 文件中设置了 Elasticsearch 的用户名密码，并且启用了 trial 模式，因为 Machine Learning 的相关功能是付费的。

```bash
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD='test123'

# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD='test123'

# Version of Elastic products
STACK_VERSION=8.17.0

# Set the cluster name
CLUSTER_NAME=ai-search

# Set to 'basic' or 'trial' to automatically start the 30-day trial
# LICENSE=basic
LICENSE=trial

# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200

# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

# Increase or decrease based on the available host memory (in bytes)
MEM_LIMIT=9073741824

# Project namespace (defaults to the current folder name if not set)
COMPOSE_PROJECT_NAME=ai-search
```

#### 部署 embedding 模型

Elasticsearch 默认提供了以下 3 种模型，`.elser_model_2` 使用稀疏向量进行索引，对英文比较友好。如果是搜索非英文，建议使用 `.multilingual-e5-small` 模型。如果要导入第三方模型，可以参考该[文档](https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-text-emb-vector-search-example.html)。

![CleanShot 2025-03-02 at 22 43 01@2x](https://github.com/user-attachments/assets/f1cecb48-c78a-4d5d-b2bc-a64d0e1e51f1)

#### 创建索引

创建索引，重点设置两个字段：
- content：存储实际的文本，用户写入只需要写 content 字段，copy_to 参数确保内容被复制到 semantic_text 字段并由 embedding 模型进行向量化。
- semantic_text：存储 embedding 后的向量。semantic_text 类型的字段默认使用 `.elser-2-elasticsearch` 作为 embedding 模型，如果要使用其他模型，需要设置 [inference_id](https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html)。

```yaml
PUT employee_handbook
{
  "mappings": {
    "properties": {
      "semantic_text": { 
        "type": "semantic_text"
      },
      "content": { 
        "type": "text",
        "copy_to": "semantic_text" 
      }
    }
  }
}
```

#### 写入数据

```bash
POST _bulk
{"index": {"_index": "employee_handbook"}}
{"title": "Company Overview", "content": "Our company was founded in 2005 and is headquartered in Beijing. With a workforce of over 1,000 employees, we specialize in artificial intelligence and software development. Our vision is to enhance customer value through technological innovation and provide high-quality solutions to global clients.", "category": "Company Overview", "link": "https://workplace.org/rules/1854"}
{"index": {"_index": "employee_handbook"}}
{"title": "Employee Code of Conduct", "content": "Employees must adhere to professional ethics, maintain integrity, respect colleagues, and avoid conflicts of interest. Disclosing company confidential information, including customer data and internal research materials, is strictly prohibited. Violators may face fines of up to 100,000 CNY or legal consequences.", "category": "Code of Conduct", "link": "https://workplace.org/info/9931"}
{"index": {"_index": "employee_handbook"}}
{"title": "Attendance Policy", "content": "Employees must report to work on time. Working hours are from 9:00 AM to 6:00 PM, with a lunch break from 12:30 PM to 1:30 PM. Being late or leaving early more than 3 times will result in a warning, more than 5 times requires a written explanation, and more than 10 times may lead to salary deductions.", "category": "Attendance Management", "link": "https://guidelines.ai/handbook/4334"}
{"index": {"_index": "employee_handbook"}}
{"title": "Salary and Benefits", "content": "The company offers competitive salaries, with annual performance-based adjustments averaging an 8% increase. Bonuses include performance incentives (up to 20% of monthly salary) and year-end bonuses (1 to 3 months' salary). Benefits include social insurance, paid annual leave (12 days per year), and holiday allowances.", "category": "Salary & Benefits", "link": "https://workplace.org/policy/7457"}
{"index": {"_index": "employee_handbook"}}
{"title": "Performance Evaluation", "content": "Performance evaluations are conducted quarterly and annually, assessing goal completion (50%), teamwork (30%), and personal growth (20%). Employees scoring below 60 for a quarter must attend additional training. A consecutive low score may result in salary reduction or reassignment.", "category": "Performance Management", "link": "https://guidelines.ai/info/9582"}
{"index": {"_index": "employee_handbook"}}
{"title": "Career Development", "content": "The company provides internal training, external training subsidies (up to 3,000 CNY per year), and promotion opportunities. Every employee has at least two career development meetings per year and can apply for cross-department learning or participate in innovation projects to enhance their skills.", "category": "Career Development", "link": "https://docs.net/info/8827"}
{"index": {"_index": "employee_handbook"}}
{"title": "Security and Confidentiality", "content": "Employees must follow data security and confidentiality policies. All company devices must have official antivirus software installed. Accessing sensitive data requires two-factor authentication. Violations may result in suspension or termination. The company conducts security audits quarterly.", "category": "Information Security", "link": "https://workplace.org/rules/9086"}
{"index": {"_index": "employee_handbook"}}
{"title": "Leave and Vacation Policy", "content": "Employees are entitled to 10 days of sick leave, 12 days of annual leave (after one year of service), 3 to 10 days of marriage leave, and 98 days of maternity leave (15 days for paternity leave). All leave requests must be approved in advance. Unauthorized absences will be considered absenteeism.", "category": "Leave Policy", "link": "https://guidelines.ai/policy/3310"}
{"index": {"_index": "employee_handbook"}}
{"title": "Travel Policy", "content": "Business travel requests must be submitted at least 5 working days in advance and approved by direct supervisors and finance. The company reimburses reasonable expenses, including transportation, accommodation, and meals. Domestic accommodation is capped at 600 CNY per night, while international stays are capped at $150 per night.", "category": "Travel Regulations", "link": "https://guidelines.ai/rules/5144"}
{"index": {"_index": "employee_handbook"}}
{"title": "Employee Reward and Punishment System", "content": "The company rewards outstanding employees with quarterly bonuses (ranging from 2,000 to 10,000 CNY) and an annual outstanding employee award (up to 50,000 CNY). Violations of company policies will result in written warnings, and severe offenses may lead to salary reductions or termination.", "category": "Rewards & Punishments", "link": "https://guidelines.ai/handbook/9306"}
```

#### 配置 ai-search 插件

另外还需要配置 ai-proxy 插件，这里省略。

```bash
needReference: true
searchFrom:
- contentField: "content"
  count: 1
  index: "employee_handbook"
  linkField: "link"
  password: "test123"
  semanticTextField: "semantic_text"
  serviceName: "elasticsearch.static"
  servicePort: 80
  timeoutMillisecond: 50000
  titleField: "title"
  type: "elasticsearch"
  username: "elastic"
searchRewrite:
  llmApiKey: "<api-key>"
  llmModelName: "qwen-long"
  llmServiceName: "llm-aliyun.internal.dns"
  llmServicePort: 443
  llmUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"
  timeoutMillisecond: 15000
```

#### 验证

```bash
curl 'http://localhost:8080/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen-long",
    "messages": [
      {
        "role": "user",
        "content": "What are the working hours according to the employee handbook?"
      }
    ]
  }'

# 返回结果
{"id":"e856547d-4b5c-9e9a-a61b-94857327626e","choices":[{"index":0,"message":{"role":"assistant",
"content":"**References:**\n[1] [Attendance Policy](https://guidelines.ai/handbook/4334)\n\nThe working hours specified 
in the employee handbook are from 9:00 AM to 6:00 PM, which includes a lunch break from 12:30 PM to 1:30 PM 
[1]."},"finish_reason":"stop"}],"created":1740926279,"model":"qwen-long","object":"chat.completion","usage":
{"prompt_tokens":567,"completion_tokens":47,"total_tokens":614}}
```

### Ⅴ. Special notes for reviews



Linked issue #97: Kubernetes 在 低版本内核中，Gateway 创建报错
- [ ] I have searched the [issues](https://github.com/alibaba/higress/issues) of this repository and believe that this is not a duplicate.

### Ⅰ. Issue Description

gateway 创建报错。

### Ⅱ. Describe what happened

  If there is an exception, please attach the exception trace:

```console
helm upgrade --install higress -n higress-system  oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/charts/higress
```
<img width="1437" alt="image" src="https://user-images.githubusercontent.com/31346321/205502041-20fb420e-7d77-493f-8ed6-30b7f285fe41.png">


### Ⅲ. Describe what you expected to happen

这种情况建议通过增加 QA 来引导用户自己配置 securityContext 以及 containerSecurityContext

### Ⅳ. How to reproduce it (as minimally and precisely as possible)

1. xxx
5. xxx
6. xxx

### Ⅴ. Anything else we need to know?


### Ⅵ. Environment:
1. Kubernetes 1.23
2. OS: CentOS7
3. Linux Kernel: 3.10
Linked issue comments: user: login: iutx
created_at: 2022-12-05T05:51:53Z
body: > @iutx 可以帮忙更新下官方文档的部署说明吗，关于适配不同内核需要做的改动，包括这个 hostnetwork 的配置说明 在这里：https://github.com/higress-group/higress-group.github.io/blob/master/docs/zh-cn/ops/deploy-by-helm.md

好的，会尝试几个内核版本再去测试下。
author_association: CONTRIBUTOR
user: login: iutx
created_at: 2022-12-05T11:12:17Z
body: /assign
author_association: CONTRIBUTOR

Diff:
diff --git a/plugins/wasm-go/extensions/ai-search/README.md b/plugins/wasm-go/extensions/ai-search/README.md
index f7829d4902..b2b8bf15cc 100644
--- a/plugins/wasm-go/extensions/ai-search/README.md
+++ b/plugins/wasm-go/extensions/ai-search/README.md
@@ -71,12 +71,17 @@ description: higress 支持通过集成搜索引擎（Google/Bing/Arxiv/Elastics
 
 ## Elasticsearch 特定配置
 
-| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
-|------|----------|----------|--------|------|
+| 名称 | 数据类型 | 填写要求 | 默认值 | 描述                    |
+|------|----------|----------|--------|-----------------------|
 | index | string | 必填 | - | 要搜索的Elasticsearch索引名称 |
-| contentField | string | 必填 | - | 要查询的内容字段名称 |
-| linkField | string | 必填 | - | 结果链接字段名称 |
-| titleField | string | 必填 | - | 结果标题字段名称 |
+| contentField | string | 必填 | - | 要查询的内容字段名称            |
+| semanticTextField | string | 必填 | - | 要查询的 embedding 字段名称   | 
+| linkField | string | 必填 | - | 结果链接字段名称              |
+| titleField | string | 必填 | - | 结果标题字段名称              |
+| username | string | 选填 | - | Elasticsearch 用户名          |
+| password | string | 选填 | - | Elasticsearch 密码          |
+
+混合搜索中使用的 [Reciprocal Rank Fusion (RRF)](https://www.elastic.co/guide/en/elasticsearch/reference/8.17/rrf.html) 查询要求 Elasticsearch 的版本在 8.8 及以上。
 
 ## 配置示例
 
@@ -192,8 +197,11 @@ searchFrom:
   servicePort: 80
   index: "knowledge_base"
   contentField: "content"
+  semanticTextField: "semantic_text"
   linkField: "url" 
   titleField: "title"
+  # username: "elastic"
+  # password: "password"
 ```
 
 ### 自定义引用格式
diff --git a/plugins/wasm-go/extensions/ai-search/README_EN.md b/plugins/wasm-go/extensions/ai-search/README_EN.md
index 623c09a1dd..e2f7ec745e 100644
--- a/plugins/wasm-go/extensions/ai-search/README_EN.md
+++ b/plugins/wasm-go/extensions/ai-search/README_EN.md
@@ -71,12 +71,17 @@ It is strongly recommended to enable this feature when using Arxiv or Elasticsea
 
 ## Elasticsearch Specific Configuration
 
-| Name | Data Type | Requirement | Default Value | Description |
-|------|-----------|-------------|---------------|-------------|
-| index | string | Required | - | Elasticsearch index name to search |
-| contentField | string | Required | - | Content field name to query |
-| linkField | string | Required | - | Result link field name |
-| titleField | string | Required | - | Result title field name |
+| Name | Data Type | Requirement | Default Value | Description                        |
+|------|-----------|-------------|---------------|------------------------------------|
+| index | string | Required    | - | Elasticsearch index name to search |
+| contentField | string | Required    | - | Content field name to query        |
+| semanticTextField | string | Required    | - | Embedding field name to query      |
+| linkField | string | Required    | - | Result link field name             |
+| titleField | string | Required    | - | Result title field name            |
+| username | string | Optional          | - | Elasticsearch username             |
+| password | string | Optional    | - | Elasticsearch password             |
+
+The [Reciprocal Rank Fusion (RRF)](https://www.elastic.co/guide/en/elasticsearch/reference/8.17/rrf.html) query used in hybrid search requires Elasticsearch version 8.8 or higher.
 
 ## Configuration Examples
 
@@ -191,8 +196,11 @@ searchFrom:
   servicePort: 80
   index: "knowledge_base"
   contentField: "content"
+  semanticTextField: "semantic_text"
   linkField: "url" 
   titleField: "title"
+  # username: "elastic"
+  # password: "password"
 ```
 
 ### Custom Reference Format
diff --git a/plugins/wasm-go/extensions/ai-search/engine/elasticsearch/elasticsearch.go b/plugins/wasm-go/extensions/ai-search/engine/elasticsearch/elasticsearch.go
index 4290558c38..85fc1055f2 100644
--- a/plugins/wasm-go/extensions/ai-search/engine/elasticsearch/elasticsearch.go
+++ b/plugins/wasm-go/extensions/ai-search/engine/elasticsearch/elasticsearch.go
@@ -1,6 +1,7 @@
 package elasticsearch
 
 import (
+	"encoding/base64"
 	"errors"
 	"fmt"
 	"net/http"
@@ -16,11 +17,14 @@ type ElasticsearchSearch struct {
 	client             wrapper.HttpClient
 	index              string
 	contentField       string
+	semanticTextField  string
 	linkField          string
 	titleField         string
 	start              int
 	count              int
 	timeoutMillisecond uint32
+	username           string
+	password           string
 }
 
 func NewElasticsearchSearch(config *gjson.Result) (*ElasticsearchSearch, error) {
@@ -41,10 +45,15 @@ func NewElasticsearchSearch(config *gjson.Result) (*ElasticsearchSearch, error)
 	if engine.index == "" {
 		return nil, errors.New("index not found")
 	}
+
 	engine.contentField = config.Get("contentField").String()
 	if engine.contentField == "" {
 		return nil, errors.New("contentField not found")
 	}
+	engine.semanticTextField = config.Get("semanticTextField").String()
+	if engine.semanticTextField == "" {
+		return nil, errors.New("semanticTextField not found")
+	}
 	engine.linkField = config.Get("linkField").String()
 	if engine.linkField == "" {
 		return nil, errors.New("linkField not found")
@@ -62,36 +71,66 @@ func NewElasticsearchSearch(config *gjson.Result) (*ElasticsearchSearch, error)
 	if engine.count == 0 {
 		engine.count = 10
 	}
+
+	engine.username = config.Get("username").String()
+	engine.password = config.Get("password").String()
+
 	return engine, nil
 }
 
 func (e ElasticsearchSearch) NeedExectue(ctx engine.SearchContext) bool {
-	return ctx.EngineType == "private"
+	return ctx.EngineType == "private" || ctx.EngineType == ""
 }
 
 func (e ElasticsearchSearch) Client() wrapper.HttpClient {
 	return e.client
 }
 
-func (e ElasticsearchSearch) CallArgs(ctx engine.SearchContext) engine.CallArgs {
-	searchBody := fmt.Sprintf(`{
-		"query": {
-			"match": {
-				"%s": {
-					"query": "%s",
-					"operator": "AND"
-				}
+func (e ElasticsearchSearch) generateAuthorizationHeader() string {
+	return fmt.Sprintf(`Basic %s`, base64.StdEncoding.EncodeToString([]byte(e.username+":"+e.password)))
+}
+
+func (e ElasticsearchSearch) generateQueryBody(ctx engine.SearchContext) string {
+	queryText := strings.Join(ctx.Querys, " ")
+	return fmt.Sprintf(`{
+		"retriever": {
+			"rrf": {
+				"retrievers": [
+					{
+						"standard": { 
+							"query": {
+								"match": {
+									"%s": "%s" 
+								}
+							}
+						}
+					},
+					{
+						"standard": { 
+							"query": {
+								"semantic": {
+									"field": "%s", 
+									"query": "%s"
+								}
+							}
+						}
+					}
+				]
 			}
 		}
-	}`, e.contentField, strings.Join(ctx.Querys, " "))
+	}`, e.contentField, queryText, e.semanticTextField, queryText)
+}
 
+func (e ElasticsearchSearch) CallArgs(ctx engine.SearchContext) engine.CallArgs {
+	queryBody := e.generateQueryBody(ctx)
 	return engine.CallArgs{
 		Method: http.MethodPost,
 		Url:    fmt.Sprintf("/%s/_search?from=%d&size=%d", e.index, e.start, e.count),
 		Headers: [][2]string{
 			{"Content-Type", "application/json"},
+			{"Authorization", e.generateAuthorizationHeader()},
 		},
-		Body:               []byte(searchBody),
+		Body:               []byte(queryBody),
 		TimeoutMillisecond: e.timeoutMillisecond,
 	}
 }
diff --git a/plugins/wasm-go/extensions/ai-search/main.go b/plugins/wasm-go/extensions/ai-search/main.go
index 720e688ccc..3aa8c6606a 100644
--- a/plugins/wasm-go/extensions/ai-search/main.go
+++ b/plugins/wasm-go/extensions/ai-search/main.go
@@ -170,7 +170,7 @@ func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
 		case "quark":
 			searchEngine, err := quark.NewQuarkSearch(&e)
 			if err != nil {
-				return fmt.Errorf("elasticsearch search engine init failed:%s", err)
+				return fmt.Errorf("quark search engine init failed:%s", err)
 			}
 			config.engine = append(config.engine, searchEngine)
 			internetExists = true
diff --git a/plugins/wasm-go/extensions/ai-search/prompts/full.md b/plugins/wasm-go/extensions/ai-search/prompts/full.md
index aec605d1b8..8e537d47c9 100644
--- a/plugins/wasm-go/extensions/ai-search/prompts/full.md
+++ b/plugins/wasm-go/extensions/ai-search/prompts/full.md
@@ -25,7 +25,7 @@ none
 3. How: 分析对于要查询的知识和资料，应该提出什么样的问题
 4. Adjust: 明确要向什么地方查询什么问题后，按下面方式对问题进行调整
   4.1. 向搜索引擎提问：用一句话概括问题，并且针对搜索引擎做问题优化
-  4.2. 向私有知识库提问：将问题拆分成多组关键词的组合，同时组合中的关键词个数尽量不要超过3个
+  4.2. 向私有知识库提问：用一句话概括问题，私有知识库不需要对关键词进行拆分
   4.3. 向Arxiv论文资料库提问：
     4.3.1. 明确问题所属领域，然后确定Arxiv的Category值，Category可选的枚举如下:
       - cs.AI: Artificial Intelligence
@@ -207,10 +207,6 @@ cs.AI: attention mechanism
 cs.AI: neuron
 q-bio.NC: brain,attention mechanism
 
-#### 向私有知识库查询多次
-private: 电子钱包,密码
-private: 张三,身份证号
-
 #### 向多个查询目标查询多次
 internet: 中国未来房价趋势
 internet: 最新中国经济政策
diff --git a/plugins/wasm-go/extensions/ai-search/prompts/private.md b/plugins/wasm-go/extensions/ai-search/prompts/private.md
index 4ba0fc62ce..4e2f8c478b 100644
--- a/plugins/wasm-go/extensions/ai-search/prompts/private.md
+++ b/plugins/wasm-go/extensions/ai-search/prompts/private.md
@@ -25,7 +25,7 @@ none
 3. How: 分析对于要查询的知识和资料，应该提出什么样的问题
 4. Adjust: 明确要向什么地方查询什么问题后，按下面方式对问题进行调整
   4.1. 向搜索引擎提问：用一句话概括问题，并且针对搜索引擎做问题优化
-  4.2. 向私有知识库提问：将问题拆分成多组关键词的组合，同时组合中的关键词个数尽量不要超过3个
+  4.2. 向私有知识库提问：用一句话概括问题，私有知识库不需要对关键词进行拆分
 5. Final: 按照下面**回复内容示例**进行回复，注意:
   - 不要输出思考过程
   - 可以向多个查询目标分别查询多次，多个查询用换行分隔，总查询次数控制在5次以内
@@ -42,10 +42,6 @@ none
 internet: 黄金价格走势
 internet: The trend of gold prices
 
-#### 向私有知识库查询多次
-private: 电子钱包,密码
-private: 张三,身份证号
-
 #### 向多个查询目标查询多次
 internet: 中国未来房价趋势
 internet: 最新中国经济政策


Reviews:
user: login: cr7258
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T10:12:12Z

Review comments:
diff_hunk: @@ -44,6 +44,8 @@ description: higress 支持通过集成搜索引擎（Google/Bing/Arxiv/Elastics
 | llmModelName | string | 必填 | - | LLM模型名称 |
 | timeoutMillisecond | number | 选填 | 30000 | API调用超时时间（毫秒） |
 
+注意：Elasticsearch 必须配置重写配置，以便 LLM 能够判断是否需要查询私有知识库。
path: plugins/wasm-go/extensions/ai-search/README.md
user: login: cr7258
body: 已更新
created_at: 2025-03-03T10:12:12Z
author_association: MEMBER

PR comments:
user: login: codecov-commenter
created_at: 2025-03-02T14:58:43Z
body: ## [Codecov](https://app.codecov.io/gh/alibaba/higress/pull/1844?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 43.39%. Comparing base [(`ef31e09`)](https://app.codecov.io/gh/alibaba/higress/commit/ef31e0931063d7e54b72c043b77242bc593d96e9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba) to head [(`e60564a`)](https://app.codecov.io/gh/alibaba/higress/commit/e60564a6d0e9208684153d87e1627a87e4bbf64f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba).
> Report is 329 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/alibaba/higress/pull/1844/graphs/tree.svg?width=650&height=150&src=pr&token=3ZFKS5X7H2&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba)](https://app.codecov.io/gh/alibaba/higress/pull/1844?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba)

```diff
@@            Coverage Diff             @@
##             main    #1844      +/-   ##
==========================================
+ Coverage   35.91%   43.39%   +7.48%     
==========================================
  Files          69       76       +7     
  Lines       11576    12301     +725     
==========================================
+ Hits         4157     5338    +1181     
+ Misses       7104     6629     -475     
- Partials      315      334      +19     
```

[see 71 files with indirect coverage changes](https://app.codecov.io/gh/alibaba/higress/pull/1844/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba)

</details>
author_association: NONE
user: login: cr7258
created_at: 2025-03-03T10:12:39Z
body: > hybrid search是否对ES版本有要求，文档里可以注明一下

要求 ES 的版本在 8.8 以上，在文档中已注明。
author_association: MEMBER