Home
» domini
»
Come risolvere la perdita di memoria degli agenti LangChain nelle conversazioni lunghe
Come risolvere la perdita di memoria degli agenti LangChain nelle conversazioni lunghe
Se un agente LangChain dimentica dettagli durante una conversazione lunga, correggete l'architettura prima di aumentare la finestra di contesto del modello. Negli attuali agenti in stile LangChain v1, la continuità della conversazione è costruita su due livelli separati: un checkpointer per lo stato a breve termine con ambito di thread e un store (archivio) per le informazioni a lungo termine che devono sopravvivere tra i thread. Le conversazioni lunghe richiedono poi una terza attenzione: la gestione del contesto, solitamente tramite il taglio o il riassunto dei messaggi più vecchi prima che sovraccarichino il modello.
Questa guida segue la documentazione ufficiale di LangChain verificata l'11 settembre 2026. Gli attuali documenti raccomandano langchain.agents.create_agent per i nuovi agenti e descrivono la persistenza LangGraph come il sistema di memoria sottostante. Esempi più vecchi basati su ConversationChain, ConversationBufferMemory o initialize_agent possono ancora apparire in materiale legacy, ma la guida alla migrazione di LangChain v1 ha spostato le catene legacy e altre funzionalità deprecate in langchain-classic. Vedere la guida ufficiale alla migrazione di LangChain v1.
Illustrazione generata da AI: Il sintomo è semplice: un fatto è stato fornito in precedenza, ma una risposta successiva non lo utilizza più. L'illustrazione è concettuale, non un'interfaccia LangChain catturata.
Cosa significa realmente "Perdita di memoria" in LangChain
Prima di modificare il codice, separate tre problemi che spesso appaiono identici dal punto di vista dell'utente.
Sintomo
Causa probabile
Livello corretto da correggere
L'agente dimentica dopo il riavvio del server
Lo stato era memorizzato solo nella memoria del processo
Checkpointer o store persistente
L'agente dimentica tra due richieste nella stessa chat
Nessun checkpointer, o è stato utilizzato un thread_id diverso
Persistenza del thread
L'agente ricorda le prime interazioni nello storage ma smette di usarle in chat molto lunghe
Il contesto del modello è diventato troppo grande o rumoroso
Riassunto, taglio, recupero
L'agente ricorda una preferenza in una chat ma non in una nuova chat
Illustrazione generata da AI: Pensate alla memoria a breve termine come allo stato di un singolo thread di conversazione. L'attuale LangChain implementa quella continuità attraverso un checkpointer piuttosto che le classi di memoria legacy spesso mostrate nei tutorial più vecchi.
Cosa vi serve prima di iniziare
Vi serve un'applicazione LangChain/LangGraph attuale, un'integrazione del modello e un posto dove persistere lo stato. Per un esperimento locale, InMemorySaver è sufficiente. Per la produzione, utilizzate un checkpointer basato su database. I documenti ufficiali di LangChain mostrano PostgreSQL attraverso il pacchetto separato langgraph-checkpoint-postgres.
Mantenete chiari quattro identificatori:
ID conversazione o chat: l'identificatore che la vostra applicazione espone agli utenti.
thread_id: la chiave di persistenza LangGraph utilizzata per riprendere lo stato di un thread.
ID utente: l'identità durevole utilizzata per namespace le memorie a lungo termine.
Chiave di memoria: la chiave per un singolo elemento durevole all'interno di un namespace dello store.
Non dovrebbero automaticamente avere lo stesso valore. Un utente può avere molti thread, e un thread può contenere molti fatti.
Passaggio 1: Riprodurre il fallimento con un test a due richieste
Iniziate con il test più piccolo possibile. Chiedete all'agente di ricordare un dettaglio unico, poi invocatelo di nuovo e chiedete quel dettaglio. Non testate la memoria con una singola chiamata invoke() perché il modello può vedere tutto in quell'unica richiesta anche quando la persistenza è rotta.
config = {"configurable": {"thread_id": "debug-thread-001"}}
agent.invoke(
{"messages": [{"role": "user", "content": "Remember that my project codename is Juniper."}]},
config,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "What is my project codename?"}]},
config,
)
Se la seconda richiesta dimentica "Juniper", ispezionate la configurazione del checkpointer e l'effettivo thread_id prima di modificare i prompt.
Passaggio 2: Aggiungere un Checkpointer per la memoria dello stesso thread
Un checkpointer persiste gli snapshot dello stato del grafo dell'agente. LangGraph lo utilizza per la memoria a breve termine, il recupero dalle interruzioni, i flussi human-in-the-loop e la tolleranza ai guasti. L'attuale guida alla persistenza descrive i checkpointers come con ambito di thread e afferma che l'applicazione accede allo stato passando un thread_id. Vedere la guida ufficiale alla persistenza di LangGraph.
InMemorySaver è eccellente per confermare che il vostro collegamento del thread funziona, ma memorizza i checkpoint nella RAM. LangGraph avverte esplicitamente che MemorySaver/InMemorySaver non persistono attraverso i riavvii del processo.
Passaggio 3: Mantenere lo stesso thread_id per la stessa conversazione
Il bug più comune a livello di applicazione è creare un nuovo thread_id su ogni richiesta HTTP. Il database potrebbe funzionare perfettamente mentre ogni richiesta avvia un diverso thread LangGraph.
Ad esempio, supponiamo che il vostro front-end abbia l'ID chat chat_8bf4. Mappate quel valore deterministico al thread LangGraph e riutilizzatelo per ogni turno in quella chat. Una nuova chat dovrebbe ricevere un nuovo ID thread.
Illustrazione generata da AI: La persistenza non rimuove il limite di contesto del modello. Un thread stabile può contenere più cronologia di quella che il modello dovrebbe ricevere su ogni chiamata.
Non utilizzate un thread_id permanente per tutte le chat appartenenti allo stesso utente. Questo fonde conversazioni non correlate in un unico flusso di stato. Se utilizzate PostgreSQL, l'attuale guida alla risoluzione dei problemi di LangGraph afferma anche che thread_id dovrebbe rimanere sotto i 255 caratteri; un UUID o un hash deterministico è più sicuro di un enorme oggetto serializzato.
Passaggio 4: Sostituire la persistenza in memoria prima della produzione
Una volta superato il test a due richieste, testate un riavvio del processo. Salvate un fatto, fermate l'applicazione, avviatela di nuovo, poi chiedete il fatto con lo stesso ID thread. Se utilizzate ancora InMemorySaver, la dimenticanza è un comportamento atteso.
I documenti ufficiali sulla memoria a breve termine mostrano una configurazione di produzione supportata da PostgreSQL utilizzando PostgresSaver:
from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://user:password@db-host/app"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup()
agent = create_agent(
model="your-provider:your-model",
tools=[],
checkpointer=checkpointer,
)
Per la configurazione del pacchetto attualmente documentata da LangChain, vedere Memoria a breve termine. Non mettete credenziali di database reali direttamente nel codice sorgente; utilizzate il vostro normale sistema di gestione dei segreti.
Illustrazione generata da AI: Un elemento particolarmente importante è l'archiviazione locale al processo: un checkpointer in memoria viene intenzionalmente perso dopo il riavvio, quindi i test di riavvio appartengono alla suite di test della memoria.
Passaggio 5: Gestire le cronologie lunghe invece di inviare tutto per sempre
Una finestra di contesto è la quantità di contesto di input e output che un modello può gestire in una singola chiamata al modello. Il checkpointing può preservare una conversazione molto lunga nello storage, ma ciò non significa che ogni messaggio storico dovrebbe essere inviato al modello per sempre.
La guida sulla memoria a breve termine di LangChain afferma che le cronologie lunghe possono superare la finestra di contesto del modello e che anche i modelli capaci di accettare la cronologia completa possono essere distratti da contenuti obsoleti o fuori tema, con maggiore latenza e costo. Le strategie documentate sono il taglio, l'eliminazione, il riassunto o l'applicazione di una politica personalizzata.
Utilizzare il riassunto quando i dettagli vecchi contano ancora
SummarizationMiddleware è l'opzione integrata attuale per sostituire la cronologia più vecchia con un riassunto compatto mantenendo i messaggi recenti. Il suo trigger può essere basato sul conteggio dei token, sul conteggio dei messaggi o su una frazione del contesto del modello.
I numeri sopra sono un esempio di politica, non impostazioni universali. Scegliete le soglie dopo aver misurato i vostri prompt, gli output degli strumenti, i limiti di contesto del modello, la latenza e la qualità del riassunto. Vedere la documentazione del middleware integrato di LangChain per le opzioni di trigger e keep attualmente supportate.
Illustrazione generata da AI: Testate il richiamo dopo abbastanza turni da attivare la vostra politica di taglio o riassunto; una chat breve può nascondere bug di contesto lungo.
Non tagliare i messaggi degli strumenti alla cieca
Se implementate un'eliminazione o un taglio personalizzato, preservate una sequenza di messaggi valida. LangChain avverte che molti provider richiedono che un messaggio dell'assistente contenente chiamate agli strumenti sia seguito dai corrispondenti messaggi di risultato degli strumenti. Rimuovere una metà di quella coppia può creare errori del provider o comportamenti confusi del modello.
Passaggio 6: Spostare i fatti durevoli in uno Store a lungo termine
Uno store è il livello di persistenza di LangGraph per i dati definiti dall'applicazione al di fuori dello stato del grafo di un singolo thread. Gli attuali documenti LangChain utilizzano gli store per informazioni che dovrebbero essere disponibili attraverso le conversazioni, come preferenze utente, fatti o conoscenza condivisa dell'applicazione.
Gli elementi dello store a lungo termine sono documenti JSON organizzati da un namespace e una chiave. Un namespace pratico spesso contiene un identificatore utente o organizzazione:
Questo è diverso dal salvare l'intera trascrizione. Memorizzate le informazioni che il vostro prodotto tratta intenzionalmente come durevoli. Se un fatto è privato o regolamentato, applicate le vostre normali politiche di conservazione, autorizzazione, crittografia ed eliminazione piuttosto che assumere che la "memoria dell'agente" ne sia esente.
Illustrazione generata da AI: Questa illustrazione utilizza etichette concettuali ampie piuttosto che nomi API attuali letterali. Per nuovo codice LangChain v1, utilizzate la distinzione checkpointer/store descritta nel testo e nei documenti ufficiali.
Utilizzare uno store basato su database in produzione
La guida ufficiale sulla memoria a lungo termine mostra sia InMemoryStore che PostgresStore, e nota esplicitamente che l'implementazione in memoria dovrebbe essere sostituita da uno store basato su database per la produzione. Elenca anche integrazioni dello store oltre a PostgreSQL. Utilizzate il backend che si adatta ai vostri requisiti di distribuzione e operativi piuttosto che selezionare un database vettoriale semplicemente perché la parola "memoria" è coinvolta.
Aggiungere ricerca semantica solo quando si necessita di richiamo sfocato
Gli store LangGraph possono essere configurati con un indice così che store.search() possa recuperare elementi per similarità semantica. Questo è utile quando avete molte memorie e non conoscete la chiave esatta. Per un piccolo insieme di preferenze strutturate, la ricerca diretta namespace/chiave è spesso più semplice e deterministica.
Passaggio 7: Rendere espliciti i percorsi di lettura e scrittura della memoria
Persistere un elemento a lungo termine non garantisce che l'agente lo utilizzi. L'applicazione ha ancora bisogno di un percorso di recupero. Gli attuali agenti LangChain permettono agli strumenti di accedere allo store fornito attraverso ToolRuntime.
from dataclasses import dataclass
from langchain.tools import tool, ToolRuntime
@dataclass
class Context:
user_id: str
@tool
def get_response_style(runtime: ToolRuntime[Context]) -> str:
store = runtime.store
if store is None:
return "No memory store configured"
namespace = ("users", runtime.context.user_id, "preferences")
item = store.get(namespace, "response_style")
return item.value["value"] if item else "default"
Potete anche costruire prompt dinamici o middleware che leggono lo stato e la memoria durevole prima di una chiamata al modello. La regola di progettazione importante è che il percorso di recupero dovrebbe essere osservabile e testabile. "L'informazione esiste da qualche parte nel database" non è sufficiente.
Illustrazione generata da AI: Un utile test di regressione chiede un fatto precedente dopo molti turni e verifica che la risposta provenga dall'inteso livello di memoria, non da testo del prompt accidentalmente duplicato.
Passaggio 8: Testare i quattro confini della memoria separatamente
Una suite di test di memoria affidabile dovrebbe coprire più di "il modello ha ricordato il mio nome una volta". Utilizzate almeno questi quattro casi:
Test
Risultato atteso
Due invocazioni, stesso ID thread
Le informazioni con ambito di thread sono disponibili
Due invocazioni, diversi ID thread
La cronologia del thread a breve termine non trapela
Riavvio dell'applicazione, stesso ID thread con checkpointer persistente
Lo stato del thread può riprendere
Nuovo thread, stesso utente con store a lungo termine
Solo i fatti durevoli intenzionalmente memorizzati possono essere richiamati
Poi aggiungete un test di conversazione lunga che supera la vostra soglia di riassunto. Affermate che i fatti durevoli importanti sopravvivono, le sequenze recenti di chiamate agli strumenti rimangono valide e la dimensione del prompt rimane entro il vostro budget di contesto target.
Illustrazione generata da AI: Trattate questo come una checklist QA concettuale. L'attuale architettura LangChain v1 dovrebbe essere validata contro le API ufficiali di checkpointer, store e middleware piuttosto che esempi di classi di memoria legacy.
Un'architettura di produzione minima
Per molte applicazioni di agenti, un design robusto appare così:
L'API riceve user_id, conversation_id e il nuovo messaggio utente.
L'applicazione mappa conversation_id a un LangGraph thread_id stabile.
Un checkpointer persistente ripristina lo stato del thread.
Uno store a lungo termine recupera solo i fatti utente o applicazione durevoli necessari per la richiesta.
Il riassunto o il taglio mantiene la cronologia rivolta al modello entro un budget di contesto misurato.
L'agente esegue gli strumenti e il modello.
Il checkpointer committa lo stato del thread aggiornato.
Solo i fatti approvati sono scritti nello store a lungo termine.
Se distribuite tramite LangGraph Agent Server, l'attuale guida alla persistenza afferma che il server gestisce automaticamente l'infrastruttura di persistenza, quindi non duplicate quel livello senza controllare il modello di distribuzione.
Errori comuni che fanno sembrare la memoria rotta
Generare un nuovo thread_id per ogni richiesta
Questo crea un nuovo stato di conversazione ogni turno. Registrate l'ID thread accanto all'ID chat della vostra applicazione e verificate il riutilizzo.
Utilizzare InMemorySaver in un servizio multi-worker o riavviabile
Lo stato locale alla RAM scompare con il processo e potrebbe non essere condiviso tra i worker. Utilizzate un backend persistente per la continuità di produzione.
Assumere che un checkpointer risolva il problema della finestra di contesto
Un checkpointer preserva lo stato; non garantisce che una trascrizione in continua crescita sia utile al modello. Aggiungete una politica esplicita di gestione del contesto.
Mettere ogni fatto storico nel prompt
Più contesto non è automaticamente un contesto migliore. Recuperate le informazioni rilevanti per il turno corrente e preservate la continuità conversazionale recente separatamente.
Trattare i riassunti come un database perfetto
I riassunti sono rappresentazioni compresse generate dal modello. Se un fatto deve essere esatto—un identificatore di account, un vincolo contrattuale, una preferenza approvata dall'utente o uno stato del workflow—memorizzatelo come dati strutturati piuttosto che sperare che sopravviva a riassunti ripetuti.
Mescolare gli ambiti a breve e lungo termine
La cronologia del thread non dovrebbe diventare silenziosamente un profilo utente globale. Viceversa, una preferenza utente destinata a seguire l'utente attraverso le chat non dovrebbe vivere solo in un thread.
Copiare tutorial di memoria pre-v1 senza controllare gli import
Se un esempio parte da catene legacy o vecchie classi di memoria, confrontatelo con l'attuale migrazione v1 e i documenti sulla memoria prima di usarlo in una nuova applicazione.
Checklist di debug
Confermate che l'agente è stato creato con un checkpointer.
Registrate e confrontate thread_id tra richieste consecutive.
Ispezionate lo stato del thread memorizzato prima di incolpare il modello.
Riavviate il processo e ripetete lo stesso test dello stesso thread.
Sostituite InMemorySaver con un checkpointer persistente per la produzione.
Misurate la crescita dei messaggi/token nelle chat lunghe.
Abilitate il riassunto o il taglio prima che la cronologia diventi eccessiva.
Mantenete valide le sequenze di chiamata/risultato degli strumenti quando rimuovete messaggi.
Spostate i fatti cross-thread in uno store a lungo termine con namespace.
Testate un nuovo thread per lo stesso utente per verificare il richiamo a lungo termine intenzionale.
Testate un utente diverso per verificare l'isolamento della memoria.
Tracciate quali elementi di memoria sono stati recuperati per ogni risposta.
In conclusione
La perdita di memoria degli agenti LangChain è raramente risolta da una finestra di contesto più grande. Prima rendete lo stato del thread persistente con un checkpointer e un thread_id stabile. Poi controllate le cronologie lunghe con il taglio o SummarizationMiddleware. Infine, posizionate i fatti che devono sopravvivere attraverso le conversazioni in uno store a lungo termine con namespace e recuperateli deliberatamente.
Quella separazione vi dà qualcosa di molto più utile della "memoria": un sistema che potete riavviare, scalare, testare, auditar e ragionare quando un utente chiede, "Perché l'agente ha dimenticato?"