Steps are the building blocks of conversational routes in @falai/agent with agent-level data collection. This document covers step configuration, data collection into the agent schema, and transition logic based on agent data.
Steps represent individual moments in a conversation where the agent can:
- Prompt the user for information
- Collect structured data into the agent-level schema
- Execute tools that work with complete agent data
- Make decisions about conversation flow based on agent data
- Skip execution if required data is already available from other routes
const nameStep = bookingRoute.initialStep.nextStep({
prompt: "What's your name?",
collect: ["customerName"], // Collects into agent-level schema
requires: [], // No prerequisites
skipIf: (data) => data.customerName, // Skip if already collected by any route
});The entry point for a route, executed when the route is first activated.
Steps that follow each other in a linear progression using nextStep().
Steps that can lead to multiple paths using branch() with conditional logic.
Steps that end the route using END_ROUTE.
Steps collect data through the collect array, which maps to the agent's JSON schema and is validated against it.
const contactStep = nameStep.nextStep({
prompt: "What's your email and phone number?",
collect: ["email", "phone"], // Maps to agent schema fields
requires: ["customerName"], // Must have name first (from agent data)
});Steps support various conditional behaviors based on agent-level data:
skipIf: Skip the step if a condition is met (evaluates against complete agent data)requires: Prerequisites that must be satisfied (checks agent data from any route)when: AI-evaluated conditions for branching
Steps can execute tools that work with complete agent-level data:
const weatherStep = planningStep.nextStep({
prompt: "I'll check the weather for your destination",
tool: weatherLookupTool, // Tool receives complete agent data
requires: ["destination", "checkIn"], // Prerequisites from agent data
});
// Tool implementation with agent data access
const weatherLookupTool: Tool<Context, [], WeatherData, HotelData> = {
id: "weather_lookup",
description: "Look up weather for destination",
parameters: { type: "object", properties: {} },
handler: async (toolContext) => {
const { data } = toolContext; // Complete agent data
if (!data.destination || !data.checkIn) {
return { data: undefined };
}
const weather = await getWeather(data.destination, data.checkIn);
return {
data: weather,
dataUpdate: {
weatherInfo: weather.summary // Update agent data
}
};
}
};Steps define what happens next through transitions:
// Simple next step
step.nextStep({
/* next step config */
});
// Branching paths
step.branch([
{
name: "optionA",
step: {
/* config */
},
},
{
name: "optionB",
step: {
/* config */
},
},
]);
// End route
finalStep.nextStep({ step: END_ROUTE });Steps support prepare and finalize hooks for setup and cleanup:
const dataStep = previousStep.nextStep({
prompt: "Collecting your information...",
collect: ["userData"],
prepare: async (context, data) => {
// Setup before AI response
console.log("Preparing data collection");
},
finalize: async (context, data) => {
// Cleanup after AI response
await saveProgress(data);
},
});Steps can execute together in a single LLM call when their data requirements are already satisfied and maxStepsPerBatch is set higher than 1. This reduces unnecessary back-and-forth and minimizes LLM costs.
Note: By default,
maxStepsPerBatchis1(single-step execution). Set it to a higher value orInfinityon the agent to enable batching.
The execution engine walks through Steps sequentially and includes them in a batch until encountering a Step that needs user input:
// Route with 3 steps
const route = agent.createRoute({
title: "Booking",
requiredFields: ["hotel", "date", "guests"],
initialStep: {
prompt: "Which hotel?",
collect: ["hotel"],
skipIf: (data) => !!data.hotel,
},
});
const askDate = route.initialStep.nextStep({
prompt: "What date?",
collect: ["date"],
skipIf: (data) => !!data.date,
});
const askGuests = askDate.nextStep({
prompt: "How many guests?",
collect: ["guests"],
skipIf: (data) => data.guests !== undefined,
});When a user says "Book Grand Hotel for 2 people on Friday":
- Pre-extraction captures:
{ hotel: "Grand Hotel", date: "Friday", guests: 2 } - All steps have their data satisfied (skipIf evaluates to true)
- Route completes in a single LLM call
The requires field specifies data prerequisites that must be present before a Step can execute:
const confirmStep = askGuests.nextStep({
prompt: "Confirm booking for {{guests}} guests at {{hotel}} on {{date}}?",
requires: ["hotel", "date", "guests"], // All must be present
collect: ["confirmed"],
});Batch behavior:
- If any
requiresfield is missing from session data (after pre-extraction), the Step needs input - The batch stops at this Step, and the LLM generates a response to collect the missing data
The collect field specifies which data fields the Step should extract from the conversation:
const contactStep = {
prompt: "What's your email and phone?",
collect: ["email", "phone"], // Extract both from response
};Batch behavior:
- If a Step has
collectfields and none of those fields have data in the session, the Step needs input - If any collect field already has data, the Step doesn't need input and can be included in the batch
The skipIf condition is evaluated for each Step during batch determination:
const premiumStep = {
prompt: "Would you like premium features?",
collect: ["wantsPremium"],
skipIf: (data) => data.userTier === "free", // Skip for free users
};Evaluation rules:
- If
skipIfevaluates totrue→ Step is skipped, continue to next Step - If
skipIfevaluates tofalse→ Step is evaluated for needs-input - If
skipIfthrows an error → Step is treated as non-skippable (safer to execute than skip)
// User provides partial info
const response1 = await agent.respond("I want to book the Grand Hotel");
// Response shows which steps executed
console.log(response1.executedSteps);
// [{ id: "ask-hotel", routeId: "booking" }]
console.log(response1.stoppedReason);
// "needs_input" - stopped at ask-date step
// User provides remaining info
const response2 = await agent.respond("2 people on Friday");
console.log(response2.executedSteps);
// [{ id: "ask-date", routeId: "booking" }, { id: "ask-guests", routeId: "booking" }]
console.log(response2.stoppedReason);
// "route_complete"- Keep step prompts clear and focused
- Use appropriate
requiresandskipIfconditions - Leverage schema validation for data integrity
- Implement error handling in lifecycle hooks
- Consider user experience in step sequencing
- Design steps to maximize batching by using
skipIfconditions - Use
requiresto enforce data dependencies between steps - Keep
collectfields focused on what each step actually needs