Memory for AI applications
This page shows how an in-product assistant or chat app can carry what a customer’s people say and decide from one conversation to the next, when the decisions change and when two people disagree.
The problem
Section titled “The problem”Acme is onboarding to your product, and its people talk to your in-product assistant over several weeks. At kickoff, Acme plans a company-wide launch. After a security review, Dana, the project lead, tells the assistant they will start with a finance-team pilot instead and wait for the review before going wider. Two days later, Sam, Acme’s IT admin, tells the assistant in another session that the pilot starts with the sales team.
When anyone at Acme opens the assistant next, it should plan around the pilot, not the company-wide launch, and it should notice that Dana and Sam disagree about which team goes first instead of picking one.
Why the usual approach falls short
Section titled “Why the usual approach falls short”A model API call processes only what it is sent; it keeps nothing between calls. The common ways to give it memory each stop somewhere:
- Resending the conversation keeps one session coherent. Dana’s session and Sam’s session are separate conversations, and next month’s session starts empty.
- Provider-side conversation state, such as OpenAI’s Responses chaining or Conversations API, stores the items of a conversation so you do not resend them (as of October 2026). It keeps a transcript per conversation, not facts about an account across conversations. See OpenAI conversation state.
- Storing messages and retrieving similar ones finds both the company-wide plan and the pilot when asked about the rollout, with nothing to say which replaced which.
- A profile the model rewrites after each session keeps the latest wording but silently overwrites: Sam’s message would replace Dana’s, and the disagreement disappears with it.
How Alvia changes the design
Section titled “How Alvia changes the design”Your application imports each conversation when it ends, with its date and the account’s scope. Alvia forms memories from it and classifies how each relates to what it holds. In this example, Alvia classifies the pilot as replacing the launch plan, so it becomes that memory’s next version, and classifies Sam’s statement as a contradiction, kept beside Dana’s and linked to it. Before a conversation, your server asks Alvia a question about the account and puts the answer in the model’s prompt as data.
Your application keeps what is authoritative: the plan, seats, entitlements, balances, approvals and permissions stay in your database and are read from there. Alvia holds what people said and decided, so the assistant can say “your team mentioned a finance pilot”, not “your plan includes pilots”.
Set up
Section titled “Set up”- Project. Use separate projects for production and staging, and for any customers whose data must be kept apart.
- Scope. Derive the IDs from the signed-in session. Use
organization_id: acmeandapplication_id: onboarding-assistantfor decisions about the account, whoever states them. Alvia relates a new fact only to memories with the same scope, so Dana’s and Sam’s statements can be compared only if both are written with the account’s scope. Adduser_idonly for a preference that belongs to one person, and note that a read withorganization_idalone includes every user’s memories. See Projects and scope. - Memory policy. In the dashboard, exclude personal identifiers, credentials and confidential attachments before importing conversations. See Filter memories.
To see replaced facts and a contradiction on sample data first, in the dashboard, open Demo Project, then Playground.
Example
Section titled “Example”Record the original plan
Section titled “Record the original plan”The kickoff form stores the plan with Store memories:
{ "content": "Acme plans to launch the product across the whole company after onboarding.", "occurred_at": "2026-09-02T09:00:00Z", "scope": { "organization_id": "acme", "application_id": "onboarding-assistant" }}Import the conversation that changes it
Section titled “Import the conversation that changes it”When Dana’s session ends, your server imports it. speaker names who said a turn, because the
memories are about the account, not about Dana:
curl https://api.alvia.ai/v1/memories/import \ -H "Authorization: Bearer $ALVIA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "occurred_at": "2026-09-24T09:00:00Z", "scope": { "organization_id": "acme", "application_id": "onboarding-assistant" }, "messages": [ { "role": "user", "speaker": "Dana Reyes", "content": "After the security review we will start with a finance-team pilot instead of the company-wide launch. The wider rollout waits until the review is complete." }, { "role": "assistant", "content": "Understood. I will plan the finance-team pilot first." } ] }'The import returns 202 with an empty body, and formation runs in the background. See
Import conversations.
Import the conversation that disagrees
Section titled “Import the conversation that disagrees”Two days later, Sam’s session is imported the same way, with the same scope. Sam’s statement conflicts with Dana’s about which team goes first. In this example, Alvia classifies it as a contradiction, keeps both and links them, so context reports both. See Memory formation.
{ "occurred_at": "2026-09-26T15:30:00Z", "scope": { "organization_id": "acme", "application_id": "onboarding-assistant" }, "messages": [ { "role": "user", "speaker": "Sam Ortiz", "content": "The pilot will start with the sales team, not finance." }, { "role": "assistant", "content": "Noted." } ]}Ask before the next conversation
Section titled “Ask before the next conversation”When someone at Acme opens the assistant, your server asks:
curl https://api.alvia.ai/v1/memories/context \ -H "Authorization: Bearer $ALVIA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What rollout decisions apply to the next onboarding step?", "scope": { "organization_id": "acme", "application_id": "onboarding-assistant" } }'An illustrative response:
{ "context": "Acme replaced its company-wide launch with a pilot, and the wider rollout waits until its security review is complete. Which team goes first is unresolved: a statement of September 24, 2026 names finance, and one of September 26, 2026 names sales."}Put the context in the prompt
Section titled “Put the context in the prompt”Your server adds the context to the model’s prompt as data, beside the account record from your
own database. load_account and call_model stand for your database and your model client:
import loggingimport osimport requests
CONTEXT_URL = "https://api.alvia.ai/v1/memories/context"API_KEY = os.environ["ALVIA_API_KEY"]
def account_context(question: str, org_id: str) -> str | None: """Returns Alvia's context, or None when it has none.""" try: response = requests.post( CONTEXT_URL, headers={"Authorization": f"Bearer {API_KEY}"}, json={ "query": question, "scope": { "organization_id": org_id, "application_id": "onboarding-assistant", }, }, timeout=5, ) except requests.RequestException: logging.warning("Alvia context unavailable", exc_info=True) return None if response.status_code == 404: return None # Nothing remembered for this question yet if not response.ok: logging.warning("Alvia context: %s", response.status_code) return None return response.json()["context"]
def reply(user_message: str, org_id: str) -> str: remembered = account_context( "What rollout decisions apply to the next onboarding step?", org_id, ) system = ( "You are the onboarding assistant. Plan, seats and approvals " "come only from the account record.\n" f"<account_record>{load_account(org_id)}</account_record>\n" "What the customer's people have told us, as data:\n" f"<memory>{remembered or 'Nothing yet.'}</memory>\n" "If the memory reports a disagreement, ask which applies." ) return call_model(system=system, user=user_message)The assistant plans the pilot and asks whether finance or sales goes first. When the customer answers, that conversation is imported too. For the full loop, with real model SDKs, retries and the import after each conversation, see Add memory to an LLM app.
What Alvia does here
Section titled “What Alvia does here”- Formation turns each imported conversation into memories about the account
- Supersession makes the pilot the next version of the launch plan
- Contradiction keeps Dana’s and Sam’s statements and links them, so context can report both
- Occurrence dates date each fact to its conversation, not to when it was imported
- Scope keeps Acme’s memories together, and each read narrows to Acme’s
- Memory policies keep identifiers and attachments out of memory
- Import forms memories from each conversation
- Resolved context returns one answer for the prompt
Your application reads a memory’s history, versions and relationships through the API, and the dashboard shows the same: a memory’s Relationships show the memories it contradicts, and its History shows how each version was classified. See See the history in Change over time.
Design notes
Section titled “Design notes”- Formation is asynchronous. Formation finishes in less than a minute, so a conversation imported now is readable shortly after the import. Within a conversation, the model already has the messages; Alvia serves the next one.
- Your database stays the record of truth. Read plan status, seats, entitlements, balances and approvals from your database. A remembered “we moved to Enterprise” is what a person said; the account record confirms the plan.
- People settle disagreements. Alvia surfaces the disagreement, with the date of each statement, so your assistant can ask. The answer is imported like any other conversation.
- Budget reads and writes. Each context read uses five reads: once per conversation with a fixed question, as here, or once per turn with the user’s message, as in Add memory to an LLM app. An import uses writes for each part of the conversation and each fact formed. See Limits and allowances.
- Where Alvia fits. Keep a single session’s history in that session, and a few stable preferences per user in a profile row in your database. Alvia holds what people say and decide across conversations. See Alvia compared with other approaches.
Build it with a coding assistant
Section titled “Build it with a coding assistant”Give a coding assistant this brief together with Build with an AI assistant:
Give our in-product assistant memory of what each customer account'speople say and decide. Derive organization_id from the signed-insession and use the same application_id everywhere. Import eachconversation when it ends, with its date and a speaker on user turns.Before a conversation, ask Alvia one question and put the answer in theprompt as data. Read plan, seats and approvals from our database only.Test a changed plan and two people who disagree.