AI steps

AI steps let workflows call a large language model (LLM) for reasoning, classification, summarization, or agent-driven execution.

AI steps can also create Elastic Agent Builder conversations and add content to them without triggering an agent response.

The ai.prompt, ai.classify, ai.summarize, and ai.agent steps reference a model by ID in the step. Configure a Generative AI connector in Kibana and pass its ID in connector-id. For ai.agent, you can instead pass an inference endpoint ID in inference-id.

If you omit both, ai.agent uses the same model that Elastic Agent Builder would use for the agent. Refer to Model configuration in Elastic Agent Builder.

If you omit connector-id from ai.prompt, ai.classify, or ai.summarize:

The step uses the first available model assigned to it in Feature Settings, under AI Prompt step, AI Classify step, or AI Summarize step. Each step comes with recommended models assigned by default, so this works without any setup. If none of the assigned models is available, the step uses the default AI connector.

The step uses the default AI connector.

Note

Generative AI connectors are deprecated. The ai.agent step accepts an inference endpoint through inference-id; the other AI steps currently take connector-id only.

For ai.prompt, ai.classify, ai.summarize, and ai.agent, connector-id, agent-id, and inference-id are top-level step fields (alongside name, type, if, foreach) and are written in kebab-case. They are not nested under with, and not connectorId. Inside with, most of their parameters use camelCase (systemPrompt, maxLength, includeRationale). The conversation steps have no top-level fields and use snake_case inside with.

Liquid expressions are evaluated in these top-level fields, so you can use templated values (for example, "{{ consts.agent_id }}").

Liquid expressions in these top-level fields aren't evaluated, so use a literal value (for example, agent-id: elastic-ai-agent) instead of "{{ consts.agent_id }}".

Schema convention. In each schema table on this page, the Location column indicates where each parameter sits in the YAML:

  • top level: Alongside type and name, at the top of the step or trigger definition.
  • `with`: Inside the step's with: block.
  • `on`: Inside the trigger's on: block.

Call an LLM with a prompt. Supports optional structured output through a JSON Schema.

Parameter Location Type Required Description
connector-id top level string No The Generative AI connector to use. If omitted, the step uses the default model described at the start of this page.
prompt with string Yes Prompt text. Supports Liquid templating.
systemPrompt with string No System prompt sent before the user prompt.
schema with object No JSON Schema for structured output.
temperature with number (0–1) No Model temperature. Lower values are more deterministic.

Output shape:

  • Free text: { content: string, metadata: object }
  • With schema: { content: <structured object matching schema>, metadata: object }
- name: summarize
  type: ai.prompt
  connector-id: "my-openai"
  with:
    prompt: |
      Summarize this alert in one sentence:
      {{ event.alerts[0] | json }}
		
- name: extract_details
  type: ai.prompt
  connector-id: "my-openai"
  with:
    prompt: "Extract key details from this alert: {{ event.alerts[0] | json }}"
    schema:
      type: "object"
      properties:
        severity:
          type: "string"
          enum: ["low", "medium", "high", "critical"]
        summary:
          type: "string"
        key_indicators:
          type: "array"
          items:
            type: "string"
		

The structured result is available at steps.extract_details.output.content.severity, and so on.

Classify input into one of a fixed set of categories. Optionally includes a rationale and supports multi-label classification.

Parameter Location Type Required Description
connector-id top level string No Generative AI connector to use.
input with string, array, or object Yes Input to classify.
categories with array of string or object Yes Allowed categories. At least one required. Each entry is a plain category-name string. Each entry can also be an object with name and description.
instructions with string No Guidance for the classifier.
allowMultipleCategories with boolean No Allow the output to include more than one category.
fallbackCategory with string or object No Category returned when the model can't confidently choose. Accepts a plain string. Can also accept an object with name and description.
includeRationale with boolean No Include the model's reasoning in the output.
temperature with number (0–1) No Model temperature.

Output shape:

Field Type Presence
category string Present when allowMultipleCategories is false (default). Always the category name, even when you define the category as an object.
categories string[] Present when allowMultipleCategories is true.
rationale string Present when includeRationale is true.
metadata object Always.
- name: classify
  type: ai.classify
  connector-id: "my-bedrock"
  with:
    input: "${{ event.alerts[0] }}"
    categories: ["true_positive", "false_positive", "needs_investigation"]
    includeRationale: true
    fallbackCategory: "needs_investigation"
		
Tip

Always set fallbackCategory in production. Without a fallback, a confused model can fail the step. With one, every invocation produces a usable category you can branch on with a switch or if.

categories and fallbackCategory also accept an object with name and description instead of a plain string. When you supply a description, the step includes it in the classification prompt sent to the model, which improves category selection when categories are closely related or ambiguous. This doesn't affect category names in the output: category and categories in the step output always contain the category name, never the description.

- name: classify_alert
  type: ai.classify
  connector-id: "my-bedrock"
  with:
    input: "{{ inputs.alert_narrative }}"
    categories:
      - name: "Phishing"
        description: "User-targeted deception to steal credentials or deliver malicious links; no sustained host compromise pattern required."
      - name: "Malware"
        description: "Execution of malicious code, ransomware, or clear C2/beaconing on an endpoint."
      - name: "Credential Access"
        description: "Brute force, password spraying, or secret dumping without confirmed large outbound data transfer."
    fallbackCategory:
      name: "Unknown"
      description: "Insufficient signal to map confidently to any defined category."
    includeRationale: true
		

You can mix plain strings and objects in the same categories list: the classifier treats a plain string as a category with no description.

Summarize content with an LLM. Input can be a string, an array, or an object.

Parameter Location Type Required Description
connector-id top level string No Generative AI connector to use.
input with string, array, or object Yes Content to summarize.
instructions with string No Summary guidance.
maxLength with number No Approximate maximum length (positive integer).
temperature with number (0–1) No Model temperature.

Output shape: { content: string, metadata?: object }.

- name: summary
  type: ai.summarize
  connector-id: "my-gemini"
  with:
    input: "${{ steps.gather.output }}"
    instructions: "Describe the root cause in two or three sentences."
    maxLength: 500
		

Invoke an Elastic Agent Builder agent as a workflow step. Useful when you want a multi-turn agent loop embedded inside a workflow. The agent handles tool selection, reasoning, and response synthesis. The workflow handles pre- and post-processing.

Parameter Location Type Required Description
agent-id top level string No Agent to invoke. Defaults to the built-in Elastic AI Agent.
connector-id top level string No Generative AI connector. Mutually exclusive with inference-id. Generative AI connectors are deprecated, so use inference-id for new workflows.
inference-id top level string No Inference endpoint ID. Mutually exclusive with connector-id.
create-conversation top level boolean No When true, persist the conversation for follow-up steps or later reference.
public-conversation top level boolean No When true, make the conversation this step creates public. Any user who can access the agent can read and continue the conversation and see it in their conversation list. Only applies when create-conversation is true. Defaults to false (private). Refer to Conversation access control.
message with string Yes User message to send to the agent.
schema with object No JSON Schema for structured output.
conversation_id with string No Continue an existing conversation by ID.
attachments with array No Attachments to provide to the agent. Each attachment has { id?, type, data?, origin?, hidden? }.

The step ignores public-conversation if create-conversation isn't true, or if it continues an existing conversation instead of creating one. Neither case returns an error.

Output shape:

Field Type Presence
message string Always. Text response, or a string representation when schema is provided.
structured_output any Present when schema is provided.
conversation_id string Present when create-conversation: true or conversation_id was provided.
Important

Use connector-id or inference-id, not both. The schema rejects a step that sets both.

- name: investigate
  type: ai.agent
  agent-id: "security-analyst-agent"
  connector-id: "my-openai"
  create-conversation: true
  with:
    message: |
      Investigate alert {{ event.alerts[0]._id }} and propose next steps.
    schema:
      type: "object"
      properties:
        findings:
          type: "array"
          items:
            type: "string"
        recommended_action:
          type: "string"
		
- name: followup
  type: ai.agent
  agent-id: "security-analyst-agent"
  with:
    conversation_id: "{{ steps.investigate.output.conversation_id }}"
    message: "Apply the recommended action and report back."
		

Create an empty Elastic Agent Builder conversation without sending a message. Use the returned conversation_id in later steps, such as ai.conversation.add_event, ai.conversation.add_user_message, or ai.agent.

All parameters go in the with block and use snake_case.

Parameter Location Type Required Description
agent_id with string No Agent to associate with the conversation. Defaults to the built-in Elastic AI Agent. The step fails if the agent doesn't exist or the user whose privileges authorize the run can't use it.
conversation_id with string (UUID) No ID for the new conversation. Must be a UUID. Generated if omitted. The step fails if a conversation with this ID already exists.
title with string No Conversation title, up to 500 characters. Defaults to New conversation.
access_control with object No Who can access the conversation. Defaults to private. Takes access_mode (private or public) and optional entries (up to 100). Each entry adds a member and has the shape { type: user, id: <user profile UID>, role: member }. Entries are allowed only for private conversations. The step fails if you set access_mode: public with any entries. Refer to Conversation access control.
template_id with string No ID of a conversation template to apply. Templates are defined by Kibana features, and you can't create your own. The template sets default metadata for the conversation. The step fails if the template doesn't exist.
metadata with object No Initial metadata values. Requires template_id, and each key must be defined by the template. Up to 100 keys. Each value can be a string (up to 10,000 characters), a number, a boolean, or an array of up to 100 strings (up to 2,000 characters each).

Output shape:

Field Type Presence
conversation_id string Always.
agent_id string Always.
template_id string When template_id is set.
metadata object Always. The conversation metadata after template defaults and your metadata values are applied. Empty if no template is used.

If you set agent_id, set the same agent in agent-id on any ai.agent step that continues the conversation. Otherwise, ai.agent runs the default Elastic AI Agent, not the agent you set when you created the conversation.

- name: create_conversation
  type: ai.conversation.create
  with:
    title: "Checkout incident"
		

Add an event to an Elastic Agent Builder conversation without triggering an agent response. Kibana features can define their own event types, and each type defines the shape of its data payload. Elastic Agent Builder provides the text_note type for notes. The agent reads a note the next time it runs on the conversation. Other event types might only be shown in the conversation and not sent to the agent. The step works the same way as the add events to a conversation API.

All parameters go in the with block and use snake_case.

Parameter Location Type Required Description
conversation_id with string Yes Conversation to add the event to.
type with string Yes Event type, up to 256 characters. Must be a custom event type defined by Kibana, such as text_note to add a note. Built-in conversation event types, such as user messages, are rejected. To add a user message, use ai.conversation.add_user_message.
data with object Depends on type Event payload, validated against the shape that type defines. Required for text_note: set text (up to 1,000 characters) and, optionally, title (up to 256 characters). Neither field can be an empty string. Other fields in data are discarded.

Output shape:

Field Type Presence
conversation_id string Always.
event_id string Always.
type string Always.
created_at string Always. ISO 8601 timestamp.

If the event type is unknown or data doesn't match the type, the step fails and adds nothing. You can't update or remove an event after you add it.

Note

Events are attributed to the user whose privileges authorize the run, not to the workflow. That user must own the conversation or be a member of it, or the conversation must be public. The user must also be able to use the conversation's agent. Otherwise, the step fails. To find out which user authorizes a run, refer to Workflow authorization.

- name: create_conversation
  type: ai.conversation.create
  with:
    title: "Checkout incident"

- name: add_update
  type: ai.conversation.add_event
  with:
    conversation_id: "{{ steps.create_conversation.output.conversation_id }}"
    type: text_note
    data:
      title: "Status update"
      text: "Checkout service latency is back to normal."

- name: summarize
  type: ai.agent
  with:
    conversation_id: "{{ steps.create_conversation.output.conversation_id }}"
    message: "Summarize the status updates in this conversation."
		

Add a user message to an existing Elastic Agent Builder conversation without triggering an agent response. The agent reads the message the next time it runs on the conversation.

All parameters go in the with block and use snake_case.

Parameter Location Type Required Description
conversation_id with string Yes Conversation to add the message to. The conversation must already exist. To create one, use ai.conversation.create.
message with string Yes Message text. Can't be empty or contain only whitespace.

Output shape: { conversation_id: string }.

Unlike ai.agent, this step doesn't accept attachments.

Note

Messages are attributed to the user whose privileges authorize the run, not to the workflow. That user must own the conversation or be a member of it, or the conversation must be public. The user must also be able to use the conversation's agent. Otherwise, the step fails. To find out which user authorizes a run, refer to Workflow authorization.

The following example runs in a workflow with an alert trigger, after an ai.conversation.create step named create_conversation. It adds the ID of the alert that triggered the workflow to the conversation:

- name: add_alert
  type: ai.conversation.add_user_message
  with:
    conversation_id: "{{ steps.create_conversation.output.conversation_id }}"
    message: "A new alert fired: {{ event.alerts[0]._id }}"