Skip to main content
Use Agent Memory when your agent needs to remember information about returning callers – preferences, past bookings, or verification status – across separate conversations. Without it, every call starts from scratch.
This feature is in early access. Functionality may evolve. Contact your PolyAI representative for guidance on production use.

How it works

Agent Memory is a key-value store attached to a user identifier (such as a phone number). Persist small, structured data between conversations so your agent can:
  • Skip repeated questions for returning callers
  • Greet callers by name or reference past interactions
  • Resume interrupted conversations where the caller left off
  • Track handoff history and resolution status
Memory is read at the start of each turn and cached for that turn. It is written at the end of a conversation – not on every turn. You can:
  • Read memory using conv.memory.get("key")
  • Write memory by setting conv.state["key"] = value (if the key is listed in state_keys)

Configuration

Enable Agent Memory

Navigate to Project > Settings > Experimental Config in Agent Studio and add the memory configuration:
{
  "memory": {
    "repeat_caller": {
      "analytics_enabled": true,
      "state_keys": ["booking_day", "preferred_language"],
      "identifier_blacklist": ["+440000000000"]
    }
  }
}
FieldDescription
analytics_enabledAdds repeat caller metrics to Studio analytics dashboards
state_keysKeys from conv.state that should be saved to memory at the end of the conversation
identifier_blacklistOptional list of identifiers to exclude (e.g., test phone numbers)
Only include keys you explicitly need in state_keys. Memory is persisted at the end of the conversation, so only values set during the call are saved.

Using memory in functions

Reading memory

Use conv.memory.get("key") in any function to retrieve a previously stored value:
def start_function(conv: Conversation):
    name = conv.memory.get("caller_name")
    if name:
        return {"utterance": f"Welcome back, {name}. How can I help you today?"}

Writing memory

Set values in conv.state – they are persisted to memory at the end of the conversation if the key is listed in state_keys:
def collect_name(conv: Conversation, caller_name: str):
    conv.state["caller_name"] = caller_name
    return {"content": f"Thanks, {caller_name}. I'll remember you next time."}
Only keys listed in state_keys in your config are persisted. Setting conv.state["key"] = value for a key not in state_keys will not save it to memory.

Full example: returning caller

def start_function(conv: Conversation):
    booking_day = conv.memory.get("booking_day")
    cheese_type = conv.memory.get("cheese_type")

    if booking_day and cheese_type:
        return {
            "utterance": f"I see you previously booked {cheese_type} for {booking_day}. Would you like to book again?"
        }
def book_delivery(conv: Conversation, cheese_type: str, booking_day: str):
    conv.state["booking_day"] = booking_day
    conv.state["cheese_type"] = cheese_type
    return {"content": f"Booked {cheese_type} for {booking_day}."}

Repeat caller analytics

When analytics_enabled is set to true, Agent Memory automatically tracks five metrics for each returning caller:
MetricDescription
REPEAT_CALLER_CONV_IDConversation ID of the caller’s first interaction
REPEAT_CALLER_DATETIMETimestamp of the first interaction
REPEAT_CALLER_QAQA outcome from the first interaction
REPEAT_CALLER_HANDOFF_REASONReason for handoff in the first interaction
REPEAT_CALLER_HANDOFF_TOHandoff destination from the first interaction
These metrics appear in your dashboards and can be used to track repeat caller patterns and resolution rates.
Repeat caller metrics always reference the first interaction in the retention window, not the most recent one. This is by design for data privacy compliance – linking calls in a chain could allow identification beyond the retention period.

Memory behavior

Timing

  • Memory is fetched once per turn from the memory service and cached. Multiple conv.memory.get() calls in the same turn do not trigger additional lookups.
  • Memory is written at the end of the conversation (after the end function executes). Values set in conv.state during a function on turn 1 are not available through conv.memory on turn 2 of the same call.

Persistence

Each write acts as a patch – it updates or adds specific keys without removing existing ones. If conversation 1 writes {"cheese_type": "gouda"} and conversation 2 writes {"booking_day": "Friday"}, the next lookup returns both fields.

Expiry

All memory identifiers and fields expire after 90 days (maximum, for GDPR compliance). Once expired, data is automatically deleted and no longer accessible.

Identifiers

The current supported identifier is the caller’s phone number. This is set automatically – you do not need to configure it.
Support for additional identifiers (email, account ID) and identifier linking across channels is planned for future releases.

Compliance

Before using Agent Memory in production, you must inform your clients. They need to ensure their use complies with applicable data protection laws, which may include updating privacy notices to reflect the new data processing.
Key compliance considerations:
  • Agent Memory is not intended for automated decision-making that significantly impacts end users
  • Do not store sensitive PII in a single field – separate fields allow independent expiry
  • Custom memory fields cannot be used to build caller profiles
  • Timestamps stored in memory should be rounded to the hour to prevent linking calls across the retention window
  • All data expires within 90 days
If your use case goes beyond analytics, call recovery, or personalisation, raise a request with your legal team before proceeding.

FAQ

Values are stored as JSON-encoded strings. You can store structured data in a single field, but it is recommended to keep fields flat so they can expire independently – especially for anything containing PII.
Yes. Use Python’s in operator:
if "booking_day" in conv.memory:
    # Memory exists for this key
Not natively – the latest write always wins. Guard against overwrites in your code:
if "cheese_type" not in conv.memory:
    conv.state["cheese_type"] = value
Memory is persisted at the end of the conversation, after the end function executes. Values set in conv.state during a function are not available in conv.memory until the next call.
Currently, memory is scoped by phone number. Support for linking identifiers across channels (voice, SMS, webchat) is planned for future releases.
Use the fields() method:
all_memory = conv.memory.fields()  # Returns a dict of all fields
Last modified on April 20, 2026