Stateful Agents
Letta agents maintain memory across conversations, unlike stateless chat APIs
This example walks you through creating your first Letta agent from scratch. Unlike traditional chatbots that forget everything between conversations, Letta agents are stateful - they maintain persistent memory and can learn about you over time.
By the end of this guide, you’ll understand how to create an agent, send it messages, and see how it automatically updates its memory based on your interactions.
You will need to install letta-client to interface with a Letta server:
bash TypeScript npm install @letta-ai/letta-client bash Python pip install letta-client
A client is a connection to a Letta server. It’s used to create and interact with agents, as well as any of Letta’s other features.
import { LettaClient } from "@letta-ai/letta-client";
// Initialize the Letta client using LETTA_API_KEY environment variableconst client = new LettaClient({ token: process.env.LETTA_API_KEY });
// If self-hosting, specify the base URL:// const client = new LettaClient({ baseUrl: "http://localhost:8283" });from letta_client import Lettaimport os
# Initialize the Letta client using LETTA_API_KEY environment variableclient = Letta(token=os.getenv("LETTA_API_KEY"))
# If self-hosting, specify the base URL:# client = Letta(base_url="http://localhost:8283")Now that we have a client, let’s create an agent with memory blocks that define what the agent knows about itself and you. Memory blocks can be used for any purpose, but we’re building a simple chatbot that stores information about its personality (persona) and you (human).
// Create your first agent// API Reference: https://docs.letta.com/api-reference/agents/createconst agent = await client.agents.create({ name: "hello_world_assistant",
// Memory blocks define what the agent knows about itself and you. // Agents can modify these blocks during conversations using memory // tools like memory_replace, memory_insert, memory_rethink, and memory. memoryBlocks: [ { label: "persona", value: "I am a friendly AI assistant here to help you learn about Letta.", }, { label: "human", value: "Name: User\nFirst interaction: Learning about Letta", }, ],
// Model configuration model: "openai/gpt-4o-mini", // embedding: "openai/text-embedding-3-small", // Only set this if self-hosting});
console.log(`Created agent: ${agent.id}`);# Create your first agent# API Reference: https://docs.letta.com/api-reference/agents/createagent = client.agents.create( name="hello_world_assistant",
# Memory blocks define what the agent knows about itself and you memory_blocks=[ { "label": "persona", "value": "I am a friendly AI assistant here to help you learn about Letta." }, { "label": "human", "value": "Name: User\nFirst interaction: Learning about Letta" } ],
# Model configuration model="openai/gpt-4o-mini", # embedding="openai/text-embedding-3-small", # Only set this if self-hosting)
print(f"Created agent: {agent.id}")Created agent: agent-a1b2c3d4-e5f6-7890-abcd-ef1234567890
Now let’s send a message to the agent to see what it can do.
// Send a message to your agent// API Reference: https://docs.letta.com/api-reference/agents/messages/createconst response = await client.agents.messages.create(agent.id, { input: "Hello! What's your purpose?",});
// Extract and print the assistant's responsefor (const message of response.messages) { if (message.messageType === "assistant_message") { console.log(`Assistant: ${message.content}`); }}# Send a message to your agent# API Reference: https://docs.letta.com/api-reference/agents/messages/createresponse = client.agents.messages.create( agent_id=agent.id, input="Hello! What's your purpose?")
# Extract and print the assistant's responsefor message in response.messages: if message.message_type == "assistant_message": print(f"Assistant: {message.content}")Assistant: Hello! I'm here to help you learn about Letta and answer anyquestions you might have. Letta is a framework for building stateful AI agentswith long-term memory. I can explain concepts, provide examples, and guide youthrough using the platform. What would you like to know?Now let’s give the agent some information about yourself. If prompted correctly, the agent can add this information to a relevant memory block using one of its default memory tools. Unless tools are modified during creation, new agents usually have memory_insert and memory_replace tools.
// Send information about yourselfconst response2 = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "My name is Cameron. Please store this information in your memory.", }, ],});
// Print out tool calls and the assistant's responsefor (const msg of response2.messages) { if (msg.messageType === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); } if (msg.messageType === "tool_call_message") { console.log( `Tool call: ${msg.toolCall.name}(${JSON.stringify(msg.toolCall.arguments)})`, ); }}# Send information about yourselfresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "My name is Cameron. Please store this information in your memory."}])
# Print out tool calls and the assistant's responsefor msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n") if msg.message_type == "tool_call_message": print(f"Tool call: {msg.tool_call.name}({msg.tool_call.arguments})")Tool call: memory_replace({"block_label": "human", "old_content": "Name: User", "new_content": "Name: Cameron"})Assistant: Got it! I've updated my memory with your name, Cameron. How can I assist you today?Let’s see what the agent remembers. We’ll print out both the summary and the full content of each memory block:
// Retrieve the agent's current memory blocks// API Reference: https://docs.letta.com/api-reference/agents/blocks/listconst blocks = await client.agents.blocks.list(agent.id);
console.log("Current Memory:");for (const block of blocks) { console.log(` ${block.label}: ${block.value.length}/${block.limit} chars`); console.log(` ${block.value}\n`);}# Retrieve the agent's current memory blocks# API Reference: https://docs.letta.com/api-reference/agents/blocks/listblocks = client.agents.blocks.list(agent_id=agent.id)
print("Current Memory:")for block in blocks: print(f" {block.label}: {len(block.value)}/{block.limit} chars") print(f" {block.value}\n")The persona block should have:
I am a friendly AI assistant here to help you learn about Letta.
The human block should have something like:
Name: Cameron
Here’s the full code in one place that you can run:
import { LettaClient } from "@letta-ai/letta-client";
async function main() { // Initialize client using LETTA_API_KEY environment variable const client = new LettaClient({ token: process.env.LETTA_API_KEY });
// If self-hosting, specify the base URL: // const client = new LettaClient({ baseUrl: "http://localhost:8283" });
// Create agent const agent = await client.agents.create({ name: "hello_world_assistant", memoryBlocks: [ { label: "persona", value: "I am a friendly AI assistant here to help you learn about Letta.", }, { label: "human", value: "Name: User\nFirst interaction: Learning about Letta", }, ], model: "openai/gpt-4o-mini", // embedding: "openai/text-embedding-3-small", // Only set this if self-hosting });
console.log(`Created agent: ${agent.id}\n`);
// Send first message let response = await client.agents.messages.create(agent.id, { messages: [{ role: "user", content: "Hello! What's your purpose?" }], });
for (const msg of response.messages) { if (msg.messageType === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); } }
// Send information about yourself response = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "My name is Cameron. Please store this information in your memory.", }, ], });
// Print out tool calls and the assistant's response for (const msg of response.messages) { if (msg.messageType === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); } if (msg.messageType === "tool_call_message") { console.log( `Tool call: ${msg.toolCall.name}(${JSON.stringify(msg.toolCall.arguments)})`, ); } }
// Inspect memory const blocks = await client.agents.blocks.list(agent.id); console.log("Current Memory:"); for (const block of blocks) { console.log(` ${block.label}: ${block.value.length}/${block.limit} chars`); console.log(` ${block.value}\n`); }}
main();from letta_client import Lettaimport os
# Initialize client using LETTA_API_KEY environment variableclient = Letta(token=os.getenv("LETTA_API_KEY"))
# If self-hosting, specify the base URL:# client = Letta(base_url="http://localhost:8283")
# Create agentagent = client.agents.create( name="hello_world_assistant", memory_blocks=[ { "label": "persona", "value": "I am a friendly AI assistant here to help you learn about Letta." }, { "label": "human", "value": "Name: User\nFirst interaction: Learning about Letta" } ], model="openai/gpt-4o-mini", # embedding="openai/text-embedding-3-small", # Only set this if self-hosting)
print(f"Created agent: {agent.id}\n")
# Send first messageresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "Hello! What's your purpose?"}])
for msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n")
# Send information about yourselfresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "My name is Cameron. Please store this information in your memory."}])
# Print out tool calls and the assistant's responsefor msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n") if msg.message_type == "tool_call_message": print(f"Tool call: {msg.tool_call.name}({msg.tool_call.arguments})")
# Inspect memoryblocks = client.agents.blocks.list(agent_id=agent.id)print("Current Memory:")for block in blocks: print(f" {block.label}: {len(block.value)}/{block.limit} chars") print(f" {block.value}\n")Stateful Agents
Letta agents maintain memory across conversations, unlike stateless chat APIs
Memory Blocks
Modular memory components that agents can read and update during conversations
Persistent Context
Agents remember user preferences, conversation history, and learned information
Automatic Updates
Agents intelligently update their memory as they learn more about you
Memory Blocks Guide
Learn how to work with memory blocks, update them, and control agent knowledge