| layout | default |
|---|---|
| title | Letta Tutorial - Chapter 5: Conversation Management |
| nav_order | 5 |
| has_children | false |
| parent | Letta Tutorial |
Welcome to Chapter 5: Conversation Management. In this part of Letta Tutorial: Stateful LLM Agents, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
Handle long-running dialogues, manage conversation state, and implement conversation patterns.
Letta excels at managing long-term conversations. This chapter covers conversation lifecycle, state management, branching conversations, and implementing conversation patterns.
Each conversation has a unique ID and maintains its own context:
from letta import create_client
client = create_client()
# Start a new conversation
conversation = client.create_conversation(agent_name="sam")
print(f"Conversation ID: {conversation.id}")
# Send messages
response1 = client.send_message(
agent_name="sam",
message="Hello, let's discuss machine learning!",
conversation_id=conversation.id
)
response2 = client.send_message(
agent_name="sam",
message="What are some good algorithms for classification?",
conversation_id=conversation.id
)Track conversation metadata:
# Get conversation details
conv = client.get_conversation(conversation.id)
print(f"Created: {conv.created_at}")
print(f"Last updated: {conv.updated_at}")
print(f"Message count: {conv.message_count}")
print(f"Summary: {conv.summary}")Access and search conversation history:
# Get recent messages
messages = client.get_messages(
agent_name="sam",
conversation_id=conversation.id,
limit=10
)
for msg in messages:
print(f"{msg.role}: {msg.content[:50]}...")
# Search within conversation
results = client.search_messages(
agent_name="sam",
conversation_id=conversation.id,
query="machine learning",
limit=5
)Automatically generate conversation summaries:
# Enable auto-summarization
conversation = client.create_conversation(
agent_name="sam",
auto_summarize=True,
summary_interval=20 # Summarize every 20 messages
)
# Or manually summarize
summary = client.summarize_conversation(
agent_name="sam",
conversation_id=conversation.id
)
print(f"Conversation summary: {summary}")Create conversation branches for exploring alternatives:
# Start main conversation
main_conv = client.create_conversation(agent_name="writer")
# Branch from a specific point
branch_conv = client.branch_conversation(
agent_name="writer",
from_conversation_id=main_conv.id,
from_message_id="msg_123" # Branch from this message
)
# Continue different paths
client.send_message(
agent_name="writer",
message="Let's make the character more heroic",
conversation_id=main_conv.id
)
client.send_message(
agent_name="writer",
message="Let's make the character more mysterious",
conversation_id=branch_conv.id
)Save conversation starters for common scenarios:
CONVERSATION_TEMPLATES = {
"code-review": {
"initial_message": "I'd like you to review this code. Please focus on best practices, security, and performance.",
"system_context": "You are conducting a thorough code review."
},
"interview-practice": {
"initial_message": "Let's practice a job interview. I'll be the candidate, you be the interviewer.",
"system_context": "You are an experienced interviewer asking insightful questions."
},
"learning-session": {
"initial_message": "I'm here to learn about [TOPIC]. Please explain concepts clearly and ask questions to check my understanding.",
"system_context": "You are a patient teacher who adapts to the student's pace."
}
}
def start_structured_conversation(agent_name, template_name, **kwargs):
"""Start a conversation from a template."""
template = CONVERSATION_TEMPLATES[template_name]
# Create conversation
conversation = client.create_conversation(agent_name=agent_name)
# Send initial message
initial_msg = template["initial_message"]
if kwargs:
initial_msg = initial_msg.format(**kwargs)
response = client.send_message(
agent_name=agent_name,
message=initial_msg,
conversation_id=conversation.id
)
return conversation, responseclass ProjectConversation:
def __init__(self, agent_name, project_name):
self.agent_name = agent_name
self.project_name = project_name
self.conversation = client.create_conversation(
agent_name=agent_name,
metadata={"project": project_name, "type": "project"}
)
def send_project_message(self, message, context=None):
"""Send message with project context."""
full_message = f"Project: {self.project_name}\n"
if context:
full_message += f"Context: {context}\n"
full_message += f"Message: {message}"
return client.send_message(
agent_name=self.agent_name,
message=full_message,
conversation_id=self.conversation.id
)
def get_project_history(self, limit=50):
"""Get project-specific message history."""
return client.get_messages(
agent_name=self.agent_name,
conversation_id=self.conversation.id,
limit=limit
)
# Usage
project_chat = ProjectConversation("coder", "web-app")
project_chat.send_project_message("Let's plan the database schema")
project_chat.send_project_message("What about user authentication?", "Following up on security requirements")class LearningSession:
def __init__(self, agent_name, student_name, topic):
self.agent_name = agent_name
self.student_name = student_name
self.topic = topic
self.session_num = 1
self.conversation = client.create_conversation(
agent_name=agent_name,
metadata={
"student": student_name,
"topic": topic,
"session_type": "learning"
}
)
def start_session(self):
"""Begin a learning session."""
welcome = f"""Starting learning session #{self.session_num} on {self.topic}
Student: {self.student_name}
Please assess my current knowledge level and create a personalized learning plan."""
response = client.send_message(
agent_name=self.agent_name,
message=welcome,
conversation_id=self.conversation.id
)
self.session_num += 1
return response
def continue_session(self, student_input):
"""Continue the learning session."""
return client.send_message(
agent_name=self.agent_name,
message=student_input,
conversation_id=self.conversation.id
)
# Usage
math_session = LearningSession("teacher", "Alice", "Calculus")
math_session.start_session()
math_session.continue_session("I'm struggling with derivatives")Archive old conversations for reference:
def archive_conversation(agent_name, conversation_id, archive_reason="completed"):
"""Archive a conversation."""
client.update_conversation(
agent_name=agent_name,
conversation_id=conversation_id,
metadata={"status": "archived", "archive_reason": archive_reason}
)
def find_similar_conversations(agent_name, query, limit=5):
"""Find conversations similar to a query."""
# Search across all conversations
results = client.search_conversations(
agent_name=agent_name,
query=query,
limit=limit
)
return results
# Archive completed project
archive_conversation("coder", "conv_123", "project_completed")
# Find similar past conversations
similar = find_similar_conversations("support", "password reset issue")Analyze conversation patterns:
def analyze_conversation(conversation_id):
"""Analyze conversation metrics."""
messages = client.get_messages(
agent_name="sam", # Need to know agent name
conversation_id=conversation_id,
limit=1000
)
stats = {
"total_messages": len(messages),
"avg_message_length": sum(len(m.content) for m in messages) / len(messages),
"conversation_duration": (messages[-1].created_at - messages[0].created_at).total_seconds(),
"user_messages": len([m for m in messages if m.role == "user"]),
"agent_messages": len([m for m in messages if m.role == "assistant"]),
}
return stats
# Get conversation insights
stats = analyze_conversation("conv_123")
print(f"Conversation lasted {stats['conversation_duration']/3600:.1f} hours")Handle conversations with multiple participants:
class MultiUserConversation:
def __init__(self, agent_name, participants):
self.agent_name = agent_name
self.participants = participants
self.conversation = client.create_conversation(
agent_name=agent_name,
metadata={
"participants": participants,
"type": "multi_user"
}
)
def add_message(self, user_name, message):
"""Add a message from a specific user."""
if user_name not in self.participants:
raise ValueError(f"User {user_name} not in participants")
formatted_message = f"[{user_name}]: {message}"
response = client.send_message(
agent_name=self.agent_name,
message=formatted_message,
conversation_id=self.conversation.id
)
return response
# Usage
group_chat = MultiUserConversation("facilitator", ["Alice", "Bob", "Charlie"])
group_chat.add_message("Alice", "I think we should focus on the UI first")
group_chat.add_message("Bob", "I agree, but let's not forget about the API")Maintain conversation context across sessions:
def resume_conversation(agent_name, conversation_id, context_summary=None):
"""Resume a previous conversation."""
if context_summary:
# Add context summary to help agent remember
client.send_message(
agent_name=agent_name,
message=f"Context summary: {context_summary}",
conversation_id=conversation_id
)
return client.get_conversation(conversation_id)
def get_conversation_context(conversation_id, max_messages=10):
"""Get recent context for resuming."""
messages = client.get_messages(
agent_name="sam", # Need agent name
conversation_id=conversation_id,
limit=max_messages
)
# Generate context summary
context = "\n".join([f"{m.role}: {m.content}" for m in messages[-5:]])
return context
# Resume with context
context = get_conversation_context("conv_123")
resume_conversation("sam", "conv_123", context)- Clear Conversation Boundaries: Know when to start new conversations vs continuing existing ones
- Regular Summarization: Keep conversations focused and manageable
- Metadata Management: Tag conversations with relevant metadata for searching
- Archival Strategy: Archive completed conversations to reduce active conversation load
- Context Preservation: Use summaries and context when resuming conversations
- Participant Management: Track conversation participants and their roles
- Performance Monitoring: Monitor conversation length, engagement, and outcomes
Next: Coordinate multiple agents working together.
Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for agent_name, self, conversation so behavior stays predictable as complexity grows.
In practical terms, this chapter helps you avoid three common failures:
- coupling core logic too tightly to one implementation path
- missing the handoff boundaries between setup, execution, and validation
- shipping changes without clear rollback or observability strategy
After working through this chapter, you should be able to reason about Chapter 5: Conversation Management as an operating subsystem inside Letta Tutorial: Stateful LLM Agents, with explicit contracts for inputs, state transitions, and outputs.
Use the implementation notes around client, conversation_id, message as your checklist when adapting these patterns to your own repository.
Under the hood, Chapter 5: Conversation Management usually follows a repeatable control path:
- Context bootstrap: initialize runtime config and prerequisites for
agent_name. - Input normalization: shape incoming data so
selfreceives stable contracts. - Core execution: run the main logic branch and propagate intermediate state through
conversation. - Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
- Output composition: return canonical result payloads for downstream consumers.
- Operational telemetry: emit logs/metrics needed for debugging and performance tuning.
When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.
Use the following upstream sources to verify implementation details while reading this chapter:
- View Repo
Why it matters: authoritative reference on
View Repo(github.com). - Awesome Code Docs
Why it matters: authoritative reference on
Awesome Code Docs(github.com).
Suggested trace strategy:
- search upstream code for
agent_nameandselfto map concrete implementation paths - compare docs claims against actual runtime/config code before reusing patterns in production