How to Handle Multi-Turn Conversations in AI
Before You Start
You need a working assistant with conversation history management. This guide builds on the history infrastructure described in How to Add Conversation History to an AI Assistant. If your assistant only handles single-turn interactions (each message is independent), start there first. Multi-turn handling is about how you use and manage that history to maintain coherent conversational state.
Step-by-Step Setup
Beyond raw message history, maintain a structured state object that tracks what the conversation has established. This state gives the model explicit information about the current context rather than requiring it to infer everything from the message log. Key state fields include: the current topic (what the user is asking about), active entities (things that have been mentioned and might be referenced again), pending tasks (multi-step operations in progress), and established facts (decisions and preferences stated during this conversation).
# Example: structured conversation state
class ConversationState:
def __init__(self, conversation_id):
self.conversation_id = conversation_id
self.current_topic = None
self.active_entities = {} # name -> description
self.pending_tasks = [] # in-progress multi-step operations
self.established_facts = [] # facts stated this session
self.topic_history = [] # previous topics for back-reference
def set_topic(self, topic):
if self.current_topic:
self.topic_history.append(self.current_topic)
self.current_topic = topic
def add_entity(self, name, description):
self.active_entities[name] = description
def to_context_string(self):
parts = []
if self.current_topic:
parts.append(f"Current topic: {self.current_topic}")
if self.active_entities:
entities = ", ".join(self.active_entities.keys())
parts.append(f"Active entities: {entities}")
if self.pending_tasks:
tasks = "; ".join(self.pending_tasks)
parts.append(f"Pending tasks: {tasks}")
if self.established_facts:
facts = "; ".join(self.established_facts[-5:])
parts.append(f"Established facts: {facts}")
return "\n".join(parts)Users naturally use pronouns and references that depend on conversational context. "Can you make it bigger?" requires knowing what "it" refers to. "Use the same approach as before" requires knowing what was discussed earlier. Language models handle this well when the relevant context is within the context window, but they lose track when the referent was mentioned many turns ago or was part of a tool result that has been truncated.
The solution is ensuring that entities introduced in conversation remain accessible. When the user mentions a specific file, database table, API endpoint, or concept, add it to the active entities in conversation state. When you trim older messages from the context, check whether any active entities were introduced in the trimmed messages and, if so, preserve them in the state summary that replaces those messages. This way, a reference to "the migration we discussed" can be resolved even if the original discussion has been summarized.
Users switch topics naturally within conversations. A developer might ask about deployment, then ask a billing question, then return to deployment with new information. The assistant needs to handle these transitions without confusing context between topics and without losing the earlier topic when the user returns to it.
Track the current topic and maintain a topic history stack. When the user switches topics, push the current topic to history and set the new one. When the user appears to return to a previous topic (referencing something discussed earlier, using entities from a previous topic), pop the relevant topic from history and restore it as current. Include the current topic in the context sent to the model so it can frame its responses appropriately.
Persistent memory helps with topic management because important facts from each topic are stored independently of the conversation flow. If the user discussed deployment settings earlier in the conversation, those settings are in memory and can be retrieved when the user returns to the deployment topic, regardless of how many unrelated messages occurred in between.
Multi-step tasks require explicit state tracking because the model cannot reliably maintain complex state through conversation context alone. If the user asks the assistant to set up a new project (which involves creating a repository, configuring CI, setting up the database, and deploying a staging environment), the assistant needs to track which steps have been completed, which are in progress, which are pending, and what information has been gathered for upcoming steps.
# Example: task state tracking
class TaskTracker:
def __init__(self):
self.tasks = {}
def create_task(self, task_id, steps):
self.tasks[task_id] = {
"steps": [{"name": s, "status": "pending", "result": None} for s in steps],
"current_step": 0
}
def complete_step(self, task_id, result=None):
task = self.tasks[task_id]
step = task["steps"][task["current_step"]]
step["status"] = "completed"
step["result"] = result
task["current_step"] += 1
def get_status_summary(self, task_id):
task = self.tasks[task_id]
completed = [s for s in task["steps"] if s["status"] == "completed"]
pending = [s for s in task["steps"] if s["status"] == "pending"]
current = task["current_step"]
summary = f"Task progress: {len(completed)}/{len(task['steps'])} steps done.\n"
if current < len(task["steps"]):
summary += f"Next: {task['steps'][current]['name']}"
return summaryInclude the task status summary in the model's context for each turn. This gives the model an explicit record of where the task stands, which is more reliable than asking it to infer progress from the conversation history. When a task spans multiple sessions, store the task state in persistent memory so it can be resumed later.
Conversations go wrong. The model misunderstands a request, the user provides ambiguous instructions, or the context gets confused after several topic switches. Conversation repair is the ability to detect these problems and recover from them without requiring the user to start over.
Detecting misunderstandings relies on user signals: explicit corrections ("No, I meant the other file"), repetition of a request (the user asks the same thing again, indicating the first answer was wrong), and escalating frustration (shorter messages, more direct language). When the assistant detects a potential misunderstanding, it should acknowledge the confusion, ask a clarifying question, and re-ground itself in the corrected context rather than doubling down on the wrong interpretation.
When context becomes confused (the model mixes up entities from different topics, references the wrong previous action, or loses track of a multi-step task), a deliberate context reset helps. The assistant can summarize its current understanding of the situation and ask the user to confirm or correct it. This is more honest and effective than continuing with confused context and producing increasingly wrong responses.
Give your assistant persistent context that survives topic switches, session breaks, and conversation repair. Adaptive Recall stores the important facts from every conversation so multi-turn handling has a reliable foundation.
Get Started Free