π¬ Multi-Turn Conversations
Build agents that remember context across multiple messages β the foundation of every chatbot.
By default, each call to RunAsync() is stateless β the agent doesn't remember
previous messages. To build a conversational agent, you need to maintain and pass the chat
history between turns. Agent Framework makes this straightforward via the AgentResponse.Messages
property.
Each AgentResponse contains all the messages produced in that turn β including
the user input, any tool calls, and the final assistant reply. By passing these messages back
on the next RunAsync() call, the model has full context of the conversation so far.
For scenarios where you want the service to store conversation history automatically (no local state), use agents backed by the Azure AI Foundry Agents service or the OpenAI Responses API β both support server-side conversation threads.
Key Concepts
- AgentResponse.Messages β the list of all messages produced in a single run
- Chat history accumulation β pass prior messages back to
RunAsync()to build up context - AgentResponse.Text β convenience property that extracts just the text of the reply
- Stateless vs stateful β local history vs service-managed threads (Foundry, Assistants API)
NuGet Packages
dotnet add package Microsoft.Agents.AI.OpenAI --prerelease
dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Azure.Identity
Code Sample
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = new AzureOpenAIClient(
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
new AzureCliCredential())
.GetChatClient("gpt-4o-mini")
.AsAIAgent(instructions: "You are a helpful assistant.");
// Accumulate the full conversation history across turns.
var history = new List<ChatMessage>();
// Turn 1 β introduce ourselves.
var response1 = await agent.RunAsync("Hi! My name is Alex and I love astronomy.", history);
history.AddRange(response1.Messages);
Console.WriteLine($"Agent: {response1.Text}");
// Turn 2 β the agent remembers who we are.
var response2 = await agent.RunAsync("What is my name, and what subject do I love?", history);
history.AddRange(response2.Messages);
Console.WriteLine($"Agent: {response2.Text}");
// Turn 3 β follow-up building on prior turns.
var response3 = await agent.RunAsync(
"Can you suggest a beginner book on that subject for me?", history);
history.AddRange(response3.Messages);
Console.WriteLine($"Agent: {response3.Text}");
Step-by-Step Explanation
-
Maintain a history list β
List<ChatMessage>accumulates all messages from every turn.ChatMessagecomes fromMicrosoft.Extensions.AI. - Pass history to RunAsync() β The second parameter is the prior conversation. The agent prepends these messages to the new request so the model has full context.
-
Accumulate the response β After each turn, add
response.Messagesto the history list. These messages include the user turn, any tool calls, and the assistant's reply. -
Use
.Textto read the reply βAgentResponse.Textaggregates allTextContentitems across all returned messages into a single string.
Expected Output
Agent: Nice to meet you, Alex! Astronomy is a fascinating subject.
Agent: Your name is Alex, and you love astronomy!
Agent: A great beginner book on astronomy is "NightWatch" by Terence Dickinson. ...
Next Steps
All Examples
- π€ Hello Agent
- π§ Function Tools
- π¬ Multi-Turn Conversations
- β‘ Streaming Responses
- π¦ Structured Output
- π Sequential Workflows
- πΈοΈ Multi-Agent Orchestration
- π¦ Ollama β Local AI
- π₯οΈ LM Studio β Local AI
- π§ Agent Memory
- π RAG
- π MCP Tools
- π OpenTelemetry
- π§ Customer Support Triage
- π¬ Research Pipeline
- π€ Tools vs Sub-Agents
Concepts Used
π Running Agents Docs