Skip to main content
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:
agent.ts
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():
main.ts
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.
main.ts
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:
main.ts
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

ApproachWhen to Use
instructions (static string)Fixed behavior rules that don’t change per request
buildInstructions() functionRules that depend on dynamic data (date, user plan)
messages arrayPer-request context data (CRM records, order history, lead info)
Both combinedFixed 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

main.ts
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)

main.ts
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:
agent.ts
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.`

Scheduling Tool with Date Validation

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")}`,
    };
  },
});

Availability Check Tool

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:
main.ts
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

PatternWhen to Use
Inject date in instructionsAlways — LLMs don’t know today’s date
buildInstructions() functionDynamic data with code logic (conditionals, formatting)
loadPrompt() with variablesPrompts managed in the portal by non-developers
Per-request agent creationWhen data must be fresh on every request (dates, user context)
Date validation in toolsAlways — 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