M365 Knowledge Worker workloads for Agent Haymaker platform
Technical architecture of the M365 Knowledge Worker workload.
haymaker_m365_workloads/
├── __init__.py Package entry; exports M365KnowledgeWorkerWorkload
├── workload.py Main workload class (deploy, stop, cleanup, status)
├── identity/
│ ├── __init__.py
│ └── user_manager.py EntraUserManager -- creates/deletes Entra ID users
├── operations/
│ ├── __init__.py
│ └── orchestrator.py ActivityOrchestrator -- runs the activity loop
├── content/
│ ├── __init__.py
│ ├── email_generator.py EmailGenerator -- LLM + template fallback
│ └── prompts.py Department-aware prompt templates
└── models/
├── __init__.py
└── worker.py WorkerIdentity, WorkerConfig, Department, ActivityPattern
M365KnowledgeWorkerWorkload extends WorkloadBase from the agent-haymaker platform. It is the top-level entry point registered via the workload.yaml manifest:
package:
entrypoint: haymaker_m365_workloads:M365KnowledgeWorkerWorkload
Responsibilities:
EntraUserManager and EmailGenerator instancesActivityOrchestrator for continuous activity generationdeploy, stop, cleanup, get_status, and get_logs to the platformEntraUserManager handles all Entra ID (Azure AD) operations through the Microsoft Graph API:
create_worker(config) – creates an Entra user with a generated UPN and secure random passworddelete_worker(worker_id) – removes a single Entra userdelete_all_workers() – removes all workers created by this manager instanceget_workers() – returns all tracked worker identitiesWorker credentials are ephemeral: passwords are generated at creation time and never stored.
ActivityOrchestrator runs the continuous activity generation loop:
EmailGenerator.generate() for email content when availableon_activity callback for telemetrystart(), stop(), and get_logs() with optional follow modeEmailGenerator produces realistic email content:
email_directive for custom topic steeringData models used across the workload:
Department – enum of valid departments (operations, engineering, sales, hr, finance, executive)ActivityPattern – per-department activity rates (email/hr, Teams/hr, docs/day, meetings/day) plus work hours and varianceWorkerConfig – input for creating a new worker (department, worker number, deployment ID)WorkerIdentity – a created worker with Entra object ID, UPN, department, and activity patternDEPARTMENT_PATTERNS is a dictionary mapping each Department to its ActivityPattern.
The workload integrates with agent-haymaker through the WorkloadBase interface:
agent-haymaker platform
└── WorkloadBase (abstract)
└── M365KnowledgeWorkerWorkload
├── deploy(config) -> deployment_id
├── get_status(deployment_id) -> DeploymentState
├── stop(deployment_id) -> bool
├── cleanup(deployment_id) -> CleanupReport
├── get_logs(deployment_id) -> AsyncIterator[str]
├── validate_config(config) -> list[str]
└── list_deployments() -> list[DeploymentState]
The platform handles:
haymaker deploy, haymaker status, etc.)save_state / load_state)workload.yamlA deployment moves through the following phases:
PENDING
│
▼
creating_workers Create Entra ID users via Graph API
│
▼
executing Activity loop running (email, Teams, calendar, documents)
│
├──▶ stopped Manual stop via `haymaker stop`
│
▼
cleanup Delete Entra users and resources via `haymaker cleanup`
│
├──▶ completed All resources removed
│
└──▶ failed Cleanup error (resources may remain)
State transitions are persisted through the platform’s save_state mechanism, allowing deployments to be tracked across process restarts.
The orchestrator runs a continuous loop with one-minute cycles:
while running:
if duration_hours exceeded:
break
for each worker:
if outside work hours (08:00-17:00 UTC):
skip
if random() < email_per_hour / 60:
send email (via EmailGenerator if available)
if random() < teams_per_hour / 60:
send Teams message
if random() < docs_per_day / 480:
create document
sleep 60 seconds
Each activity:
on_activity callback for telemetry aggregationThe per-minute probability is rate_per_hour / 60, which produces the expected hourly rate over time while maintaining natural variance between cycles.