@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.
- package/README.md +106 -0
- package/dist/cjs/src/core/BatchExecutor.d.ts +353 -0
- package/dist/cjs/src/core/BatchExecutor.d.ts.map +1 -0
- package/dist/cjs/src/core/BatchExecutor.js +842 -0
- package/dist/cjs/src/core/BatchExecutor.js.map +1 -0
- package/dist/cjs/src/core/BatchPromptBuilder.d.ts +86 -0
- package/dist/cjs/src/core/BatchPromptBuilder.d.ts.map +1 -0
- package/dist/cjs/src/core/BatchPromptBuilder.js +201 -0
- package/dist/cjs/src/core/BatchPromptBuilder.js.map +1 -0
- package/dist/cjs/src/core/ResponseModal.d.ts +34 -0
- package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/src/core/ResponseModal.js +460 -34
- package/dist/cjs/src/core/ResponseModal.js.map +1 -1
- package/dist/cjs/src/index.d.ts +2 -0
- package/dist/cjs/src/index.d.ts.map +1 -1
- package/dist/cjs/src/index.js +7 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/types/agent.d.ts +9 -1
- package/dist/cjs/src/types/agent.d.ts.map +1 -1
- package/dist/cjs/src/types/route.d.ts +98 -0
- package/dist/cjs/src/types/route.d.ts.map +1 -1
- package/dist/src/core/BatchExecutor.d.ts +353 -0
- package/dist/src/core/BatchExecutor.d.ts.map +1 -0
- package/dist/src/core/BatchExecutor.js +837 -0
- package/dist/src/core/BatchExecutor.js.map +1 -0
- package/dist/src/core/BatchPromptBuilder.d.ts +86 -0
- package/dist/src/core/BatchPromptBuilder.d.ts.map +1 -0
- package/dist/src/core/BatchPromptBuilder.js +197 -0
- package/dist/src/core/BatchPromptBuilder.js.map +1 -0
- package/dist/src/core/ResponseModal.d.ts +34 -0
- package/dist/src/core/ResponseModal.d.ts.map +1 -1
- package/dist/src/core/ResponseModal.js +460 -34
- package/dist/src/core/ResponseModal.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/agent.d.ts +9 -1
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/route.d.ts +98 -0
- package/dist/src/types/route.d.ts.map +1 -1
- package/docs/api/overview.md +124 -0
- package/docs/architecture/multi-step-execution.md +243 -0
- package/docs/core/ai-integration/prompt-composition.md +135 -0
- package/docs/core/ai-integration/response-processing.md +146 -0
- package/docs/core/conversation-flows/data-collection.md +143 -0
- package/docs/core/conversation-flows/step-transitions.md +132 -0
- package/docs/core/conversation-flows/steps.md +112 -0
- package/docs/core/error-handling.md +193 -0
- package/docs/guides/getting-started/README.md +102 -0
- package/docs/guides/migration/README.md +23 -0
- package/docs/guides/migration/multi-step-execution.md +303 -0
- package/package.json +4 -2
- package/src/core/BatchExecutor.ts +1156 -0
- package/src/core/BatchPromptBuilder.ts +275 -0
- package/src/core/ResponseModal.ts +605 -35
- package/src/index.ts +2 -0
- package/src/types/agent.ts +9 -1
- package/src/types/route.ts +119 -0
|
@@ -12,6 +12,138 @@ Step transitions handle:
|
|
|
12
12
|
- **Route Completion**: Detect when routes reach their end
|
|
13
13
|
- **Loop Prevention**: Avoid infinite traversal in complex flows
|
|
14
14
|
|
|
15
|
+
## Stopping Conditions in Batch Execution
|
|
16
|
+
|
|
17
|
+
When executing multiple steps in a batch, the engine stops for specific reasons indicated by the `stoppedReason` field in the response.
|
|
18
|
+
|
|
19
|
+
### StoppedReason Values
|
|
20
|
+
|
|
21
|
+
| Reason | Description | Behavior |
|
|
22
|
+
|--------|-------------|----------|
|
|
23
|
+
| `needs_input` | Step requires data not yet available | Batch stops, LLM generates response to collect data |
|
|
24
|
+
| `end_route` | Reached END_ROUTE marker | Route is complete, no more steps to execute |
|
|
25
|
+
| `route_complete` | All steps in route processed | Route finished successfully |
|
|
26
|
+
| `prepare_error` | Error in prepare hook | Batch stops, error returned with last successful state |
|
|
27
|
+
| `llm_error` | Error during LLM call | Batch stops, session state preserved |
|
|
28
|
+
| `validation_error` | Data validation failed | Batch continues, errors included in response |
|
|
29
|
+
| `finalize_error` | Error in finalize hook | Non-fatal, logged and execution continues |
|
|
30
|
+
|
|
31
|
+
### Needs-Input Stopping
|
|
32
|
+
|
|
33
|
+
The most common stopping condition. A step needs input when:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Step needs input if requires fields are missing
|
|
37
|
+
const step1 = {
|
|
38
|
+
prompt: "Confirm your booking",
|
|
39
|
+
requires: ["hotel", "date"], // Both must be present
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Step needs input if collecting and no collect fields have data
|
|
43
|
+
const step2 = {
|
|
44
|
+
prompt: "What's your preference?",
|
|
45
|
+
collect: ["preference", "notes"], // Needs input if BOTH are missing
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### End-Route Stopping
|
|
50
|
+
|
|
51
|
+
Batch stops when reaching the END_ROUTE marker:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const finalStep = confirmStep.nextStep({
|
|
55
|
+
prompt: "Booking confirmed! Anything else?",
|
|
56
|
+
}).endRoute(); // Creates END_ROUTE transition
|
|
57
|
+
|
|
58
|
+
// Response will have:
|
|
59
|
+
// stoppedReason: "end_route"
|
|
60
|
+
// isRouteComplete: true
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Route-Complete Stopping
|
|
64
|
+
|
|
65
|
+
When all steps have been processed without hitting END_ROUTE:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// If the last step has no transitions and doesn't need input
|
|
69
|
+
const response = await agent.respond("Complete my booking");
|
|
70
|
+
|
|
71
|
+
// Response will have:
|
|
72
|
+
// stoppedReason: "route_complete"
|
|
73
|
+
// isRouteComplete: true
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Transitions Within Batched Execution
|
|
77
|
+
|
|
78
|
+
During batch execution, transitions work as follows:
|
|
79
|
+
|
|
80
|
+
1. **Linear transitions** - Steps connected via `nextStep()` are evaluated sequentially
|
|
81
|
+
2. **SkipIf evaluation** - Each step's `skipIf` is checked before inclusion
|
|
82
|
+
3. **Needs-input check** - If a step needs input, batch stops there
|
|
83
|
+
4. **END_ROUTE detection** - Batch stops when reaching route end
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Example: Steps A → B → C → END_ROUTE
|
|
87
|
+
// If user provides data for A and B but not C:
|
|
88
|
+
|
|
89
|
+
const response = await agent.respond("Data for A and B");
|
|
90
|
+
|
|
91
|
+
// Batch includes: [A, B]
|
|
92
|
+
// Stops at: C (needs_input)
|
|
93
|
+
// Next call will continue from C
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Error Stopping Conditions
|
|
97
|
+
|
|
98
|
+
Errors during batch execution have different behaviors:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Prepare hook error - stops immediately
|
|
102
|
+
const stepWithPrepare = {
|
|
103
|
+
prompt: "Processing...",
|
|
104
|
+
prepare: async (context, data) => {
|
|
105
|
+
if (!data.valid) throw new Error("Invalid data");
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
// stoppedReason: "prepare_error"
|
|
109
|
+
// Session state: last successful state preserved
|
|
110
|
+
|
|
111
|
+
// LLM error - stops with preserved state
|
|
112
|
+
// stoppedReason: "llm_error"
|
|
113
|
+
// Session state: preserved from before LLM call
|
|
114
|
+
|
|
115
|
+
// Validation error - continues but reports error
|
|
116
|
+
// stoppedReason: "validation_error"
|
|
117
|
+
// Collected data: partial data preserved
|
|
118
|
+
|
|
119
|
+
// Finalize hook error - logged, continues
|
|
120
|
+
// stoppedReason: original reason (not changed)
|
|
121
|
+
// Error: included in response for logging
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Checking Stop Reason in Response
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const response = await agent.respond("Book a hotel");
|
|
128
|
+
|
|
129
|
+
switch (response.stoppedReason) {
|
|
130
|
+
case 'needs_input':
|
|
131
|
+
console.log("Waiting for user input");
|
|
132
|
+
break;
|
|
133
|
+
case 'end_route':
|
|
134
|
+
case 'route_complete':
|
|
135
|
+
console.log("Route finished:", response.isRouteComplete);
|
|
136
|
+
break;
|
|
137
|
+
case 'prepare_error':
|
|
138
|
+
case 'llm_error':
|
|
139
|
+
console.error("Error occurred:", response.error);
|
|
140
|
+
break;
|
|
141
|
+
case 'validation_error':
|
|
142
|
+
console.warn("Validation issues:", response.error);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
15
147
|
## Conditional Skipping
|
|
16
148
|
|
|
17
149
|
### SkipIf Logic
|
|
@@ -145,6 +145,115 @@ const dataStep = previousStep.nextStep({
|
|
|
145
145
|
});
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
+
## Multi-Step Batch Execution
|
|
149
|
+
|
|
150
|
+
Steps can execute together in a single LLM call when their data requirements are already satisfied. This reduces unnecessary back-and-forth and minimizes LLM costs.
|
|
151
|
+
|
|
152
|
+
### How Steps Are Batched
|
|
153
|
+
|
|
154
|
+
The execution engine walks through Steps sequentially and includes them in a batch until encountering a Step that needs user input:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Route with 3 steps
|
|
158
|
+
const route = agent.createRoute({
|
|
159
|
+
title: "Booking",
|
|
160
|
+
requiredFields: ["hotel", "date", "guests"],
|
|
161
|
+
initialStep: {
|
|
162
|
+
prompt: "Which hotel?",
|
|
163
|
+
collect: ["hotel"],
|
|
164
|
+
skipIf: (data) => !!data.hotel,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const askDate = route.initialStep.nextStep({
|
|
169
|
+
prompt: "What date?",
|
|
170
|
+
collect: ["date"],
|
|
171
|
+
skipIf: (data) => !!data.date,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const askGuests = askDate.nextStep({
|
|
175
|
+
prompt: "How many guests?",
|
|
176
|
+
collect: ["guests"],
|
|
177
|
+
skipIf: (data) => data.guests !== undefined,
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
When a user says "Book Grand Hotel for 2 people on Friday":
|
|
182
|
+
1. Pre-extraction captures: `{ hotel: "Grand Hotel", date: "Friday", guests: 2 }`
|
|
183
|
+
2. All steps have their data satisfied (skipIf evaluates to true)
|
|
184
|
+
3. Route completes in a single LLM call
|
|
185
|
+
|
|
186
|
+
### The `requires` Field in Batch Context
|
|
187
|
+
|
|
188
|
+
The `requires` field specifies data prerequisites that must be present before a Step can execute:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const confirmStep = askGuests.nextStep({
|
|
192
|
+
prompt: "Confirm booking for {{guests}} guests at {{hotel}} on {{date}}?",
|
|
193
|
+
requires: ["hotel", "date", "guests"], // All must be present
|
|
194
|
+
collect: ["confirmed"],
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Batch behavior:**
|
|
199
|
+
- If any `requires` field is missing from session data (after pre-extraction), the Step **needs input**
|
|
200
|
+
- The batch stops at this Step, and the LLM generates a response to collect the missing data
|
|
201
|
+
|
|
202
|
+
### The `collect` Field in Batch Context
|
|
203
|
+
|
|
204
|
+
The `collect` field specifies which data fields the Step should extract from the conversation:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const contactStep = {
|
|
208
|
+
prompt: "What's your email and phone?",
|
|
209
|
+
collect: ["email", "phone"], // Extract both from response
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Batch behavior:**
|
|
214
|
+
- If a Step has `collect` fields and **none** of those fields have data in the session, the Step **needs input**
|
|
215
|
+
- If **any** collect field already has data, the Step doesn't need input and can be included in the batch
|
|
216
|
+
|
|
217
|
+
### SkipIf Evaluation During Batch Determination
|
|
218
|
+
|
|
219
|
+
The `skipIf` condition is evaluated for each Step during batch determination:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const premiumStep = {
|
|
223
|
+
prompt: "Would you like premium features?",
|
|
224
|
+
collect: ["wantsPremium"],
|
|
225
|
+
skipIf: (data) => data.userTier === "free", // Skip for free users
|
|
226
|
+
};
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Evaluation rules:**
|
|
230
|
+
1. If `skipIf` evaluates to `true` → Step is skipped, continue to next Step
|
|
231
|
+
2. If `skipIf` evaluates to `false` → Step is evaluated for needs-input
|
|
232
|
+
3. If `skipIf` throws an error → Step is treated as non-skippable (safer to execute than skip)
|
|
233
|
+
|
|
234
|
+
### Batch Execution Example
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// User provides partial info
|
|
238
|
+
const response1 = await agent.respond("I want to book the Grand Hotel");
|
|
239
|
+
|
|
240
|
+
// Response shows which steps executed
|
|
241
|
+
console.log(response1.executedSteps);
|
|
242
|
+
// [{ id: "ask-hotel", routeId: "booking" }]
|
|
243
|
+
|
|
244
|
+
console.log(response1.stoppedReason);
|
|
245
|
+
// "needs_input" - stopped at ask-date step
|
|
246
|
+
|
|
247
|
+
// User provides remaining info
|
|
248
|
+
const response2 = await agent.respond("2 people on Friday");
|
|
249
|
+
|
|
250
|
+
console.log(response2.executedSteps);
|
|
251
|
+
// [{ id: "ask-date", routeId: "booking" }, { id: "ask-guests", routeId: "booking" }]
|
|
252
|
+
|
|
253
|
+
console.log(response2.stoppedReason);
|
|
254
|
+
// "route_complete"
|
|
255
|
+
```
|
|
256
|
+
|
|
148
257
|
## Best Practices
|
|
149
258
|
|
|
150
259
|
- Keep step prompts clear and focused
|
|
@@ -152,3 +261,6 @@ const dataStep = previousStep.nextStep({
|
|
|
152
261
|
- Leverage schema validation for data integrity
|
|
153
262
|
- Implement error handling in lifecycle hooks
|
|
154
263
|
- Consider user experience in step sequencing
|
|
264
|
+
- Design steps to maximize batching by using `skipIf` conditions
|
|
265
|
+
- Use `requires` to enforce data dependencies between steps
|
|
266
|
+
- Keep `collect` fields focused on what each step actually needs
|
|
@@ -11,6 +11,199 @@ The framework handles errors across multiple layers:
|
|
|
11
11
|
- **Session Data Synchronization** - Consistent error handling for agent-session data operations
|
|
12
12
|
- **Tool Execution Errors** - Graceful handling of tool failures
|
|
13
13
|
- **Validation Errors** - Schema and data validation error recovery
|
|
14
|
+
- **Batch Execution Errors** - Error handling during multi-step batch execution
|
|
15
|
+
|
|
16
|
+
## Batch Execution Error Handling
|
|
17
|
+
|
|
18
|
+
When executing multiple steps in a batch, errors are handled according to their category and severity.
|
|
19
|
+
|
|
20
|
+
### Error Categories
|
|
21
|
+
|
|
22
|
+
| Error Type | Severity | Behavior |
|
|
23
|
+
|------------|----------|----------|
|
|
24
|
+
| `pre_extraction` | Warning | Log warning, continue with empty extraction |
|
|
25
|
+
| `skipif_evaluation` | Warning | Treat step as non-skippable, include in batch |
|
|
26
|
+
| `prepare_hook` | Fatal | Stop batch execution, return error response |
|
|
27
|
+
| `llm_call` | Fatal | Stop batch, preserve last successful session state |
|
|
28
|
+
| `data_validation` | Non-fatal | Include errors in response, preserve partial data |
|
|
29
|
+
| `finalize_hook` | Non-fatal | Log error, continue with remaining hooks |
|
|
30
|
+
|
|
31
|
+
### Pre-Extraction Errors
|
|
32
|
+
|
|
33
|
+
Failures during data extraction from user message:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Pre-extraction is an optimization; failure shouldn't block execution
|
|
37
|
+
try {
|
|
38
|
+
const preExtracted = await preExtractData(message, route);
|
|
39
|
+
session = mergeCollected(session, preExtracted);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
// Log warning but continue
|
|
42
|
+
console.warn("Pre-extraction failed:", error.message);
|
|
43
|
+
// Continue with empty extraction result
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### SkipIf Evaluation Errors
|
|
48
|
+
|
|
49
|
+
Exceptions thrown by skipIf conditions:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Safer to execute than skip when condition is indeterminate
|
|
53
|
+
let shouldSkip = false;
|
|
54
|
+
try {
|
|
55
|
+
shouldSkip = await step.evaluateSkipIf(context);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.warn(`skipIf error for step ${step.id}, treating as non-skippable`);
|
|
58
|
+
shouldSkip = false; // Include step in batch
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Prepare Hook Errors
|
|
63
|
+
|
|
64
|
+
Failures in step prepare hooks stop batch execution:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const response = await agent.respond("Process my request");
|
|
68
|
+
|
|
69
|
+
if (response.stoppedReason === 'prepare_error') {
|
|
70
|
+
console.error("Prepare hook failed:", response.error);
|
|
71
|
+
// {
|
|
72
|
+
// type: 'prepare_hook',
|
|
73
|
+
// message: 'Validation failed in prepare hook',
|
|
74
|
+
// stepId: 'validate-step',
|
|
75
|
+
// details: { ... }
|
|
76
|
+
// }
|
|
77
|
+
|
|
78
|
+
// Session state is preserved from before the failed hook
|
|
79
|
+
console.log(response.session); // Last successful state
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### LLM Call Errors
|
|
84
|
+
|
|
85
|
+
Provider failures, timeouts, and rate limits:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const response = await agent.respond("Generate response");
|
|
89
|
+
|
|
90
|
+
if (response.stoppedReason === 'llm_error') {
|
|
91
|
+
console.error("LLM call failed:", response.error);
|
|
92
|
+
// {
|
|
93
|
+
// type: 'llm_call',
|
|
94
|
+
// message: 'Rate limit exceeded',
|
|
95
|
+
// details: { ... }
|
|
96
|
+
// }
|
|
97
|
+
|
|
98
|
+
// Session preserved from before LLM call
|
|
99
|
+
// Can retry with same session
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Data Validation Errors
|
|
104
|
+
|
|
105
|
+
Schema validation failures for collected data:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const response = await agent.respond("Book for 100 guests");
|
|
109
|
+
|
|
110
|
+
if (response.stoppedReason === 'validation_error') {
|
|
111
|
+
console.warn("Validation failed:", response.error);
|
|
112
|
+
// {
|
|
113
|
+
// type: 'data_validation',
|
|
114
|
+
// message: 'Validation failed for 1 field(s): guests',
|
|
115
|
+
// details: [
|
|
116
|
+
// { field: 'guests', value: 100, message: 'Exceeds maximum of 10' }
|
|
117
|
+
// ]
|
|
118
|
+
// }
|
|
119
|
+
|
|
120
|
+
// Valid partial data is still preserved
|
|
121
|
+
console.log(response.session.data); // Contains valid fields
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Finalize Hook Errors
|
|
126
|
+
|
|
127
|
+
Failures in finalize hooks are logged but don't stop execution:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Finalize hooks are for cleanup; one failure shouldn't block others
|
|
131
|
+
const response = await agent.respond("Complete booking");
|
|
132
|
+
|
|
133
|
+
// Even if finalize hooks fail, response is returned
|
|
134
|
+
// Errors are logged and included in response.error if present
|
|
135
|
+
if (response.error?.type === 'finalize_hook') {
|
|
136
|
+
console.warn("Some finalize hooks failed:", response.error.details);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Partial Progress Preservation
|
|
141
|
+
|
|
142
|
+
Errors preserve partial progress:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Batch: [step1, step2, step3]
|
|
146
|
+
// step2's prepare hook fails
|
|
147
|
+
|
|
148
|
+
const response = await agent.respond("Process all steps");
|
|
149
|
+
|
|
150
|
+
// step1 completed successfully
|
|
151
|
+
// step2 failed during prepare
|
|
152
|
+
// step3 never executed
|
|
153
|
+
|
|
154
|
+
console.log(response.executedSteps);
|
|
155
|
+
// [{ id: "step1", routeId: "route" }]
|
|
156
|
+
|
|
157
|
+
console.log(response.stoppedReason);
|
|
158
|
+
// "prepare_error"
|
|
159
|
+
|
|
160
|
+
// Session contains data from step1
|
|
161
|
+
console.log(response.session.data);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Error Response Structure
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
interface BatchExecutionError {
|
|
168
|
+
/** Type of error that occurred */
|
|
169
|
+
type: 'pre_extraction' | 'skipif_evaluation' | 'prepare_hook' |
|
|
170
|
+
'llm_call' | 'data_validation' | 'finalize_hook';
|
|
171
|
+
/** Error message */
|
|
172
|
+
message: string;
|
|
173
|
+
/** Step where error occurred (if applicable) */
|
|
174
|
+
stepId?: string;
|
|
175
|
+
/** Additional error details */
|
|
176
|
+
details?: unknown;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Handling Batch Errors
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const response = await agent.respond(message);
|
|
184
|
+
|
|
185
|
+
// Check for errors
|
|
186
|
+
if (response.error) {
|
|
187
|
+
switch (response.error.type) {
|
|
188
|
+
case 'prepare_hook':
|
|
189
|
+
// Fatal - batch stopped
|
|
190
|
+
await handlePrepareError(response.error);
|
|
191
|
+
break;
|
|
192
|
+
case 'llm_call':
|
|
193
|
+
// Fatal - can retry
|
|
194
|
+
await retryWithBackoff(() => agent.respond(message));
|
|
195
|
+
break;
|
|
196
|
+
case 'data_validation':
|
|
197
|
+
// Non-fatal - ask user to correct
|
|
198
|
+
await promptForCorrection(response.error.details);
|
|
199
|
+
break;
|
|
200
|
+
case 'finalize_hook':
|
|
201
|
+
// Non-fatal - log and continue
|
|
202
|
+
console.warn("Finalize error:", response.error);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
14
207
|
|
|
15
208
|
## Streaming Error Propagation
|
|
16
209
|
|
|
@@ -13,6 +13,7 @@ By the end of this guide, you'll have a working AI agent that can:
|
|
|
13
13
|
- ✅ Maintain context across multiple turns
|
|
14
14
|
- ✅ Use tools to perform actions
|
|
15
15
|
- ✅ Handle complex conversation flows
|
|
16
|
+
- ✅ Execute multiple steps in a single LLM call (multi-step execution)
|
|
16
17
|
|
|
17
18
|
**Time estimate:** 15-30 minutes
|
|
18
19
|
|
|
@@ -389,6 +390,107 @@ demonstrateLifecycleHooks();
|
|
|
389
390
|
- ✅ Understood "Next Friday, 2 people, $2000 budget" as structured data
|
|
390
391
|
- ✅ Skipped asking for already-known information
|
|
391
392
|
- ✅ Used the ToolManager API to create and execute tools with simplified context
|
|
393
|
+
- ✅ Batched multiple steps together when data was available
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## ⚡ Multi-Step Execution Benefits
|
|
398
|
+
|
|
399
|
+
@falai/agent automatically batches multiple steps together when their data requirements are satisfied, reducing LLM calls and improving conversation flow.
|
|
400
|
+
|
|
401
|
+
### How It Works
|
|
402
|
+
|
|
403
|
+
When a user provides information that satisfies multiple steps, they execute together:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Traditional approach: 3 separate turns
|
|
407
|
+
// Turn 1: "Which hotel?" → "Grand Hotel"
|
|
408
|
+
// Turn 2: "What date?" → "Friday"
|
|
409
|
+
// Turn 3: "How many guests?" → "2"
|
|
410
|
+
|
|
411
|
+
// With multi-step execution: 1 turn
|
|
412
|
+
const response = await agent.respond("Book Grand Hotel for 2 on Friday");
|
|
413
|
+
|
|
414
|
+
// All 3 steps execute in a single LLM call!
|
|
415
|
+
console.log(response.executedSteps);
|
|
416
|
+
// [
|
|
417
|
+
// { id: "ask-hotel", routeId: "booking" },
|
|
418
|
+
// { id: "ask-date", routeId: "booking" },
|
|
419
|
+
// { id: "ask-guests", routeId: "booking" }
|
|
420
|
+
// ]
|
|
421
|
+
|
|
422
|
+
console.log(response.stoppedReason);
|
|
423
|
+
// "route_complete"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Benefits
|
|
427
|
+
|
|
428
|
+
1. **Fewer LLM Calls** - Multiple steps in one call reduces costs
|
|
429
|
+
2. **Better UX** - Users don't repeat information they've already provided
|
|
430
|
+
3. **Faster Responses** - Less back-and-forth means quicker completion
|
|
431
|
+
4. **Automatic** - No code changes needed, works with existing routes
|
|
432
|
+
|
|
433
|
+
### Seeing It In Action
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
// Create a booking route with multiple steps
|
|
437
|
+
const bookingRoute = agent.createRoute({
|
|
438
|
+
title: "Hotel Booking",
|
|
439
|
+
requiredFields: ["hotel", "date", "guests"],
|
|
440
|
+
initialStep: {
|
|
441
|
+
prompt: "Which hotel?",
|
|
442
|
+
collect: ["hotel"],
|
|
443
|
+
skipIf: (data) => !!data.hotel,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const askDate = bookingRoute.initialStep.nextStep({
|
|
448
|
+
prompt: "What date?",
|
|
449
|
+
collect: ["date"],
|
|
450
|
+
skipIf: (data) => !!data.date,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const askGuests = askDate.nextStep({
|
|
454
|
+
prompt: "How many guests?",
|
|
455
|
+
collect: ["guests"],
|
|
456
|
+
skipIf: (data) => data.guests !== undefined,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// User provides all info at once
|
|
460
|
+
const response = await agent.respond(
|
|
461
|
+
"I want to book the Grand Hotel for 2 people next Friday"
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Pre-extraction captures all data
|
|
465
|
+
// All steps execute in one batch
|
|
466
|
+
// Route completes immediately!
|
|
467
|
+
|
|
468
|
+
console.log(response.message);
|
|
469
|
+
// "Perfect! I've booked the Grand Hotel for 2 guests on Friday."
|
|
470
|
+
|
|
471
|
+
console.log(response.isRouteComplete);
|
|
472
|
+
// true
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Understanding the Response
|
|
476
|
+
|
|
477
|
+
The response includes information about what executed:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
const response = await agent.respond("Book for 2 guests");
|
|
481
|
+
|
|
482
|
+
// Which steps ran
|
|
483
|
+
console.log(response.executedSteps);
|
|
484
|
+
// [{ id: "ask-guests", routeId: "booking" }]
|
|
485
|
+
|
|
486
|
+
// Why execution stopped
|
|
487
|
+
console.log(response.stoppedReason);
|
|
488
|
+
// "needs_input" - waiting for hotel and date
|
|
489
|
+
|
|
490
|
+
// Or if complete:
|
|
491
|
+
// "route_complete" - all steps finished
|
|
492
|
+
// "end_route" - reached END_ROUTE marker
|
|
493
|
+
```
|
|
392
494
|
|
|
393
495
|
---
|
|
394
496
|
|
|
@@ -4,6 +4,29 @@ This directory contains migration guides for major changes and updates to the `@
|
|
|
4
4
|
|
|
5
5
|
## Available Migration Guides
|
|
6
6
|
|
|
7
|
+
### [Multi-Step Execution Migration Guide](./multi-step-execution.md)
|
|
8
|
+
|
|
9
|
+
**Latest Update** - Guide for understanding and migrating to multi-step batch execution.
|
|
10
|
+
|
|
11
|
+
**What's New:**
|
|
12
|
+
- 🚀 **Multi-Step Batching**: Multiple steps execute in a single LLM call
|
|
13
|
+
- ⚡ **Reduced LLM Costs**: Fewer calls for the same outcome
|
|
14
|
+
- 🎯 **Better UX**: Less back-and-forth in conversations
|
|
15
|
+
- 📊 **New Response Fields**: `executedSteps`, `stoppedReason`, `error`
|
|
16
|
+
|
|
17
|
+
**Key Changes:**
|
|
18
|
+
- Steps batch together when data requirements are satisfied
|
|
19
|
+
- Pre-extraction happens before batch determination
|
|
20
|
+
- Hook execution order: all prepare → LLM → all finalize
|
|
21
|
+
- SkipIf conditions affect batch determination
|
|
22
|
+
|
|
23
|
+
**Migration Status:**
|
|
24
|
+
- ✅ **API Compatible**: Public API shape unchanged
|
|
25
|
+
- ⚠️ **Behavioral Change**: Execution semantics differ
|
|
26
|
+
- ✅ **Gradual Migration**: Review hooks and tests
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
7
30
|
### [ResponseModal Refactor Migration Guide](./response-modal-refactor.md)
|
|
8
31
|
|
|
9
32
|
**Latest Update** - Comprehensive guide for migrating to the new ResponseModal architecture.
|