Skip to main content

Module 10: Multi-Turn Conversations

Building conversational applications with context preservation.

What You’ll Learn

  • Maintaining context across multiple exchanges
  • The query/response pattern for conversations
  • How Claude remembers previous messages in a session

Single-Turn vs Multi-Turn

PatternAPIContext
Single-turnQuery.text()No memory between calls
Multi-turnClaudeSyncClient or ClaudeAsyncClientRemembers all previous messages
Both clients support multi-turn conversations with full context preservation. Choose based on your programming paradigm.

The Conversation Loop Pattern

import org.springaicommunity.claude.agent.sdk.ClaudeClient;
import org.springaicommunity.claude.agent.sdk.ClaudeSyncClient;
import org.springaicommunity.claude.agent.sdk.config.PermissionMode;
import org.springaicommunity.claude.agent.sdk.transport.CLIOptions;
import java.nio.file.Path;

try (ClaudeSyncClient client = ClaudeClient.sync()
        .workingDirectory(Path.of("."))
        .model(CLIOptions.MODEL_HAIKU)
        .permissionMode(PermissionMode.BYPASS_PERMISSIONS)
        .build()) {

    // Turn 1: Establish context
    String response1 = client.connectText("My favorite programming language is Java.");
    System.out.println(response1);

    // Turn 2: Claude remembers the context from Turn 1
    String response2 = client.queryText("What is my favorite programming language?");
    System.out.println(response2);  // "Java"

    // Turn 3: Continue building on context
    String response3 = client.queryText("Spell it backwards.");
    System.out.println(response3);  // "avaJ"
}
The pattern differs by paradigm: blocking uses connectText()/queryText(), reactive uses connect().text()/query().text() with flatMap chaining. Context is preserved across all turns in both.

How It Works

┌──────────────────────────────────────────────────────────┐
│                    Claude CLI Session                     │
├──────────────────────────────────────────────────────────┤
│  Turn 1: "My favorite language is Java"                  │
│    → Claude: "Great choice!"                             │
│                                                          │
│  Turn 2: "What is my favorite language?"                 │
│    → Claude: "Your favorite is Java"                     │
│         (remembers from Turn 1)                          │
│                                                          │
│  Turn 3: "Spell it backwards"                            │
│    → Claude: "avaJ"                                      │
│         (remembers Java from context)                    │
└──────────────────────────────────────────────────────────┘

Full Message Access

When you need metadata, tool use details, or cost information:
import org.springaicommunity.claude.agent.sdk.types.Message;
import org.springaicommunity.claude.agent.sdk.types.AssistantMessage;
import org.springaicommunity.claude.agent.sdk.types.ResultMessage;

for (Message msg : client.connectAndReceive("Create a test file")) {
    System.out.println(msg);  // All types have useful toString()

    if (msg instanceof AssistantMessage am) {
        am.getToolUses().forEach(tool ->
            System.out.println("Tool used: " + tool.name()));
    } else if (msg instanceof ResultMessage rm) {
        System.out.printf("Cost: $%.6f%n", rm.totalCostUsd());
    }
}
Use connectAndReceive() and queryAndReceive() when you need access to all message types.

Key Points

  • Both ClaudeSyncClient and ClaudeAsyncClient support multi-turn conversations
  • connectText(prompt) starts the session and returns the text response
  • queryText(prompt) sends follow-ups and returns the text response
  • connectAndReceive()/queryAndReceive() for full message access
  • Context persists until the client is closed

When to Use Multi-Turn

Use CaseWhy Multi-Turn
ChatbotsUser expects continuous conversation
Iterative tasks”Now do X with that” references prior output
Guided workflowsStep-by-step with context from previous steps
Debugging”Why did you do that?” requires memory

Tradeoffs

  • Memory growth: Context accumulates with each turn. Long conversations consume more tokens and increase latency as Claude re-processes the full history.
  • No partial context: You cannot selectively forget messages. The entire conversation history persists until the session closes.
  • Session coupling: The client must remain open for the conversation to continue. Network interruptions or process crashes lose the session.
  • Cost implications: Each turn includes all prior messages in the API call. A 20-turn conversation sends the full history 20 times.
Avoid multi-turn for simple, independent queries. Use Query.text() when context is unnecessary.

Source Code

View on GitHub

Running the Example

mvn compile exec:java -pl module-10-multi-turn

Next Module

Module 11: Session Resume - Continuing conversations across restarts.