﻿---
title: AI steps
description: Reference for the four AI step types (prompt, classify, summarize, and agent) that bring LLM calls into workflows.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/workflows/steps/ai-steps
products:
  - Elastic Cloud Enterprise
  - Elastic Cloud Hosted
  - Elastic Cloud Serverless
  - Elastic Cloud on Kubernetes
  - Elastic Stack
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Generally available since 9.4, Preview in 9.3
---

# AI steps
AI steps let workflows call a large language model (LLM) for reasoning, classification, summarization, or agent-driven execution. All four AI step types share a connector-based auth model: configure a generative AI connector in Kibana, then reference it by ID from a workflow step. When `connector-id` is omitted, the default connector configured for the workflow is used.

## Step types

- [`ai.prompt`](#ai-prompt): Send a prompt to a model and receive a response. Supports structured output with a JSON Schema. <applies-to>Elastic Stack: Generally available since 9.4, Elastic Stack: Preview in 9.3</applies-to>
- [`ai.classify`](#ai-classify): Classify input into a fixed set of categories. <applies-to>Elastic Stack: Generally available since 9.4</applies-to>
- [`ai.summarize`](#ai-summarize): Generate a summary of the provided content. <applies-to>Elastic Stack: Generally available since 9.4</applies-to>
- [`ai.agent`](#ai-agent): Invoke an Elastic Agent Builder agent as a workflow step. <applies-to>Elastic Stack: Generally available since 9.4, Elastic Stack: Preview in 9.3</applies-to>

`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 AI parameters use `camelCase` (`systemPrompt`, `maxLength`, `includeRationale`).
<applies-switch>
  <applies-item title="{ stack: ga 9.5, serverless: ga }" applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Planned">
    Liquid expressions are evaluated in these top-level fields, so you can use templated values (for example, `"{{ consts.agent_id }}"`).
  </applies-item>

  <applies-item title="stack: ga 9.3-9.4" applies-to="Elastic Stack: Generally available from 9.3 to 9.4">
    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 }}"`.
  </applies-item>
</applies-switch>

**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.


## `ai.prompt`

<applies-to>
  - Elastic Stack: Generally available since 9.4
  - Elastic Stack: Preview in 9.3
</applies-to>

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 GenAI connector to use. Defaults to the workflow's default connector if omitted. |
| `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 }`

```yaml
- name: summarize
  type: ai.prompt
  connector-id: "my-openai"
  with:
    prompt: |
      Summarize this alert in one sentence:
      {{ event.alerts[0] | json }}
```


### Example: Structured output

```yaml
- 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.

## `ai.classify`

<applies-to>
  - Elastic Stack: Generally available since 9.4
</applies-to>

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       | GenAI connector to use.                                                                                                                                                                                                                                         |
| `input`                   | `with`    | string, array, or object              | Yes      | Input to classify.                                                                                                                                                                                                                                              |
| `categories`              | `with`    | `(string \| { name, description })[]` | Yes      | Allowed categories. At least one required. Each entry is either a plain category-name string or an object with `name` and `description`. <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> |
| `instructions`            | `with`    | string                                | No       | Guidance for the classifier.                                                                                                                                                                                                                                    |
| `allowMultipleCategories` | `with`    | boolean                               | No       | Allow the output to include more than one category.                                                                                                                                                                                                             |
| `fallbackCategory`        | `with`    | `string \| { name, description }`     | No       | Category returned when the model can't confidently choose. Accepts a plain string or an object with `name` and `description`. <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>            |
| `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.                                                                                                                                |

```yaml
- 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`](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/workflows/steps/switch) or [`if`](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/workflows/steps/if).
</tip>


### Example: Category descriptions

<applies-to>
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Planned
</applies-to>

`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.
```yaml
- 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.

## `ai.summarize`

<applies-to>
  - Elastic Stack: Generally available since 9.4
</applies-to>

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       | GenAI 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 }`.
```yaml
- 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
```


## `ai.agent`

<applies-to>
  - Elastic Stack: Generally available since 9.4
  - Elastic Stack: Preview in 9.3
</applies-to>

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       | GenAI connector. Mutually exclusive with `inference-id`.                                           |
| `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.                      |
| `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? }`. |

**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.
</important>


### Example: Call an agent

```yaml
- 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"
```


### Example: Continue a conversation

```yaml
- 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."
```


## Related

- [Configured AI connectors](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/deploy-manage/manage-connectors): Set up the GenAI connector referenced by `connector-id`.
- [Switch step](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/workflows/steps/switch): A common pairing: classify with `ai.classify`, then dispatch with `switch`.
- [Human-in-the-loop](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/workflows/authoring-techniques/human-in-the-loop): Pair AI classifications with a reviewer gate for uncertain cases.
- [Elastic Agent Builder overview](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/7644/explore-analyze/ai-features/elastic-agent-builder): Concepts behind the agents referenced by `ai.agent`.