Handling Conversation Context and Message History with Claude: A Complete Guide
To maintain conversation context with Claude, accumulate a Python list of message dictionaries with role and content keys, passing the complete history to client.messages.create() on every API call.
The anthropics/prompt-eng-interactive-tutorial repository demonstrates the canonical pattern for managing multi-turn dialogues with Anthropic's Claude API. This approach relies on stateless API calls with stateful client-side message accumulation, enabling complex tool-use workflows and contextual follow-ups without server-side session management.
The Core Pattern: Role-Based Message Lists
The foundation of conversation management in Claude applications rests on a simple Python list containing dictionary objects. Each dictionary represents a single turn in the dialogue with two required fields:
role: Identifies the speaker as"user"for human inputs,"assistant"for Claude's responses, or"system"for persistent instructionscontent: The raw text payload containing the actual message or markup
In Anthropic 1P/10.1_Appendix_Chaining Prompts.ipynb, this pattern enables sequential prompt chaining by appending new dictionaries to the list after each exchange. Because the Anthropic API is stateless, the client must preserve and resubmit the entire messages list with every request to maintain continuity across turns.
Managing Tool Use with Conversation History
The tutorial's Anthropic 1P/10.2_Appendix_Tool Use.ipynb file extends the basic pattern to support function calling workflows. This implementation demonstrates how to interrupt Claude's response, execute local code, and resume the conversation with tool results injected into the message history.
Step 1: Initialize with System Instructions
Begin by constructing a system prompt that defines available tools and their schemas. This prompt remains constant across all turns in the conversation.
system_prompt_tools_general_explanation = """
You have access to a set of functions you can use to answer the user's question.
"""
system_prompt_tools_specific_tools = """
<tools>
<tool_description>
<tool_name>do_pairwise_arithmetic</tool_name>
<description>Multiplies two large integers</description>
<parameters>
<parameter><name>first_operand</name><type>int</type></parameter>
<parameter><name>second_operand</name><type>int</type></parameter>
<parameter><name>operator</name><type>str</type></parameter>
</parameters>
</tool_description>
</tools>
"""
Step 2: Send Initial User Message with Stop Sequences
Create the first user message and pass it to client.messages.create() with stop sequences that halt generation when Claude attempts a function call.
user_msg = {"role": "user", "content": "Multiply 1,984,135 by 9,343,116"}
response = get_completion(
[user_msg],
system_prompt=system_prompt,
stop_sequences=["</function_calls>"]
)
The stop_sequences=["</function_calls>"] parameter forces the API to return immediately when Claude generates the closing XML tag, allowing the client to intercept the function call before Claude proceeds to generate a final answer.
Step 3: Extract Parameters Using find_parameter
Parse the partial assistant output to extract tool arguments using the find_parameter helper function.
def find_parameter(message, name):
start_tag = f'name="{name}">'
start = message.find(start_tag)
if start == -1:
return None
start += len(start_tag)
end = message.find("<", start)
return message[start:end]
first_operand = find_parameter(response, "first_operand")
second_operand = find_parameter(response, "second_operand")
operator = find_parameter(response, "operator")
Step 4: Execute Tool and Format Results
Execute the requested operation locally using do_pairwise_arithmetic, then format the result using construct_successful_function_run_injection_prompt.
result = do_pairwise_arithmetic(int(first_operand), int(second_operand), operator)
function_results = construct_successful_function_run_injection_prompt(
[{'tool_name': 'do_pairwise_arithmetic', 'tool_result': result}]
)
Step 5: Reconstruct Message History with Tool Results
Append both the assistant's partial response and a new user message containing the tool output to the conversation history.
full_first_response = response + "</function_calls>"
messages = [
user_msg,
{"role": "assistant", "content": full_first_response},
{"role": "user", "content": function_results}
]
final_answer = get_completion(messages, system_prompt=system_prompt)
This reconstruction preserves the full context: the original query, Claude's intent to use a tool, and the actual tool execution result.
Complete Working Example
The following self-contained script demonstrates the end-to-end flow for handling conversation context with tool integration:
import anthropic
# Initialize client
client = anthropic.Anthropic(api_key="your-api-key")
def get_completion(messages, system_prompt="", stop_sequences=None):
"""Helper wrapper for Anthropic API calls."""
resp = client.messages.create(
model="claude-3-sonnet-20240229",
max_tokens=2000,
temperature=0.0,
system=system_prompt,
messages=messages,
stop_sequences=stop_sequences,
)
return resp.content[0].text
def find_parameter(message, name):
"""Extract parameter values from XML-like function call markup."""
start_tag = f'name="{name}">'
start = message.find(start_tag)
if start == -1:
return None
start += len(start_tag)
end = message.find("<", start)
return message[start:end]
def do_pairwise_arithmetic(a, b, op):
"""Local tool implementation."""
if op == "*":
return a * b
elif op == "+":
return a + b
return None
def construct_successful_function_run_injection_prompt(tool_results):
"""Format tool results for Claude's expected markup."""
results_xml = "<function_results>\n"
for item in tool_results:
results_xml += "<result>\n"
results_xml += f"<tool_name>{item['tool_name']}</tool_name>\n"
results_xml += f"<stdout>\n{item['tool_result']}\n</stdout>\n"
results_xml += "</result>\n"
results_xml += "</function_results>"
return results_xml
# System prompt defining tool capabilities
system_prompt = """
You have access to a set of functions you can use to answer the user's question.
<tools>
<tool_description>
<tool_name>do_pairwise_arithmetic</tool_name>
<description>Performs arithmetic operations</description>
<parameters>
<parameter><name>first_operand</name><type>int</type></parameter>
<parameter><name>second_operand</name><type>int</type></parameter>
<parameter><name>operator</name><type>str</type></parameter>
</parameters>
</tool_description>
</tools>
"""
# Turn 1: User asks a question requiring calculation
user_message = {"role": "user", "content": "Multiply 1,984,135 by 9,343,116"}
partial_reply = get_completion(
[user_message],
system_prompt=system_prompt,
stop_sequences=["</function_calls>"]
)
# Extract and execute
first = int(find_parameter(partial_reply, "first_operand"))
second = int(find_parameter(partial_reply, "second_operand"))
op = find_parameter(partial_reply, "operator")
calc_result = do_pairwise_arithmetic(first, second, op)
# Construct tool result message
tool_output = construct_successful_function_run_injection_prompt(
[{'tool_name': 'do_pairwise_arithmetic', 'tool_result': calc_result}]
)
# Turn 2: Submit full history including tool result
messages = [
user_message,
{"role": "assistant", "content": partial_reply + "</function_calls>"},
{"role": "user", "content": tool_output}
]
final_answer = get_completion(messages, system_prompt=system_prompt)
print(final_answer)
Key Implementation Files
The tutorial repository contains several critical files demonstrating conversation context patterns:
Anthropic 1P/10.1_Appendix_Chaining Prompts.ipynb: Demonstrates basic message list accumulation for sequential prompting without toolsAnthropic 1P/10.2_Appendix_Tool Use.ipynb: Contains the complete tool-use workflow withstop_sequencesand message reconstructionAnthropic 1P/hints.py: Provides reference documentation for the role-based message structure and XML markup contractsAmazonBedrock/utils/hints.py: Adapts the same patterns for AWS Bedrock runtime environments
Summary
- Accumulate message history in a Python list of dictionaries with
roleandcontentkeys - Pass the complete list to
client.messages.create()on every turn to maintain context - Use
stop_sequencesto interrupt Claude for tool execution, then append results as new user messages - Preserve system prompts consistently across all turns to maintain task definition
- Format tool results using the XML-like markup structure expected by Claude with
construct_successful_function_run_injection_prompt
Frequently Asked Questions
How does Claude maintain context across multiple API calls?
Claude does not maintain server-side session state. According to the anthropics/prompt-eng-interactive-tutorial source code, the client application must preserve conversation history by accumulating all previous messages in a list and passing the complete list to the messages parameter of client.messages.create() on every request. This stateless architecture ensures each API call contains the full context required for coherent multi-turn dialogue.
What is the correct format for message history entries?
Each entry in the message history must be a Python dictionary containing exactly two keys: role (a string set to "user", "assistant", or "system") and content (a string containing the message text). As implemented in 10.2_Appendix_Tool Use.ipynb, the content field may contain plain text or structured XML-like markup for tool interactions.
How do you handle tool results in the conversation flow?
When Claude generates a function call, the client intercepts the partial output using stop_sequences, executes the requested tool locally with do_pairwise_arithmetic, then formats the result using construct_successful_function_run_injection_prompt. This formatted output, along with the assistant's partial response, is appended to the history before requesting a final completion.
Can conversation history be truncated to save tokens?
Yes, while the tutorial demonstrates passing complete histories, production applications may implement sliding window approaches to limit context length. However, any truncation must preserve the alternating user/assistant structure and retain critical system prompts to maintain conversation coherence, as the API requires valid message sequences for proper context processing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →