@falai/agent 0.1.0-alpha2 → 0.1.0-alpha3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +106 -0
  2. package/dist/cjs/src/core/BatchExecutor.d.ts +353 -0
  3. package/dist/cjs/src/core/BatchExecutor.d.ts.map +1 -0
  4. package/dist/cjs/src/core/BatchExecutor.js +842 -0
  5. package/dist/cjs/src/core/BatchExecutor.js.map +1 -0
  6. package/dist/cjs/src/core/BatchPromptBuilder.d.ts +86 -0
  7. package/dist/cjs/src/core/BatchPromptBuilder.d.ts.map +1 -0
  8. package/dist/cjs/src/core/BatchPromptBuilder.js +201 -0
  9. package/dist/cjs/src/core/BatchPromptBuilder.js.map +1 -0
  10. package/dist/cjs/src/core/ResponseModal.d.ts +34 -0
  11. package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -1
  12. package/dist/cjs/src/core/ResponseModal.js +460 -34
  13. package/dist/cjs/src/core/ResponseModal.js.map +1 -1
  14. package/dist/cjs/src/index.d.ts +2 -0
  15. package/dist/cjs/src/index.d.ts.map +1 -1
  16. package/dist/cjs/src/index.js +7 -1
  17. package/dist/cjs/src/index.js.map +1 -1
  18. package/dist/cjs/src/types/agent.d.ts +9 -1
  19. package/dist/cjs/src/types/agent.d.ts.map +1 -1
  20. package/dist/cjs/src/types/route.d.ts +98 -0
  21. package/dist/cjs/src/types/route.d.ts.map +1 -1
  22. package/dist/src/core/BatchExecutor.d.ts +353 -0
  23. package/dist/src/core/BatchExecutor.d.ts.map +1 -0
  24. package/dist/src/core/BatchExecutor.js +837 -0
  25. package/dist/src/core/BatchExecutor.js.map +1 -0
  26. package/dist/src/core/BatchPromptBuilder.d.ts +86 -0
  27. package/dist/src/core/BatchPromptBuilder.d.ts.map +1 -0
  28. package/dist/src/core/BatchPromptBuilder.js +197 -0
  29. package/dist/src/core/BatchPromptBuilder.js.map +1 -0
  30. package/dist/src/core/ResponseModal.d.ts +34 -0
  31. package/dist/src/core/ResponseModal.d.ts.map +1 -1
  32. package/dist/src/core/ResponseModal.js +460 -34
  33. package/dist/src/core/ResponseModal.js.map +1 -1
  34. package/dist/src/index.d.ts +2 -0
  35. package/dist/src/index.d.ts.map +1 -1
  36. package/dist/src/index.js +2 -0
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/types/agent.d.ts +9 -1
  39. package/dist/src/types/agent.d.ts.map +1 -1
  40. package/dist/src/types/route.d.ts +98 -0
  41. package/dist/src/types/route.d.ts.map +1 -1
  42. package/docs/api/overview.md +124 -0
  43. package/docs/architecture/multi-step-execution.md +243 -0
  44. package/docs/core/ai-integration/prompt-composition.md +135 -0
  45. package/docs/core/ai-integration/response-processing.md +146 -0
  46. package/docs/core/conversation-flows/data-collection.md +143 -0
  47. package/docs/core/conversation-flows/step-transitions.md +132 -0
  48. package/docs/core/conversation-flows/steps.md +112 -0
  49. package/docs/core/error-handling.md +193 -0
  50. package/docs/guides/getting-started/README.md +102 -0
  51. package/docs/guides/migration/README.md +23 -0
  52. package/docs/guides/migration/multi-step-execution.md +303 -0
  53. package/package.json +4 -2
  54. package/src/core/BatchExecutor.ts +1156 -0
  55. package/src/core/BatchPromptBuilder.ts +275 -0
  56. package/src/core/ResponseModal.ts +605 -35
  57. package/src/index.ts +2 -0
  58. package/src/types/agent.ts +9 -1
  59. package/src/types/route.ts +119 -0
@@ -0,0 +1,243 @@
1
+ # Multi-Step Execution Architecture
2
+
3
+ ## Overview
4
+
5
+ @falai/agent supports **multi-step execution**, enabling multiple consecutive Steps to execute in a single LLM call. This reduces unnecessary multi-turn conversations, minimizes LLM costs, and improves user experience when steps don't require new user input.
6
+
7
+ The core insight is simple: **if a Step's data requirements are already satisfied, there's no reason to pause and wait for user input**. By detecting which Steps can execute together and combining them into a single batch, we reduce LLM calls while improving conversation UX.
8
+
9
+ ## Design Priorities
10
+
11
+ 1. **Conversation UX** - Reduce unnecessary back-and-forth
12
+ 2. **LLM Cost Optimization** - Fewer calls for the same outcome
13
+ 3. **Predictability** - Clear, simple rules for when execution pauses
14
+ 4. **Framework Understandability** - Keep the Step/Route mental model intact
15
+
16
+ ## Batch Execution Flow
17
+
18
+ ```mermaid
19
+ flowchart TD
20
+ A[User Message] --> B[Pre-Extraction]
21
+ B --> C[Batch Determination]
22
+ C --> D{More Steps?}
23
+ D -->|Yes| E[Evaluate Step]
24
+ E --> F{Needs Input?}
25
+ F -->|No| G[Add to Batch]
26
+ G --> D
27
+ F -->|Yes| H[Stop Batch]
28
+ D -->|No| H
29
+ H --> I[Execute Prepare Hooks]
30
+ I --> J[Build Combined Prompt]
31
+ J --> K[Single LLM Call]
32
+ K --> L[Extract Response Data]
33
+ L --> M[Execute Finalize Hooks]
34
+ M --> N[Update Session]
35
+ N --> O[Return Response]
36
+ ```
37
+
38
+ ## Execution Phases
39
+
40
+ ### Phase 1: Pre-Extraction
41
+
42
+ When processing a user message, the engine performs **pre-extraction** before determining the batch. This phase:
43
+
44
+ 1. Attempts to extract data for all fields defined in the Route's `requiredFields` and `optionalFields`
45
+ 2. Merges pre-extracted data into session data
46
+ 3. Maximizes batching by satisfying Step requirements upfront
47
+
48
+ ```typescript
49
+ // User: "I want to book the Grand Hotel for 2 people next Friday"
50
+
51
+ // Pre-extraction automatically captures:
52
+ {
53
+ hotelName: "Grand Hotel",
54
+ guests: 2,
55
+ date: "next Friday"
56
+ }
57
+ ```
58
+
59
+ ### Phase 2: Batch Determination
60
+
61
+ The `BatchExecutor` walks through Steps using this algorithm:
62
+
63
+ ```
64
+ For each Step starting from current position:
65
+ a. Check if it's END_ROUTE → stop with 'end_route'
66
+ b. Evaluate skipIf condition
67
+ c. If skipIf is true → skip Step, continue to next
68
+ d. If skipIf throws error → treat as non-skippable
69
+ e. Evaluate needsInput(step, sessionDataAfterPreExtraction)
70
+ f. If needsInput is false → include Step in batch, continue to next
71
+ g. If needsInput is true → stop with 'needs_input'
72
+ ```
73
+
74
+ ### Phase 3: Hook Execution (Prepare)
75
+
76
+ Before the LLM call, all `prepare` hooks for batched Steps execute in Step order. If any prepare hook fails, batch execution stops immediately.
77
+
78
+ ### Phase 4: LLM Call
79
+
80
+ A single LLM call is made with a combined prompt that includes all Step prompts and collect fields.
81
+
82
+ ### Phase 5: Data Collection
83
+
84
+ All `collect` fields from all Steps in the batch are extracted from the LLM response and validated against the agent schema.
85
+
86
+ ### Phase 6: Hook Execution (Finalize)
87
+
88
+ After the LLM response, all `finalize` hooks execute in Step order. Finalize hook failures are logged but don't stop execution.
89
+
90
+ ## Needs-Input Detection Algorithm
91
+
92
+ The `needsInput` function implements the core execution rule:
93
+
94
+ ```typescript
95
+ function needsInput<TData>(
96
+ step: Step<unknown, TData>,
97
+ sessionDataAfterPreExtraction: Partial<TData>
98
+ ): boolean {
99
+ // Check requires - all must be present (after pre-extraction)
100
+ if (step.requires && step.requires.length > 0) {
101
+ const missingRequired = step.requires.some(
102
+ field => sessionDataAfterPreExtraction[field] === undefined
103
+ );
104
+ if (missingRequired) return true;
105
+ }
106
+
107
+ // Check collect - needs input if collecting and no data exists (after pre-extraction)
108
+ if (step.collect && step.collect.length > 0) {
109
+ const hasAnyCollectData = step.collect.some(
110
+ field => sessionDataAfterPreExtraction[field] !== undefined
111
+ );
112
+ if (!hasAnyCollectData) return true;
113
+ }
114
+
115
+ return false;
116
+ }
117
+ ```
118
+
119
+ A Step **needs input** when:
120
+
121
+ 1. It has `requires` fields and at least one is missing from session data (after pre-extraction)
122
+ 2. It has non-empty `collect` fields and none of those fields have data (after pre-extraction)
123
+
124
+ ## Pre-Extraction's Role in Batching
125
+
126
+ Pre-extraction is critical for maximizing batch size. Consider this scenario:
127
+
128
+ ```typescript
129
+ // Route with 3 steps
130
+ const route = agent.createRoute({
131
+ title: "Booking",
132
+ requiredFields: ["hotel", "date", "guests"],
133
+ steps: [
134
+ { prompt: "Which hotel?", collect: ["hotel"] },
135
+ { prompt: "What date?", collect: ["date"] },
136
+ { prompt: "How many guests?", collect: ["guests"] }
137
+ ]
138
+ });
139
+
140
+ // User message: "Book Grand Hotel for 2 people on Friday"
141
+ ```
142
+
143
+ **Without pre-extraction:**
144
+ - Step 1 needs input (no hotel data) → batch stops
145
+ - 3 separate LLM calls needed
146
+
147
+ **With pre-extraction:**
148
+ - Pre-extraction captures: `{ hotel: "Grand Hotel", date: "Friday", guests: 2 }`
149
+ - All steps have their data satisfied
150
+ - All 3 steps execute in a single batch
151
+ - 1 LLM call needed
152
+
153
+ ## Key Components
154
+
155
+ ### BatchExecutor
156
+
157
+ The core component responsible for:
158
+ - Determining which Steps can execute together (`determineBatch`)
159
+ - Executing prepare/finalize hooks (`executePrepareHooks`, `executeFinalizeHooks`)
160
+ - Collecting data from LLM responses (`collectBatchData`)
161
+ - Orchestrating complete batch execution (`executeBatch`)
162
+
163
+ ### BatchPromptBuilder
164
+
165
+ Combines multiple Step prompts into a single coherent prompt:
166
+ - Preserves each Step's individual prompt intent
167
+ - Includes data collection instructions for all `collect` fields
168
+ - Produces a single LLM call regardless of Step count
169
+
170
+ ## Example: Multi-Step Batch
171
+
172
+ ```typescript
173
+ // User provides all booking info at once
174
+ const response = await agent.respond(
175
+ "I want to book the Grand Hotel for 2 people next Friday"
176
+ );
177
+
178
+ // Response includes:
179
+ {
180
+ message: "Perfect! I've booked the Grand Hotel for 2 guests on Friday.",
181
+ executedSteps: [
182
+ { id: "ask-hotel", routeId: "booking" },
183
+ { id: "ask-date", routeId: "booking" },
184
+ { id: "ask-guests", routeId: "booking" }
185
+ ],
186
+ stoppedReason: "route_complete",
187
+ session: {
188
+ data: {
189
+ hotel: "Grand Hotel",
190
+ date: "Friday",
191
+ guests: 2
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## Debugging
198
+
199
+ Enable debug mode to see batch execution decisions:
200
+
201
+ ```typescript
202
+ const agent = new Agent({
203
+ name: "Assistant",
204
+ provider: provider,
205
+ debug: true // Enable detailed logging
206
+ });
207
+ ```
208
+
209
+ Look for these log messages:
210
+
211
+ ```
212
+ [BatchExecutor] Starting batch determination from step index 0
213
+ [BatchExecutor] Including step ask-hotel in batch (all requirements satisfied)
214
+ [BatchExecutor] Including step ask-date in batch (all requirements satisfied)
215
+ [BatchExecutor] Step ask-guests needs input, stopping batch
216
+ [BatchExecutor] Batch determination complete. Steps: 2, Stopped reason: needs_input
217
+ ```
218
+
219
+ ## Event Emission
220
+
221
+ The BatchExecutor emits events for observability:
222
+
223
+ - `batch_start` - When batch determination begins
224
+ - `step_included` - When a step is included in the batch
225
+ - `step_skipped` - When a step is skipped (due to skipIf)
226
+ - `batch_stop` - When batch determination stops
227
+ - `batch_complete` - When batch execution completes
228
+
229
+ ```typescript
230
+ const executor = new BatchExecutor();
231
+
232
+ executor.addEventListener((event) => {
233
+ console.log(`[${event.type}]`, event.details);
234
+ });
235
+ ```
236
+
237
+ ---
238
+
239
+ **Related Documentation:**
240
+ - [Steps](../core/conversation-flows/steps.md) - Step configuration and batching
241
+ - [Step Transitions](../core/conversation-flows/step-transitions.md) - Stopping conditions
242
+ - [Data Collection](../core/conversation-flows/data-collection.md) - Pre-extraction details
243
+ - [Prompt Composition](../core/ai-integration/prompt-composition.md) - Combined prompts
@@ -218,3 +218,138 @@ Prompts are optimized for token efficiency:
218
218
  - Leverage route-specific overrides for specialized behavior
219
219
  - Monitor token usage and optimize prompt length
220
220
  - Test prompts with different AI providers for consistency
221
+
222
+ ## BatchPromptBuilder for Multi-Step Execution
223
+
224
+ When multiple steps execute in a single batch, the `BatchPromptBuilder` combines their prompts into a single coherent prompt.
225
+
226
+ ### Combined Prompt Structure
227
+
228
+ ```
229
+ [Agent Identity & Personality]
230
+ [Route Context]
231
+
232
+ ## Current Conversation Flow
233
+
234
+ You are handling multiple aspects of this conversation in a single response.
235
+
236
+ ### Step 1: [Step Description]
237
+ [Step Prompt]
238
+
239
+ ### Step 2: [Step Description]
240
+ [Step Prompt]
241
+
242
+ [... additional steps ...]
243
+
244
+ ## Data Collection
245
+
246
+ Extract the following information from your response:
247
+ - field1 (type): description
248
+ - field2 (type): description
249
+
250
+ ## Response Format
251
+
252
+ Return JSON with:
253
+ - message: Your response to the user
254
+ - [collected fields as top-level properties]
255
+ ```
256
+
257
+ ### How Prompts Are Merged
258
+
259
+ The `BatchPromptBuilder` preserves each step's intent while creating a unified prompt:
260
+
261
+ ```typescript
262
+ // Individual step prompts
263
+ const step1 = { prompt: "What's your name?", collect: ["name"] };
264
+ const step2 = { prompt: "What's your email?", collect: ["email"] };
265
+ const step3 = { prompt: "How can I help?", collect: ["request"] };
266
+
267
+ // Combined prompt includes all three
268
+ const result = await batchPromptBuilder.buildBatchPrompt({
269
+ steps: [step1, step2, step3],
270
+ route,
271
+ history,
272
+ context,
273
+ session,
274
+ agentOptions,
275
+ });
276
+
277
+ // result.prompt contains unified prompt
278
+ // result.collectFields = ["name", "email", "request"]
279
+ // result.stepCount = 3
280
+ ```
281
+
282
+ ### Collect Fields Aggregation
283
+
284
+ All `collect` fields from all steps are combined and deduplicated:
285
+
286
+ ```typescript
287
+ // Steps with overlapping collect fields
288
+ const steps = [
289
+ { collect: ["name", "email"] },
290
+ { collect: ["email", "phone"] }, // email appears twice
291
+ { collect: ["preferences"] }
292
+ ];
293
+
294
+ // Combined collect fields (deduplicated)
295
+ // ["name", "email", "phone", "preferences"]
296
+ ```
297
+
298
+ ### Schema-Aware Field Descriptions
299
+
300
+ When the agent has a schema, field descriptions are included in the prompt:
301
+
302
+ ```typescript
303
+ // Agent schema
304
+ const schema = {
305
+ properties: {
306
+ email: { type: "string", format: "email", description: "User's email address" },
307
+ guests: { type: "number", minimum: 1, description: "Number of guests" }
308
+ }
309
+ };
310
+
311
+ // Generated data collection section:
312
+ // ## Data Collection
313
+ // Extract the following information from your response:
314
+ // - email (string): User's email address
315
+ // - guests (number): Number of guests
316
+ ```
317
+
318
+ ### Single vs Multi-Step Prompts
319
+
320
+ The prompt structure adapts based on batch size:
321
+
322
+ ```typescript
323
+ // Single step batch
324
+ // ## Current Step
325
+ // [Step prompt]
326
+
327
+ // Multi-step batch
328
+ // ## Current Conversation Flow
329
+ // You are handling multiple aspects of this conversation in a single response.
330
+ // ### Step 1: ...
331
+ // ### Step 2: ...
332
+ ```
333
+
334
+ ### Using BatchPromptBuilder
335
+
336
+ ```typescript
337
+ import { BatchPromptBuilder } from "@falai/agent";
338
+
339
+ const builder = new BatchPromptBuilder<MyContext, MyData>();
340
+
341
+ const result = await builder.buildBatchPrompt({
342
+ steps: batchResult.steps,
343
+ route: currentRoute,
344
+ history: conversationHistory,
345
+ context: agentContext,
346
+ session: currentSession,
347
+ agentOptions: agent.getAgentOptions(),
348
+ });
349
+
350
+ // Use result.prompt for LLM call
351
+ const llmResponse = await provider.generateMessage({
352
+ prompt: result.prompt,
353
+ // ...
354
+ });
355
+ ```
@@ -277,6 +277,150 @@ Built-in monitoring capabilities:
277
277
  - **Debug logging** - Detailed processing traces
278
278
  - **Performance profiling** - Identify bottlenecks
279
279
 
280
+ ## Batch Execution Response Fields
281
+
282
+ When using multi-step execution, the `AgentResponse` includes additional fields for batch execution information.
283
+
284
+ ### New AgentResponse Fields
285
+
286
+ ```typescript
287
+ interface AgentResponse<TData = unknown> {
288
+ /** The generated message */
289
+ message: string;
290
+ /** Updated session state */
291
+ session?: SessionState<TData>;
292
+ /** Tool calls made during response */
293
+ toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
294
+ /** Whether the route is complete */
295
+ isRouteComplete?: boolean;
296
+
297
+ // Multi-step execution fields
298
+ /** Steps executed in this response */
299
+ executedSteps?: StepRef[];
300
+ /** Why execution stopped */
301
+ stoppedReason?: StoppedReason;
302
+ }
303
+ ```
304
+
305
+ ### executedSteps Array
306
+
307
+ Lists all steps that were executed in the batch:
308
+
309
+ ```typescript
310
+ const response = await agent.respond("Book Grand Hotel for 2 on Friday");
311
+
312
+ console.log(response.executedSteps);
313
+ // [
314
+ // { id: "ask-hotel", routeId: "booking" },
315
+ // { id: "ask-guests", routeId: "booking" },
316
+ // { id: "ask-date", routeId: "booking" }
317
+ // ]
318
+ ```
319
+
320
+ Each `StepRef` contains:
321
+ - `id` - The step identifier
322
+ - `routeId` - The route this step belongs to
323
+
324
+ ### stoppedReason Field
325
+
326
+ Indicates why batch execution stopped:
327
+
328
+ ```typescript
329
+ type StoppedReason =
330
+ | 'needs_input' // Step requires uncollected data
331
+ | 'end_route' // Reached END_ROUTE
332
+ | 'route_complete' // All Steps processed
333
+ | 'prepare_error' // Error in prepare hook
334
+ | 'llm_error' // Error during LLM call
335
+ | 'validation_error' // Error validating collected data
336
+ | 'finalize_error'; // Error in finalize hook (non-fatal)
337
+ ```
338
+
339
+ ### BatchExecutionResult Structure
340
+
341
+ The internal batch execution result provides detailed information:
342
+
343
+ ```typescript
344
+ interface BatchExecutionResult<TData = unknown> {
345
+ /** The generated message */
346
+ message: string;
347
+ /** Updated session state */
348
+ session: SessionState<TData>;
349
+ /** Steps that were executed */
350
+ executedSteps: StepRef[];
351
+ /** Why execution stopped */
352
+ stoppedReason: StoppedReason;
353
+ /** Collected data from the batch */
354
+ collectedData?: Partial<TData>;
355
+ /** Any errors that occurred */
356
+ error?: BatchExecutionError;
357
+ }
358
+ ```
359
+
360
+ ### Error Information
361
+
362
+ When errors occur, detailed information is available:
363
+
364
+ ```typescript
365
+ interface BatchExecutionError {
366
+ /** Type of error that occurred */
367
+ type: 'pre_extraction' | 'skipif_evaluation' | 'prepare_hook' |
368
+ 'llm_call' | 'data_validation' | 'finalize_hook';
369
+ /** Error message */
370
+ message: string;
371
+ /** Step where error occurred (if applicable) */
372
+ stepId?: string;
373
+ /** Additional error details */
374
+ details?: unknown;
375
+ }
376
+ ```
377
+
378
+ ### Using Response Fields
379
+
380
+ ```typescript
381
+ const response = await agent.respond("Complete my booking");
382
+
383
+ // Check what was executed
384
+ console.log(`Executed ${response.executedSteps?.length || 0} steps`);
385
+
386
+ // Check why execution stopped
387
+ switch (response.stoppedReason) {
388
+ case 'needs_input':
389
+ console.log("Waiting for more information from user");
390
+ break;
391
+ case 'route_complete':
392
+ case 'end_route':
393
+ console.log("Route finished successfully");
394
+ break;
395
+ case 'validation_error':
396
+ console.log("Data validation issues:", response.error);
397
+ break;
398
+ default:
399
+ console.log("Stopped due to:", response.stoppedReason);
400
+ }
401
+
402
+ // Check route completion
403
+ if (response.isRouteComplete) {
404
+ console.log("All required data collected");
405
+ }
406
+ ```
407
+
408
+ ### Session State After Batch
409
+
410
+ The session state reflects the final step position:
411
+
412
+ ```typescript
413
+ const response = await agent.respond("Book for 2 guests");
414
+
415
+ // Session shows current position
416
+ console.log(response.session?.currentStep);
417
+ // { id: "ask-date", routeId: "booking" }
418
+
419
+ // Session data includes all collected values
420
+ console.log(response.session?.data);
421
+ // { hotel: "Grand Hotel", guests: 2 }
422
+ ```
423
+
280
424
  ## Best Practices
281
425
 
282
426
  - Design schemas for reliable AI extraction
@@ -285,3 +429,5 @@ Built-in monitoring capabilities:
285
429
  - Use streaming for better user experience
286
430
  - Leverage tool results for context enrichment
287
431
  - Validate data at multiple levels (schema + business rules)
432
+ - Check `executedSteps` to understand batch behavior
433
+ - Handle different `stoppedReason` values appropriately
@@ -13,6 +13,149 @@ The agent-level data collection system provides:
13
13
  - **Natural Conversations**: AI handles information gathering conversationally
14
14
  - **Validation & Enrichment**: Agent-level lifecycle hooks for data processing
15
15
  - **Session Persistence**: Data survives across conversation turns and route transitions
16
+ - **Batch Data Collection**: Multiple steps can collect data in a single LLM call
17
+
18
+ ## Data Collection Across Batched Steps
19
+
20
+ When multiple steps execute in a single batch, data collection works across all steps simultaneously.
21
+
22
+ ### How Batch Data Collection Works
23
+
24
+ 1. **Gather collect fields** - All `collect` fields from all steps in the batch are combined
25
+ 2. **Single LLM call** - The combined prompt instructs the LLM to extract all fields
26
+ 3. **Extract from response** - All specified fields are extracted from the LLM response
27
+ 4. **Validate against schema** - Collected data is validated against the agent schema
28
+ 5. **Update session** - All collected values are merged into session data
29
+
30
+ ```typescript
31
+ // Batch with 3 steps, each collecting different fields
32
+ const batch = [
33
+ { collect: ["name"] },
34
+ { collect: ["email", "phone"] },
35
+ { collect: ["preferences"] }
36
+ ];
37
+
38
+ // Combined collection: ["name", "email", "phone", "preferences"]
39
+ // Single LLM response extracts all fields at once
40
+ ```
41
+
42
+ ### Pre-Extraction and Batch Determination
43
+
44
+ Pre-extraction happens **before** batch determination and directly impacts which steps can be batched:
45
+
46
+ ```typescript
47
+ // User message: "I'm John, email john@example.com, I prefer dark mode"
48
+
49
+ // Phase 1: Pre-extraction
50
+ const preExtracted = {
51
+ name: "John",
52
+ email: "john@example.com",
53
+ preferences: { theme: "dark" }
54
+ };
55
+
56
+ // Phase 2: Batch determination (with pre-extracted data merged)
57
+ // Step 1: collect: ["name"] → name exists → doesn't need input
58
+ // Step 2: collect: ["email"] → email exists → doesn't need input
59
+ // Step 3: collect: ["preferences"] → preferences exists → doesn't need input
60
+ // Result: All 3 steps batched together
61
+ ```
62
+
63
+ ### Pre-Extraction Configuration
64
+
65
+ Pre-extraction is automatic when routes define data fields:
66
+
67
+ ```typescript
68
+ // Option 1: Route-level required fields
69
+ agent.createRoute({
70
+ title: "Booking",
71
+ requiredFields: ["hotel", "date", "guests"],
72
+ // Pre-extraction enabled automatically
73
+ });
74
+
75
+ // Option 2: Route-level optional fields
76
+ agent.createRoute({
77
+ title: "Booking",
78
+ optionalFields: ["specialRequests"],
79
+ // Pre-extraction enabled automatically
80
+ });
81
+
82
+ // Option 3: Steps with collect arrays
83
+ agent.createRoute({
84
+ title: "Booking",
85
+ steps: [
86
+ { collect: ["hotel"] }, // Pre-extraction enabled automatically
87
+ ]
88
+ });
89
+ ```
90
+
91
+ ### Batch Collection Example
92
+
93
+ ```typescript
94
+ const response = await agent.respond(
95
+ "I'm Alice, alice@example.com, and I want to book for 2 guests"
96
+ );
97
+
98
+ // Response includes all collected data
99
+ console.log(response.session.data);
100
+ // {
101
+ // name: "Alice",
102
+ // email: "alice@example.com",
103
+ // guests: 2
104
+ // }
105
+
106
+ // Shows which steps executed
107
+ console.log(response.executedSteps);
108
+ // [
109
+ // { id: "ask-name", routeId: "booking" },
110
+ // { id: "ask-email", routeId: "booking" },
111
+ // { id: "ask-guests", routeId: "booking" }
112
+ // ]
113
+ ```
114
+
115
+ ### Validation Across Batch
116
+
117
+ Data validation happens after collection for all fields:
118
+
119
+ ```typescript
120
+ // Agent schema defines validation rules
121
+ const agent = new Agent({
122
+ schema: {
123
+ type: "object",
124
+ properties: {
125
+ email: { type: "string", format: "email" },
126
+ guests: { type: "number", minimum: 1, maximum: 10 }
127
+ }
128
+ }
129
+ });
130
+
131
+ // If validation fails for any field:
132
+ const response = await agent.respond("Book for 100 guests");
133
+
134
+ // Response includes validation errors
135
+ if (response.stoppedReason === 'validation_error') {
136
+ console.log(response.error);
137
+ // {
138
+ // type: 'data_validation',
139
+ // message: 'Validation failed for 1 field(s): guests',
140
+ // details: [{ field: 'guests', message: 'Value exceeds maximum of 10' }]
141
+ // }
142
+ }
143
+ ```
144
+
145
+ ### Partial Data Preservation
146
+
147
+ Even when validation fails, valid partial data is preserved:
148
+
149
+ ```typescript
150
+ // User provides valid name but invalid email
151
+ const response = await agent.respond("I'm John, email: not-an-email");
152
+
153
+ // Valid data is still collected
154
+ console.log(response.session.data.name); // "John"
155
+
156
+ // Invalid data triggers validation error
157
+ console.log(response.stoppedReason); // "validation_error"
158
+ ```
16
159
 
17
160
  ## Agent-Level Schema Definition
18
161