Skip to main content
You have: Robin answering and greeting, from Answer the phone. The caller gives their number, and the agent keeps it.

What you add

First, the state. It is a Pydantic model. Every field has a default, because every value starts the call empty.
salon/state.py
Phone is a type the library ships. It cleans a number and saves it with a plus and a country code. A number that cannot be right is refused. Next, let the prompt read the state. A {{name}} placeholder is filled from the state before each model request.
salon/agent.py
Last, build a CallState for each call and give it to the agent. save_values=True on the spec adds a tool the model calls to save these values.

See a refusal

You can check the state without a call. This is the same check the model’s save goes through.
612345 is not a phone number, so nothing is saved. The refusal says what to fix, and the model reads it. The confirm map says which task step must hear the caller agree to the number. Only that step can confirm it. You add the step in Book step by step, so the agent itself does not use confirm yet.

Try it

Start the agent as before. Then say these two lines.
  1. “My number is six one two three four five.” Robin should ask again, and ask for the country code.
  2. “Sorry. Plus three four, six one two, three four five, six seven eight.” Robin saves it.
Robin has the number now. The prompt shows it in {{customer_phone}} on the next turn.

Why it works this way

A plain agent lets the model write any text into its memory. Here every save passes the type first, and the whole batch is refused if one value is wrong. The model gets a refusal worded for it, so it asks the caller instead of guessing. The state is a normal Pydantic model. The same State class works on LiveKit and Pipecat.

What you have now

Next: Check the diary

Give Robin a tool that reads the salon’s diary.
Go deeper: State.