Lesson 10: History
Add conversation history so the agent remembers what you said earlier in the session.
- Session Storage: How Agno stores conversations in PostgreSQL.
- Ephemeral Sessions: History that resets when you restart the script.
- Persistent Sessions: History that survives restarts.
- Context Injection: What actually gets sent to the LLM.
What History Actually Does
In Lesson 9, each turn was independent. The LLM received only the system prompt and current message. Now we add history:
Turn 1: "My name is Bob"
→ LLM receives: [system, "My name is Bob"]
→ Response: "Nice to meet you, Bob!"
Turn 2: "What is my name?"
→ LLM receives: [system, "My name is Bob", "Nice to meet you, Bob!", "What is my name?"]
→ Response: "Your name is Bob."
The agent stores each exchange in PostgreSQL. Before the next turn, it loads previous messages and prepends them to the context. The LLM sees the full conversation, so it can answer follow-up questions.
Ephemeral Sessions
Each script run gets a new session ID. History accumulates during the run but resets when you restart.
"""
Lesson 10a: Ephemeral Sessions
Adds chat history storage in Postgres. Previous messages are injected into the
context window before each turn. Session is ephemeral - new session ID on each
script run, so history resets when you restart.
Run: uv run 10-history-ephemeral.py
Try: "My name is Bob" → "What is my name?"
Observe in Phoenix (http://localhost:6006):
- Prior messages from same session in LLM input
- Current datetime injected into context
- History grows with each turn
Reset: uv run tools/reset_data.py
"""
import os
from dotenv import load_dotenv
from phoenix.otel import register
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.db.postgres import PostgresDb
load_dotenv()
register(project_name="10-history-ephemeral", auto_instrument=True, batch=True, verbose=True)
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
agent = Agent(
name="Assistant",
model=OpenAIChat(id=os.getenv("OPENAI_MODEL_ID")),
instructions="You are a helpful assistant. Be concise.",
db=db,
add_history_to_context=True,
num_history_runs=5,
add_datetime_to_context=True,
markdown=True,
)
agent.cli_app(stream=True)
What's New
Database connection:
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
Connects to the ai database. Agno creates tables automatically on first run.
History settings:
db=db,
add_history_to_context=True,
num_history_runs=5,
add_datetime_to_context=True,
db: Where to store sessionsadd_history_to_context: Load previous messages before each turnnum_history_runs: How many previous exchanges to include (5 = last 5 user/assistant pairs)add_datetime_to_context: Inject current timestamp so the agent knows "now"
Try It
uv run 10-history-ephemeral.py
> My name is Bob
Nice to meet you, Bob!
> What is my name?
Your name is Bob.
> Ctrl+C (stop script)
Now restart and try again:
uv run 10-history-ephemeral.py
> What is my name?
I don't know your name. You haven't told me yet.
New run = new session = no history. The agent genuinely doesn't know—the previous session's data exists in Postgres, but this run has a different session ID.
Persistent Sessions
Use a fixed session ID to maintain history across restarts.
"""
Lesson 10b: Persistent Sessions
Uses fixed session_id to persist history across script restarts. Unlike 10a,
this maintains the same conversation thread in Postgres between runs. Stop the
script, restart it, and the agent still remembers the conversation.
Run: uv run 10-history-persistent.py
Try: "My name is Alice" → Ctrl+C → re-run script → "What's my name?"
Observe in Phoenix (http://localhost:6006):
- Same session_id across multiple script runs
- Full conversation history restored on restart
- Compare with 10a traces
Reset: uv run tools/reset_data.py
"""
import os
from dotenv import load_dotenv
from phoenix.otel import register
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.db.postgres import PostgresDb
load_dotenv()
register(project_name="10-history-persistent", auto_instrument=True, batch=True, verbose=True)
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
agent = Agent(
name="Assistant",
model=OpenAIChat(id=os.getenv("OPENAI_MODEL_ID")),
instructions="You are a helpful assistant. Be concise.",
db=db,
session_id="demo-session-001", # fixed session = persistent history
add_history_to_context=True,
num_history_runs=10,
add_datetime_to_context=True,
markdown=True,
)
agent.cli_app(stream=True)
The Key Difference
session_id="demo-session-001", # fixed session = persistent history
That's it. One line. Same session ID means Agno loads the same conversation from Postgres, regardless of when you run the script.
Try It
uv run 10-history-persistent.py
> My name is Alice
Nice to meet you, Alice!
> Ctrl+C (stop script)
Wait a minute. Restart:
uv run 10-history-persistent.py
> What's my name?
Your name is Alice.
The agent remembers because it loaded the previous session from the database.
When to Use Which
Ephemeral sessions (no fixed session_id):
- Chat widgets where each visit is independent
- Parallel processing where each item needs isolation
- Testing and development
Persistent sessions (fixed session_id):
- User accounts where you want conversation continuity
- Long-running tasks that might be interrupted
- Support threads that span multiple interactions
In production, you'd typically generate session IDs per user or per conversation thread, not hardcode them.
Observe in Phoenix
Open http://localhost:6006 and compare traces between the two scripts.
Ephemeral (10-history-ephemeral):
- Each run shows a new session
- Message arrays grow within a run, reset between runs
Persistent (10-history-persistent):
- Same session across runs
- Message arrays include history from previous runs
- You can see the conversation accumulating over time
Look at the LLM input for any trace—you'll see the prior messages injected before the current one. That's history in action.
What's Stored in Postgres
Agno creates tables in the ai database with the agno_ prefix. Runs are stored as a JSONB array inside agno_sessions—not as separate rows.
-- View your sessions
SELECT session_id, created_at FROM agno_sessions ORDER BY created_at DESC;
-- View conversation history for a session
SELECT
session_id,
run->>'run_id' AS run_id,
run->'input'->>'input_content' AS user_prompt,
run->>'content' AS ai_response,
(run->'metrics'->>'total_tokens')::int AS total_tokens,
(run->'metrics'->>'duration')::float AS duration_seconds,
to_timestamp((run->>'created_at')::bigint) AS created_at
FROM
agno_sessions,
jsonb_array_elements(runs) AS run
ORDER BY created_at DESC;
Each "run" is one user input + agent response pair, stored as a JSON element in the runs array. The num_history_runs parameter controls how many of these get loaded into context.
Key Concepts
| Concept | This Lesson |
|---|---|
| Session | Container for a conversation thread |
| History | Previous messages replayed to the LLM |
| Ephemeral | New session ID each run (default) |
| Persistent | Fixed session ID across runs |
| num_history_runs | How much history to load |
What's Next
History lets the agent remember this conversation. But what about facts that should persist across all conversations? In Lesson 11, we add memory—the agent will remember things about you even in new sessions.