Lesson 4: BookingStep — Pick a doctor, book, end
Prompt with carried state
BookingStep pulls state from SymptomsStep before prompting:
public getPrompt(): string {
const doctors = this.getState('doctors');
const symptoms = this.getState('symptoms');
return `You have the user's symptoms: ${symptoms}. Here are the available doctors: ${JSON.stringify(doctors)}. Help the user pick a doctor and an available time slot. Once they decide, use the 'book_appointment' tool.`;
}
State isolation means only the booking memory is loaded here—no leakage from earlier chat turns.
Tool + handler
public defineTool(): ToolType[] {
return [
{
name: 'book_appointment',
description: 'Book an appointment with the selected doctor at the chosen time.',
schema: z.object({
doctorName: z.string().describe('Name of the selected doctor'),
timeSlot: z.string().describe('The chosen time slot'),
}),
},
];
}
protected async book_appointment(tool: ToolCall): Promise<ToolResponseType> {
const { doctorName, timeSlot } = tool.args;
this.saveState({ doctorName, timeSlot, booked: true });
return { step: EndStep, tool: `Successfully booked with ${doctorName} at ${timeSlot}. Thank you!` };
}
- Another narrow tool surface: only booking +
end_chat. - Booking persists confirmation fields and jumps directly to
EndStep.
End the flow
src/myflow/medical-flow/medical-flow.ts wires the sequence:
return [
new SymptomsStep(this, true).setTemperature(0.5).useModel(model),
new BookingStep(this, false).useModel(model),
new EndStep(this).useMemory('end').useModel(model),
];
- Explicit order keeps debugging simple (one list, deterministic transitions).
EndStepuses its own memory slice (end) so the goodbye prompt stays clean.- Temperature is relaxed on symptoms intake, then the rest reuses the flow default.
[!Pattern] This is the “tool-controlled step switch” pattern: tools decide when to exit the current step and which step to activate next, giving you traceable, testable control flow.