LLMs don’t know the current date, who your user is, or what plan they’re on. You need to inject this information into the agent’s context. This page covers the most common patterns for working with dynamic data in production agents.
The Problem
Without dynamic data, your agent is blind to reality:
// The agent has NO idea what day it is
// If a user asks "schedule for tomorrow", the agent can't answer correctly
const agent = new Agent({
instructions: 'You are a scheduling assistant.',
model: openai('gpt-4o'),
});
Injecting Current Date and Time
The most common issue: LLMs don’t know today’s date. Always inject it into the instructions.
Using a Function for Instructions
Instead of a static string, use a function that builds the instructions dynamically:
import { Agent, openai } from '@runflow-ai/sdk';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
function buildInstructions() {
const now = new Date();
const today = format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR });
const time = format(now, 'HH:mm');
return `You are a scheduling assistant.
## Current Date and Time
- Today is: ${today}
- Current time: ${time} (Brasília timezone, UTC-3)
- Use this as reference for all date calculations
## Behavior
- When the user says "tomorrow", calculate from today's date
- When the user says "next week", calculate from this week
- Always confirm dates explicitly: "Tuesday, March 15th at 2pm"
- Never guess dates — always calculate from the current date above
## Tools
- Use schedule-appointment to book appointments
- Use check-availability to verify open slots`;
}
export const schedulingAgent = new Agent({
name: 'Scheduling Assistant',
instructions: buildInstructions(),
model: openai('gpt-4o'),
memory: { maxTurns: 20 },
tools: {
scheduleAppointment: scheduleAppointmentTool,
checkAvailability: checkAvailabilityTool,
},
});
If you build the instructions at module load time (outside main()), the date will be set when the agent starts and won’t update between requests. For most use cases this is fine since deploys are frequent. If you need per-request dates, see the next pattern.
Per-Request Dynamic Instructions
When you need the date to be accurate on every single request, rebuild instructions inside main():
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
function buildInstructions() {
const now = new Date();
return `You are a scheduling assistant.
## Current Date and Time
- Today: ${format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR })}
- Time: ${format(now, 'HH:mm')} (UTC-3)
- Use this for all date calculations`;
}
export async function main(input: any) {
identify(input.email || input.phone || 'anonymous');
// Create agent with fresh date on every request
const agent = new Agent({
name: 'Scheduling Assistant',
instructions: buildInstructions(),
model: openai('gpt-4o'),
memory: { maxTurns: 20 },
});
const result = await agent.process({
message: input.message,
sessionId: input.sessionId,
});
return { message: result.message };
}
Injecting Context via messages
The agent.process() method accepts a messages array alongside message. Use it to inject structured context — user profile data, CRM records, previous interactions, or any information the agent needs to respond well.
Basic Pattern
const result = await agent.process({
message: input.message,
sessionId: input.sessionId,
messages: [
{
role: 'system',
content: `## Customer Profile
- Name: João Silva
- Plan: Enterprise
- Account since: 2023-01-15
- Open tickets: 2`,
},
],
});
These messages are prepended to the conversation, so the agent sees them as context before the user’s message.
Fetching Context Dynamically in main.ts
The most common pattern: fetch data from your database or CRM and inject it as context messages.
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
const agent = new Agent({
name: 'Support Agent',
instructions: `You are a customer support agent.
## Behavior
- Use the customer profile provided in context to personalize responses
- If the customer has open tickets, ask if they're related
- Prioritize Enterprise customers`,
model: openai('gpt-4o'),
memory: { maxTurns: 20 },
});
export async function main(input: any) {
identify(input.email || input.phone || 'anonymous');
// Fetch context from your systems
const customer = await fetchCustomer(input.email);
const openTickets = await fetchOpenTickets(customer.id);
const recentOrders = await fetchRecentOrders(customer.id, { limit: 3 });
const contextMessages = [];
// Customer profile
contextMessages.push({
role: 'system' as const,
content: `## Customer Profile
- Name: ${customer.name}
- Email: ${customer.email}
- Plan: ${customer.plan}
- Account since: ${customer.createdAt}
- Preferred language: ${customer.language}`,
});
// Open tickets
if (openTickets.length > 0) {
contextMessages.push({
role: 'system' as const,
content: `## Open Tickets
${openTickets.map((t: any) => `- ${t.id}: ${t.subject} (${t.status})`).join('\n')}`,
});
}
// Recent orders
if (recentOrders.length > 0) {
contextMessages.push({
role: 'system' as const,
content: `## Recent Orders
${recentOrders.map((o: any) => `- ${o.id}: ${o.status} — R$ ${o.total} (${o.date})`).join('\n')}`,
});
}
const result = await agent.process({
message: input.message,
sessionId: input.sessionId,
messages: contextMessages,
});
return { message: result.message };
}
Combining messages with Date Context
You can use both buildInstructions() for the system prompt and messages for per-request context:
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
const agent = new Agent({
name: 'Sales Agent',
instructions: `You are a sales agent for ACME Corp.
## Behavior
- Use the lead info provided in context
- Reference the current date for follow-up scheduling
- Be consultative, not pushy`,
model: openai('gpt-4o'),
memory: { maxTurns: 30 },
});
export async function main(input: any) {
identify(input.email || input.phone || 'anonymous');
const lead = await fetchLead(input.email);
const now = new Date();
const result = await agent.process({
message: input.message,
sessionId: input.sessionId,
messages: [
{
role: 'system',
content: `## Current Date
Today: ${format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR })}
Time: ${format(now, 'HH:mm')} (UTC-3)`,
},
{
role: 'system',
content: `## Lead Info
- Name: ${lead.name}
- Company: ${lead.company}
- Role: ${lead.role}
- Interest: ${lead.interest}
- Last contact: ${lead.lastContactDate || 'First contact'}
- Score: ${lead.score}/10`,
},
],
});
return { message: result.message };
}
When to Use messages vs instructions
| Approach | When to Use |
|---|
instructions (static string) | Fixed behavior rules that don’t change per request |
buildInstructions() function | Rules that depend on dynamic data (date, user plan) |
messages array | Per-request context data (CRM records, order history, lead info) |
| Both combined | Fixed instructions + dynamic context data |
A good rule of thumb: put behavior rules in instructions and data in messages. The instructions tell the agent how to behave; the messages tell it what it’s working with.
Injecting User Context
Pass user-specific information into the instructions so the agent knows who it’s talking to:
Basic User Info
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
function buildInstructions(user: { name: string; plan: string; language: string }) {
return `You are a customer support agent for ACME Corp.
## Customer Context
- Name: ${user.name}
- Plan: ${user.plan}
- Language: ${user.language}
## Behavior
- Address the customer by name
- Respond in ${user.language}
- If they're on the Free plan, mention upgrade options when relevant
- If they're on the Enterprise plan, prioritize their requests`;
}
export async function main(input: any) {
identify(input.email || input.phone || 'anonymous');
// Fetch user data from your system
const user = await fetchUserFromDB(input.email);
const agent = new Agent({
name: 'Support Agent',
instructions: buildInstructions({
name: user.name,
plan: user.plan,
language: user.preferredLanguage || 'pt',
}),
model: openai('gpt-4o'),
memory: { maxTurns: 20 },
});
const result = await agent.process({
message: input.message,
sessionId: input.sessionId,
});
return { message: result.message };
}
With Debt/Financial Info (Collections)
function buildCollectionsPrompt(debt: {
customerName: string;
amount: number;
dueDate: string;
daysOverdue: number;
}) {
return `You are a professional debt collection agent.
## Customer & Debt Info
- Customer: ${debt.customerName}
- Amount due: R$ ${debt.amount.toFixed(2)}
- Original due date: ${debt.dueDate}
- Days overdue: ${debt.daysOverdue}
## Strategy
${debt.daysOverdue <= 7
? '- Be gentle — this is a recent overdue. A friendly reminder is enough.'
: debt.daysOverdue <= 30
? '- Be firm but empathetic. Offer a payment plan if needed.'
: '- This is significantly overdue. Offer flexible payment options. Escalate if refused.'}
## Rules
- Never be aggressive or threatening
- You may share the amount with the customer directly
- Never share the amount with third parties`;
}
Using loadPrompt() with Variables
For prompts managed in the Runflow portal, use loadPrompt() with template variables:
import { Agent, openai, loadPrompt } from '@runflow-ai/sdk';
import { format } from 'date-fns';
const agent = new Agent({
name: 'Support Agent',
instructions: loadPrompt('customer-support', {
currentDate: format(new Date(), 'yyyy-MM-dd'),
product: 'CRM Pro',
language: 'Portuguese',
}),
model: openai('gpt-4o'),
});
In the portal, your prompt template would look like:
You are a support agent for {{product}}.
Today's date is {{currentDate}}.
Respond in {{language}}.
Use loadPrompt() when you want non-developers (product managers, prompt engineers) to edit prompts through the portal without code changes. Use local functions when the prompt logic is complex or involves conditionals.
Date Handling for Scheduling
Scheduling is one of the hardest tasks for LLMs. Here are patterns that work in production.
Always Provide Today’s Date
The single most important thing: always tell the LLM what today’s date is.
instructions: `...
## Current Date
Today is Wednesday, March 12, 2025. Current time: 14:30 (UTC-3).
When the user says:
- "tomorrow" → Thursday, March 13, 2025
- "next Monday" → Monday, March 17, 2025
- "in 2 weeks" → Wednesday, March 26, 2025
Always confirm the calculated date with the user before scheduling.`
Don’t trust the LLM to calculate dates correctly. Validate in the tool:
tools/schedule-appointment.ts
import { createTool } from '@runflow-ai/sdk';
import { track } from '@runflow-ai/sdk/observability';
import { z } from 'zod';
import { parseISO, isBefore, isWeekend, format, startOfDay } from 'date-fns';
export const scheduleAppointmentTool = createTool({
id: 'schedule-appointment',
description: 'Schedule an appointment on a specific date and time',
inputSchema: z.object({
date: z.string().describe('Appointment date in ISO format (YYYY-MM-DD)'),
time: z.string().describe('Appointment time (HH:mm)'),
description: z.string().describe('What the appointment is about'),
}),
execute: async ({ context }) => {
const appointmentDate = parseISO(`${context.date}T${context.time}:00`);
const now = new Date();
// Validate: not in the past
if (isBefore(appointmentDate, now)) {
return {
success: false,
error: `Cannot schedule in the past. The requested date ${context.date} ${context.time} has already passed. Current date is ${format(now, 'yyyy-MM-dd HH:mm')}.`,
};
}
// Validate: not on weekends
if (isWeekend(appointmentDate)) {
return {
success: false,
error: `${format(appointmentDate, 'EEEE, MMMM d')} is a weekend. Please choose a weekday.`,
};
}
// Validate: business hours (9-18)
const hour = parseInt(context.time.split(':')[0]);
if (hour < 9 || hour >= 18) {
return {
success: false,
error: 'Appointments are only available between 9:00 and 18:00.',
};
}
// Schedule the appointment
const appointment = await createAppointment({
date: appointmentDate,
description: context.description,
});
track('appointment_scheduled', {
date: context.date,
dayOfWeek: format(appointmentDate, 'EEEE'),
});
return {
success: true,
id: appointment.id,
date: format(appointmentDate, "EEEE, MMMM d 'at' HH:mm"),
confirmation: `Appointment confirmed for ${format(appointmentDate, "EEEE, MMMM d 'at' HH:mm")}`,
};
},
});
Let the agent check available slots instead of guessing:
tools/check-availability.ts
import { createTool } from '@runflow-ai/sdk';
import { z } from 'zod';
import { parseISO, format, addDays, isWeekend } from 'date-fns';
export const checkAvailabilityTool = createTool({
id: 'check-availability',
description: 'Check available appointment slots for a date or date range',
inputSchema: z.object({
date: z.string().describe('Start date in ISO format (YYYY-MM-DD)'),
days: z.number().optional().describe('Number of days to check (default: 1, max: 7)'),
}),
execute: async ({ context }) => {
const startDate = parseISO(context.date);
const daysToCheck = Math.min(context.days || 1, 7);
const slots: { date: string; times: string[] }[] = [];
for (let i = 0; i < daysToCheck; i++) {
const day = addDays(startDate, i);
if (isWeekend(day)) continue;
// Fetch from your calendar/booking system
const available = await getAvailableSlots(day);
slots.push({
date: format(day, 'yyyy-MM-dd (EEEE)'),
times: available.map((s: any) => s.time),
});
}
if (!slots.length) {
return { available: false, message: 'No available slots in the requested period' };
}
return { available: true, slots };
},
});
Combining Everything: Scheduling Agent
A complete example that combines date injection, user context, and scheduling tools:
import { Agent, openai } from '@runflow-ai/sdk';
import { identify, track } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
import { scheduleAppointmentTool } from './tools/schedule-appointment';
import { checkAvailabilityTool } from './tools/check-availability';
function buildInstructions(userName: string) {
const now = new Date();
const today = format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR });
const time = format(now, 'HH:mm');
return `You are a scheduling assistant for ACME Clinic.
## Current Date and Time
- Today: ${today}
- Time: ${time} (Brasília, UTC-3)
## Customer
- Name: ${userName}
## Behavior
- Address the customer by name
- Always check availability before scheduling
- Confirm the full date and time with the customer before booking
- Business hours: Monday to Friday, 9:00 to 18:00
- Respond in Portuguese
## Tools
- Use check-availability FIRST to see open slots
- Use schedule-appointment to book after customer confirms
- Never schedule without checking availability first`;
}
export async function main(input: any) {
if (!input?.message) {
return { error: 'message is required' };
}
const phone = input.phone || input.from;
identify(phone || input.email || 'anonymous');
// Fetch user info
const user = await fetchUser(phone);
const agent = new Agent({
name: 'Scheduling Assistant',
instructions: buildInstructions(user?.name || 'Cliente'),
model: openai('gpt-4o'),
memory: { maxTurns: 20 },
modelConfig: { temperature: 0 },
tools: {
checkAvailability: checkAvailabilityTool,
scheduleAppointment: scheduleAppointmentTool,
},
observability: 'full',
});
try {
const result = await agent.process({
message: input.message,
sessionId: input.sessionId || `scheduling_${phone}`,
});
track('scheduling_interaction', {
hasAppointment: result.metadata?.toolsUsed?.includes('schedule-appointment'),
});
return { message: result.message };
} catch (error) {
console.error('[scheduling] Error:', error);
return { error: 'An error occurred. Please try again.' };
}
}
Summary
| Pattern | When to Use |
|---|
| Inject date in instructions | Always — LLMs don’t know today’s date |
buildInstructions() function | Dynamic data with code logic (conditionals, formatting) |
loadPrompt() with variables | Prompts managed in the portal by non-developers |
| Per-request agent creation | When data must be fresh on every request (dates, user context) |
| Date validation in tools | Always — never trust the LLM to calculate dates correctly |
Never trust the LLM to calculate dates. Always validate dates in your tools — check for past dates, weekends, business hours, and conflicts. The LLM should propose, your tool should validate.
Next Steps