Skip to content

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_run and enable_agentic_memory, learn to customize MemoryManager and 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:

  1. user_id identifies "whose memories these are";
  2. The configured database stores each user's memory records (default table name agno_memories);
  3. In subsequent runs, that user's memories are automatically added to the model context;
  4. 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):

ModeConfigBehavior
Auto modeupdate_memory_on_run=TrueRuns a fixed memory-extraction round after each run, processing new input into memories
Agentic modeenable_agentic_memory=TrueGives 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.

python
# 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 memories

Note 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:

python
# 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 memory

16.4 Manual Management and Inspection of Memories ​

In debugging and compliance scenarios, you need to directly view or even modify memory content:

python
# 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:

MechanismConfigTargetPurpose
User memoryupdate_memory_on_run=TrueUser facts (cross-session)Profiling
Memory optimization strategyagno.memory.strategies.SummarizeStrategy etc.The memory list itselfPrevent unbounded memory growth; merge/compress old entries
Session summaryenable_session_summaries=TrueA single session's history messagesControl 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:

python
# 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 conversation

16.6 Privacy and Compliance Considerations ​

The memory system inherently involves personal information. Before going live, answer four questions:

  1. Collection boundary: Clearly write in memory_capture_instructions that only business-necessary facts are collected; avoid "顺手" (casual) storage of sensitive info;
  2. Isolation: Ensure user_id comes from a trusted login system, not a client-controlled parameter — otherwise User A could forge an ID to read User B's memories;
  3. Right to be forgotten: Provide a "forget me" entry point (delete by memory_id or clear_memories()), aligned with GDPR-style requirements;
  4. Retention period: Schedule periodic cleanup tasks for the memories table. Stale profiles often do more harm than good.

Chapter Summary ​

  • Memory scopes by user_id and is shared across sessions, complementing history (session) and state (session);
  • Pick one of two modes: update_memory_on_run=True (fixed extraction) or enable_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 ​

  1. 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().
  2. 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.
  3. Script 20 consecutive messages to the same session; print response.metrics with and without enable_session_summaries to quantify the token savings from the summarization mechanism.

After completing the hands-on exercises, proceed to Chapter 17: Knowledge Bases and RAG Basics.