Skip to main content

Agent Framework Overview

Agent Framework Overview

The Mastra Agent Framework provides a database-driven, profile-based agent system that enables runtime configuration without code deployment.

note

**Modern Approach:** All new agents should use the Unified Agent Platform which provides consistent configuration, automatic caching, and database-validated model resolution with 77% less code.

This page covers the general agent architecture and patterns used across all Mastra workloads.

For workload-specific agent details, see:

Core Agent Concepts

Profile-Based Configuration

All agents in the Mastra framework use the profile-based configuration system:

  • Database-Driven Prompts: Agent system prompts are stored in Core.AgentProfiles table
  • Runtime Updates: Change agent behavior without redeploying code
  • Versioning System: Complete audit trail with VersionNumber and IsCurrent flags
  • Hash-Based Caching: Agents rebuild automatically when profile hash changes
  • LLM Overrides: Per-agent model selection and parameter tuning

For comprehensive information on how this works, see Agent Prompt Overrides.

Agent Identity

Each agent has two key identifiers:

  • Registry ID: The unique identifier used in code to access the agent (e.g., eop-k12-safety-agent)
  • Profile ID: The identifier used in Core.AgentProfiles table to load configuration (e.g., k12-safety-agent)

These may differ - the registry ID is for runtime access while the profile ID links to database configuration.

Agent Patterns

All new agents should use the Unified Agent Platform:

import { AgentDescriptor, getOrCreateFactory } from '../shared';

// 1. Declare what your agent needs
export const MY_AGENT_DESCRIPTOR: AgentDescriptor = {
profileId: 'my-agent',
registryId: 'my-agent',
defaultName: 'My Agent',
defaultPrompt: 'You are an agent that...',
tools: [myTool],
seedConfig: { shouldSeed: true },
};

// 2. Get agent instance (factory handles everything)
export async function getMyAgent(): Promise<Agent> {
const factory = getOrCreateFactory(MY_AGENT_DESCRIPTOR);
return factory.getAgent();
}

Benefits:

  • ✅ 77% less code (K12 agent: 266 → 61 lines)
  • ✅ Automatic profile loading and caching
  • ✅ Database-validated model resolution
  • ✅ Consistent behavior across all agents
  • ✅ Profile-hash-based cache invalidation
  • ✅ Dependency injection for testing

Agents using this pattern:

  • All TAP agents (image-caption, blocklist, recovered, etc.)
  • K12 safety agent
  • All reasoning engine agents
  • Testing agent

Legacy Patterns (Pre-Unified Platform)

warning

These patterns are legacy and should be migrated to the Unified Agent Platform. They require manual profile loading, model resolution, and caching logic that is error-prone and duplicates 100+ lines per agent.

The Mastra framework previously supported multiple manual instantiation patterns:

Singleton Pattern (Legacy):

// Single cached instance, rebuilds on profile changes
let cachedAgent = null;
let cachedProfileHash = null;

export async function getAgent() {
const profile = await agentProfilesService.getCurrent('agent-profile-id');

if (cachedAgent && profile.ProfileHash === cachedProfileHash) {
return cachedAgent; // Return cached instance
}

// Build new agent with updated profile
cachedProfileHash = profile.ProfileHash;
cachedAgent = createAgent(profile);
return cachedAgent;
}

Registry Pattern (Legacy):

// Returns object containing multiple agents
export function buildAgents() {
return {
'agent-one': createAgentOne(),
'agent-two': createAgentTwo(),
'agent-three': createAgentThree(),
};
}

// Usage: Access by registry key
const agents = buildAgents();
const agent = agents['agent-one'];

Async Builder Pattern (Legacy):

// For agents requiring async initialization
export async function createAgent() {
const config = await fetchConfiguration();
const model = await initializeModel(config);

return new Agent({
model,
instructions: config.systemPrompt
});
}

Individual Getter Pattern (Legacy):

// Separate getter for each agent
export async function getAgentOne() {
const profile = await agentProfilesService.getCurrent('agent-one');
return createAgent(profile);
}

export async function getAgentTwo() {
const profile = await agentProfilesService.getCurrent('agent-two');
return createAgent(profile);
}

Current usage:

  • QRG agents still use individual getter pattern (migration pending)

Profile Loading System

Agents use agentProfilesService to load configuration from Core.AgentProfiles table:

// Example of how agents load their profiles internally
const profile = await agentProfilesService.getCurrent('agent-profile-id');

if (profile) {
// Apply system prompt override
systemPrompt = profile.systemPrompt ?? defaultSystemPrompt;

// Apply LLM overrides (temperature, topP, etc.)
llmConfig = { ...defaultConfig, ...profile.llmOverrides };
}

Profile features:

  • Prompts can be managed via Admin UI or direct SQL
  • Supports versioning via ProfileHash and VersionNumber
  • Automatic rebuilding when profile changes
  • LLM model overrides per profile (deployment name, model parameters)

For complete details, see Agent Prompt Overrides.

LLM Configuration

Default Configuration

Agents use Azure AI Inference models with default configuration from DB defaults and environment variables:

import { getAzureInferenceChatModel } from 'src/mastra/providers/azure-ai-inference.ts';

// Default model from DB defaults and/or environment
const model = await getAzureInferenceChatModel();

Profile Overrides

Agent profiles can override LLM settings:

  • Deployment/Model Selection: Specify which Azure AI Foundry deployment to use
  • Temperature: Control randomness (0.0 = deterministic, 2.0 = very random)
  • Top P: Nucleus sampling threshold
  • Frequency Penalty: Reduce repetition of tokens
  • Presence Penalty: Encourage new topics
  • Max Output Tokens: Limit response length

These are stored in Core.AgentProfiles.LlmOverridesJson and applied at runtime.

Model Capabilities

Model capabilities are tracked in Core.LLMModelCapabilities table for deployment planning and feature compatibility checking.

Debugging Agent Issues

Enable Debug Logging

# K12 agent debugging
K12_DEBUG_AGENT_OVERRIDES=true

# General logging
# Check logs for [agent-name] prefixed messages

Common Issues

Unexpected System Prompts:

  • Check Core.AgentProfiles table for current profile
  • Verify IsCurrent = 1 for the intended profile
  • Check ProfileHash to confirm which version is loaded

LLM Parameter Changes Not Applied:

  • Confirm LlmOverridesJson is valid JSON in the profile record
  • Verify the selected model supports withSettings method
  • Check that numeric values are within valid ranges

Profile Hash Changes:

  • Profile hash changes trigger automatic rebuilds (logged)
  • Agents cache by profile hash for performance
  • To force rebuild, update the profile or restart the service

Log Messages to Look For

[agent-name] Loading profile for profile-id
[agent-name] Profile hash: abc123def456
[agent-name] Profile changed, rebuilding agent
[agent-name] Applied LLM overrides: { temperature: 0.7, topP: 0.9 }

Testing Agents

Unit Tests

Agent tests are distributed across workload test directories:

  • K12 Tests: packages/domain-k12/tests/
  • TAP Tests: packages/domain-tap/tests/ and tests/tap/

Test Patterns

import { getK12SafetyAgent } from 'packages/domain-k12/src/agents/k12-safety-agent.ts';

describe('K12 Safety Agent', () => {
it('should generate EOP content', async () => {
const agent = await getK12SafetyAgent();
const result = await agent.generate({
messages: [{ role: 'user', content: 'Generate safety plan' }]
});

expect(result).toBeDefined();
});
});

End-to-End Workflow Testing

Validate agent-driven workflows end-to-end by testing complete workflow executions that include agent steps.

Reasoning Engine Agents

Reasoning engine agents provide specialized capabilities for analyzing time-series data, identifying patterns, computing correlations, and generating insights. These agents work together in a pipeline to transform raw structured datasets into actionable analysis.

Location: src/mastra/agents/reasoning-engine/

All reasoning engine agents follow the same profile-based override system as other Mastra agents, allowing runtime configuration via Core.AgentProfiles.

**For detailed Reasoning Engine agent documentation**, see Reasoning Engine Agents.

Agent Summary

The Reasoning Engine includes specialized agents organized into core and correlation groups:

Core Agents: (src/mastra/agents/reasoning-engine/core/)

  • Orchestrator - Creates execution plans based on analysis objectives
  • Temporal - Analyzes time-series trends and statistics
  • Pattern - Identifies patterns in data using algorithmic + AI analysis
  • Domain - (Reserved for future RAG-based knowledge retrieval)
  • Explanation - Synthesizes findings into coherent narratives
  • Validation - Verifies claims match underlying data and assembles final result

Correlation Agents: (src/mastra/agents/reasoning-engine/correlation/)

  • Normalizer - Prepares datasets for correlation analysis
  • Analysis - Interprets correlation coefficients and highlights insights
note

Tone/phrasing adjustment is handled by the outer workflow (business logic layer), not by the reasoning engine.

**Full agent specifications**, including registry IDs, profile IDs, input/output schemas, and usage examples, are documented in Reasoning Engine Agents.

Code Locations

Agent Implementations

  • Base Agent Framework: src/mastra/agents/
  • K12 Agents: src/mastra/agents/k12/
  • TAP Agents: packages/domain-tap/src/agents/
  • Reasoning Engine Agents: src/mastra/agents/reasoning-engine/

Profile Management

  • Profile Service: Agent profile loading and caching logic
  • Database Schema: db/sqlproj/Mastra.CoreDb/model/Tables/Core.AgentProfiles.sql
  • Admin Tools: Tools for managing agent profiles

Tests

  • K12 Tests: packages/domain-k12/tests/
  • TAP Tests: packages/domain-tap/tests/ and tests/tap/
  • Test Utilities: Shared test harnesses under tests/

Next Steps