Elasticsearch 开放推理 API 增加了对 Google 的 Gemini 模型的支持

作者:来自 Elastic Jonathan Buttner

了解如何使用 Elasticsearch 开放推理 API 结合 Google 的 Gemini 模型进行内容生成、问答和摘要。

Elasticsearch 具有与行业领先的生成式 AI 工具和提供商的原生集成。查看我们的网络研讨会,了解如何超越 RAG 基础,或使用 Elastic 向量数据库构建可投入生产的应用

为了为你的用例构建最佳搜索解决方案,现在可以开始免费云试用或在本地机器上尝试 Elastic。


我们很高兴地宣布 Elasticsearch 开放 inference API 的最新扩展:通过 Google Cloud 的 Vertex AI 平台集成 Google 的 Gemini 模型!

Elasticsearch 开放推理 chat completion API 提供了一种标准化、功能丰富且熟悉的方式来访问大型语言模型。借助此功能,Elasticsearch 开发者可以使用大型语言模型(例如 Gemini 2.5 Pro)来构建生成式 AI 应用,包括问答、摘要和文本生成等用例。

现在,他们可以使用自己的 Google Cloud 帐号执行聊天补全任务、存储摘要、在 ES|QL 或直接通过推理 API 使用 Google 的 Gemini 模型,并利用 Elasticsearch 全面的 AI 搜索工具和经过验证的向量数据库能力。

使用 Google 的 Gemini 模型回答问题并生成工具调用

在这篇博客中,我们将演示如何使用 Google 的 Gemini 模型,通过 ES|QL 回答关于存储在 Elasticsearch 中的数据的问题。在开始与 Elasticsearch 交互之前,请确保你拥有一个 Google Cloud Vertex AI 帐号以及必要的密钥。我们将在配置 inference 端点时使用它们。

创建 Vertex AI 服务帐号并获取密钥

  • 前往 console.cloud.google.com/iam-admin/s...

  • 点击 "Create service account" 按钮。

    • 输入一个适合你的名称。
    • 点击 "Create and continue"。
    • 为其授予 Vertex AI User 角色。
    • 点击 "Add another role",然后授予 "Service account token creator" 角色。此角色用于允许服务帐号生成所需的访问令牌。
    • 点击 "Done"。
  • 前往 console.cloud.google.com/iam-admin/s... 并点击刚创建的服务帐号。

  • 进入 "keys" 标签页,点击 "Add key -> Create new key -> JSON -> Create"。

    • 如果你收到错误信息 "Service account key creation is disabled",则需要管理员修改组织策略 iam.disableServiceAccountKeyCreation 或授予例外。
  • 服务帐号密钥应会自动下载。

现在我们已经下载了服务帐号密钥,在使用它创建推理端点之前,需要先对其进行格式化。以下 sed 命令将会转义引号并去除换行符。

swift 复制代码
`sed 's/"/\\"/g' <path to your service account file> | tr -d "\n"` AI写代码

设置推理端点并与 Google 的 Gemini 模型交互

我们将使用 Kibana 的 Console 在 Elasticsearch 中执行以下步骤,而无需设置 IDE。

首先,我们配置一个推理端点,用于与 Gemini 交互:

bash 复制代码
`

1.  PUT _inference/chat_completion/gemini_chat_completion
2.  {
3.    "service": "googlevertexai",
4.    "service_settings": {
5.      "service_account_json": "<output string from the sed command>",
6.      "model_id": "gemini-2.5-pro",
7.      "location": "us-central1",
8.      "project_id": "<project id>"
9.    }
10.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

在成功创建推理端点后,我们会收到如下类似的响应,状态码为 200 OK:

bash 复制代码
`

1.  {
2.      "inference_id": "gemini_chat_completion",
3.      "task_type": "chat_completion",
4.      "service": "googlevertexai",
5.      "service_settings": {
6.          "project_id": "<project id>",
7.          "location": "us-central1",
8.          "model_id": "gemini-2.5-pro",
9.          "rate_limit": {
10.              "requests_per_minute": 1000
11.          }
12.      }
13.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

现在我们可以调用已配置的端点,对任意文本输入执行聊天补全,并实时接收响应。让我们请 Gemini 简要介绍一下 ES|QL:

bash 复制代码
`

1.  POST _inference/chat_completion/gemini_chat_completion/_stream
2.  {
3.      "messages": [
4.          {
5.              "role": "user",
6.              "content": "What is a short one line description of Elastic ES|QL?"
7.          }
8.      ]
9.  }

`AI写代码

我们应该会收到一个状态码为 200 OK 的流式响应,其中包含对 ES|QL 的简短描述:

css 复制代码
`

1.  event: message
2.  data: {"id":"VvZ3aOSwMpu3nvgPoLfSgQU","choices":[{"delta":{"content":"ES|QL is a powerful, piped query language used","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

4.  event: message
5.  data: {"id":"VvZ3aOSwMpu3nvgPoLfSgQU","choices":[{"delta":{"content":" to search, transform, and aggregate data in Elasticsearch in a single, sequential query.","role":"model"},"finish_reason":"STOP","index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk","usage":{"completion_tokens":28,"prompt_tokens":13,"total_tokens":1469}}

7.  event: message
8.  data: [DONE]

`AI写代码

推理聊天补全 API 支持工具调用,使我们能够以可靠的方式与托管在 Vertex AI 平台上的 Google Gemini 模型交互,从而获取关于我们数据的信息。

我们将让 Gemini 编写一个 ES|QL 查询,用于检索在 response 字段中包含失败状态码的文档,并对结果进行总结。由于 response 字段可能没有映射为整数类型,因此我们需要让 Gemini 获取映射信息,并相应地调整查询。

我们假设已经实现了一个 Gemini 可以交互的工具。为了演示,我们将在 Dev Console 中自行执行该工具调用。

初始请求:

bash 复制代码
`

1.  POST _inference/chat_completion/gemini_chat_completion/_stream
2.  {
3.      "messages": [
4.          {
5.             "role": "user",
6.              "content": "You are an expert in ES|QL, the query language for Elasticsearch. I have added the sample data from https://www.elastic.co/docs/manage-data/ingest/sample-data. The index is called kibana_sample_data_logs. Can you retrieve the mapping, generate and execute an ES|QL query to retrieve the most recent two web logs with a failure status code for the response field? To retrieve the most recent logs please use SORT on the @timestamp field. After retrieving the logs, please summarize the results in a concise manner. The response field may not be mapped as an integer, so be sure to handle it correctly."
7.          }
8.      ],
9.      "tools": [
10.          {
11.              "type": "function",
12.              "function": {
13.                  "name": "retrieve_index_mapping",
14.                  "description": "Retrieves the mapping for an Elasticsearch index.",
15.                  "parameters": {
16.                      "type": "object",
17.                      "properties": {
18.                          "index_name": {
19.                              "type": "string",
20.                              "description": "The name of the index to retrieve the mapping for."
21.                          }
22.                      },
23.                      "required": [
24.                          "index_name"
25.                      ]
26.                  }
27.              }
28.          },
29.          {
30.              "type": "function",
31.              "function": {
32.                  "name": "execute_esql_query",
33.                  "description": "Executes an ES|QL query against an Elasticsearch index.",
34.                  "parameters": {
35.                      "type": "object",
36.                      "properties": {
37.                          "query": {
38.                              "type": "string",
39.                              "description": "The ES|QL query to execute."
40.                          }
41.                      },
42.                      "required": [
43.                          "query"
44.                      ]
45.                  }
46.              }
47.          }
48.      ],
49.      "tool_choice": "auto",
50.      "temperature": 0.7
51.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)收起代码块![](https://csdnimg.cn/release/blogv2/dist/pc/img/arrowup-line-top-White.png)

Gemini 响应了一个工具调用,用于获取索引 kibana_sample_data_logs 的映射信息:

swift 复制代码
`

1.  event: message
2.  data: {"id":"agZ4aP6OGZapnvgPtbCBkQU","choices":[{"delta":{"role":"model","tool_calls":[{"index":0,"id":"retrieve_index_mapping","function":{"arguments":"{\"index_name\":\"kibana_sample_data_logs\"}","name":"retrieve_index_mapping"},"type":"function"}]},"finish_reason":"STOP","index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk","usage":{"completion_tokens":16,"prompt_tokens":182,"total_tokens":663}}

4.  event: message
5.  data: [DONE]

`AI写代码

为了模拟工具调用,我们将使用以下命令获取映射信息:

bash 复制代码
`GET kibana_sample_data_logs/_mapping`AI写代码

为了完成工具调用的模拟,我们将在下一次向 Gemini 的请求中包含该响应:

包含映射结果的请求:

swift 复制代码
`

1.  POST _inference/chat_completion/gemini_chat_completion/_stream
2.  {
3.      "messages": [
4.          {
5.              "role": "user",
6.              "content": "You are an expert in ES|QL, the query language for Elasticsearch. I have added the sample data from https://www.elastic.co/docs/manage-data/ingest/sample-data. The index is called kibana_sample_data_logs. Can you retrieve the mapping, generate and execute an ES|QL query to retrieve the most recent two web logs with a failure status code for the response field? To retrieve the most recent logs please use SORT on the @timestamp field. After retrieving the logs, please summarize the results in a concise manner. The response field may not be mapped as an integer, so be sure to handle it correctly."
7.          },
8.          {
9.              "role": "assistant",
10.              "content": "",
11.              "tool_calls": [
12.                  {
13.                      "function": {
14.                          "name": "retrieve_index_mapping",
15.                          "arguments": "{\"index_name\":\"kibana_sample_data_logs\"}"
16.                      },
17.                      "id": "1",
18.                      "type": "function"
19.                  }
20.              ]
21.          },
22.          {
23.              "role": "tool",
24.              "content": "{\".ds-kibana_sample_data_logs-2025.07.16-000001\":{\"mappings\":{\"_data_stream_timestamp\":{\"enabled\":true},\"properties\":{\"@timestamp\":{\"type\":\"date\"},\"agent\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"bytes\":{\"type\":\"long\"},\"bytes_counter\":{\"type\":\"long\",\"time_series_metric\":\"counter\"},\"bytes_gauge\":{\"type\":\"long\",\"time_series_metric\":\"gauge\"},\"clientip\":{\"type\":\"ip\"},\"event\":{\"properties\":{\"dataset\":{\"type\":\"keyword\"}}},\"extension\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"geo\":{\"properties\":{\"coordinates\":{\"type\":\"geo_point\"},\"dest\":{\"type\":\"keyword\"},\"src\":{\"type\":\"keyword\"},\"srcdest\":{\"type\":\"keyword\"}}},\"host\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"index\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ip\":{\"type\":\"ip\"},\"ip_range\":{\"type\":\"ip_range\"},\"machine\":{\"properties\":{\"os\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ram\":{\"type\":\"long\"}}},\"memory\":{\"type\":\"double\"},\"message\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"phpmemory\":{\"type\":\"long\"},\"referer\":{\"type\":\"keyword\"},\"request\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"time_series_dimension\":true}}},\"response\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"tags\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"timestamp\":{\"type\":\"alias\",\"path\":\"@timestamp\"},\"timestamp_range\":{\"type\":\"date_range\"},\"url\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"utc_time\":{\"type\":\"date\"}}}}}",
25.              "tool_call_id": "1"
26.          }
27.      ],
28.      "tools": [
29.          {
30.              "type": "function",
31.              "function": {
32.                  "name": "retrieve_index_mapping",
33.                  "description": "Retrieves the mapping for an Elasticsearch index.",
34.                  "parameters": {
35.                      "type": "object",
36.                      "properties": {
37.                          "index_name": {
38.                              "type": "string",
39.                              "description": "The name of the index to retrieve the mapping for."
40.                          }
41.                      },
42.                      "required": [
43.                          "index_name"
44.                      ]
45.                  }
46.              }
47.          },
48.          {
49.              "type": "function",
50.              "function": {
51.                  "name": "execute_esql_query",
52.                  "description": "Executes an ES|QL query against an Elasticsearch index.",
53.                  "parameters": {
54.                      "type": "object",
55.                      "properties": {
56.                          "query": {
57.                              "type": "string",
58.                              "description": "The ES|QL query to execute."
59.                          }
60.                      },
61.                      "required": [
62.                          "query"
63.                      ]
64.                  }
65.              }
66.          }
67.      ],
68.      "tool_choice": "auto",
69.      "temperature": 0.7
70.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)收起代码块![](https://csdnimg.cn/release/blogv2/dist/pc/img/arrowup-line-top-White.png)

现在 Gemini 已经获得了映射信息,它响应了一个工具调用,用于执行查询以检索最近的两条 Web 日志:

vbnet 复制代码
`

1.  event: message
2.  data: {"id":"3AZ4aMfXLJapnvgPtbCBkQU","choices":[{"delta":{"content":"\n","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

4.  event: message
5.  data: {"id":"3AZ4aMfXLJapnvgPtbCBkQU","choices":[{"delta":{"role":"model","tool_calls":[{"index":0,"id":"execute_esql_query","function":{"arguments":"{\"query\":\"FROM kibana_sample_data_logs WHERE TO_INT(response) >= 400 ORDER BY \\\"@timestamp\\\" DESC LIMIT 2\"}","name":"execute_esql_query"},"type":"function"}]},"finish_reason":"STOP","index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk","usage":{"completion_tokens":38,"prompt_tokens":665,"total_tokens":1652}}

7.  event: message
8.  data: [DONE]

`AI写代码

仔细观察,你会发现不同子句前缺少管道字符。我们假设的工具调用将模拟 Kibana 并返回一个错误,从而让我们看到 Gemini 如何处理该问题。Kibana 的 ES|QL 查询控制台会返回如下错误:

arduino 复制代码
`[esql] > Couldn't parse Elasticsearch ES|QL query. Check your query and try again. Error: line 1:30: mismatched input 'WHERE' expecting {<EOF>, '|', ',', 'metadata'}`AI写代码

让我们将其包含在 execute_esql_query 的响应中。

包含错误的请求:

swift 复制代码
`

1.  POST _inference/chat_completion/gemini_chat_completion/_stream
2.  {
3.      "messages": [
4.          {
5.              "role": "user",
6.              "content": "You are an expert in ES|QL, the query language for Elasticsearch. I have added the sample data from https://www.elastic.co/docs/manage-data/ingest/sample-data. The index is called kibana_sample_data_logs. Can you retrieve the mapping, generate and execute an ES|QL query to retrieve the most recent two web logs with a failure status code for the response field? To retrieve the most recent logs please use SORT on the @timestamp field. After retrieving the logs, please summarize the results in a concise manner. The response field may not be mapped as an integer, so be sure to handle it correctly."
7.          },
8.          {
9.              "role": "assistant",
10.              "content": "",
11.              "tool_calls": [
12.                  {
13.                      "function": {
14.                          "name": "retrieve_index_mapping",
15.                          "arguments": "{\"index_name\":\"kibana_sample_data_logs\"}"
16.                      },
17.                      "id": "1",
18.                      "type": "function"
19.                  }
20.              ]
21.          },
22.          {
23.              "role": "tool",
24.              "content": "{\".ds-kibana_sample_data_logs-2025.07.16-000001\":{\"mappings\":{\"_data_stream_timestamp\":{\"enabled\":true},\"properties\":{\"@timestamp\":{\"type\":\"date\"},\"agent\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"bytes\":{\"type\":\"long\"},\"bytes_counter\":{\"type\":\"long\",\"time_series_metric\":\"counter\"},\"bytes_gauge\":{\"type\":\"long\",\"time_series_metric\":\"gauge\"},\"clientip\":{\"type\":\"ip\"},\"event\":{\"properties\":{\"dataset\":{\"type\":\"keyword\"}}},\"extension\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"geo\":{\"properties\":{\"coordinates\":{\"type\":\"geo_point\"},\"dest\":{\"type\":\"keyword\"},\"src\":{\"type\":\"keyword\"},\"srcdest\":{\"type\":\"keyword\"}}},\"host\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"index\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ip\":{\"type\":\"ip\"},\"ip_range\":{\"type\":\"ip_range\"},\"machine\":{\"properties\":{\"os\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ram\":{\"type\":\"long\"}}},\"memory\":{\"type\":\"double\"},\"message\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"phpmemory\":{\"type\":\"long\"},\"referer\":{\"type\":\"keyword\"},\"request\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"time_series_dimension\":true}}},\"response\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"tags\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"timestamp\":{\"type\":\"alias\",\"path\":\"@timestamp\"},\"timestamp_range\":{\"type\":\"date_range\"},\"url\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"utc_time\":{\"type\":\"date\"}}}}}",
25.              "tool_call_id": "1"
26.          },
27.          {
28.              "role": "assistant",
29.              "content": "",
30.              "tool_calls": [
31.                  {
32.                      "function": {
33.                          "name": "execute_esql_query",
34.                          "arguments": "{\"query\":\"FROM kibana_sample_data_logs WHERE TO_INT(response) >= 400 ORDER BY \\\"@timestamp\\\" DESC LIMIT 2\"}"
35.                      },
36.                      "id": "2",
37.                      "type": "function"
38.                  }
39.              ]
40.          },
41.          {
42.              "role": "tool",
43.              "content": "[esql] > Couldn't parse Elasticsearch ES|QL query. Check your query and try again. Error: line 1:30: mismatched input 'WHERE' expecting {<EOF>, '|', ',', 'metadata'}",
44.              "tool_call_id": "2"
45.          }
46.      ],
47.      "tools": [
48.          {
49.              "type": "function",
50.              "function": {
51.                  "name": "retrieve_index_mapping",
52.                  "description": "Retrieves the mapping for an Elasticsearch index.",
53.                  "parameters": {
54.                      "type": "object",
55.                      "properties": {
56.                          "index_name": {
57.                              "type": "string",
58.                              "description": "The name of the index to retrieve the mapping for."
59.                          }
60.                      },
61.                      "required": [
62.                          "index_name"
63.                      ]
64.                  }
65.              }
66.          },
67.          {
68.              "type": "function",
69.              "function": {
70.                  "name": "execute_esql_query",
71.                  "description": "Executes an ES|QL query against an Elasticsearch index.",
72.                  "parameters": {
73.                      "type": "object",
74.                      "properties": {
75.                          "query": {
76.                              "type": "string",
77.                              "description": "The ES|QL query to execute."
78.                          }
79.                      },
80.                      "required": [
81.                          "query"
82.                      ]
83.                  }
84.              }
85.          }
86.      ],
87.      "tool_choice": "auto",
88.      "temperature": 0.7
89.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)收起代码块![](https://csdnimg.cn/release/blogv2/dist/pc/img/arrowup-line-top-White.png)

Gemini 能够识别错误并生成正确的查询:

vbnet 复制代码
``

1.  event: message
2.  data: {"id":"7gh4aPLAMvzcnvgP1q6EgQw","choices":[{"delta":{"content":"\n","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

4.  event: message
5.  data: {"id":"7gh4aPLAMvzcnvgP1q6EgQw","choices":[{"delta":{"content":"I","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

7.  event: message
8.  data: {"id":"7gh4aPLAMvzcnvgP1q6EgQw","choices":[{"delta":{"content":"'m sorry, it seems I made a mistake in the previous ES|QL query. Let me correct it and try again. The different parts of the query should be separated by a pipe `|` character.","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

10.  event: message
11.  data: {"id":"7gh4aPLAMvzcnvgP1q6EgQw","choices":[{"delta":{"role":"model","tool_calls":[{"index":0,"id":"execute_esql_query","function":{"arguments":"{\"query\":\"FROM kibana_sample_data_logs | WHERE TO_INT(response) >= 400 | ORDER BY \\\"@timestamp\\\" DESC | LIMIT 2\"}","name":"execute_esql_query"},"type":"function"}]},"finish_reason":"STOP","index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk","usage":{"completion_tokens":85,"prompt_tokens":750,"total_tokens":1620}}

13.  event: message
14.  data: [DONE]

``AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

我们的下一条消息将包含在 Kibana 中执行正确格式查询的结果。要检索文档,我们可以直接在 Discover 中执行查询并复制文档内容,或者在 Dev Tools 中执行查询。假设我们的工具实现使用了 Discover,并检索了文档的完整源数据。

包含文档结果的请求:

swift 复制代码
`

1.  POST _inference/chat_completion/gemini_chat_completion/_stream
2.  {
3.      "messages": [
4.          {
5.              "role": "user",
6.              "content": "You are an expert in ES|QL, the query language for Elasticsearch. I have added the sample data from https://www.elastic.co/docs/manage-data/ingest/sample-data. The index is called kibana_sample_data_logs. Can you retrieve the mapping, generate and execute an ES|QL query to retrieve the most recent two web logs with a failure status code for the response field? To retrieve the most recent logs please use SORT on the @timestamp field. After retrieving the logs, please summarize the results in a concise manner. The response field may not be mapped as an integer, so be sure to handle it correctly."
7.          },
8.          {
9.              "role": "assistant",
10.              "content": "",
11.              "tool_calls": [
12.                  {
13.                      "function": {
14.                          "name": "retrieve_index_mapping",
15.                          "arguments": "{\"index_name\":\"kibana_sample_data_logs\"}"
16.                      },
17.                      "id": "1",
18.                      "type": "function"
19.                  }
20.              ]
21.          },
22.          {
23.              "role": "tool",
24.              "content": "{\".ds-kibana_sample_data_logs-2025.07.16-000001\":{\"mappings\":{\"_data_stream_timestamp\":{\"enabled\":true},\"properties\":{\"@timestamp\":{\"type\":\"date\"},\"agent\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"bytes\":{\"type\":\"long\"},\"bytes_counter\":{\"type\":\"long\",\"time_series_metric\":\"counter\"},\"bytes_gauge\":{\"type\":\"long\",\"time_series_metric\":\"gauge\"},\"clientip\":{\"type\":\"ip\"},\"event\":{\"properties\":{\"dataset\":{\"type\":\"keyword\"}}},\"extension\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"geo\":{\"properties\":{\"coordinates\":{\"type\":\"geo_point\"},\"dest\":{\"type\":\"keyword\"},\"src\":{\"type\":\"keyword\"},\"srcdest\":{\"type\":\"keyword\"}}},\"host\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"index\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ip\":{\"type\":\"ip\"},\"ip_range\":{\"type\":\"ip_range\"},\"machine\":{\"properties\":{\"os\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"ram\":{\"type\":\"long\"}}},\"memory\":{\"type\":\"double\"},\"message\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"phpmemory\":{\"type\":\"long\"},\"referer\":{\"type\":\"keyword\"},\"request\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"time_series_dimension\":true}}},\"response\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"tags\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"timestamp\":{\"type\":\"alias\",\"path\":\"@timestamp\"},\"timestamp_range\":{\"type\":\"date_range\"},\"url\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}},\"utc_time\":{\"type\":\"date\"}}}}}",
25.              "tool_call_id": "1"
26.          },
27.          {
28.              "role": "assistant",
29.              "content": "",
30.              "tool_calls": [
31.                  {
32.                      "function": {
33.                          "name": "execute_esql_query",
34.                          "arguments": "{\"query\":\"FROM kibana_sample_data_logs WHERE TO_INT(response) >= 400 SORT \\\"@timestamp\\\" DESC LIMIT 2\"}"
35.                      },
36.                      "id": "2",
37.                      "type": "function"
38.                  }
39.              ]
40.          },
41.          {
42.              "role": "tool",
43.              "content": "[esql] > Couldn't parse Elasticsearch ES|QL query. Check your query and try again. Error: line 1:30: mismatched input 'WHERE' expecting {<EOF>, '|', ',', 'metadata'}",
44.              "tool_call_id": "2"
45.          },
46.          {
47.              "role": "assistant",
48.              "content": "",
49.              "tool_calls": [
50.                  {
51.                      "function": {
52.                          "name": "execute_esql_query",
53.                          "arguments": "{\"query\":\"FROM kibana_sample_data_logs | WHERE TO_INT(response) >= 400 | SORT \\\"@timestamp\\\" DESC | LIMIT 2\"}"
54.                      },
55.                      "id": "3",
56.                      "type": "function"
57.                  }
58.              ]
59.          },
60.          {
61.              "role": "tool",
62.              "content": "[{\"@timestamp\":\"2025-07-06T03:30:25.131Z\",\"agent\":\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\",\"agent.keyword\":\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\",\"bytes\":0,\"bytes_counter\":13069,\"bytes_gauge\":0,\"clientip\":\"120.49.143.213\",\"event.dataset\":\"sample_web_logs\",\"extension\":\"css\",\"extension.keyword\":\"css\",\"geo.coordinates\":\"POINT (-78.18499861 36.96015)\",\"geo.dest\":\"DE\",\"geo.src\":\"US\",\"geo.srcdest\":\"US:DE\",\"host\":\"cdn.elastic-elastic-elastic.org\",\"host.keyword\":\"cdn.elastic-elastic-elastic.org\",\"index\":\"kibana_sample_data_logs\",\"index.keyword\":\"kibana_sample_data_logs\",\"ip\":\"120.49.143.213\",\"machine.os\":\"ios\",\"machine.os.keyword\":\"ios\",\"machine.ram\":20401094656,\"message\":\"120.49.143.213 - - [2018-07-22T03:30:25.131Z] \\\"GET /styles/main.css_1 HTTP/1.1\\\" 503 0 \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\\\"\",\"message.keyword\":\"120.49.143.213 - - [2018-07-22T03:30:25.131Z] \\\"GET /styles/main.css_1 HTTP/1.1\\\" 503 0 \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\\\"\",\"referer\":\"http://twitter.com/success/konstantin-feoktistov\",\"request\":\"/styles/main.css\",\"request.keyword\":\"/styles/main.css\",\"response\":\"503\",\"response.keyword\":\"503\",\"tags\":[\"success\",\"login\"],\"tags.keyword\":[\"login\",\"success\"],\"timestamp\":\"2025-07-06T03:30:25.131Z\",\"url\":\"https://cdn.elastic-elastic-elastic.org/styles/main.css_1\",\"url.keyword\":\"https://cdn.elastic-elastic-elastic.org/styles/main.css_1\",\"utc_time\":\"2025-07-06T03:30:25.131Z\"},{\"@timestamp\":\"2025-07-06T03:49:40.669Z\",\"agent\":\"Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1\",\"agent.keyword\":\"Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1\",\"bytes\":0,\"bytes_counter\":29674,\"bytes_gauge\":0,\"clientip\":\"106.225.58.146\",\"event.dataset\":\"sample_web_logs\",\"extension\":\"\",\"extension.keyword\":\"\",\"geo.coordinates\":\"POINT (-85.80931806 35.98531194)\",\"geo.dest\":\"CN\",\"geo.src\":\"US\",\"geo.srcdest\":\"US:CN\",\"host\":\"www.elastic.co\",\"host.keyword\":\"www.elastic.co\",\"index\":\"kibana_sample_data_logs\",\"index.keyword\":\"kibana_sample_data_logs\",\"ip\":\"106.225.58.146\",\"machine.os\":\"win 7\",\"machine.os.keyword\":\"win 7\",\"machine.ram\":17179869184,\"message\":\"106.225.58.146 - - [2018-07-22T03:49:40.669Z] \\\"GET /apm_1 HTTP/1.1\\\" 503 0 \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1\\\"\",\"message.keyword\":\"106.225.58.146 - - [2018-07-22T03:49:40.669Z] \\\"GET /apm_1 HTTP/1.1\\\" 503 0 \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1\\\"\",\"referer\":\"http://www.elastic-elastic-elastic.com/success/richard-o-covey\",\"request\":\"/apm\",\"request.keyword\":\"/apm\",\"response\":\"503\",\"response.keyword\":\"503\",\"tags\":[\"success\",\"security\"],\"tags.keyword\":[\"security\",\"success\"],\"timestamp\":\"2025-07-06T03:49:40.669Z\",\"url\":\"https://www.elastic.co/downloads/apm_1\",\"url.keyword\":\"https://www.elastic.co/downloads/apm_1\",\"utc_time\":\"2025-07-06T03:49:40.669Z\"}]",
63.              "tool_call_id": "3"
64.          }
65.      ],
66.      "tools": [
67.          {
68.              "type": "function",
69.              "function": {
70.                  "name": "retrieve_index_mapping",
71.                  "description": "Retrieves the mapping for an Elasticsearch index.",
72.                  "parameters": {
73.                      "type": "object",
74.                      "properties": {
75.                          "index_name": {
76.                              "type": "string",
77.                              "description": "The name of the index to retrieve the mapping for."
78.                          }
79.                      },
80.                      "required": [
81.                          "index_name"
82.                      ]
83.                  }
84.              }
85.          },
86.          {
87.              "type": "function",
88.              "function": {
89.                  "name": "execute_esql_query",
90.                  "description": "Executes an ES|QL query against an Elasticsearch index.",
91.                  "parameters": {
92.                      "type": "object",
93.                      "properties": {
94.                          "query": {
95.                              "type": "string",
96.                              "description": "The ES|QL query to execute."
97.                          }
98.                      },
99.                      "required": [
100.                          "query"
101.                      ]
102.                  }
103.              }
104.          }
105.      ],
106.      "tool_choice": "auto",
107.      "temperature": 0.7
108.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)收起代码块![](https://csdnimg.cn/release/blogv2/dist/pc/img/arrowup-line-top-White.png)

Gemini 的最后一次响应重申了对话迄今为止的内容,并总结了检索到的两条文档,就像我们要求的那样:

以下是总结结果的响应:

swift 复制代码
``

1.  event: message
2.  data: {"id":"5Qx4aKerCZj3ld8PnuzrqAQ","choices":[{"delta":{"content":"\nthought\n1. **Search**: The user wants to query Elasticsearch using ES|QL. The available tool `execute_esql_query` is a perfect match for this.\n\n2. **Parameters**: The `execute_esql_query` tool requires a `query` string.\n\n3. **Query Generation**: The user wants to \"retrieve the most recent two web logs with a failure status code for the response field\". They also mention that the `response` field might not be an integer and that we should use `SORT` on the `@timestamp` field.\n\n   - **FROM clause**: The index is specified as `kibana_sample_data_logs`. So, `FROM kibana_sample_data_logs`.\n   - **WHERE clause**: A \"failure status code\" is typically a 4xx or 5xx code. The user also mentions the `response` field might not be an integer. The `TO_INT()` function in ES|QL can be used to cast the `response` field to an integer for numerical comparison. So, the condition will be `TO_INT(response) >= 400`.\n   - **SORT clause**: The user wants the \"most recent\" logs and explicitly asks to \"use SORT on the @timestamp field\". To get the most recent, we need to sort in descending order. So, `SORT \"@timestamp\" DESC`.\n   - **LIMIT clause**: The user wants the \"most recent two\" logs. So, `LIMIT 2`.\n\n4. **Initial Query Construction**: Putting it all together, the initial query is `FROM kibana_sample_data_logs WHERE TO_INT(response) >= 400 SORT \"@timestamp\" DESC LIMIT 2`.\n\n5. **Tool Call**: Call the `execute_esql_query` tool with the generated query.\n\n6. **Execution Analysis**: The first tool call `print(default_api.execute_esql_query(query = \"FROM kibana_sample_data_logs WHERE TO_INT(response) >= 400 SORT \\\"@timestamp\\\" DESC LIMIT 2\"))` fails with a parsing error: `mismatched input 'WHERE' expecting {<EOF>, '|', ',', 'metadata'}`.\n\n7. **Correction**: The error message indicates that the `WHERE` keyword is not expected after the `FROM` clause. In ES|QL, pipes (`|`) are used to separate different stages of the query. The correct syntax is to pipe the output of the `FROM` clause to the `WHERE` clause.\n\n8. **Corrected Query Construction**: The corrected query is `FROM kibana_sample_data_logs | WHERE TO_INT(response) >= 400 | SORT \"@timestamp\" DESC | LIMIT 2`.\n\n9. **Second Tool Call**: Call the `execute_esql_query` tool with the corrected query.\n\n10. **Execution Analysis**: The second tool call is successful and returns a JSON object with two log entries.\n\n11. **Summarization**: The user wants the results to be summarized \"in a concise manner\". I need to extract the key information from the returned JSON and present it to the user.\n\n    - The first log has a timestamp of `2025-07-06T03:30:25.131Z`, a response code of `503`, a client IP of `120.49.143.213`, and the request was for `/styles/main.css`.\n    - The second log has a timestamp of `2025-07-06T03:49:40.669Z`, a response code of `503`, a client IP of `106.225.58.146`, and the request was for `/apm`.\n\n12. **Final Response**: Combine the corrected action and the summary into a final response for the user. Acknowledge the initial error and explain the correction. Then, present the summarized results.","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

4.  event: message
5.  data: {"id":"5Qx4aKerCZj3ld8PnuzrqAQ","choices":[{"delta":{"content":"I have retrieved the mapping for the `kibana_sample_data_logs` index and executed an ES|QL query to retrieve the two most recent web logs with a failure status code.\n\nI initially made a mistake in the ES|QL syntax,","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

7.  event: message
8.  data: {"id":"5Qx4aKerCZj3ld8PnuzrqAQ","choices":[{"delta":{"content":" but I have corrected it and successfully retrieved the data.\n\nHere is a summary of the results:\n\nThe most recent web log with a failure status code has a timestamp of **2025-07-06T03:49:40.669Z**, a response code of","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

10.  event: message
11.  data: {"id":"5Qx4aKerCZj3ld8PnuzrqAQ","choices":[{"delta":{"content":" **503**, a client IP of **106.225.58.146** and the request was for **/apm**.\n\nThe second most recent web log with a failure status code has a timestamp of **2025-07-06T03:30:","role":"model"},"index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk"}

13.  event: message
14.  data: {"id":"5Qx4aKerCZj3ld8PnuzrqAQ","choices":[{"delta":{"content":"25.131Z**, a response code of **503**, a client IP of **120.49.143.213** and the request was for **/styles/main.css**.","role":"model"},"finish_reason":"STOP","index":0}],"model":"gemini-2.5-pro","object":"chat.completion.chunk","usage":{"completion_tokens":1099,"prompt_tokens":2287,"total_tokens":3386}}

16.  event: message
17.  data: [DONE]

``AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

使用 ES|QL 的 Completion 命令总结结果

在之前的工作流中,我们使用了 Google 的 Gemini 模型来总结两条失败的 Web 日志。我们也可以利用 ES|QL 的 Completion 命令的能力,直接在 Kibana 中总结结果。首先,我们需要创建一个使用 completion 任务类型的新推理端点。

bash 复制代码
`

1.  PUT _inference/completion/gemini_completion
2.  {
3.    "service": "googlevertexai",
4.    "service_settings": {
5.      "service_account_json": "<output string from the sed command>",
6.      "model_id": "gemini-2.5-flash-lite",
7.      "location": "us-central1",
8.      "project_id": "<project id>"
9.    }
10.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

同样,在成功创建推理端点后,我们会收到如下类似的响应,状态码为 200 OK:

bash 复制代码
`

1.  {
2.      "inference_id": "gemini_completion",
3.      "task_type": "completion",
4.      "service": "googlevertexai",
5.      "service_settings": {
6.          "project_id": "<project id>",
7.          "location": "us-central1",
8.          "model_id": "gemini-2.5-flash-lite",
9.          "rate_limit": {
10.              "requests_per_minute": 1000
11.          }
12.      }
13.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

现在我们可以执行 Gemini 为我们生成的 ES|QL 查询的修改版本。

sql 复制代码
`FROM kibana_sample_data_logs | WHERE TO_INT(response) >= 400 | SORT "@timestamp" DESC | LIMIT 2 | EVAL prompt = CONCAT("Please summarize this result: ", MV_CONCAT(message.keyword, referer)) | COMPLETION prompt WITH gemini_completion | KEEP prompt, completion`AI写代码

对查询的修改如下:

  • EVAL prompt ------ 该命令创建一个字段,并将其与文本一起发送给 Gemini,以提供特定字段并指示如何处理这些字段。在我们的示例中,它会要求 Gemini 总结 message 和 referer 字段的内容

  • COMPLETION ------ 该命令会将每个结果传递给 LLM,以执行我们指示它完成的任务

  • WITH gemini_completion ------ 这是我们之前创建的推理端点 ID

  • KEEP prompt, completion ------ 这指示 ES|QL 生成一个表格,包含我们发送给 Gemini 的 prompt 以及收到的结果

结果应如下图所示。

总结

我们持续将最先进的 AI 工具和提供商引入 Elasticsearch,希望你和我们一样对与 Google Cloud 的 Vertex AI 平台及 Google 的 Gemini 模型的集成感到兴奋!将 Google Cloud 的生成式 AI 能力与 Elasticsearch 向量数据库和 AI 搜索工具结合,提供高相关性、可投入生产的搜索和分析体验。

访问 Elasticsearch Labs 上的 Google Cloud Vertex AI 页面,或在 Search Labs GitHub 上尝试其他示例笔记本。

要在本地测试运行 Elasticsearch,请使用 Docker 安装并启动本地的 Elasticsearch 和 Kibana,命令如下:

sql 复制代码
`curl -fsSL https://elastic.co/start-local | sh` AI写代码

通过免费 Elastic Cloud 试用或通过 Google Cloud Marketplace 订阅即可开始使用。

原文:www.elastic.co/search-labs...

相关推荐
半夏知半秋2 小时前
Elasticsearch 分词器
大数据·学习·elasticsearch·搜索引擎·全文检索
小猪佩奇TONY5 小时前
OpenGL-ES 学习(18) ---- 实例化渲染
学习·elasticsearch·信息可视化
半夏知半秋6 小时前
MongoDB 与 Elasticsearch 数据同步方案整理
大数据·数据库·mongodb·elasticsearch·搜索引擎
武子康6 小时前
大数据-178 Elasticsearch 7.3 Java 实战:索引与文档 CRUD 全流程示例
大数据·后端·elasticsearch
摇滚侠7 小时前
ElasticSearch 教程入门到精通,核心概念,系统架构,单节点集群,故障转移,水平扩容,笔记33、34、35、36、37
笔记·elasticsearch·系统架构
摇滚侠7 小时前
ElasticSearch 教程入门到精通,部署环境,Windows 集群部署,笔记29、30
大数据·笔记·elasticsearch
摇滚侠17 小时前
ElasticSearch 教程入门到精通,文档创建查询修改删除,笔记10、11、12
笔记·elasticsearch
jason成都1 天前
elasticsearch部署时创建用户密码
大数据·elasticsearch·jenkins
摇滚侠1 天前
ElasticSearch 教程入门到精通,文档新增修改,文档查询删除,文档批量新增批量删除,笔记21、笔记22、笔记23
笔记·elasticsearch·搜索引擎
醇氧1 天前
【git】 撤回一个本地提交
大数据·git·elasticsearch