Chapter 16 · Memory: User Profiles and Summaries
Learning objectives: Understand the essential difference between memory and history/state, master two memory modes —
update_memory_on_runandenable_agentic_memory, learn to customizeMemoryManagerand manage memories manually, and distinguish between "memory optimization" and "session summaries."
16.1 What Is Memory: Cross-Session User Profiles
The conclusion from Chapter 14 unfolds here: history remembers "what we聊过 (chatted about)" (session dimension), memory remembers "who this person is" (user dimension). In customer-service and personal-assistant applications, a user's preferences shouldn't disappear when a session ends — distilled facts like "this user prefers email communication" or "they're responsible for the payments module" are user memories.
The official docs describe a four-step working mechanism:
user_ididentifies "whose memories these are";- The configured database stores each user's memory records (default table name
agno_memories); - In subsequent runs, that user's memories are automatically added to the model context;
- The memory mode determines when records are created, updated, or deleted.
16.2 Two Memory Modes: Auto-Extraction vs. Agent-Driven Decisions
Agno provides two mutually exclusive memory modes (carried over in 3.x):
| Mode | Config | Behavior |
|---|---|---|
| Auto mode | update_memory_on_run=True | Runs a fixed memory-extraction round after each run, processing new input into memories |
| Agentic mode | enable_agentic_memory=True | Gives the Agent a set of memory tools; the model autonomously decides when to create, update, or delete |
Pick one
The official docs are explicit: when both are enabled simultaneously, agentic mode takes precedence and auto-extraction is skipped. Pick exactly one per Agent.
# memory_basic.py —— Minimal cross-session memory example
import os
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
db=SqliteDb(db_file="tmp/memory.db"), # Memory and sessions share this database
update_memory_on_run=True, # Auto-extract memories after each run
markdown=True,
)
# Session 1: state facts
agent.print_response(
"My name is Xiaogao. I enjoy hiking on weekends and prefer short emails on workdays.",
user_id="gao", # Key: memories are attached to user_id
session_id="onboarding",
)
# Session 2 (different session): different topic, but the profile persists
agent.print_response(
"How should I receive project weekly reports going forward?",
user_id="gao",
session_id="weekly-report",
) # Model can answer "short emails" because it reads gao's memoriesNote that user_id is the key to memory — all sessions under the same user_id share the profile. This is precisely what distinguishes memory from history and state.
16.3 Custom MemoryManager: Making Extraction More Controllable
The default memory extraction uses the main model. In production, two common requirements arise: use a cheaper smaller model for extraction and customize the "what to remember vs. what to ignore" rules. Both require explicitly constructing a MemoryManager:
# memory_manager.py —— Custom memory manager
import os
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.memory import MemoryManager # Verified v2 import path
from agno.models.openai import OpenAIChat
manager = MemoryManager(
# Use a smaller, cheaper model for extraction — fast and cost-effective
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
db=SqliteDb(db_file="tmp/memory.db"),
# Override the default "memory capture instructions" to tighten the extraction scope
memory_capture_instructions=(
"Only record long-lived user facts (identity, role, stable preferences). "
"Ignore one-off requests and transient emotions."
),
)
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
db=SqliteDb(db_file="tmp/memory.db"),
memory_manager=manager, # Wire in the custom manager
update_memory_on_run=True,
)
agent.print_response("I've been torn about whether to change jobs lately", user_id="gao", session_id="s1")
# "torn about changing jobs" is a transient state — per the instructions above, it should NOT be written to long-term memory16.4 Manual Management and Inspection of Memories
In debugging and compliance scenarios, you need to directly view or even modify memory content:
# memory_admin.py —— View / delete user memories
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
db = SqliteDb(db_file="tmp/memory.db")
agent = Agent(db=db, update_memory_on_run=True)
# Read: list all memories for a user
memories = agent.get_user_memories(user_id="gao") or []
for m in memories:
print(m.memory_id, "->", m.memory)
# Database level also supports clearing and rebuilding (common in test environments)
db.clear_memories()get_user_memories() returns structured objects (with memory_id and memory text), making it suitable for building "user profile pages," audit exports, or deletion workflows — the official docs flag this as the standard entry point for compliance reviews.
16.5 Memory Optimization vs. Session Summaries: Two Things Not to Confuse
Beginners often conflate three "summarization" mechanisms. In Agno they are entirely distinct:
| Mechanism | Config | Target | Purpose |
|---|---|---|---|
| User memory | update_memory_on_run=True | User facts (cross-session) | Profiling |
| Memory optimization strategy | agno.memory.strategies.SummarizeStrategy etc. | The memory list itself | Prevent unbounded memory growth; merge/compress old entries |
| Session summary | enable_session_summaries=True | A single session's history messages | Control token cost in long conversations |
Memory optimization: as memory entries accumulate they consume more context and may contradict each other; SummarizeStrategy uses the model to merge redundant memories into a small number of summary-style entries. Session summaries, on the other hand, target a single session internally — automatically generating rolling summaries to replace full history when a conversation gets too long:
# session_summary.py —— Rolling summaries for long conversations
import os
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
db=SqliteDb(db_file="tmp/chat.db"),
enable_session_summaries=True, # Session summaries auto-generated and updated
add_history_to_context=False, # No longer carry full history; rely on summaries
markdown=True,
)
sid = "long-chat-001"
for i in range(5):
agent.print_response(f"Let's discuss requirement point {i + 1}...", session_id=sid)
summary = agent.get_session_summary(session_id=sid)
if summary:
print(summary.summary) # Condensed version of the entire conversation16.6 Privacy and Compliance Considerations
The memory system inherently involves personal information. Before going live, answer four questions:
- Collection boundary: Clearly write in
memory_capture_instructionsthat only business-necessary facts are collected; avoid "顺手" (casual) storage of sensitive info; - Isolation: Ensure
user_idcomes from a trusted login system, not a client-controlled parameter — otherwise User A could forge an ID to read User B's memories; - Right to be forgotten: Provide a "forget me" entry point (delete by
memory_idorclear_memories()), aligned with GDPR-style requirements; - Retention period: Schedule periodic cleanup tasks for the memories table. Stale profiles often do more harm than good.
Chapter Summary
- Memory scopes by
user_idand is shared across sessions, complementing history (session) and state (session); - Pick one of two modes:
update_memory_on_run=True(fixed extraction) orenable_agentic_memory=True(model-driven decisions); MemoryManager(model=..., db=...)supports an independent extraction model plus custom capture instructions for controllable extraction behavior;get_user_memories()is the standard entry point for debugging and compliance review;- Distinguish three "summarization tasks": user memory, memory optimization (SummarizeStrategy), and session summaries (
enable_session_summaries).
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What is the core difference between user memory and chat history?
2. What happens when both update_memory_on_run=True and enable_agentic_memory=True are enabled?
3. To use a cheaper model for memory extraction and restrict "what to remember", the correct approach is?
4. Regarding enable_session_summaries=True, which statement is correct?
🛠️ Hands-On Exercises
- Run the same 5-sentence self-introduction (containing personal info) under both "auto mode" and "agentic mode"; compare the number of memory entries and wording differences returned by
get_user_memories(). - Add "never record any phone numbers or ID card numbers" to
memory_capture_instructions, then intentionally send a message containing a fake phone number; verify whether the extractor obeys the rule. - Script 20 consecutive messages to the same session; print
response.metricswith and withoutenable_session_summariesto quantify the token savings from the summarization mechanism.
After completing the hands-on exercises, proceed to Chapter 17: Knowledge Bases and RAG Basics.