Memoria episodica e semantica: ricordare i turni e ricordare i fatti
Due archivi che rispondono a domande diverse. La memoria episodica conserva ciò che è stato detto e quando; quella semantica conserva fatti estratti e deduplicati sull’utente. Il recupero attinge per impostazione predefinita ai fatti e ricorre alle trascrizioni solo quando la domanda riguarda realmente ciò che è accaduto.
Fatti chiave
- Livello
- Intermedio
- Runtime
- Python • API OpenAI
- Pattern
- Flusso ispezionabile con confini di sistema visibili
- Interazione
- Sandbox live • Script
- Aggiornato
- 28 luglio 2026
Naviga questo esempio
Pattern
Relazioni con la doctrineQuali principi questo pattern esprime nel codice, e quali la sua forma predefinita infrange.Libreria
Sfoglia gli esempiRiapri la libreria completa per confrontare pattern vicini e percorsi collegati.Interazione
Esegui ora nel sandboxProva l'interazione direttamente nella superficie guidata di questo esempio.Sorgente
Apri codice completoLeggi l'implementazione reale, i punti evidenziati e i requisiti runtime.MCP
Chiama via MCPUsa la stessa risorsa dentro agenti, export deterministici e setup MCP.
Principi collegati
Relazioni con la doctrine
Queste sono forme di ragionamento: come un agente decide, quando agisce, cosa ricorda. Per comporre l'interfaccia intorno a esso, vedi l'AI Pattern Blueprint. Per trigger, pianificazione, caricamento del contesto e ripristino, vedi Agent runtime architecture.
Questo pattern esprime questi principi direttamente nel codice.
P7 · Stabilire fiducia attraverso l'ispezionabilità
La memoria semantica è un archivio chiave-valore leggibile, anziché un blob di embedding opaco, quindi è possibile leggere, verificare e correggere elemento per elemento ciò che il sistema ritiene di sapere su un utente. Un livello di memoria i cui contenuti possono essere ispezionati solo chiedendo al modello che cosa ricorda non è ispezionabile in alcun senso utile.
episodic_semantic.py:FACTS
La forma classica di questo pattern li viola per impostazione predefinita. Ognuno riporta la correzione.
P4 · Applicare la divulgazione progressiva all'agenzia del sistema
La memoria recuperata viene inserita silenziosamente nel prompt, quindi una risposta plasmata da qualcosa detto settimane prima appare identica a una plasmata dal turno corrente. Correzione: indicare quali fatti ricordati hanno contribuito alla risposta, a un livello tale da consentire al lettore di intervenire, così che un fatto errato o obsoleto possa essere corretto nel punto in cui provoca il danno, anziché in un momento successivo.
episodic_semantic.py:answerP5 · Sostituire la magia implicita con modelli mentali chiari
All’utente non viene mai comunicato che un fatto su di lui è stato estratto e memorizzato. Dal suo punto di vista, l’assistente semplicemente inizia a sapere delle cose, creando il tipico momento di magia implicita e, quando sono coinvolti dettagli personali, un problema di fiducia più che un motivo di soddisfazione. Correzione: rendere visibile ciò che è stato appreso nel momento in cui viene scritto e fare in modo che l’archivio dei fatti sia consultabile e modificabile, anziché soltanto desumibile dal comportamento.
episodic_semantic.py:remember
Lo decide la tua implementazione, non il pattern. Nessun verdetto.
P8 · Rendere espliciti i passaggi, le approvazioni e i blocchi
La possibilità per l’utente di rifiutare, modificare o eliminare ciò che è stato ricordato dipende interamente da una decisione del wrapper. Il pattern scrive in un archivio e vi legge; non assume alcuna posizione in materia di consenso, conservazione o cancellazione. Nella maggior parte delle giurisdizioni, questa posizione non è facoltativa, ma non spetta al pattern assumerla.
episodic_semantic.py:remember
Le relazioni con la doctrine sono analisi di prima parte, non il risultato di una validazione. Esegui il validatore sul tuo codice per ottenere un verdetto con punteggio.
episodic_semantic.py
"""
Episodic and semantic memory: remember turns, and remember facts
Two stores, because they answer different questions.
Episodic what was said, when. Searchable transcript of past turns.
Semantic what is true. Extracted, deduplicated facts about the user.
Turn -> store episode -> extract facts -> upsert semantic
Query -> retrieve facts (+ episodes only if needed) -> answer
The failure this prevents is subtle. Loading whole past conversations into
context to "remember" the user is expensive and actively harmful: old
transcripts pull the model toward whatever was being discussed then, not what
is being asked now. Facts are small, current, and steer nothing.
So the default retrieval here is facts only. Episodes are fetched when the
question is genuinely about what happened rather than about what is true.
Run: python episodic_semantic.py
"""
from typing import Literal
from dotenv import load_dotenv
from openai import OpenAI
from pydantic import BaseModel, Field
load_dotenv()
client = OpenAI()
MODEL = "gpt-5.4-mini"
# --------------------------------------------------------------
# Two stores. In production these are a vector index and a table;
# the shape of the pattern is the same.
# --------------------------------------------------------------
EPISODES: list[dict[str, str]] = []
FACTS: dict[str, str] = {}
class ExtractedFact(BaseModel):
key: str = Field(
description="Stable snake_case identifier, e.g. 'preferred_language'."
)
value: str = Field(description="The fact itself, as short as possible.")
class Extraction(BaseModel):
facts: list[ExtractedFact] = Field(
description=(
"Durable facts about the user or their situation. Empty when the "
"turn contains nothing worth remembering. Most turns do not."
)
)
class Retrieval(BaseModel):
needs_episodes: bool = Field(
description=(
"True only when the question is about what was previously said or "
"done, rather than about what is currently true."
)
)
why: str
def remember(user_text: str, assistant_text: str) -> list[ExtractedFact]:
"""Append the episode verbatim, then distil anything durable into facts."""
EPISODES.append({"user": user_text, "assistant": assistant_text})
completion = client.beta.chat.completions.parse(
model=MODEL,
messages=[
{
"role": "system",
"content": (
"Extract durable facts about the user from this exchange. "
"A durable fact is still true next month. Preferences, "
"constraints, and stable context qualify. Questions asked, "
"one-off requests, and anything about the current task do "
"not. Return an empty list when nothing qualifies."
),
},
{"role": "user", "content": f"User: {user_text}\nAssistant: {assistant_text}"},
],
response_format=Extraction,
)
extracted = completion.choices[0].message.parsed.facts
for fact in extracted:
# Upsert, so a changed preference replaces rather than accumulates.
# Append-only fact stores end up holding both the old answer and the
# new one, with nothing to say which is current.
FACTS[fact.key] = fact.value
return extracted
def decide_retrieval(question: str) -> Retrieval:
completion = client.beta.chat.completions.parse(
model=MODEL,
messages=[
{
"role": "system",
"content": (
"Decide whether answering needs the past transcript, or "
"whether the known facts are enough. Transcripts are "
"expensive and drag the answer toward old topics, so "
"prefer facts unless the question is genuinely about what "
"was said or done."
),
},
{"role": "user", "content": question},
],
response_format=Retrieval,
)
return completion.choices[0].message.parsed
def answer(question: str) -> str:
plan = decide_retrieval(question)
print(f" retrieval: facts{' + episodes' if plan.needs_episodes else ' only'}")
print(f" why : {plan.why}")
known = "\n".join(f"- {k}: {v}" for k, v in FACTS.items()) or "(nothing known)"
context = f"Known facts:\n{known}"
if plan.needs_episodes:
transcript = "\n".join(
f"User: {e['user']}\nAssistant: {e['assistant']}" for e in EPISODES
)
context += f"\n\nPast turns:\n{transcript}"
completion = client.chat.completions.create(
model=MODEL,
messages=[
{
"role": "system",
"content": (
"Answer using the context provided. If it does not contain "
"the answer, say you do not know rather than guessing."
),
},
{"role": "user", "content": f"{context}\n\nQuestion: {question}"},
],
)
return completion.choices[0].message.content or ""
if __name__ == "__main__":
print("\n> seeding two turns")
for user_text, assistant_text in [
("I run a small ceramics shop on Shopify.", "Noted, thanks."),
("Keep any tooling under 200 a month, that is my ceiling.", "Understood."),
]:
new_facts = remember(user_text, assistant_text)
print(f" learned: {[(f.key, f.value) for f in new_facts] or 'nothing durable'}")
for question in [
"What platform am I on, and what is my budget?",
"What exactly did I say when I mentioned the budget?",
]:
print(f"\n> {question}")
print(f" answer: {answer(question)}")
Principi correlati
- P4trustApplicare la divulgazione progressiva all'agenzia del sistemaFornire per impostazione predefinita le informazioni minime necessarie, consentendo agli utenti di ispezionare ulteriori dettagli quando è richiesta fiducia, comprensione o intervento.Apri il principio →
- P5delegationSostituire la magia implicita con modelli mentali chiariIl prodotto dovrebbe aiutare gli utenti a comprendere cosa il sistema può fare, cosa sta facendo attualmente, cosa non può fare e quali condizioni governano il suo comportamento.Apri il principio →
- P7trustStabilire fiducia attraverso l'ispezionabilitàGli utenti dovrebbero essere in grado di esaminare come è stato prodotto un risultato quando la fiducia, la responsabilità o la qualità della decisione sono importanti.Apri il principio →
- P8trustRendere espliciti i passaggi, le approvazioni e i blocchiQuando il sistema non può procedere, la ragione dovrebbe essere immediatamente visibile, insieme a qualsiasi azione richiesta dall'utente o da un'altra dipendenza.Apri il principio →