Blueprint/Chapter 6
Chapter 6

The 7 Components of an Agent

By Zavier SandersSeptember 21, 2025

From prompts to human-in-the-loop: build a complete agent by understanding each essential component.

Prefer something you can ship today? Start with theQuickstart: Ship One Agent with Mastra— then come back here to deepen the concepts.

Introduction

Every functional agent—whether a simple chatbot or a complex autonomous system—is built from the same seven fundamental components.

Understanding these components is crucial because:

  • You'll build better agents by knowing which components to use when
  • You'll debug faster by identifying which component is causing issues
  • You'll scale more easily by composing components in different ways

The 7 Components

Here's how all the components fit together:

┌─────────────────────────────────────────────────────────────┐
│                         AI AGENT                            │
│                                                             │
│  ┌──────────┐    ┌──────────┐    ┌──────────────┐           │
│  │  Prompt  │───▶│  Model   │───▶│    Output    │           │
│  │ (Guide)  │    │ (Reason) │    │  (Results)   │           │
│  └──────────┘    └────┬─────┘    └──────────────┘           │
│                       │                                     │
│         ┌─────────────┼─────────────┐                       │
│         │             │             │                       │
│    ┌────▼───┐   ┌────▼────┐   ┌───▼─────┐                   │ 
│    │  Data  │   │  Tools  │   │Knowledge│                   │
│    │(Runtime)│  │(Actions)│   │  (RAG)  │                   │
│    └─────────┘  └─────────┘   └─────────┘                   │
│                                                             │
│                   ┌──────────────────┐                      │
│                   │ Human-in-Loop    │                      │
│                   │  (Approval)      │                      │
│                   └──────────────────┘                      │
└─────────────────────────────────────────────────────────────┘

Component Overview

  1. Prompt - Instructions that guide the agent's behavior
  2. Data - Runtime information the agent needs to operate
  3. Knowledge - Long-term information the agent can retrieve
  4. Tools - Functions the agent can call to take action
  5. Models - The LLM that powers reasoning
  6. Output - How the agent formats and delivers results
  7. Human-in-the-Loop - When and how humans intervene

The Example We'll Build

Throughout this chapter, we'll build a Job Alerts Agent that:

  • Monitors job boards (RSS/API)
  • Filters jobs based on your criteria
  • Summarizes relevant opportunities
  • Sends you a daily digest
  • Asks for approval before sending
┌──────────────────────────────────────────────────────────┐
│               Job Alerts Agent Flow                      │
│                                                          │
│  1. Fetch Jobs ──▶ 2. Filter & Score ──▶ 3. Summarize    │
│      (Tool)            (Data + Model)        (Output)    │
│                                                          │
│                         │                                │
│                         ▼                                │
│                   4. Request Approval                    │
│                      (Human-in-Loop)                     │
│                         │                                │
│                    ┌────┴────┐                           │
│                    │         │                           │
│              Approved    Rejected                        │
│                    │         │                           │
│                    ▼         ▼                           │
│              5. Send Email  Cancel                       │
│                 (Tool)                                   │
└──────────────────────────────────────────────────────────┘

By the end, you'll have a complete, production-ready agent that demonstrates all seven components working together.


Component 1: Prompt

The prompt is your agent's instruction manual. It defines what the agent does, how it behaves, and what it prioritizes.

Why Prompts Matter

The prompt is the most important component. A good prompt can make a weak model perform well. A bad prompt can make a strong model perform poorly.

Anatomy of a Good Prompt

1. ROLE: Who the agent is
2. TASK: What the agent does
3. CONTEXT: Information available
4. CONSTRAINTS: Rules and limitations

Example: Job Alerts Agent Prompt

const JOB_ALERT_PROMPT = `You are a Job Alerts Agent that helps users stay informed about relevant job opportunities.

# YOUR ROLE
You monitor job boards and filter opportunities based on user preferences. You provide clear, actionable summaries.

# YOUR TASK
1. Analyze job postings from multiple sources
2. Filter based on user criteria (keywords, location, experience)
3. Summarize relevant opportunities concisely
4. Highlight: title, company, location, salary, requirements

# CONSTRAINTS
- Only include jobs matching 70%+ of criteria
- Keep summaries to 2-3 sentences
- Always include apply link
- Flag remote vs on-site clearly
- Never include jobs older than 7 days

# OUTPUT FORMAT
For each job:
- 📋 Title & Company
- 📍 Location (Remote/Hybrid/On-site)
- 💰 Salary (if available)
- ✅ Match score
- 📝 Summary
- 🔗 Apply link`;

Building with Mastra

import { Agent } from '@mastra/core';

const jobAlertAgent = new Agent({
  name: 'Job Alert Agent',
  instructions: JOB_ALERT_PROMPT,
  model: {
    provider: 'OPEN_AI',
    name: 'gpt-4o',
    toolChoice: 'auto',
  },
});

Component 2: Data

Data is runtime information that flows into your agent—the dynamic context needed to make decisions.

Data vs Knowledge

Data: Changes frequently, passed at runtime
Knowledge: Changes rarely, stored and retrieved

Example: Today's job postings (data) vs. User preferences (knowledge)

Structured Data Pattern

import { z } from 'zod';

const JobContext = z.object({
  userCriteria: z.object({
    keywords: z.array(z.string()),
    minSalary: z.number(),
    location: z.string(),
    remote: z.boolean(),
  }),
  jobPostings: z.array(z.object({
    id: z.string(),
    title: z.string(),
    company: z.string(),
    salary: z.string().optional(),
    location: z.string(),
    techStack: z.array(z.string()),
  })),
});

async function generateAlert(context: z.infer<typeof JobContext>) {
  const validated = JobContext.parse(context);
  
  const prompt = `
User Criteria: ${JSON.stringify(validated.userCriteria)}

Jobs: ${validated.jobPostings.map(j => `${j.title} at ${j.company}`).join('\n')}

Filter and summarize matches.`;

  return await jobAlertAgent.generate(prompt);
}

Component 3: Knowledge

Knowledge is long-term information your agent retrieves when needed via RAG (Retrieval-Augmented Generation).

Data vs Knowledge: Visual Distinction

┌─────────────────────────────────────────────────────────────┐
│                    DATA (Component 2)                       │
│                                                             │
│  ┌──────────┐       ┌──────────┐       ┌──────────┐         │
│  │ Today's  │       │  User    │       │ Current  │         │
│  │  Jobs    │       │  Input   │       │  State   │         │
│  └──────────┘       └──────────┘       └──────────┘         │
│                                                             │
│  • Passed at runtime                                        │
│  • Changes frequently                                       │
│  • Specific to this request                                 │
│  • Small enough to fit in prompt                            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                  KNOWLEDGE (Component 3)                    │
│                                                             │
│  ┌──────────┐       ┌──────────┐       ┌──────────┐         │
│  │  Job     │       │  User    │       │ Company  │         │
│  │ Archive  │       │ Profile  │       │   Docs   │         │
│  │(10,000s) │       │(History) │       │          │         │
│  └──────────┘       └──────────┘       └──────────┘         │
│                                                             │
│  • Stored in vector DB                                      │
│  • Changes rarely                                           │
│  • Retrieved via search                                     │
│  • Too large for prompts                                    │
└─────────────────────────────────────────────────────────────┘

Example Decision:
• Today's 20 new jobs → DATA (pass directly)
• Last year's 5,000 jobs → KNOWLEDGE (store & search)

When to Use Knowledge

  • Information too large for prompts
  • Rarely changes
  • Multiple agents share it
  • Needs search/filtering

RAG with Mastra

import { Mastra } from '@mastra/core';

export const mastra = new Mastra({
  rag: {
    provider: 'PGVECTOR',
    config: {
      connectionString: process.env.DATABASE_URL!,
    },
  },
});

// Index knowledge
await mastra.rag.ingest({
  collectionName: 'job-archive',
  documents: jobs.map(job => ({
    id: job.id,
    content: `${job.title} at ${job.company}. ${job.description}`,
    metadata: { jobId: job.id, company: job.company },
  })),
});

// Query knowledge
const results = await mastra.rag.query({
  collectionName: 'job-archive',
  query: 'TypeScript jobs in San Francisco',
  limit: 5,
});

Component 4: Tools

Tools are functions your agent calls to interact with the world.

How Tool Execution Works

┌─────────────────────────────────────────────────────────────┐
│                   Tool Execution Flow                       │
│                                                             │
│  1. User Request                                           │
│     "Find TypeScript jobs and email me"                    │
│            │                                                │
│            ▼                                                │
│  2. Agent Reasoning (Model)                                │
│     "I need to: fetch jobs, then send email"               │
│            │                                                │
│            ▼                                                │
│  3. Tool Call: fetchJobs                                   │
│     ┌─────────────────────────────────┐                   │
│     │ Parameters:                      │                   │
│     │ - source: "stackoverflow"        │                   │
│     │ - keywords: ["TypeScript"]       │                   │
│     │ - limit: 20                      │                   │
│     └─────────────────────────────────┘                   │
│            │                                                │
│            ▼                                                │
│  4. Tool Executes                                          │
│     Fetches from API, validates data                       │
│            │                                                │
│            ▼                                                │
│  5. Tool Returns Result                                    │
│     { success: true, jobs: [...] }                         │
│            │                                                │
│            ▼                                                │
│  6. Agent Processes Result                                 │
│     "I got 15 jobs, now format and send email"             │
│            │                                                │
│            ▼                                                │
│  7. Tool Call: sendEmail                                   │
│     ┌─────────────────────────────────┐                   │
│     │ Parameters:                      │                   │
│     │ - to: "user@example.com"         │                   │
│     │ - subject: "15 TypeScript Jobs"  │                   │
│     │ - html: "<formatted jobs>"       │                   │
│     └─────────────────────────────────┘                   │
│            │                                                │
│            ▼                                                │
│  8. Final Response                                         │
│     "I found 15 jobs and sent them to your email"          │
└─────────────────────────────────────────────────────────────┘

Tool Design Principles

  1. Single responsibility - Each tool does one thing well
  2. Clear descriptions - Agent knows when to use it
  3. Strict validation - Prevent bad inputs
  4. Error handling - Graceful failures

Example Tools

import { z } from 'zod';

const fetchJobsTool = {
  description: 'Fetch job postings from a specified job board',
  parameters: z.object({
    source: z.enum(['stackoverflow', 'remoteok']),
    limit: z.number().min(1).max(50).default(20),
  }),
  execute: async ({ source, limit }) => {
    const jobs = await fetchFromSource(source, limit);
    return { success: true, count: jobs.length, jobs };
  },
};

const sendEmailTool = {
  description: 'Send job alert email to user',
  parameters: z.object({
    to: z.string().email(),
    subject: z.string(),
    content: z.string(),
  }),
  execute: async ({ to, subject, content }) => {
    const result = await resend.emails.send({
      from: process.env.EMAIL_FROM!,
      to,
      subject,
      html: content,
    });
    return { success: true, emailId: result.data?.id };
  },
};

// Add to agent
const agentWithTools = new Agent({
  name: 'Job Alert Agent',
  instructions: JOB_ALERT_PROMPT,
  model: { provider: 'OPEN_AI', name: 'gpt-4o' },
  tools: {
    fetchJobs: fetchJobsTool,
    sendEmail: sendEmailTool,
  },
});

Component 5: Models

The model is the LLM that powers reasoning. Choose based on cost, speed, and capability.

Model Selection Matrix

┌──────────────────────────────────────────────────────────────┐
│              COST vs CAPABILITY vs SPEED                     │
│                                                              │
│   High                                                       │
│   Cost   │                                                   │
│          │         ◉ gpt-4o                                  │
│          │         (Most capable)                            │
│          │         • Complex reasoning                       │
│          │         • Best quality                            │
│          │         • ~$0.01/1K tokens                        │
│          │                                                   │
│   Mid    │   ◉ gpt-4o-mini                                   │
│   Cost   │   (Best value)                                    │
│          │   • Good reasoning                                │
│          │   • Fast                                          │
│          │   • ~$0.0001/1K tokens                            │
│          │                                                   │
│   Low    │                                                   │
│   Cost   │                                                   │
│          └───────────────────────────────────────────────▶  │
│           Simple          Moderate         Complex           │
│           Tasks            Tasks            Tasks            │
│                                                              │
│  Decision Guide:                                            │
│  • Classification → gpt-4o-mini                             │
│  • Summarization → gpt-4o-mini                              │
│  • Analysis → gpt-4o                                        │
│  • Creative writing → gpt-4o                                │
│  • Production scale → gpt-4o-mini                           │
└──────────────────────────────────────────────────────────────┘

Model Selection Criteria

Use CaseModelWhy
Simple classificationgpt-4o-miniFast, cheap, accurate
Complex reasoninggpt-4oMost capable
Production at scalegpt-4o-miniCost-effective
Streaming responsesgpt-4oBest UX
Multi-step workflowsgpt-4oBetter planning

Configuring Models

const agent = new Agent({
  name: 'Job Alert Agent',
  instructions: JOB_ALERT_PROMPT,
  model: {
    provider: 'OPEN_AI',
    name: 'gpt-4o-mini', // Fast and cheap for production
    toolChoice: 'auto',
    temperature: 0.3, // Lower = more consistent
    maxTokens: 2000,
  },
});

Multi-Model Pattern

// Use different models for different tasks
const classifierAgent = new Agent({
  model: { provider: 'OPEN_AI', name: 'gpt-4o-mini' }, // Fast classification
});

const summarizerAgent = new Agent({
  model: { provider: 'OPEN_AI', name: 'gpt-4o' }, // Better summaries
});

Component 6: Output

Output is how the agent formats and delivers results. Structure it for your use case.

Output Patterns

Pattern 1: Structured Output

import { generateObject } from 'ai';
import { z } from 'zod';

const JobAlertSchema = z.object({
  matchingJobs: z.array(z.object({
    title: z.string(),
    company: z.string(),
    matchScore: z.number(),
    summary: z.string(),
    applyUrl: z.string(),
  })),
  totalMatches: z.number(),
  recommendation: z.string(),
});

async function generateStructuredAlert(criteria: Criteria, jobs: Job[]) {
  const result = await generateObject({
    model: openai('gpt-4o'),
    schema: JobAlertSchema,
    prompt: `Analyze these jobs and return structured matches: ${JSON.stringify(jobs)}`,
  });

  return result.object; // Typed output!
}

Pattern 2: Streaming Output

async function streamJobAlert(criteria: Criteria) {
  const stream = await agent.stream(
    `Find jobs matching: ${JSON.stringify(criteria)}`
  );

  for await (const chunk of stream) {
    process.stdout.write(chunk);
  }
}

Pattern 3: HTML Output

function formatAsHTML(jobs: Job[]) {
  return `
<!DOCTYPE html>
<html>
<body>
  <h1>Your Daily Job Alert</h1>
  ${jobs.map(job => `
    <div style="border: 1px solid #ccc; padding: 16px; margin: 16px 0;">
      <h2>${job.title}</h2>
      <p><strong>${job.company}</strong> | ${job.location}</p>
      <p>${job.summary}</p>
      <a href="${job.applyUrl}">Apply Now</a>
    </div>
  `).join('')}
</body>
</html>`;
}

Component 7: Human-in-the-Loop

HITL is when agents pause for human approval before taking action.

Human-in-the-Loop Workflow

┌─────────────────────────────────────────────────────────────┐
│              Human-in-the-Loop Flow                         │
│                                                             │
│  1. Agent Completes Task                                   │
│     ┌────────────────────────────────┐                     │
│     │ Found 12 matching jobs         │                     │
│     │ Ready to send email            │                     │
│     └────────────────────────────────┘                     │
│            │                                                │
│            ▼                                                │
│  2. Check if Approval Needed                               │
│     ┌─────────────┬──────────────┐                        │
│     │ High stakes?│ First time?  │                         │
│     │ Low confidence? User pref? │                         │
│     └─────────────┴──────────────┘                        │
│            │                                                │
│        ┌───┴───┐                                           │
│        │       │                                            │
│       Yes     No                                            │
│        │       │                                            │
│        │       └──────────────────────┐                    │
│        ▼                               ▼                    │
│  3. Request Approval             5. Execute Action         │
│     ┌────────────────────────┐       Immediately           │
│     │ Email/Slack/Dashboard  │                             │
│     │ "Review before send?"  │                             │
│     └────────────────────────┘                             │
│            │                                                │
│            ▼                                                │
│  4. Wait for Human Decision                                │
│     ┌────────┬────────┐                                    │
│     │        │        │                                     │
│  Approve  Reject  Modify                                    │
│     │        │        │                                     │
│     ▼        ▼        ▼                                     │
│  Execute  Cancel  Re-run                                    │
│  Action           with changes                              │
│                                                             │
│  6. Log Decision                                           │
│     Store approval/rejection with reason                    │
└─────────────────────────────────────────────────────────────┘

When to Use HITL

  • High-stakes decisions - Money, legal, public communication
  • First-time operations - New user, new workflow
  • Low confidence scores - Agent is unsure
  • User preference - User wants control

Implementing HITL

class JobAlertWithApproval {
  async generate(userId: string) {
    // 1. Agent finds matching jobs
    const matches = await this.findMatches(userId);
    
    // 2. Request approval
    const approved = await this.requestApproval(userId, matches);
    
    // 3. Only send if approved
    if (approved) {
      await this.sendEmail(userId, matches);
      return { sent: true, jobCount: matches.length };
    }
    
    return { sent: false, reason: 'User declined' };
  }

  private async requestApproval(userId: string, matches: Job[]) {
    // Store pending approval
    await db.pendingApprovals.create({
      userId,
      type: 'job-alert',
      data: matches,
      expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
    });

    // Send approval request
    await this.sendApprovalRequest(userId, matches);
    
    // Wait for response (via webhook/polling)
    return await this.waitForApproval(userId);
  }
}

Approval UI Pattern

// API route for approval
app.post('/api/approve/:approvalId', async (req, res) => {
  const { approvalId } = req.params;
  const { approved } = req.body;

  const approval = await db.pendingApprovals.findById(approvalId);
  
  if (approved) {
    // Execute the action
    await sendEmailTool.execute({
      to: approval.userEmail,
      subject: 'Your Job Matches',
      content: formatJobs(approval.data),
    });
  }

  await db.pendingApprovals.delete(approvalId);
  res.json({ success: true });
});

Complete Working Example

Here's the full job alerts agent with all 7 components:

import { Agent, Mastra } from '@mastra/core';
import { z } from 'zod';
import { Resend } from 'resend';

// Component 1: Prompt
const PROMPT = `You are a Job Alerts Agent...`;

// Component 3: Knowledge (RAG setup)
const mastra = new Mastra({
  rag: {
    provider: 'PGVECTOR',
    config: { connectionString: process.env.DATABASE_URL! },
  },
});

// Component 4: Tools
const tools = {
  fetchJobs: {
    description: 'Fetch jobs from a source',
    parameters: z.object({
      source: z.enum(['stackoverflow', 'remoteok']),
      limit: z.number().default(20),
    }),
    execute: async ({ source, limit }) => {
      const jobs = await fetchFromSource(source, limit);
      return { jobs };
    },
  },
  sendEmail: {
    description: 'Send email alert',
    parameters: z.object({
      to: z.string().email(),
      subject: z.string(),
      html: z.string(),
    }),
    execute: async ({ to, subject, html }) => {
      const resend = new Resend(process.env.RESEND_API_KEY);
      await resend.emails.send({
        from: process.env.EMAIL_FROM!,
        to,
        subject,
        html,
      });
      return { sent: true };
    },
  },
};

// Component 5: Model + All Components
const agent = new Agent({
  name: 'Job Alert Agent',
  instructions: PROMPT,
  model: {
    provider: 'OPEN_AI',
    name: 'gpt-4o-mini',
    toolChoice: 'auto',
  },
  tools,
});

// Main service with all 7 components
class JobAlertService {
  async generateDailyAlert(userId: string) {
    // Component 2: Load data
    const criteria = await this.loadUserCriteria(userId);
    const jobs = await this.fetchRecentJobs();

    // Component 3: Query knowledge
    const historicalMatches = await mastra.rag.query({
      collectionName: 'job-archive',
      query: `Previous matches for user ${userId}`,
    });

    // Agent processes with tools
    const result = await agent.generate(`
User: ${userId}
Criteria: ${JSON.stringify(criteria)}
Jobs: ${JSON.stringify(jobs)}
Previous matches: ${JSON.stringify(historicalMatches)}

Find matching jobs and prepare an email.`);

    // Component 7: HITL - Request approval
    const approved = await this.requestApproval(userId, result);

    if (approved) {
      // Component 6: Send structured output
      return { sent: true, result: result.text };
    }

    return { sent: false, reason: 'Awaiting approval' };
  }

  private async loadUserCriteria(userId: string) {
    return {
      keywords: ['TypeScript', 'React'],
      minSalary: 120000,
      remote: true,
    };
  }

  private async fetchRecentJobs() {
    // Fetch from job boards
    return [];
  }

  private async requestApproval(userId: string, result: any) {
    // Send approval request, wait for response
    return true;
  }
}

// Usage
const service = new JobAlertService();
await service.generateDailyAlert('user-123');

How to Run It

  1. Install dependencies:
npm install @mastra/core @ai-sdk/openai zod resend
  1. Set environment variables:
OPENAI_API_KEY=your-key
RESEND_API_KEY=your-key
EMAIL_FROM=alerts@yourdomain.com
DATABASE_URL=postgresql://...
  1. Run:
npx tsx job-alert-agent.ts

How to Extend It

Add more job boards:

tools.fetchJobs = {
  // Add 'hn-hiring', 'linkedin', etc.
  parameters: z.object({
    source: z.enum(['stackoverflow', 'remoteok', 'hn-hiring', 'linkedin']),
  }),
};

Add salary negotiation advice:

tools.getSalaryInsights = {
  description: 'Get salary insights for a role',
  execute: async ({ title, location }) => {
    // Call salary API
    return { median: 150000, range: [120000, 180000] };
  },
};

Add calendar integration:

tools.scheduleInterview = {
  description: 'Schedule interview in calendar',
  execute: async ({ jobId, date }) => {
    // Add to Google Calendar
  },
};

Key Takeaways

  1. Prompt is foundation - Everything else builds on good instructions
  2. Data vs Knowledge - Pass frequent data, store rare knowledge
  3. Tools enable action - Without tools, agents only talk
  4. Choose models wisely - Balance cost, speed, capability
  5. Structure output - Make it easy to consume
  6. HITL for safety - Pause before high-stakes actions
  7. Compose iteratively - Start simple, add components as needed

What's Next

You now understand the 7 components. Next chapter: Triggers - when and how agents wake up to do their work.

Then we'll dive into real patterns: event processing, monitoring, scheduled analysis, and more.

Get chapter updates & code samples

We’ll email diagrams, code snippets, and additions.