02 — Conversations & Memory
In the previous lesson each call to agent.run() was independent — the agent
had no memory of earlier turns. In this lesson you will learn how to:
- Use AgentSession for multi-turn conversations.
- Add InMemoryHistoryProvider so the agent remembers automatically.
Why sessions matter
Without a session, every call to agent.run() starts from scratch. The agent
does not know what the user said before. A session carries the conversation
state so the agent can build on earlier context.
AgentSession at a glance
| Property | Purpose |
|---|---|
session_id |
Local unique identifier for this session |
service_session_id |
Remote service conversation ID (when service-managed history is used) |
state |
Mutable dictionary shared with context and history providers |
Example 1 — Multi-turn with AgentSession
File: examples/02-conversations/multi_turn.py
import asyncio
import os
from dotenv import load_dotenv
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
load_dotenv()
async def main() -> None:
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ.get("FOUNDRY_MODEL", "gpt-5.4-mini"),
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
name="SymptomChecker",
instructions=(
"You are a helpful symptom-checker assistant. "
"Ask clarifying questions about the patient's symptoms. "
"Remember what the patient has already told you. "
"Always remind the user to see a real doctor."
),
)
# Create a session to maintain conversation state across turns
session = agent.create_session()
# Turn 1
result1 = await agent.run(
"I have been sneezing a lot and my eyes are itchy.",
session=session,
)
print(f"Agent: {result1}\n")
# Turn 2 — the agent remembers turn 1
result2 = await agent.run(
"It started about three days ago.",
session=session,
)
print(f"Agent: {result2}\n")
# Turn 3 — ask for a summary
result3 = await agent.run(
"Can you summarise what you know about my symptoms so far?",
session=session,
)
print(f"Agent: {result3}\n")
What is happening
agent.create_session()creates a newAgentSession.- Passing the same
sessionto eachagent.run()call keeps the conversation context. - By turn 3 the agent can summarise information from turns 1 and 2.
Example 2 — Memory with InMemoryHistoryProvider
AgentSession carries context within a single script run. To make the agent
store and reload conversation messages automatically, add a history
provider.
File: examples/02-conversations/memory_provider.py
from agent_framework import Agent, InMemoryHistoryProvider
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ.get("FOUNDRY_MODEL", "gpt-5.4-mini"),
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
name="MemoryBot",
instructions="You are a patient-intake assistant. Remember everything the patient tells you.",
context_providers=[
InMemoryHistoryProvider("memory", load_messages=True),
],
)
session = agent.create_session()
await agent.run("My name is Alice and I am 34 years old.", session=session)
await agent.run("I am allergic to penicillin.", session=session)
result = await agent.run("What do you know about me so far?", session=session)
print(result)
What is InMemoryHistoryProvider?
- It is the built-in history provider that stores conversation messages in memory.
load_messages=Truetells it to reload the full history before each LLM call, so the model always sees everything.- For production you could replace it with a database-backed history provider (e.g., Cosmos DB) — the interface is the same.
Context providers — the bigger picture
History providers are a specific kind of context provider. Context providers run before and after each agent invocation:
They can:
- Inject extra instructions or messages (
context.extend_instructions()). - Read or write to
session.state. - Store and reload message history.
You can write custom context providers by inheriting from ContextProvider:
from agent_framework import AgentSession, ContextProvider, SessionContext
class UserPreferenceProvider(ContextProvider):
def __init__(self) -> None:
super().__init__("user-preferences")
async def before_run(self, *, agent, session, context, state):
if favourite := state.get("favourite_food"):
context.extend_instructions(
self.source_id,
f"User's favourite food is {favourite}.",
)
In this workshop we use InMemoryHistoryProvider, which stores history in
memory for the duration of the script. In a real application you would swap it
for a persistent provider (e.g. backed by a database) so the agent remembers
conversations across sessions. Custom context providers like the example above
let you inject any extra information — but that is beyond the scope of this
workshop.
Try it
Key takeaways
- AgentSession maintains conversation state across
agent.run()calls. - InMemoryHistoryProvider automatically stores and reloads message history.
- Context providers are the extensibility point for injecting context, history, and instructions around each LLM call.