Lex's Intent-and-Slot Conversation Model
How structured bot design differs from open-ended LLM chat.
Search across all documentation pages
How structured bot design differs from open-ended LLM chat.
Amazon Lex builds conversational interfaces around a fixed vocabulary of intents and slots, defined ahead of time through a management API and then matched against user input at runtime. This is a fundamentally different shape from a large language model chatting freely - Lex resolves an utterance to one of a known set of goals, then works to fill a known set of parameters for that goal. This page builds the mental model that the rest of the lex section assumes: what an intent and a slot actually are, how the SDK splits build-time from runtime, and why that structure is a feature, not a limitation, for a large class of bots. Lex V2 is the current API generation used throughout - V1 is legacy and should not be used for new work.
Two ideas do most of the work in Lex: the intent and the slot.
An intent is a named user goal - BookHotel, OrderPizza, CheckOrderStatus. You teach Lex to recognize an intent by giving it sample utterances, example phrases a user might say ("I'd like to book a hotel", "book a room in {City}"). Lex's NLU matches a live utterance against these patterns and picks the closest intent, along with a confidence score.
A slot is a named parameter an intent needs before it can be fulfilled - City, CheckInDate, Nights for BookHotel. Each slot has a slot type, which defines what values are valid: a built-in type like AMAZON.Date, or a custom type you define as an enumeration (Small, Medium, Large for a pizza size). Lex asks the user for missing slots one at a time until the intent is fully filled, then moves to fulfillment.
This is a closed-world design. Lex does not free-associate an answer; it maps an utterance onto one of the intents you defined, and only accepts slot values it can resolve against a slot type. That is the trade-off this whole section explores: less flexible than an LLM, but predictable, auditable, and cheap to run at scale.
The SDK mirrors the intent/slot model with two distinct clients, because authoring a bot and running a conversation are different jobs with different lifecycles.
Build-time (management) uses lexv2-models in boto3 or @aws-sdk/client-lex-models-v2 in the AWS SDK for JavaScript. This is where CreateBot, CreateBotLocale, CreateIntent, CreateSlotType, and CreateSlot live - you call these once (or when the design changes), then BuildBotLocale to compile the NLU model, and CreateBotVersion plus CreateBotAlias to publish a stable, deployable snapshot.
Runtime (conversation) uses lexv2-runtime in boto3 or @aws-sdk/client-lex-runtime-v2 in the AWS SDK for JavaScript. RecognizeText sends one text utterance and gets back the matched intent, filled slots, and any prompt messages; RecognizeUtterance does the same for audio input. Every runtime call is stateless on the wire - you pass a sessionId and the previous sessionState, and Lex returns the next sessionState for you to carry forward.
# --- Python (boto3) ---
import boto3
lex_runtime = boto3.client("lexv2-runtime", region_name="us-east-1")
resp = lex_runtime.recognize_text(
botId="BOTID123", botAliasId="TSTALIASID", localeId="en_US",
sessionId="user-42", text="I'd like to book a hotel in Seattle",
)
print(resp["sessionState"]["intent"]["name"], resp["sessionState"]["dialogAction"]["type"])// --- TypeScript (AWS SDK v3) ---
import { LexRuntimeV2Client, RecognizeTextCommand } from "@aws-sdk/client-lex-runtime-v2";
const client = new LexRuntimeV2Client({ region: "us-east-1" });
const resp = await client.send(new RecognizeTextCommand({
botId: "BOTID123", botAliasId: "TSTALIASID", localeId: "en_US",
sessionId: "user-42", text: "I'd like to book a hotel in Seattle",
}));
console.log(resp.sessionState?.intent?.name, resp.sessionState?.dialogAction?.type);The response's sessionState.dialogAction.type (ElicitSlot, ConfirmIntent, Delegate, or Close) is Lex telling your client what to do next - typically, show the next prompt message and wait for the next RecognizeText call with the same sessionId.
Once an intent's slots are filled, Lex moves to fulfillment - where your business logic actually runs. This is not built into Lex; you attach a Lambda function as a code hook on the intent, and Lex invokes it with the current sessionState and expects an updated one back. The same code-hook mechanism can also run during the dialog (a dialog code hook), letting you validate a slot value or look up data before Lex asks its next question. This split - Lex owns matching and slot-filling, your Lambda owns logic and data - is what makes Lex bots feel scriptable rather than hardcoded.
Because everything is schema-driven, a Lex bot is also inherently auditable: every possible conversation path traces back to a finite set of intents, slots, and code-hook branches. That is valuable for regulated or transactional flows where you need to reason about (and test) every path a conversation can take - a property an open-ended LLM chat does not have by default.
lexv2-models) and running a conversation against it (lexv2-runtime) are separate SDK clients with separate permissions.lexv2-models / lexv2-runtime).sessionId and reacting to dialogAction correctly.An intent is the user's overall goal (BookHotel); a slot is one named parameter that intent needs (City, CheckInDate). An intent typically has several slots, each with its own slot type.
Building/managing uses lexv2-models (boto3) or @aws-sdk/client-lex-models-v2 (v3). Running a live conversation uses lexv2-runtime (boto3) or @aws-sdk/client-lex-runtime-v2 (v3).
No. Lex V2 is the current generation with the API surface covered in this section. Treat V1 as legacy.
The dialogAction.type in sessionState - ElicitSlot for the next missing slot, ConfirmIntent before fulfillment, Delegate to let Lex decide, or Close when the turn is done.
In a Lambda function attached to the intent as a code hook (dialog and/or fulfillment). Lex calls it with the current session state and expects an updated one back.
When the conversation is open-ended, needs broad reasoning, or cannot be enumerated as a fixed set of intents and slots. See the dedicated comparison page for the full trade-off.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 24, 2026