Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Obs AI Assistant] Fix chat on the Alerts page #197126

Merged
merged 7 commits into from
Oct 30, 2024

Conversation

viduni94
Copy link
Contributor

@viduni94 viduni94 commented Oct 21, 2024

Closes #184214

Summary

Problem

The Observability AI Assistant doesn't work on the Alerts page - errors out with a 400 status code from OpenAI.
The reason for this is that the description for the function get_data_on_screen is too long, and there is a token limit for function descriptions by OpenAI.

Note: This error does not occur with Gemini or Bedrock because simulated function calling is enabled by default for them. With simulated function calling, all functions and their descriptions are appended to the system message, therefore doesn't run into a token limit error as opposed to OpenAI.

Solution

Append the function description to the system message instead of sending it with the function.

The implementation includes:

  • Registering an AdHoc instruction
  • Retrieving AdHoc instructions
  • Combine the retrieved AdHoc instructions with the other adHoc instructions passed to the chat and pass all AdHoc instructions to the getSystemMessageFromInstructions function.

This correctly orders the description for the get_data_on_screen function at the end of the system message

@viduni94 viduni94 added release_note:fix v9.0.0 backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) labels Oct 21, 2024
@viduni94 viduni94 self-assigned this Oct 21, 2024
@viduni94 viduni94 requested a review from a team as a code owner October 21, 2024 19:16
@botelastic botelastic bot added ci:project-deploy-observability Create an Observability project Team:Obs AI Assistant Observability AI Assistant labels Oct 21, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ai-assistant (Team:Obs AI Assistant)

Copy link
Contributor

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

@@ -123,7 +124,10 @@ export const createOpenAiAdapter: LlmApiAdapterFactory = ({
...(!!functionsForOpenAI?.length
? {
tools: functionsForOpenAI.map((fn) => ({
function: pick(fn, 'name', 'description', 'parameters'),
function:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can go for a different fix that doesn't special-case the get_data_on_screen function. Instead of using the function description, we can add an instruction in the same place where we are registering the get_data_on_screen function:

.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks @dgieselaar
I'm looking into it.

Copy link
Contributor Author

@viduni94 viduni94 Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dgieselaar

I implemented the above change as follows:

if (allData.length) {
      this.registerFunction(
        {
          name: GET_DATA_ON_SCREEN_FUNCTION_NAME,
          description: 'Get data that is on the screen',
          visibility: FunctionVisibility.AssistantOnly,
          parameters: {....},
        async ({ arguments: { data: dataNames } }) => {
          return {
            content: allData.filter((data) => dataNames.includes(data.name)),
          };
        },
        ['all']
      );

      const instruction: RegisterInstructionCallback = ({ availableFunctionNames }) =>
        availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)
          ? `The ${GET_DATA_ON_SCREEN_FUNCTION_NAME} function will ${dedent(`Get data that is on the screen:
            ${allData.map((data) => `${data.name}: ${data.description}`).join('\n')}
          `)}`
          : undefined;

      this.registerInstruction({ instruction, scopes: ['all'] });
    }

However, when testing I noticed that,

  • When I register the instruction at the time the function is being registered, the instruction is appended to the top of the prompt, as follows:
{
      role: 'system',
      content: 'The get_data_on_screen function will Get data that is on the screen:\n' +
        '.es-query: An available rule is Elasticsearch query.\n' +
        'observability.rules.custom_threshold: An available rule is Custom threshold.\n' +
        'xpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\n' +
        'slo.rules.burnRate: An available rule is SLO burn rate.\n' +
        'metrics.alert.threshold: An available rule is Metric threshold.\n' +
        'metrics.alert.inventory.threshold: An available rule is Inventory.\n' +
        'logs.alert.document.count: An available rule is Log threshold.\n' +
        'xpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\n' +
        'xpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\n' +
        'xpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\n' +
        'xpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\n' +
        'xpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\n' +
        'apm.error_rate: An available rule is Error count threshold.\n' +
        'apm.transaction_error_rate: An available rule is Failed transaction rate threshold.\n' +
        'apm.transaction_duration: An available rule is Latency threshold.\n' +
        'apm.anomaly: An available rule is APM Anomaly.\n' +
        '\n' +
        'You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n' +
        '\n' +
        "  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n" +
        '\n' +
        '  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation........',
      function_call: undefined,
      name: undefined
    }

This is probably because the system instructions are registered afterwards.. do you have any thoughts on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, @sphilipse you also have a requirement to treat certain instructions differently, correct? is that about ordering or some other mechanism?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah that was about the way we use scopes to filter out some things in some cases, but I think that's resolved with the work we're doing in #196322.

We already have ways to fix ordering, right? System instructions are added to the top, but there are separate instructions that are added lower. You just need to not register them as an AdHocInstruction (see: https://github.com/elastic/kibana/blob/main/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts and https://github.com/elastic/kibana/blob/main/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/get_system_message_from_instructions.ts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @sphilipse
I've updated the PR with the implementation to register adhoc instructions.

With this implementation, I am able to register the description of the get_data_on_screen function as an adhoc instruction and order it at the end of the system message as specified by the getSystemMessageFromInstructions function.

cc: @dgieselaar

@viduni94 viduni94 force-pushed the chat-doesnt-work-on-the-alert-page branch 5 times, most recently from 620a7ee to b19f5ab Compare October 28, 2024 13:34
@viduni94 viduni94 requested review from dgieselaar and a team October 28, 2024 19:09
viduni94 added a commit to viduni94/kibana that referenced this pull request Oct 30, 2024
@viduni94 viduni94 force-pushed the chat-doesnt-work-on-the-alert-page branch from f1530f0 to e3af6ec Compare October 30, 2024 13:10
@elasticmachine
Copy link
Contributor

elasticmachine commented Oct 30, 2024

💚 Build Succeeded

  • Buildkite Build
  • Commit: e3af6ec
  • Kibana Serverless Image: docker.elastic.co/kibana-ci/kibana-serverless:pr-197126-e3af6ec01a85

Metrics [docs]

✅ unchanged

History

cc @viduni94

@viduni94 viduni94 merged commit 354bec4 into elastic:main Oct 30, 2024
25 checks passed
@kibanamachine
Copy link
Contributor

Starting backport for target branches: 8.x

https://github.com/elastic/kibana/actions/runs/11597841867

kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Oct 30, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
@kibanamachine
Copy link
Contributor

💔 All backports failed

Status Branch Result
8.x Could not create pull request: Validation Failed: {"resource":"Issue","code":"custom","field":"body","message":"body is too long (maximum is 65536 characters)"}

Manual backport

To create the backport manually run:

node scripts/backport --pr 197126

Questions ?

Please refer to the Backport tool documentation

viduni94 added a commit to viduni94/kibana that referenced this pull request Oct 31, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
viduni94 added a commit to viduni94/kibana that referenced this pull request Oct 31, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
viduni94 added a commit to viduni94/kibana that referenced this pull request Oct 31, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
viduni94 added a commit to viduni94/kibana that referenced this pull request Nov 1, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
@kibanamachine kibanamachine added the backport missing Added to PRs automatically when the are determined to be missing a backport. label Nov 1, 2024
@kibanamachine
Copy link
Contributor

Looks like this PR has a backport PR but it still hasn't been merged. Please merge it ASAP to keep the branches relatively in sync.

viduni94 added a commit to viduni94/kibana that referenced this pull request Nov 4, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
viduni94 added a commit to viduni94/kibana that referenced this pull request Nov 4, 2024
Closes elastic#184214

## Summary

### Problem
The Observability AI Assistant doesn't work on the Alerts page - errors
out with a 400 status code from OpenAI.
The reason for this is that the description for the function
`get_data_on_screen` is too long, and there is a token limit for
function descriptions by OpenAI.

**Note:** This error does not occur with Gemini or Bedrock because
simulated function calling is enabled by default for them. With
simulated function calling, all functions and their descriptions are
appended to the system message, therefore doesn't run into a token limit
error as opposed to OpenAI.

### Solution
Append the function description to the system message instead of sending
it with the function.

The implementation includes:
- Registering an AdHoc instruction
- Retrieving AdHoc instructions
- Combine the retrieved AdHoc instructions with the other adHoc
instructions passed to the chat and pass all AdHoc instructions to the
`getSystemMessageFromInstructions` function.

This correctly orders the description for the `get_data_on_screen`
function at the end of the system message

_OpenAI request object **before** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "description": "Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly.",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

_OpenAI request object **after** the above update:_
<details>
  <summary>Click to expand JSON</summary>

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.\n\n  It's very important to not assume what the user is meaning. Ask them for clarification if needed.\n\n  If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.\n\n  In KQL (\"kqlFilter\")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\  /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!\n\n  You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.\n\n  Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.\n\n  If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results\n  returned to you, before executing the same tool or another tool again if needed.\n\n  DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (`service.name == \"foo\"`) with \"kqlFilter\" (`service.name:\"foo\"`).\n\n  The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the Stack Management app under the option AI Assistants.\n  If the user asks how to change the language, reply in the same language the user asked in.\n\nYou MUST use the \"query\" function when the user wants to:\n  - visualize data\n  - run any arbitrary query\n  - breakdown or filter ES|QL queries that are displayed on the current page\n  - convert queries from another language to ES|QL\n  - asks general questions about ES|QL\n\n  DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or explain anything about the ES|QL query language yourself.\n  DO NOT UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always use the \"query\" function for this.\n\n  If the user asks for a query, and one of the dataset info functions was called and returned no results, you should still call the query function to generate an example query.\n\n  Even if the \"query\" function was used before that, follow it up with the \"query\" function. If a query fails, do not attempt to correct it yourself. Again you should call the \"query\" function,\n  even if it has been called before.\n\n  When the \"visualize_query\" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a \"visualize_query\" function call with your own visualization attempt.\n  If the \"execute_query\" function has been called, summarize these results for the user. The user does not see a visualization in this case.\n\nYou MUST use the \"get_dataset_info\"  function before calling the \"query\" or the \"changes\" functions.\n\nIf a function requires an index, you MUST use the results from the dataset info functions.\n\nYou have access to data on the screen by calling the \"get_data_on_screen\" function.\nUse it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the \"context\" function.\nData that is compact enough automatically gets included in the response for the \"context\" function.\n\nYou can use the \"summarize\" function to store new information you have learned in a knowledge database.\nOnly use this function when the user asks for it.\nAll summaries MUST be created in English, even if the conversation was carried out in a different language.\nThe \"get_data_on_screen\" function will Get data that is on the screen:\n.es-query: An available rule is Elasticsearch query.\nobservability.rules.custom_threshold: An available rule is Custom threshold.\nxpack.ml.anomaly_detection_alert: An available rule is Anomaly detection.\nslo.rules.burnRate: An available rule is SLO burn rate.\nmetrics.alert.threshold: An available rule is Metric threshold.\nmetrics.alert.inventory.threshold: An available rule is Inventory.\nlogs.alert.document.count: An available rule is Log threshold.\nxpack.uptime.alerts.tlsCertificate: An available rule is Uptime TLS.\nxpack.uptime.alerts.monitorStatus: An available rule is Uptime monitor status.\nxpack.uptime.alerts.durationAnomaly: An available rule is Uptime Duration Anomaly.\nxpack.synthetics.alerts.monitorStatus: An available rule is Synthetics monitor status.\nxpack.synthetics.alerts.tls: An available rule is Synthetics TLS certificate.\napm.error_rate: An available rule is Error count threshold.\napm.transaction_error_rate: An available rule is Failed transaction rate threshold.\napm.transaction_duration: An available rule is Latency threshold.\napm.anomaly: An available rule is APM Anomaly."
    },
    { "role": "user", "content": "Can you explain this page?" },
    { "role": "assistant", "content": "", "function_call": { "name": "context", "arguments": "{}" } },
    {
      "role": "user",
      "content": "{\"screen_description\":\"The user is looking at http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all). The current time range is 2024-10-21T18:23:54.539Z - 2024-10-21T18:38:54.539Z.\",\"learnings\":[],\"data_on_screen\":[{\"name\":\".es-query\",\"value\":\"Elasticsearch query Alert when matches are found during the latest query run.\",\"description\":\"An available rule is Elasticsearch query.\"},{\"name\":\"observability.rules.custom_threshold\",\"value\":\"Custom threshold Alert when any Observability data type reaches or exceeds a given value.\",\"description\":\"An available rule is Custom threshold.\"},{\"name\":\"xpack.ml.anomaly_detection_alert\",\"value\":\"Anomaly detection Alert when anomaly detection jobs results match the condition.\",\"description\":\"An available rule is Anomaly detection.\"},{\"name\":\"slo.rules.burnRate\",\"value\":\"SLO burn rate Alert when your SLO burn rate is too high over a defined period of time.\",\"description\":\"An available rule is SLO burn rate.\"},{\"name\":\"metrics.alert.threshold\",\"value\":\"Metric threshold Alert when the metrics aggregation exceeds the threshold.\",\"description\":\"An available rule is Metric threshold.\"},{\"name\":\"metrics.alert.inventory.threshold\",\"value\":\"Inventory Alert when the inventory exceeds a defined threshold.\",\"description\":\"An available rule is Inventory.\"},{\"name\":\"logs.alert.document.count\",\"value\":\"Log threshold Alert when the log aggregation exceeds the threshold.\",\"description\":\"An available rule is Log threshold.\"},{\"name\":\"xpack.uptime.alerts.tlsCertificate\",\"value\":\"Uptime TLS Alert when the TLS certificate of an Uptime monitor is about to expire.\",\"description\":\"An available rule is Uptime TLS.\"},{\"name\":\"xpack.uptime.alerts.monitorStatus\",\"value\":\"Uptime monitor status Alert when a monitor is down or an availability threshold is breached.\",\"description\":\"An available rule is Uptime monitor status.\"},{\"name\":\"xpack.uptime.alerts.durationAnomaly\",\"value\":\"Uptime Duration Anomaly Alert when the Uptime monitor duration is anomalous.\",\"description\":\"An available rule is Uptime Duration Anomaly.\"},{\"name\":\"xpack.synthetics.alerts.monitorStatus\",\"value\":\"Synthetics monitor status Alert when a monitor is down.\",\"description\":\"An available rule is Synthetics monitor status.\"},{\"name\":\"xpack.synthetics.alerts.tls\",\"value\":\"Synthetics TLS certificate Alert when the TLS certificate of a Synthetics monitor is about to expire.\",\"description\":\"An available rule is Synthetics TLS certificate.\"},{\"name\":\"apm.error_rate\",\"value\":\"Error count threshold Alert when the number of errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Error count threshold.\"},{\"name\":\"apm.transaction_error_rate\",\"value\":\"Failed transaction rate threshold Alert when the rate of transaction errors in a service exceeds a defined threshold.\",\"description\":\"An available rule is Failed transaction rate threshold.\"},{\"name\":\"apm.transaction_duration\",\"value\":\"Latency threshold Alert when the latency of a specific transaction type in a service exceeds a defined threshold.\",\"description\":\"An available rule is Latency threshold.\"},{\"name\":\"apm.anomaly\",\"value\":\"APM Anomaly Alert when either the latency, throughput, or failed transaction rate of a service is anomalous.\",\"description\":\"An available rule is APM Anomaly.\"}]}",
      "name": "context"
    }
  ],
  "stream": true,
  "tools": [
    {
      "function": {
        "name": "get_data_on_screen",
        "parameters": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "description": "The pieces of data you want to look at it. You can request one, or multiple",
              "items": {
                "type": "string",
                "enum": [
                  ".es-query",
                  "observability.rules.custom_threshold",
                  "xpack.ml.anomaly_detection_alert",
                  "slo.rules.burnRate",
                  "metrics.alert.threshold",
                  "metrics.alert.inventory.threshold",
                  "logs.alert.document.count",
                  "xpack.uptime.alerts.tlsCertificate",
                  "xpack.uptime.alerts.monitorStatus",
                  "xpack.uptime.alerts.durationAnomaly",
                  "xpack.synthetics.alerts.monitorStatus",
                  "xpack.synthetics.alerts.tls",
                  "apm.error_rate",
                  "apm.transaction_error_rate",
                  "apm.transaction_duration",
                  "apm.anomaly"
                ]
              }
            }
          },
          "required": ["data"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "query",
        "description": "This function generates, executes and/or visualizes a query\n      based on the user's request. It also explains how ES|QL works and how to\n      convert queries from one language to another. Make sure you call one of\n      the get_dataset functions first if you need index or field names. This\n      function takes no input.",
        "parameters": { "type": "object", "properties": {} }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_alerts_dataset_info",
        "description": "Use this function to get information about alerts data.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the current time range, in datemath, like now-24h or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the current time range, in datemath, like now-24h or an ISO timestamp"
            }
          }
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "alerts",
        "description": "Get alerts for Observability.  Make sure get_alerts_dataset_info was called before.\n        Use this to get open (and optionally recovered) alerts for Observability assets, like services,\n        hosts or containers.\n        Display the response in tabular format if appropriate.\n      ",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The start of the time range, in Elasticsearch date math, like `now`."
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in Elasticsearch date math, like `now-24h`."
            },
            "kqlFilter": { "type": "string", "description": "Filter alerts by field:value pairs" },
            "includeRecovered": {
              "type": "boolean",
              "description": "Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned"
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "changes",
        "description": "Returns change points like spikes and dips for logs and metrics.",
        "parameters": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "description": "The beginning of the time range, in datemath, like now-24h, or an ISO timestamp"
            },
            "end": {
              "type": "string",
              "description": "The end of the time range, in datemath, like now, or an ISO timestamp"
            },
            "logs": {
              "description": "Analyze changes in log patterns. If no index is given, the default logs index pattern will be used",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of logs" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the logs" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "The text field that contains the message to be analyzed, usually `message`. ONLY use field names from the conversation."
                  }
                },
                "required": ["name"]
              }
            },
            "metrics": {
              "description": "Analyze changes in metrics. DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy, leave empty unless needed.",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "The name of this set of metrics" },
                  "index": { "type": "string", "description": "The index or index pattern where to find the metrics" },
                  "kqlFilter": {
                    "type": "string",
                    "description": "A KQL filter to filter the log documents by, e.g. my_field:foo"
                  },
                  "field": {
                    "type": "string",
                    "description": "Metric field that contains the metric. Only use if the metric aggregation type is not count."
                  },
                  "type": {
                    "type": "string",
                    "description": "The type of metric aggregation to perform. Defaults to count",
                    "enum": ["count", "avg", "sum", "min", "max", "p95", "p99"]
                  },
                  "groupBy": {
                    "type": "array",
                    "description": "Optional keyword fields to group metrics by.",
                    "items": { "type": "string" }
                  }
                },
                "required": ["index", "name"]
              }
            }
          },
          "required": ["start", "end"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "summarize",
        "description": "Use this function to store facts in the knowledge database if the user requests it.\n        You can score the learnings with a confidence metric, whether it is a correction on a previous learning.\n        An embedding will be created that you can recall later with a semantic search.\n        When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic\n        search later, and that it would have answered the user's original request.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later."
            },
            "text": {
              "type": "string",
              "description": "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request."
            },
            "is_correction": {
              "type": "boolean",
              "description": "Whether this is a correction for a previous learning."
            },
            "confidence": {
              "type": "string",
              "description": "How confident you are about this being a correct and useful learning",
              "enum": ["low", "medium", "high"]
            },
            "public": {
              "type": "boolean",
              "description": "Whether this information is specific to the user, or generally applicable to any user of the product"
            }
          },
          "required": ["id", "text", "is_correction", "confidence", "public"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "elasticsearch",
        "description": "Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Elasticsearch endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "path": {
              "type": "string",
              "description": "The path of the Elasticsearch endpoint, including query parameters"
            },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "path"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "kibana",
        "description": "Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.",
        "parameters": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "description": "The HTTP method of the Kibana endpoint",
              "enum": ["GET", "PUT", "POST", "DELETE", "PATCH"]
            },
            "pathname": {
              "type": "string",
              "description": "The pathname of the Kibana endpoint, excluding query parameters"
            },
            "query": { "type": "object", "description": "The query parameters, as an object" },
            "body": { "type": "object", "description": "The body of the request" }
          },
          "required": ["method", "pathname"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "get_dataset_info",
        "description": "Use this function to get information about indices/datasets available and the fields available on them.\n\n      providing empty string as index name will retrieve all indices\n      else list of all fields for the given index will be given. if no fields are returned this means no indices were matched by provided index pattern.\n      wildcards can be part of index name.",
        "parameters": {
          "type": "object",
          "properties": {
            "index": {
              "type": "string",
              "description": "index pattern the user is interested in or empty string to get information about all available indices"
            }
          },
          "required": ["index"]
        }
      },
      "type": "function"
    },
    {
      "function": {
        "name": "execute_connector",
        "description": "Use this function when user explicitly asks to call a kibana connector.",
        "parameters": {
          "type": "object",
          "properties": {
            "id": { "type": "string", "description": "The id of the connector" },
            "params": { "type": "object", "description": "The connector parameters" }
          },
          "required": ["id", "params"]
        }
      },
      "type": "function"
    }
  ],
  "temperature": 0
}
```
</details>

---------

Co-authored-by: Søren Louv-Jansen <[email protected]>
(cherry picked from commit 354bec4)
@kibanamachine
Copy link
Contributor

Looks like this PR has a backport PR but it still hasn't been merged. Please merge it ASAP to keep the branches relatively in sync.

Ikuni17 pushed a commit that referenced this pull request Nov 4, 2024
# Backport

This will backport the following commits from `main` to `8.x`:
 - [Obs AI Assistant] Fix chat on the Alerts page (#197126) (354bec4)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Viduni
Wickramarachchi","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-30T16:48:04Z","message":"[Obs
AI Assistant] Fix chat on the Alerts page (#197126)\n\nCloses
https://github.com/elastic/kibana/issues/184214\r\n\r\n##
Summary\r\n\r\n### Problem\r\nThe Observability AI Assistant doesn't
work on the Alerts page - errors\r\nout with a 400 status code from
OpenAI.\r\nThe reason for this is that the description for the
function\r\n`get_data_on_screen` is too long, and there is a token limit
for\r\nfunction descriptions by OpenAI.\r\n\r\n**Note:** This error does
not occur with Gemini or Bedrock because\r\nsimulated function calling
is enabled by default for them. With\r\nsimulated function calling, all
functions and their descriptions are\r\nappended to the system message,
therefore doesn't run into a token limit\r\nerror as opposed to
OpenAI.\r\n\r\n### Solution\r\nAppend the function description to the
system message instead of sending\r\nit with the function.\r\n\r\nThe
implementation includes:\r\n- Registering an AdHoc instruction \r\n-
Retrieving AdHoc instructions\r\n- Combine the retrieved AdHoc
instructions with the other adHoc\r\ninstructions passed to the chat and
pass all AdHoc instructions to the\r\n`getSystemMessageFromInstructions`
function.\r\n\r\nThis correctly orders the description for the
`get_data_on_screen`\r\nfunction at the end of the system
message\r\n\r\n\r\n_OpenAI request object **before** the above
update:_\r\n<details>\r\n <summary>Click to expand
JSON</summary>\r\n\r\n```json\r\n{\r\n \"messages\": [\r\n {\r\n
\"role\": \"system\",\r\n \"content\": \"You are a helpful assistant for
Elastic Observability. Your goal is to help the Elastic Observability
users to quickly assess what is happening in their observed systems. You
can help them visualise and analyze data, investigate their systems,
perform root cause analysis or identify optimisation
opportunities.\\n\\n It's very important to not assume what the user is
meaning. Ask them for clarification if needed.\\n\\n If you are unsure
about which function should be used and with what arguments, ask the
user for clarification or confirmation.\\n\\n In KQL
(\\\"kqlFilter\\\")) escaping happens with double quotes, not single
quotes. Some characters that need escaping are: ':()\\\\ /\\\". Always
put a field value in double quotes. Best:
service.name:\\\"opbeans-go\\\". Wrong: service.name:opbeans-go. This is
very important!\\n\\n You can use Github-flavored Markdown in your
responses. If a function returns an array, consider using a Markdown
table to format the response.\\n\\n Note that ES|QL (the Elasticsearch
Query Language which is a new piped language) is the preferred query
language.\\n\\n If you want to call a function or tool, only call it a
single time per message. Wait until the function has been executed and
its results\\n returned to you, before executing the same tool or
another tool again if needed.\\n\\n DO NOT UNDER ANY CIRCUMSTANCES USE
ES|QL syntax (`service.name == \\\"foo\\\"`) with \\\"kqlFilter\\\"
(`service.name:\\\"foo\\\"`).\\n\\n The user is able to change the
language which they want you to reply in on the settings page of the AI
Assistant for Observability, which can be found in the Stack Management
app under the option AI Assistants.\\n If the user asks how to change
the language, reply in the same language the user asked in.\\n\\nYou
MUST use the \\\"query\\\" function when the user wants to:\\n -
visualize data\\n - run any arbitrary query\\n - breakdown or filter
ES|QL queries that are displayed on the current page\\n - convert
queries from another language to ES|QL\\n - asks general questions about
ES|QL\\n\\n DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or
explain anything about the ES|QL query language yourself.\\n DO NOT
UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always
use the \\\"query\\\" function for this.\\n\\n If the user asks for a
query, and one of the dataset info functions was called and returned no
results, you should still call the query function to generate an example
query.\\n\\n Even if the \\\"query\\\" function was used before that,
follow it up with the \\\"query\\\" function. If a query fails, do not
attempt to correct it yourself. Again you should call the \\\"query\\\"
function,\\n even if it has been called before.\\n\\n When the
\\\"visualize_query\\\" function has been called, a visualization has
been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a
\\\"visualize_query\\\" function call with your own visualization
attempt.\\n If the \\\"execute_query\\\" function has been called,
summarize these results for the user. The user does not see a
visualization in this case.\\n\\nYou MUST use the
\\\"get_dataset_info\\\" function before calling the \\\"query\\\" or
the \\\"changes\\\" functions.\\n\\nIf a function requires an index, you
MUST use the results from the dataset info functions.\\n\\nYou have
access to data on the screen by calling the \\\"get_data_on_screen\\\"
function.\\nUse it to help the user understand what they are looking at.
A short summary of what they are looking at is available in the return
of the \\\"context\\\" function.\\nData that is compact enough
automatically gets included in the response for the \\\"context\\\"
function.\\n\\nYou can use the \\\"summarize\\\" function to store new
information you have learned in a knowledge database.\\nOnly use this
function when the user asks for it.\\nAll summaries MUST be created in
English, even if the conversation was carried out in a different
language.\"\r\n },\r\n { \"role\": \"user\", \"content\": \"Can you
explain this page?\" },\r\n { \"role\": \"assistant\", \"content\":
\"\", \"function_call\": { \"name\": \"context\", \"arguments\": \"{}\"
} },\r\n {\r\n \"role\": \"user\",\r\n \"content\":
\"{\\\"screen_description\\\":\\\"The user is looking at
http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all).
The current time range is 2024-10-21T18:23:54.539Z -
2024-10-21T18:38:54.539Z.\\\",\\\"learnings\\\":[],\\\"data_on_screen\\\":[{\\\"name\\\":\\\".es-query\\\",\\\"value\\\":\\\"Elasticsearch
query Alert when matches are found during the latest query
run.\\\",\\\"description\\\":\\\"An available rule is Elasticsearch
query.\\\"},{\\\"name\\\":\\\"observability.rules.custom_threshold\\\",\\\"value\\\":\\\"Custom
threshold Alert when any Observability data type reaches or exceeds a
given value.\\\",\\\"description\\\":\\\"An available rule is Custom
threshold.\\\"},{\\\"name\\\":\\\"xpack.ml.anomaly_detection_alert\\\",\\\"value\\\":\\\"Anomaly
detection Alert when anomaly detection jobs results match the
condition.\\\",\\\"description\\\":\\\"An available rule is Anomaly
detection.\\\"},{\\\"name\\\":\\\"slo.rules.burnRate\\\",\\\"value\\\":\\\"SLO
burn rate Alert when your SLO burn rate is too high over a defined
period of time.\\\",\\\"description\\\":\\\"An available rule is SLO
burn
rate.\\\"},{\\\"name\\\":\\\"metrics.alert.threshold\\\",\\\"value\\\":\\\"Metric
threshold Alert when the metrics aggregation exceeds the
threshold.\\\",\\\"description\\\":\\\"An available rule is Metric
threshold.\\\"},{\\\"name\\\":\\\"metrics.alert.inventory.threshold\\\",\\\"value\\\":\\\"Inventory
Alert when the inventory exceeds a defined
threshold.\\\",\\\"description\\\":\\\"An available rule is
Inventory.\\\"},{\\\"name\\\":\\\"logs.alert.document.count\\\",\\\"value\\\":\\\"Log
threshold Alert when the log aggregation exceeds the
threshold.\\\",\\\"description\\\":\\\"An available rule is Log
threshold.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.tlsCertificate\\\",\\\"value\\\":\\\"Uptime
TLS Alert when the TLS certificate of an Uptime monitor is about to
expire.\\\",\\\"description\\\":\\\"An available rule is Uptime
TLS.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.monitorStatus\\\",\\\"value\\\":\\\"Uptime
monitor status Alert when a monitor is down or an availability threshold
is breached.\\\",\\\"description\\\":\\\"An available rule is Uptime
monitor
status.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.durationAnomaly\\\",\\\"value\\\":\\\"Uptime
Duration Anomaly Alert when the Uptime monitor duration is
anomalous.\\\",\\\"description\\\":\\\"An available rule is Uptime
Duration
Anomaly.\\\"},{\\\"name\\\":\\\"xpack.synthetics.alerts.monitorStatus\\\",\\\"value\\\":\\\"Synthetics
monitor status Alert when a monitor is
down.\\\",\\\"description\\\":\\\"An available rule is Synthetics
monitor
status.\\\"},{\\\"name\\\":\\\"xpack.synthetics.alerts.tls\\\",\\\"value\\\":\\\"Synthetics
TLS certificate Alert when the TLS certificate of a Synthetics monitor
is about to expire.\\\",\\\"description\\\":\\\"An available rule is
Synthetics TLS
certificate.\\\"},{\\\"name\\\":\\\"apm.error_rate\\\",\\\"value\\\":\\\"Error
count threshold Alert when the number of errors in a service exceeds a
defined threshold.\\\",\\\"description\\\":\\\"An available rule is
Error count
threshold.\\\"},{\\\"name\\\":\\\"apm.transaction_error_rate\\\",\\\"value\\\":\\\"Failed
transaction rate threshold Alert when the rate of transaction errors in
a service exceeds a defined threshold.\\\",\\\"description\\\":\\\"An
available rule is Failed transaction rate
threshold.\\\"},{\\\"name\\\":\\\"apm.transaction_duration\\\",\\\"value\\\":\\\"Latency
threshold Alert when the latency of a specific transaction type in a
service exceeds a defined threshold.\\\",\\\"description\\\":\\\"An
available rule is Latency
threshold.\\\"},{\\\"name\\\":\\\"apm.anomaly\\\",\\\"value\\\":\\\"APM
Anomaly Alert when either the latency, throughput, or failed transaction
rate of a service is anomalous.\\\",\\\"description\\\":\\\"An available
rule is APM Anomaly.\\\"}]}\",\r\n \"name\": \"context\"\r\n }\r\n
],\r\n \"stream\": true,\r\n \"tools\": [\r\n {\r\n \"function\": {\r\n
\"name\": \"get_data_on_screen\",\r\n \"description\": \"Get data that
is on the screen:\\n.es-query: An available rule is Elasticsearch
query.\\nobservability.rules.custom_threshold: An available rule is
Custom threshold.\\nxpack.ml.anomaly_detection_alert: An available rule
is Anomaly detection.\\nslo.rules.burnRate: An available rule is SLO
burn rate.\\nmetrics.alert.threshold: An available rule is Metric
threshold.\\nmetrics.alert.inventory.threshold: An available rule is
Inventory.\\nlogs.alert.document.count: An available rule is Log
threshold.\\nxpack.uptime.alerts.tlsCertificate: An available rule is
Uptime TLS.\\nxpack.uptime.alerts.monitorStatus: An available rule is
Uptime monitor status.\\nxpack.uptime.alerts.durationAnomaly: An
available rule is Uptime Duration
Anomaly.\\nxpack.synthetics.alerts.monitorStatus: An available rule is
Synthetics monitor status.\\nxpack.synthetics.alerts.tls: An available
rule is Synthetics TLS certificate.\\napm.error_rate: An available rule
is Error count threshold.\\napm.transaction_error_rate: An available
rule is Failed transaction rate threshold.\\napm.transaction_duration:
An available rule is Latency threshold.\\napm.anomaly: An available rule
is APM Anomaly.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"data\": {\r\n \"type\": \"array\",\r\n
\"description\": \"The pieces of data you want to look at it. You can
request one, or multiple\",\r\n \"items\": {\r\n \"type\":
\"string\",\r\n \"enum\": [\r\n \".es-query\",\r\n
\"observability.rules.custom_threshold\",\r\n
\"xpack.ml.anomaly_detection_alert\",\r\n \"slo.rules.burnRate\",\r\n
\"metrics.alert.threshold\",\r\n
\"metrics.alert.inventory.threshold\",\r\n
\"logs.alert.document.count\",\r\n
\"xpack.uptime.alerts.tlsCertificate\",\r\n
\"xpack.uptime.alerts.monitorStatus\",\r\n
\"xpack.uptime.alerts.durationAnomaly\",\r\n
\"xpack.synthetics.alerts.monitorStatus\",\r\n
\"xpack.synthetics.alerts.tls\",\r\n \"apm.error_rate\",\r\n
\"apm.transaction_error_rate\",\r\n \"apm.transaction_duration\",\r\n
\"apm.anomaly\"\r\n ]\r\n }\r\n }\r\n },\r\n \"required\":
[\"data\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"query\",\r\n \"description\": \"This
function generates, executes and/or visualizes a query\\n based on the
user's request. It also explains how ES|QL works and how to\\n convert
queries from one language to another. Make sure you call one of\\n the
get_dataset functions first if you need index or field names. This\\n
function takes no input.\",\r\n \"parameters\": { \"type\": \"object\",
\"properties\": {} }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"get_alerts_dataset_info\",\r\n
\"description\": \"Use this function to get information about alerts
data.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"start\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The start of the current time range, in datemath,
like now-24h or an ISO timestamp\"\r\n },\r\n \"end\": {\r\n \"type\":
\"string\",\r\n \"description\": \"The end of the current time range, in
datemath, like now-24h or an ISO timestamp\"\r\n }\r\n }\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"alerts\",\r\n \"description\": \"Get alerts for
Observability. Make sure get_alerts_dataset_info was called before.\\n
Use this to get open (and optionally recovered) alerts for Observability
assets, like services,\\n hosts or containers.\\n Display the response
in tabular format if appropriate.\\n \",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"start\": {\r\n
\"type\": \"string\",\r\n \"description\": \"The start of the time
range, in Elasticsearch date math, like `now`.\"\r\n },\r\n \"end\":
{\r\n \"type\": \"string\",\r\n \"description\": \"The end of the time
range, in Elasticsearch date math, like `now-24h`.\"\r\n },\r\n
\"kqlFilter\": { \"type\": \"string\", \"description\": \"Filter alerts
by field:value pairs\" },\r\n \"includeRecovered\": {\r\n \"type\":
\"boolean\",\r\n \"description\": \"Whether to include recovered/closed
alerts. Defaults to false, which means only active alerts will be
returned\"\r\n }\r\n },\r\n \"required\": [\"start\", \"end\"]\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"changes\",\r\n \"description\": \"Returns change points like
spikes and dips for logs and metrics.\",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"start\": {\r\n
\"type\": \"string\",\r\n \"description\": \"The beginning of the time
range, in datemath, like now-24h, or an ISO timestamp\"\r\n },\r\n
\"end\": {\r\n \"type\": \"string\",\r\n \"description\": \"The end of
the time range, in datemath, like now, or an ISO timestamp\"\r\n },\r\n
\"logs\": {\r\n \"description\": \"Analyze changes in log patterns. If
no index is given, the default logs index pattern will be used\",\r\n
\"type\": \"array\",\r\n \"items\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"name\": { \"type\": \"string\", \"description\":
\"The name of this set of logs\" },\r\n \"index\": { \"type\":
\"string\", \"description\": \"The index or index pattern where to find
the logs\" },\r\n \"kqlFilter\": {\r\n \"type\": \"string\",\r\n
\"description\": \"A KQL filter to filter the log documents by, e.g.
my_field:foo\"\r\n },\r\n \"field\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The text field that contains the message to be
analyzed, usually `message`. ONLY use field names from the
conversation.\"\r\n }\r\n },\r\n \"required\": [\"name\"]\r\n }\r\n
},\r\n \"metrics\": {\r\n \"description\": \"Analyze changes in metrics.
DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy,
leave empty unless needed.\",\r\n \"type\": \"array\",\r\n \"items\":
{\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"name\": {
\"type\": \"string\", \"description\": \"The name of this set of
metrics\" },\r\n \"index\": { \"type\": \"string\", \"description\":
\"The index or index pattern where to find the metrics\" },\r\n
\"kqlFilter\": {\r\n \"type\": \"string\",\r\n \"description\": \"A KQL
filter to filter the log documents by, e.g. my_field:foo\"\r\n },\r\n
\"field\": {\r\n \"type\": \"string\",\r\n \"description\": \"Metric
field that contains the metric. Only use if the metric aggregation type
is not count.\"\r\n },\r\n \"type\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The type of metric aggregation to perform. Defaults
to count\",\r\n \"enum\": [\"count\", \"avg\", \"sum\", \"min\",
\"max\", \"p95\", \"p99\"]\r\n },\r\n \"groupBy\": {\r\n \"type\":
\"array\",\r\n \"description\": \"Optional keyword fields to group
metrics by.\",\r\n \"items\": { \"type\": \"string\" }\r\n }\r\n },\r\n
\"required\": [\"index\", \"name\"]\r\n }\r\n }\r\n },\r\n \"required\":
[\"start\", \"end\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n
{\r\n \"function\": {\r\n \"name\": \"summarize\",\r\n \"description\":
\"Use this function to store facts in the knowledge database if the user
requests it.\\n You can score the learnings with a confidence metric,
whether it is a correction on a previous learning.\\n An embedding will
be created that you can recall later with a semantic search.\\n When you
create this summarisation, make sure you craft it in a way that can be
recalled with a semantic\\n search later, and that it would have
answered the user's original request.\",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"id\": {\r\n \"type\":
\"string\",\r\n \"description\": \"An id for the document. This should
be a short human-readable keyword field with only alphabetic characters
and underscores, that allow you to update it later.\"\r\n },\r\n
\"text\": {\r\n \"type\": \"string\",\r\n \"description\": \"A
human-readable summary of what you have learned, described in such a way
that you can recall it later with semantic search, and that it would
have answered the user's original request.\"\r\n },\r\n
\"is_correction\": {\r\n \"type\": \"boolean\",\r\n \"description\":
\"Whether this is a correction for a previous learning.\"\r\n },\r\n
\"confidence\": {\r\n \"type\": \"string\",\r\n \"description\": \"How
confident you are about this being a correct and useful learning\",\r\n
\"enum\": [\"low\", \"medium\", \"high\"]\r\n },\r\n \"public\": {\r\n
\"type\": \"boolean\",\r\n \"description\": \"Whether this information
is specific to the user, or generally applicable to any user of the
product\"\r\n }\r\n },\r\n \"required\": [\"id\", \"text\",
\"is_correction\", \"confidence\", \"public\"]\r\n }\r\n },\r\n
\"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n \"name\":
\"elasticsearch\",\r\n \"description\": \"Call Elasticsearch APIs on
behalf of the user. Make sure the request body is valid for the API that
you are using. Only call this function when the user has explicitly
requested it.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"method\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The HTTP method of the Elasticsearch endpoint\",\r\n
\"enum\": [\"GET\", \"PUT\", \"POST\", \"DELETE\", \"PATCH\"]\r\n },\r\n
\"path\": {\r\n \"type\": \"string\",\r\n \"description\": \"The path of
the Elasticsearch endpoint, including query parameters\"\r\n },\r\n
\"body\": { \"type\": \"object\", \"description\": \"The body of the
request\" }\r\n },\r\n \"required\": [\"method\", \"path\"]\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"kibana\",\r\n \"description\": \"Call Kibana APIs on behalf
of the user. Only call this function when the user has explicitly
requested it, and you know how to call it, for example by querying the
knowledge base or having the user explain it to you. Assume that
pathnames, bodies and query parameters may have changed since your
knowledge cut off date.\",\r\n \"parameters\": {\r\n \"type\":
\"object\",\r\n \"properties\": {\r\n \"method\": {\r\n \"type\":
\"string\",\r\n \"description\": \"The HTTP method of the Kibana
endpoint\",\r\n \"enum\": [\"GET\", \"PUT\", \"POST\", \"DELETE\",
\"PATCH\"]\r\n },\r\n \"pathname\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The pathname of the Kibana endpoint, excluding query
parameters\"\r\n },\r\n \"query\": { \"type\": \"object\",
\"description\": \"The query parameters, as an object\" },\r\n \"body\":
{ \"type\": \"object\", \"description\": \"The body of the request\"
}\r\n },\r\n \"required\": [\"method\", \"pathname\"]\r\n }\r\n },\r\n
\"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n \"name\":
\"get_dataset_info\",\r\n \"description\": \"Use this function to get
information about indices/datasets available and the fields available on
them.\\n\\n providing empty string as index name will retrieve all
indices\\n else list of all fields for the given index will be given. if
no fields are returned this means no indices were matched by provided
index pattern.\\n wildcards can be part of index name.\",\r\n
\"parameters\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n
\"index\": {\r\n \"type\": \"string\",\r\n \"description\": \"index
pattern the user is interested in or empty string to get information
about all available indices\"\r\n }\r\n },\r\n \"required\":
[\"index\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"execute_connector\",\r\n
\"description\": \"Use this function when user explicitly asks to call a
kibana connector.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"id\": { \"type\": \"string\", \"description\":
\"The id of the connector\" },\r\n \"params\": { \"type\": \"object\",
\"description\": \"The connector parameters\" }\r\n },\r\n \"required\":
[\"id\", \"params\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n }\r\n
],\r\n \"temperature\": 0\r\n}\r\n```\r\n</details>\r\n\r\n_OpenAI
request object **after** the above update:_\r\n<details>\r\n
<summary>Click to expand JSON</summary>\r\n\r\n```json\r\n{\r\n
\"messages\": [\r\n {\r\n \"role\": \"system\",\r\n \"content\": \"You
are a helpful assistant for Elastic Observability. Your goal is to help
the Elastic Observability users to quickly assess what is happening in
their observed systems. You can help them visualise and analyze data,
investigate their systems, perform root cause analysis or identify
optimisation opportunities.\\n\\n It's very important to not assume what
the user is meaning. Ask them for clarification if needed.\\n\\n If you
are unsure about which function should be used and with what arguments,
ask the user for clarification or confirmation.\\n\\n In KQL
(\\\"kqlFilter\\\")) escaping happens with double quotes, not single
quotes. Some characters that need escaping are: ':()\\\\ /\\\". Always
put a field value in double quotes. Best:
service.name:\\\"opbeans-go\\\". Wrong: service.name:opbeans-go. This is
very important!\\n\\n You can use Github-flavored Markdown in your
responses. If a function returns an array, consider using a Markdown
table to format the response.\\n\\n Note that ES|QL (the Elasticsearch
Query Language which is a new piped language) is the preferred query
language.\\n\\n If you want to call a function or tool, only call it a
single time per message. Wait until the function has been executed and
its results\\n returned to you, before executing the same tool or
another tool again if needed.\\n\\n DO NOT UNDER ANY CIRCUMSTANCES USE
ES|QL syntax (`service.name == \\\"foo\\\"`) with \\\"kqlFilter\\\"
(`service.name:\\\"foo\\\"`).\\n\\n The user is able to change the
language which they want you to reply in on the settings page of the AI
Assistant for Observability, which can be found in the Stack Management
app under the option AI Assistants.\\n If the user asks how to change
the language, reply in the same language the user asked in.\\n\\nYou
MUST use the \\\"query\\\" function when the user wants to:\\n -
visualize data\\n - run any arbitrary query\\n - breakdown or filter
ES|QL queries that are displayed on the current page\\n - convert
queries from another language to ES|QL\\n - asks general questions about
ES|QL\\n\\n DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries or
explain anything about the ES|QL query language yourself.\\n DO NOT
UNDER ANY CIRCUMSTANCES try to correct an ES|QL query yourself - always
use the \\\"query\\\" function for this.\\n\\n If the user asks for a
query, and one of the dataset info functions was called and returned no
results, you should still call the query function to generate an example
query.\\n\\n Even if the \\\"query\\\" function was used before that,
follow it up with the \\\"query\\\" function. If a query fails, do not
attempt to correct it yourself. Again you should call the \\\"query\\\"
function,\\n even if it has been called before.\\n\\n When the
\\\"visualize_query\\\" function has been called, a visualization has
been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a
\\\"visualize_query\\\" function call with your own visualization
attempt.\\n If the \\\"execute_query\\\" function has been called,
summarize these results for the user. The user does not see a
visualization in this case.\\n\\nYou MUST use the
\\\"get_dataset_info\\\" function before calling the \\\"query\\\" or
the \\\"changes\\\" functions.\\n\\nIf a function requires an index, you
MUST use the results from the dataset info functions.\\n\\nYou have
access to data on the screen by calling the \\\"get_data_on_screen\\\"
function.\\nUse it to help the user understand what they are looking at.
A short summary of what they are looking at is available in the return
of the \\\"context\\\" function.\\nData that is compact enough
automatically gets included in the response for the \\\"context\\\"
function.\\n\\nYou can use the \\\"summarize\\\" function to store new
information you have learned in a knowledge database.\\nOnly use this
function when the user asks for it.\\nAll summaries MUST be created in
English, even if the conversation was carried out in a different
language.\\nThe \\\"get_data_on_screen\\\" function will Get data that
is on the screen:\\n.es-query: An available rule is Elasticsearch
query.\\nobservability.rules.custom_threshold: An available rule is
Custom threshold.\\nxpack.ml.anomaly_detection_alert: An available rule
is Anomaly detection.\\nslo.rules.burnRate: An available rule is SLO
burn rate.\\nmetrics.alert.threshold: An available rule is Metric
threshold.\\nmetrics.alert.inventory.threshold: An available rule is
Inventory.\\nlogs.alert.document.count: An available rule is Log
threshold.\\nxpack.uptime.alerts.tlsCertificate: An available rule is
Uptime TLS.\\nxpack.uptime.alerts.monitorStatus: An available rule is
Uptime monitor status.\\nxpack.uptime.alerts.durationAnomaly: An
available rule is Uptime Duration
Anomaly.\\nxpack.synthetics.alerts.monitorStatus: An available rule is
Synthetics monitor status.\\nxpack.synthetics.alerts.tls: An available
rule is Synthetics TLS certificate.\\napm.error_rate: An available rule
is Error count threshold.\\napm.transaction_error_rate: An available
rule is Failed transaction rate threshold.\\napm.transaction_duration:
An available rule is Latency threshold.\\napm.anomaly: An available rule
is APM Anomaly.\"\r\n },\r\n { \"role\": \"user\", \"content\": \"Can
you explain this page?\" },\r\n { \"role\": \"assistant\", \"content\":
\"\", \"function_call\": { \"name\": \"context\", \"arguments\": \"{}\"
} },\r\n {\r\n \"role\": \"user\",\r\n \"content\":
\"{\\\"screen_description\\\":\\\"The user is looking at
http://localhost:5601/phq/app/observability/alerts?_a=(filters:!(),kuery:%27%27,rangeFrom:now-24h,rangeTo:now,status:all).
The current time range is 2024-10-21T18:23:54.539Z -
2024-10-21T18:38:54.539Z.\\\",\\\"learnings\\\":[],\\\"data_on_screen\\\":[{\\\"name\\\":\\\".es-query\\\",\\\"value\\\":\\\"Elasticsearch
query Alert when matches are found during the latest query
run.\\\",\\\"description\\\":\\\"An available rule is Elasticsearch
query.\\\"},{\\\"name\\\":\\\"observability.rules.custom_threshold\\\",\\\"value\\\":\\\"Custom
threshold Alert when any Observability data type reaches or exceeds a
given value.\\\",\\\"description\\\":\\\"An available rule is Custom
threshold.\\\"},{\\\"name\\\":\\\"xpack.ml.anomaly_detection_alert\\\",\\\"value\\\":\\\"Anomaly
detection Alert when anomaly detection jobs results match the
condition.\\\",\\\"description\\\":\\\"An available rule is Anomaly
detection.\\\"},{\\\"name\\\":\\\"slo.rules.burnRate\\\",\\\"value\\\":\\\"SLO
burn rate Alert when your SLO burn rate is too high over a defined
period of time.\\\",\\\"description\\\":\\\"An available rule is SLO
burn
rate.\\\"},{\\\"name\\\":\\\"metrics.alert.threshold\\\",\\\"value\\\":\\\"Metric
threshold Alert when the metrics aggregation exceeds the
threshold.\\\",\\\"description\\\":\\\"An available rule is Metric
threshold.\\\"},{\\\"name\\\":\\\"metrics.alert.inventory.threshold\\\",\\\"value\\\":\\\"Inventory
Alert when the inventory exceeds a defined
threshold.\\\",\\\"description\\\":\\\"An available rule is
Inventory.\\\"},{\\\"name\\\":\\\"logs.alert.document.count\\\",\\\"value\\\":\\\"Log
threshold Alert when the log aggregation exceeds the
threshold.\\\",\\\"description\\\":\\\"An available rule is Log
threshold.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.tlsCertificate\\\",\\\"value\\\":\\\"Uptime
TLS Alert when the TLS certificate of an Uptime monitor is about to
expire.\\\",\\\"description\\\":\\\"An available rule is Uptime
TLS.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.monitorStatus\\\",\\\"value\\\":\\\"Uptime
monitor status Alert when a monitor is down or an availability threshold
is breached.\\\",\\\"description\\\":\\\"An available rule is Uptime
monitor
status.\\\"},{\\\"name\\\":\\\"xpack.uptime.alerts.durationAnomaly\\\",\\\"value\\\":\\\"Uptime
Duration Anomaly Alert when the Uptime monitor duration is
anomalous.\\\",\\\"description\\\":\\\"An available rule is Uptime
Duration
Anomaly.\\\"},{\\\"name\\\":\\\"xpack.synthetics.alerts.monitorStatus\\\",\\\"value\\\":\\\"Synthetics
monitor status Alert when a monitor is
down.\\\",\\\"description\\\":\\\"An available rule is Synthetics
monitor
status.\\\"},{\\\"name\\\":\\\"xpack.synthetics.alerts.tls\\\",\\\"value\\\":\\\"Synthetics
TLS certificate Alert when the TLS certificate of a Synthetics monitor
is about to expire.\\\",\\\"description\\\":\\\"An available rule is
Synthetics TLS
certificate.\\\"},{\\\"name\\\":\\\"apm.error_rate\\\",\\\"value\\\":\\\"Error
count threshold Alert when the number of errors in a service exceeds a
defined threshold.\\\",\\\"description\\\":\\\"An available rule is
Error count
threshold.\\\"},{\\\"name\\\":\\\"apm.transaction_error_rate\\\",\\\"value\\\":\\\"Failed
transaction rate threshold Alert when the rate of transaction errors in
a service exceeds a defined threshold.\\\",\\\"description\\\":\\\"An
available rule is Failed transaction rate
threshold.\\\"},{\\\"name\\\":\\\"apm.transaction_duration\\\",\\\"value\\\":\\\"Latency
threshold Alert when the latency of a specific transaction type in a
service exceeds a defined threshold.\\\",\\\"description\\\":\\\"An
available rule is Latency
threshold.\\\"},{\\\"name\\\":\\\"apm.anomaly\\\",\\\"value\\\":\\\"APM
Anomaly Alert when either the latency, throughput, or failed transaction
rate of a service is anomalous.\\\",\\\"description\\\":\\\"An available
rule is APM Anomaly.\\\"}]}\",\r\n \"name\": \"context\"\r\n }\r\n
],\r\n \"stream\": true,\r\n \"tools\": [\r\n {\r\n \"function\": {\r\n
\"name\": \"get_data_on_screen\",\r\n \"parameters\": {\r\n \"type\":
\"object\",\r\n \"properties\": {\r\n \"data\": {\r\n \"type\":
\"array\",\r\n \"description\": \"The pieces of data you want to look at
it. You can request one, or multiple\",\r\n \"items\": {\r\n \"type\":
\"string\",\r\n \"enum\": [\r\n \".es-query\",\r\n
\"observability.rules.custom_threshold\",\r\n
\"xpack.ml.anomaly_detection_alert\",\r\n \"slo.rules.burnRate\",\r\n
\"metrics.alert.threshold\",\r\n
\"metrics.alert.inventory.threshold\",\r\n
\"logs.alert.document.count\",\r\n
\"xpack.uptime.alerts.tlsCertificate\",\r\n
\"xpack.uptime.alerts.monitorStatus\",\r\n
\"xpack.uptime.alerts.durationAnomaly\",\r\n
\"xpack.synthetics.alerts.monitorStatus\",\r\n
\"xpack.synthetics.alerts.tls\",\r\n \"apm.error_rate\",\r\n
\"apm.transaction_error_rate\",\r\n \"apm.transaction_duration\",\r\n
\"apm.anomaly\"\r\n ]\r\n }\r\n }\r\n },\r\n \"required\":
[\"data\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"query\",\r\n \"description\": \"This
function generates, executes and/or visualizes a query\\n based on the
user's request. It also explains how ES|QL works and how to\\n convert
queries from one language to another. Make sure you call one of\\n the
get_dataset functions first if you need index or field names. This\\n
function takes no input.\",\r\n \"parameters\": { \"type\": \"object\",
\"properties\": {} }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"get_alerts_dataset_info\",\r\n
\"description\": \"Use this function to get information about alerts
data.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"start\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The start of the current time range, in datemath,
like now-24h or an ISO timestamp\"\r\n },\r\n \"end\": {\r\n \"type\":
\"string\",\r\n \"description\": \"The end of the current time range, in
datemath, like now-24h or an ISO timestamp\"\r\n }\r\n }\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"alerts\",\r\n \"description\": \"Get alerts for
Observability. Make sure get_alerts_dataset_info was called before.\\n
Use this to get open (and optionally recovered) alerts for Observability
assets, like services,\\n hosts or containers.\\n Display the response
in tabular format if appropriate.\\n \",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"start\": {\r\n
\"type\": \"string\",\r\n \"description\": \"The start of the time
range, in Elasticsearch date math, like `now`.\"\r\n },\r\n \"end\":
{\r\n \"type\": \"string\",\r\n \"description\": \"The end of the time
range, in Elasticsearch date math, like `now-24h`.\"\r\n },\r\n
\"kqlFilter\": { \"type\": \"string\", \"description\": \"Filter alerts
by field:value pairs\" },\r\n \"includeRecovered\": {\r\n \"type\":
\"boolean\",\r\n \"description\": \"Whether to include recovered/closed
alerts. Defaults to false, which means only active alerts will be
returned\"\r\n }\r\n },\r\n \"required\": [\"start\", \"end\"]\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"changes\",\r\n \"description\": \"Returns change points like
spikes and dips for logs and metrics.\",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"start\": {\r\n
\"type\": \"string\",\r\n \"description\": \"The beginning of the time
range, in datemath, like now-24h, or an ISO timestamp\"\r\n },\r\n
\"end\": {\r\n \"type\": \"string\",\r\n \"description\": \"The end of
the time range, in datemath, like now, or an ISO timestamp\"\r\n },\r\n
\"logs\": {\r\n \"description\": \"Analyze changes in log patterns. If
no index is given, the default logs index pattern will be used\",\r\n
\"type\": \"array\",\r\n \"items\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"name\": { \"type\": \"string\", \"description\":
\"The name of this set of logs\" },\r\n \"index\": { \"type\":
\"string\", \"description\": \"The index or index pattern where to find
the logs\" },\r\n \"kqlFilter\": {\r\n \"type\": \"string\",\r\n
\"description\": \"A KQL filter to filter the log documents by, e.g.
my_field:foo\"\r\n },\r\n \"field\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The text field that contains the message to be
analyzed, usually `message`. ONLY use field names from the
conversation.\"\r\n }\r\n },\r\n \"required\": [\"name\"]\r\n }\r\n
},\r\n \"metrics\": {\r\n \"description\": \"Analyze changes in metrics.
DO NOT UNDER ANY CIRCUMSTANCES use date or metric fields for groupBy,
leave empty unless needed.\",\r\n \"type\": \"array\",\r\n \"items\":
{\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"name\": {
\"type\": \"string\", \"description\": \"The name of this set of
metrics\" },\r\n \"index\": { \"type\": \"string\", \"description\":
\"The index or index pattern where to find the metrics\" },\r\n
\"kqlFilter\": {\r\n \"type\": \"string\",\r\n \"description\": \"A KQL
filter to filter the log documents by, e.g. my_field:foo\"\r\n },\r\n
\"field\": {\r\n \"type\": \"string\",\r\n \"description\": \"Metric
field that contains the metric. Only use if the metric aggregation type
is not count.\"\r\n },\r\n \"type\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The type of metric aggregation to perform. Defaults
to count\",\r\n \"enum\": [\"count\", \"avg\", \"sum\", \"min\",
\"max\", \"p95\", \"p99\"]\r\n },\r\n \"groupBy\": {\r\n \"type\":
\"array\",\r\n \"description\": \"Optional keyword fields to group
metrics by.\",\r\n \"items\": { \"type\": \"string\" }\r\n }\r\n },\r\n
\"required\": [\"index\", \"name\"]\r\n }\r\n }\r\n },\r\n \"required\":
[\"start\", \"end\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n
{\r\n \"function\": {\r\n \"name\": \"summarize\",\r\n \"description\":
\"Use this function to store facts in the knowledge database if the user
requests it.\\n You can score the learnings with a confidence metric,
whether it is a correction on a previous learning.\\n An embedding will
be created that you can recall later with a semantic search.\\n When you
create this summarisation, make sure you craft it in a way that can be
recalled with a semantic\\n search later, and that it would have
answered the user's original request.\",\r\n \"parameters\": {\r\n
\"type\": \"object\",\r\n \"properties\": {\r\n \"id\": {\r\n \"type\":
\"string\",\r\n \"description\": \"An id for the document. This should
be a short human-readable keyword field with only alphabetic characters
and underscores, that allow you to update it later.\"\r\n },\r\n
\"text\": {\r\n \"type\": \"string\",\r\n \"description\": \"A
human-readable summary of what you have learned, described in such a way
that you can recall it later with semantic search, and that it would
have answered the user's original request.\"\r\n },\r\n
\"is_correction\": {\r\n \"type\": \"boolean\",\r\n \"description\":
\"Whether this is a correction for a previous learning.\"\r\n },\r\n
\"confidence\": {\r\n \"type\": \"string\",\r\n \"description\": \"How
confident you are about this being a correct and useful learning\",\r\n
\"enum\": [\"low\", \"medium\", \"high\"]\r\n },\r\n \"public\": {\r\n
\"type\": \"boolean\",\r\n \"description\": \"Whether this information
is specific to the user, or generally applicable to any user of the
product\"\r\n }\r\n },\r\n \"required\": [\"id\", \"text\",
\"is_correction\", \"confidence\", \"public\"]\r\n }\r\n },\r\n
\"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n \"name\":
\"elasticsearch\",\r\n \"description\": \"Call Elasticsearch APIs on
behalf of the user. Make sure the request body is valid for the API that
you are using. Only call this function when the user has explicitly
requested it.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"method\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The HTTP method of the Elasticsearch endpoint\",\r\n
\"enum\": [\"GET\", \"PUT\", \"POST\", \"DELETE\", \"PATCH\"]\r\n },\r\n
\"path\": {\r\n \"type\": \"string\",\r\n \"description\": \"The path of
the Elasticsearch endpoint, including query parameters\"\r\n },\r\n
\"body\": { \"type\": \"object\", \"description\": \"The body of the
request\" }\r\n },\r\n \"required\": [\"method\", \"path\"]\r\n }\r\n
},\r\n \"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n
\"name\": \"kibana\",\r\n \"description\": \"Call Kibana APIs on behalf
of the user. Only call this function when the user has explicitly
requested it, and you know how to call it, for example by querying the
knowledge base or having the user explain it to you. Assume that
pathnames, bodies and query parameters may have changed since your
knowledge cut off date.\",\r\n \"parameters\": {\r\n \"type\":
\"object\",\r\n \"properties\": {\r\n \"method\": {\r\n \"type\":
\"string\",\r\n \"description\": \"The HTTP method of the Kibana
endpoint\",\r\n \"enum\": [\"GET\", \"PUT\", \"POST\", \"DELETE\",
\"PATCH\"]\r\n },\r\n \"pathname\": {\r\n \"type\": \"string\",\r\n
\"description\": \"The pathname of the Kibana endpoint, excluding query
parameters\"\r\n },\r\n \"query\": { \"type\": \"object\",
\"description\": \"The query parameters, as an object\" },\r\n \"body\":
{ \"type\": \"object\", \"description\": \"The body of the request\"
}\r\n },\r\n \"required\": [\"method\", \"pathname\"]\r\n }\r\n },\r\n
\"type\": \"function\"\r\n },\r\n {\r\n \"function\": {\r\n \"name\":
\"get_dataset_info\",\r\n \"description\": \"Use this function to get
information about indices/datasets available and the fields available on
them.\\n\\n providing empty string as index name will retrieve all
indices\\n else list of all fields for the given index will be given. if
no fields are returned this means no indices were matched by provided
index pattern.\\n wildcards can be part of index name.\",\r\n
\"parameters\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n
\"index\": {\r\n \"type\": \"string\",\r\n \"description\": \"index
pattern the user is interested in or empty string to get information
about all available indices\"\r\n }\r\n },\r\n \"required\":
[\"index\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n },\r\n {\r\n
\"function\": {\r\n \"name\": \"execute_connector\",\r\n
\"description\": \"Use this function when user explicitly asks to call a
kibana connector.\",\r\n \"parameters\": {\r\n \"type\": \"object\",\r\n
\"properties\": {\r\n \"id\": { \"type\": \"string\", \"description\":
\"The id of the connector\" },\r\n \"params\": { \"type\": \"object\",
\"description\": \"The connector parameters\" }\r\n },\r\n \"required\":
[\"id\", \"params\"]\r\n }\r\n },\r\n \"type\": \"function\"\r\n }\r\n
],\r\n \"temperature\":
0\r\n}\r\n```\r\n</details>\r\n\r\n---------\r\n\r\nCo-authored-by:
Søren Louv-Jansen
<[email protected]>","sha":"354bec4ca4587a8d6562138fcae79bbc7d32ade6"},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[]}]
BACKPORT-->
@kibanamachine kibanamachine removed the backport missing Added to PRs automatically when the are determined to be missing a backport. label Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) ci:project-deploy-observability Create an Observability project release_note:fix Team:Obs AI Assistant Observability AI Assistant v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Observability AI Assistant] Chat doesn't work on alert page
6 participants