worked case · generative agents

What happens when AI characters can remember?

A generative agent is a fictional character controlled by software. It uses an AI language model to create dialogue and plans, but it also needs a memory: what it has seen, what mattered, and what it currently believes.

What you are looking at. This is a three-character thought experiment about how information moves through separate memories. It is not a model of a real town, a complete human mind, or conscious behavior.

3 characters · 3 simulated days · 1 changing fact

one moment inside a character simplified
1 · sees
an event enters this character's world

Sam says, “I have decided to run for mayor.”

2 · stores
the event becomes this character's memory

“Sam told me he plans to run for mayor.”

3 · recalls
a later situation brings that memory back

The mayoral news matters when politics comes up.

4 · acts
the language model receives that memory

The character mentions Sam's campaign.

Each character repeats this loop with a separate memory. No character automatically knows what another one knows.

00 · the problem

Why not just give every character the same memory?

Because then everyone knows everything the instant it happens, and the story dies. People — and agents, users, and accounts — only know what actually reached them. So the hard part is not storing memories; it is deciding who is allowed to have which one, and being able to show later how it got there.

one shared pile

Give everyone the same memory.

  • Everyone knows everything instantly. Nothing can be private or out of date.
  • A retraction updates all of them at once, which is not how news travels.
  • No character can be asked why it said what it said.
with memseek

Give each character its own memory.

  • A memory belongs to one character.
  • A handful is recalled before that character speaks.
  • Conclusions cite the memories under them.
  1. day one

    Sam tells Isabella his news. Klaus is not there, so Klaus does not know it.

    “I have decided to run for mayor.”
  2. that night

    Isabella reviews her own day and draws one conclusion from it.

    “Sam is campaigning for mayor and asking regulars at the cafe for support, so tonight’s party is where it will come up.”
  3. day three

    Sam withdraws and tells Klaus. Nobody has told Isabella yet.

    Klaus now believes: “Sam has withdrawn.” Isabella still believes: “Sam is running for mayor.” Isabella is not broken. She simply has not heard, and the record shows exactly that.
  4. what you write

    Write what a character saw. Ask for what it should remember now.

    await memseek.records.ingest(entity="agent:isabella",
        text="Sam told me he plans to run for mayor.")
    
    recall = await memseek.search("what should I do tonight?",
        entity="agent:isabella")
3 characters, three different beliefsOn day three one knows Sam withdrew and two do not. That disagreement is the correct answer.
A correction travels only by conversationBeliefs change when a character is told, not when a database row changes.
Who knew what, and whenEvery belief has a dated path back to the event that put it there.

New to Memseek? You write one small configuration file describing what your application should remember. After that your application only appends what happened; Memseek does the deriving, keeps every conclusion linked to the evidence underneath it, and hands your agent a bounded briefing instead of a pile of text. Everything below is that file and what it produces — how it works.

the words the rest of this page usesplain english
memory stream
One character’s diary: everything it saw or said, in order.
importance
A 1–10 score for how much a memory mattered. A campaign decision outranks buying bread.
reflection
A conclusion drawn from several memories, stored with links to them.
entity
Whose memory this is. Every record belongs to exactly one character.
1
Isabella observes

The event is written only to the character who experienced it.

The simulation controls the clock and encounter. When Sam tells Isabella about the campaign, the application writes one observation under Isabella’s entity. Klaus receives no copy, so his memory cannot retrieve the news yet.

simulated encounterSam tells Isabella, “I have decided to run for mayor.”
spoken message Sam → Isabella
“I have decided to run for mayor.”
who was present

Isabella heard the statement. Klaus was not part of this encounter.

simulation
writes by observer
Isabella · main/observation +1 record

Sam told me he plans to run for mayor.

entity
Isabella
source
the encounter
processors
embedding + importance
Klaus · no write 0 records

He cannot recall or act on the campaign until the information reaches him through a later event.

Visible effect: the same simulated world now produces different knowledge states for two characters.

collections/core.yamlthe private stream
- name: main
  mode: mixed
  schema:
    required: [text]
  required_processors:
    - embedding_v1
    - importance
  search_profile: pg_default
  answerable: true
entity

The entity is supplied with the write, not hard-coded here. Isabella, Klaus, and Sam therefore use the same contract but keep separate records.

importance

Score how consequential the memory is from 1–10; that score later competes with relevance and recency during recall.

answerable

Allow cited synthesis over the stream, while still requiring entity scope at query time.

writes now

One immutable main/observation record for Isabella — “Sam told me he plans to run for mayor.” — with its time and an importance of 8. Klaus still has zero evidence of the campaign.

2
a later moment recalls it

Memory competes on relevance, importance, and recency before the model speaks.

When Isabella later plans a gathering, the application queries only her entity. Semantic or text similarity finds related records, importance favors the campaign announcement over small talk, and time decay prevents ancient memories from dominating forever.

conf/rank_default.yamlthe recall equation
variants:
  hybrid:
    - sum
    - - [product, 1.0,
         [normalize, [max,
          [[similarity], [text_match]]]]]
      - [product, 1.0,
         [normalize, [score, importance]]]
      - [product, 1.0,
         [decay, [age_hours, last_accessed],
          {midpoint: 24, exponent: 1}]]
max(similarity, text_match)

Let either meaning or exact wording establish relevance; one weak method does not cancel the other.

score.importance

Favor memories the processor judged consequential, even when several candidates are equally topical.

decay

Reduce recency weight around a 24-hour midpoint. This changes selection, not the stored memory itself.

writes next

No new belief is written by recall. What enters Isabella’s context is the selected memory itself — “The Valentine’s party is tonight and Klaus is invited.” — carrying the ID of the record it came from.

3
experience becomes insight

Enough important memories can produce a reflection, but only with citations.

At the end of the day, the reflection derivation fires after the accumulated importance crosses 150. It asks three high-level questions, searches Isabella’s memories and prior reflections, then emits insights that must cite the records the model actually saw.

derivations/reflection.yamlthe synthesis rule
trigger:
  accumulator: {metric: importance,
                threshold: 150}
sources:
  recent_memories:
    kind: changes
    collections: [main]
tasks:
  - {id: qs, use: llm}
  - {id: evidence_by_question, use: search}
  - {id: result, use: llm}
emit:
  collection: reflections
  type: reflection
accumulator

Make reflection event-driven but not constant: trivial scenes alone do not justify a new abstraction.

evidence_by_question

Retrieve beyond the latest batch so the insight may connect the campaign with earlier goals or relationships.

citations

The output schema requires full visible UUIDs; an invented insight or invisible source fails validation.

writes next

A reflections record for Isabella: “Sam is campaigning for mayor and is asking regulars at the cafe for support, so tonight’s party is where his campaign will come up.” It cites the observations it was drawn from. Klaus still does not inherit it.

4
only then does the model act

The prompt assembles current profile, schedule, and selected memory—not the town’s whole history.

The live artifact makes the handoff explicit. Isabella’s profile, upcoming calendar, and task-relevant memories become named blocks with independent token budgets. When she later tells Klaus, his new observation changes what he can know. When Sam withdraws, only agents who receive that correction can update.

artifacts/agent_prompt.yamlthe decision context
blocks:
  profile:
    document: {collections: [profiles]}
    max_tokens: 2000
  calendar:
    view: upcoming_calendar@1
    max_tokens: 2500
  memory:
    view: agent_relevant_memory@1
    max_tokens: 3500
template: |
  CURRENT PROFILE: {{profile}}
  UPCOMING CALENDAR: {{calendar}}
  RELEVANT MEMORY: {{memory}}
blocks

Keep stable identity, authored plans, and recalled experience separate so each input can be inspected and budgeted.

view

Run the declared retrieval at render time for this character and this task; do not pre-bake global town knowledge.

template

Define the literal context boundary the dialogue model sees. The simulation, not the model, remains responsible for time and encounters.

final state

Dialogue is generated from bounded, character-specific context: Isabella’s next line can mention the campaign, and Klaus’s cannot until someone tells him. Observations, reflections, profile versions, plans, and prompt snapshots remain separately auditable.

Continue into the complete two-day simulation →

01 · from behavior to software

What does a character need in order to remember?

The left side describes the behavior in everyday language. The right side names the software part that makes it possible. These are the building blocks adapted from Park et al.'s 2023 Generative Agents paper.

A private historyRemember what this character saw and said.
Memory streamA time-ordered log of observations and conversations.
a record in it“Sam told me he plans to run for mayor.” isabella · day 1, 09:14
A sense of what matteredTreat a major decision differently from small talk.
Importance scoreThe model rates each memory from 1 to 10.
what it scoredthe campaign announcement: 8 · “I bought bread last week”: 2
A way to find related memoriesBring up the party when someone mentions tonight's plans.
Relevance searchMatches both meaning and words, not just exact phrases.
what it matcheda conversation about “tonight’s plans” finds “The Valentine’s party is tonight and Klaus is invited.”
A preference for recent eventsYesterday usually matters more than last year.
Recency scoreOlder memories gradually receive less weight.
what it reorderedyesterday’s party invitation outranks last week’s trip to the bakery.
A limited focusChoose a small set of memories before speaking.
Recall rankingCombines relevance, importance, and recency.
what it selected12 memories out of hundreds, before Isabella says one line.
A way to make sense of experienceTurn many events into a conclusion such as “Sam is campaigning.”
ReflectionA higher-level insight that cites the memories behind it.
the reflection it wrote“Sam is campaigning for mayor and is asking regulars at the cafe for support, so tonight’s party is where his campaign will come up.”
A current picture of itselfKeep roles, commitments, and open questions up to date.
Agent profileA summary that changes when new evidence arrives.
Isabella’s current profile“Runs Hobbs Cafe. Hosting a Valentine’s party tonight and still hoping Klaus attends. Recently learned Sam is running for mayor.”
IntentionsRepresent what the character plans to do next.
Plans and calendarScheduled activities authored by the simulation.
tonight’s entries14:00 prepare the cafe · 17:00 greet guests · 19:00 the party
Context for the next decisionGive the language model only what it needs right now.
Prompt assemblyCombines profile, schedule, and selected memories.
the literal text handed to the modelCURRENT PROFILE: …  UPCOMING CALENDAR: …  RELEVANT MEMORY: …

02 · how recall works

Why does one memory come to mind instead of another?

A character may collect hundreds of memories, but the language model cannot—and should not—receive all of them every time. Before a character speaks or plans, the system selects a small set using three understandable signals.

example · Isabella meets Klaus at the cafetwo memories compete
comes to mind
relevant · important · recent

“The Valentine's party is tonight and Klaus is invited.”

stays back
less relevant · ordinary · older

“I bought bread last week.”

The first memory is placed in the character's prompt; the second is still stored, but not used for this moment.
the three signalsplain language
relevanceis it related?→ the situation now

A memory about the party matches a conversation about tonight's plans.

importancedid it matter?→ rated 1–10

A campaign decision receives more weight than routine small talk.

recencywhen was it?→ newer ranks higher

Recent memories receive more weight, while older ones fade gradually.

This is a useful engineering approximation, not a claim that human memory can be reduced to three numbers. In Memseek, the weights are visible configuration, so a developer can inspect and change what the software favors.

03 · three simulated days

Watch one belief move through three separate minds.

Isabella runs a cafe, Klaus is a student, and Sam is involved in local politics. At first, each knows different things. Their beliefs become shared only through the conversations the simulation schedules.

  • d1
    two facts begin in two mindsIsabella knows about her party. Sam knows he plans to run for mayor. As the three characters meet, they pass those facts to one another.
  • each character reflects aloneOvernight, each one reviews only its own memories and forms higher-level conclusions. A conclusion keeps links to the observations that support it.
  • d2
    memory shapes the next dayThe characters make new plans, meet again, and attend Isabella's party. By the end of the day, all three believe Sam is running.
  • d3
    reality changes for one personSam withdraws from the race. At that moment only Sam's memory changes; Klaus and Isabella still reasonably believe yesterday's news.
  • the correction travelsSam tells Klaus and Isabella. Their memories and profiles update only after each conversation, creating a record of who knew what at each moment.
day 3 morning · the same fact, three viewsbefore Sam tells them
Sam · updated
observed the change directly

“I have withdrawn from the mayoral race.”

Klaus · old
has not heard the correction

“Sam is running for mayor.”

Isabella · old
has not heard the correction

“Sam is running for mayor.”

This disagreement is expected. Separate memories mean there is no invisible global update.

Because each saved statement names the earlier memory it came from, the system can later trace a belief back through the conversations that carried it. That is what “cited memory” and “provenance” mean on this page: the software keeps the receipts.

04 · from your app

How the software produces one turn.

For technical readers, the loop below is the entire handoff. The simulation records what happened, asks for the memories relevant now, assembles a compact briefing, and gives that briefing to its language model.

simulation.pypublic SDK
# 1 · Klaus relays a memory. His statement points to his own source.
await memseek.records.ingest(
    collection="main", type="chat", entity="agent:klaus",
    text="Klaus told Sam that Isabella is planning a party at Hobbs Cafe.",
    content={"heard_from": "agent:isabella"},
    derived_from=[klaus_source_record_id],   # ← the memory Klaus recalled
)

# 2 · about to act: recall across observations, plans, reflections
recall = await memseek.search(
    query="what should I do this afternoon?",
    entity="agent:klaus", mode="hybrid", k=12,
    include=["text", "scores", "occurred_at"],
)
for hit in recall["hits"]:
    hit["scores"]["importance"]   # the paper's term, on the record

# 3 · assemble the prompt: summary + schedule + relevant memory
prompt = await memseek.render_artifact(
    "daily_agent_prompt", entity="agent:klaus",
)
prompt["rendered"]                        # hand this to your model
prompt["manifest"]["input_record_ids"]   # exactly what it knew

# 4 · your simulator's own call — memseek is not in this loop
line = await your_model.say(prompt["rendered"], in_character="klaus")

The runnable example is examples/generative_agents_toy.py plus the shipped Memseek catalog. Dialogue and reflections are generated by a real model at run time, so the wording on this page is explanatory rather than a captured transcript. The architecture is adapted from Park et al., Generative Agents: Interactive Simulacra of Human Behavior, UIST '23.

start building

Run the three-character experiment.

The script drives the clock, conversations, overnight reflections, and day-three correction. It then shows who knew each fact and traces one belief back to the observations that produced it.

examples/generative_agents_toy.py
# postgres, the api, and a worker
$ make database && source .env.sh
$ uv run memseek migrate
$ uv run uvicorn memseek.api:app &
$ uv run memseek worker &
# then the simulation
$ uv run python examples/generative_agents_toy.py
day 2 · interview: 3 of 3 agents know about the party