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.
**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:
- K12 Agents - EOP and QRG agents for emergency planning
- TAP Agents - Image captioning, tagging, and content moderation agents
- Reasoning Engine Agents - Wellbeing data analysis agents
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.AgentProfilestable - Runtime Updates: Change agent behavior without redeploying code
- Versioning System: Complete audit trail with
VersionNumberandIsCurrentflags - 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.AgentProfilestable 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
Modern Pattern: Unified Agent Platform (Recommended)
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)
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
ProfileHashandVersionNumber - 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.AgentProfilestable for current profile - Verify
IsCurrent = 1for the intended profile - Check
ProfileHashto confirm which version is loaded
LLM Parameter Changes Not Applied:
- Confirm
LlmOverridesJsonis valid JSON in the profile record - Verify the selected model supports
withSettingsmethod - 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
Tone/phrasing adjustment is handled by the outer workflow (business logic layer), not by the reasoning engine.
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/