Skip to content

Multi-Turn Conversation Design

A single prompt gets a single answer. A conversation gets context, follow-ups, clarifications, and corrections, if you manage the message history correctly. This lesson covers building prompts for multi-turn interactions: tracking state, managing context growth, and handling the conversation as data.

Owl mascot

What you'll learn

  • Multi-turn prompts pass the full message history with each call, every previous exchange is context
  • State tracking means extracting and updating structured data across turns: user preferences, task progress, collected information
  • Context management prevents the conversation from growing past token limits while preserving what matters

The problem

A single-turn prompt is a function: input in, output out. A conversation is a state machine: each turn depends on every previous turn, the user's preferences accumulate, and the conversation grows until it hits the context limit. Without deliberate conversation design, your agent forgets what happened three turns ago, repeats itself, or loses track of what it was doing.

Build it

Step 1: Basic conversation loop

Implement a standard conversational loop to maintain continuity across multiple interactions. This foundational setup appends user and assistant messages sequentially.

python
messages = [{"role": "system", "content": "You are a helpful coding assistant."}]

while True:
    user_input = input("> ")
    messages.append({"role": "user", "content": user_input})

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=messages[0]["content"],
        messages=messages[1:]  # system prompt passed separately in Anthropic
    )

    messages.append({"role": "assistant", "content": response.content[0].text})
    print(response.content[0].text)

Step 2: Track state across turns

Extract and track critical state variables outside the raw message history. This prevents the agent from losing track of tasks as the conversation context lengthens.

python
conversation_state = {
    "task": None,
    "files_modified": [],
    "tests_run": False
}

def update_state(assistant_response, tool_calls):
    # Parse the response and tool calls to update conversation state
    if "file written" in assistant_response.lower():
        conversation_state["files_modified"].append(extract_filename(assistant_response))
    if "tests passed" in assistant_response.lower():
        conversation_state["tests_run"] = True

Step 3: Inject state into the system prompt

Inject the tracked state back into the system prompt dynamically. This ensures the model always has immediate access to the most current session context.

python
def build_system_prompt(base_prompt, state):
    state_summary = f"""
Current task: {state['task'] or 'None'}
Files modified: {', '.join(state['files_modified']) or 'None'}
Tests run: {'Yes' if state['tests_run'] else 'No'}
"""
    return base_prompt + "\n\n## Current Session State\n" + state_summary

Step 4: Manage context growth

Apply middle-truncation to prevent the conversation from exceeding token limits. This preserves the essential system instructions and the most recent context while pruning older exchanges.

python
MAX_MESSAGES = 20

def trim_history(messages, max_messages):
    if len(messages) <= max_messages:
        return messages
    # Keep system prompt + first user message + last N messages
    return [messages[0], messages[1]] + messages[-(max_messages - 2):]

What goes wrong

MistakeHow you notice itThe fix
Conversation grows past context limitErrors or the model forgets early contextTrim history, summarize early turns, or use a sliding window
State drifts across turnsAgent contradicts itself or repeats completed stepsExplicitly track state in a structured object and inject a summary into each prompt
Assistant and user roles alternate incorrectlyAPI error about role orderingMessages must alternate: user, assistant, user, assistant. Tool results count as user messages
System prompt included in message arrayDuplicate instructions or API errorAnthropic's API takes system separately. OpenAI includes it as the first message with role "system"

Confirm it worked

Run a simulated conversation to verify the agent accurately recalls information from earlier turns. This test confirms the context management logic works as expected.

python
# Run a 5-turn conversation and verify the agent remembers turn 1
messages = [{"role": "system", "content": "Remember the user's name."}]
messages.append({"role": "user", "content": "My name is Alice."})
# ... run turn ...
messages.append({"role": "user", "content": "What's my name?"})
response = client.messages.create(model="claude-sonnet-4-6", max_tokens=1024, system=messages[0]["content"], messages=messages[1:])
assert "Alice" in response.content[0].text

Next: Prompt Chaining & Pipelines