Your first agent
Build your first Daydreams agent and discover the power of contexts.
What Makes Daydreams Different?
Most AI frameworks treat conversations as stateless - every interaction starts from scratch. But real conversations aren't like that. Daydreams changes everything with contexts.
The Magic of Contexts
Imagine you're building a customer support agent. With traditional frameworks:
User: "I need help with my order"
Agent: "What's your order number?"
User: "12345"
Agent: "What's your order number?" // 😕 Already forgot!
With Daydreams contexts:
User: "I need help with my order"
Agent: "What's your order number?"
User: "12345"
Agent: "I see order #12345. It was shipped yesterday!" // 🎉 Remembers!
Contexts are isolated workspaces that:
- 🧠 Remember - Each conversation has its own memory
- 🔒 Isolate - Different users never see each other's data
- 🎯 Focus - Specialized behaviors for different situations
- 🔄 Persist - Memory survives between conversations
How Daydreams Works
An agent in Daydreams follows this cycle:
- Listen - Receives input (message, event, API call)
- Think - Uses an LLM to understand and decide
- Act - Performs actions or sends responses
- Remember - Saves important information in context
This happens continuously, with the context providing memory and state throughout.
Installation
Let's build your first stateful agent! Start by installing Daydreams:
pnpm add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
npm install @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
bun add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
yarn add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
Important: Set your OPENAI_API_KEY
environment variable before continuing.
Your First Context-Aware Agent
Let's build a personal assistant that remembers you - your name, preferences, and conversation history. This showcases the true power of contexts.
Step 1: Create your project
mkdir my-first-agent && cd my-first-agent
touch agent.ts
Step 2: Build a stateful agent
import { createDreams, context, action } from "@daydreamsai/core";
import { cliExtension } from "@daydreamsai/cli";
import { openai } from "@ai-sdk/openai";
import * as z from "zod";
// Create a context - this is where the magic happens!
const assistantContext = context({
type: "personal-assistant",
// Each user gets their own context instance
schema: z.object({
userId: z.string().describe("Unique identifier for the user"),
}),
// Initialize memory for new users
create: () => ({
userName: "",
lastTopic: "",
preferences: {},
conversationCount: 0,
}),
// Define what the LLM sees about this context
render: (state) => {
const { userName, conversationCount, lastTopic, preferences } =
state.memory;
return `
Personal Assistant for User: ${state.args.userId}
${userName ? `Name: ${userName}` : "Name: Unknown (ask for their name!)"}
Conversations: ${conversationCount}
${lastTopic ? `Last topic: ${lastTopic}` : ""}
${
Object.keys(preferences).length > 0
? `Preferences: ${JSON.stringify(preferences, null, 2)}`
: "No preferences saved yet"
}
`.trim();
},
// Instructions that guide the assistant's behavior
instructions: `You are a personal assistant with memory. You should:
- Remember information about the user across conversations
- Ask for their name if you don't know it
- Learn their preferences over time
- Reference previous conversations when relevant
- Be helpful and personalized based on what you know`,
// Track conversation count
onRun: async (ctx) => {
ctx.memory.conversationCount++;
},
});
// Add actions the assistant can perform
assistantContext.setActions([
action({
name: "remember-name",
description: "Remember the user's name",
schema: z.object({
name: z.string().describe("The user's name"),
}),
handler: async ({ name }, ctx) => {
ctx.memory.userName = name;
return {
remembered: true,
message: `I'll remember your name is ${name}`,
};
},
}),
action({
name: "update-topic",
description: "Remember what we're discussing",
schema: z.object({
topic: z.string().describe("Current conversation topic"),
}),
handler: async ({ topic }, ctx) => {
ctx.memory.lastTopic = topic;
return { updated: true };
},
}),
]);
// Create the agent
const agent = createDreams({
model: openai("gpt-4o-mini"),
extensions: [cliExtension],
contexts: [assistantContext],
});
// Start the interactive CLI
async function main() {
await agent.start();
console.log("\n🤖 Personal Assistant Started!");
console.log("💡 Try telling me your name or preferences.");
console.log("💡 Exit and restart - I'll still remember you!\n");
// Simulate different users with different context instances
const userId = process.argv[2] || "default-user";
console.log(`Starting session for user: ${userId}\n`);
// Run the assistant for this specific user
await agent.run({
context: assistantContext,
args: { userId }, // This creates/loads a unique context instance
});
console.log("\n👋 See you next time!");
}
main().catch(console.error);
Step 3: Experience the magic of contexts
Run your agent:
# Start as the default user
node agent.ts
# Or start as a specific user
node agent.ts alice
node agent.ts bob
Try this conversation flow:
You: Hi there!
Assistant: Hello! I don't think we've been properly introduced. What's your name?
You: I'm Alice
Assistant: Nice to meet you, Alice! I'll remember that for next time.
You: I love coffee and hate mornings
Assistant: I've noted your preferences! You love coffee and hate mornings.
You: What do you know about me?
Assistant: I know your name is Alice, you love coffee, and you hate mornings.
We've had 1 conversation so far.
[Exit and restart the agent with 'node agent.ts alice']
You: Do you remember me?
Assistant: Of course, Alice! I remember you love coffee and hate mornings.
This is our 2nd conversation together.
What Just Happened?
- Context Creation - When you started with user "alice", Daydreams created a unique context instance
- Memory Persistence - The context saved Alice's name and preferences
- Isolation - If you run
node agent.ts bob
, Bob gets a completely separate context - Stateful Behavior - The agent's responses are personalized based on context memory
Understanding Context Power
Let's see how contexts solve real problems:
Problem 1: Multi-User Support
// Each user automatically gets their own isolated context
await agent.run({
context: assistantContext,
args: { userId: "alice" }, // Alice's personal workspace
});
await agent.run({
context: assistantContext,
args: { userId: "bob" }, // Bob's separate workspace
});
// Alice and Bob never see each other's data!
Problem 2: Different Behaviors for Different Situations
// Different contexts for different purposes
const casualChatContext = context({
type: "casual-chat",
instructions: "Be friendly and conversational",
});
const technicalSupportContext = context({
type: "tech-support",
instructions: "Be precise and solution-focused",
});
const salesContext = context({
type: "sales",
instructions: "Be helpful but also mention relevant products",
});
// Same agent, different personalities based on context!
Problem 3: Complex State Management
interface GameMemory {
level: number;
score: number;
inventory: string[];
currentRoom: string;
}
const gameContext = context<GameMemory>({
type: "adventure-game",
create: () => ({
level: 1,
score: 0,
inventory: ["torch"],
currentRoom: "entrance",
}),
// Game state persists between sessions!
});
What You Built
Your customer service agent demonstrates the three key Daydreams concepts:
1. Context Isolation
Each customer gets their own workspace:
// Different customers = different context instances
args: {
customerId: "CUST001";
} // Alice's data
args: {
customerId: "CUST002";
} // Bob's data (completely separate)
2. Context Composition
Combine contexts with .use()
:
use: (state) => [
{
context: accountContext,
args: { customerId: state.args.customerId }, // Share customer ID
},
];
The LLM automatically sees both customer support AND account data.
3. Action Scoping
Actions are available based on active contexts:
save-customer-info
,create-ticket
→ Only in customer contextlink-account
,check-balance
→ Only in account context- Global actions → Available everywhere
Test the Complete Flow
node agent.ts
Try this conversation:
You: Hi, my name is Sarah Johnson, email sarah@email.com
Agent: [Uses save-customer-info action]
You: I need help with billing. My account is ACC12345
Agent: [Uses link-account action, then can use check-balance]
You: Create a ticket for my billing issue
Agent: [Uses create-ticket action]
You: Thanks, that's resolved now
Agent: [Uses resolve-ticket action]
Key Concepts Learned
- Contexts provide isolated, stateful workspaces
.use()
composes contexts for modular functionality.setActions()
scopes actions to specific contexts- Memory persists between conversations for the same context args
- LLM sees all active context data and available actions
Next Steps
- Contexts - Deep dive into context patterns
- Building Blocks - Understand actions, inputs, outputs
- Extensions - Package functionality for reuse
- Building Block Operations - Common patterns
Challenge: Add a third context for order management using .use()
and give
it order-specific actions!