AI agents become more useful when they are not just chatbots, but small systems that can understand a request, call tools, retrieve knowledge, and return structured results.
For this example, we built a Personal Study Planner Agent with HazelJS.
The goal is simple:
A student gives their exam date, available study time, and weak topics. The agent creates a realistic study plan.
This is an easy project, but it still shows important HazelJS agent patterns.
What the Project Includes
The project uses:
-
@hazeljs/corefor modules, controllers, and services -
@hazeljs/agentfor agents, tools, delegation, and supervisor routing -
@hazeljs/ragfor study-method retrieval -
@hazeljs/guardrailsfor safer input/output handling -
@hazeljs/evalfor golden tests -
@hazeljs/inspectorfor the/__hazelweb view
The app has four agents:
StudyIntakeAgentStudyResourceAgentStudyScheduleAgentStudyCoachAgent
Each agent has a focused job.
Why This Example Works Well
A study planner is simple enough to understand quickly, but it still has a real workflow:
- Extract study constraints
- Find useful study methods
- Build a schedule
- Return a clear plan
That makes it a good beginner-friendly HazelJS agent example.
A sample request looks like this:
I have 10 days before my algebra exam. I can study 60 minutes daily. Weak topics are quadratic equations and word problems.
The system can extract the exam, timeline, daily capacity, weak topics, and urgency.
Agent 1: Study Intake
The first agent is StudyIntakeAgent.
Its job is to understand the student’s request.
@Agent({
name: 'StudyIntakeAgent',
description: 'Extracts exam goals, time constraints, weak topics, learning style, and urgency.',
})
@Service()
export class StudyIntakeAgent {
@Tool({
name: 'extractStudyProfile',
description: 'Extract structured study planning information from a student request.',
})
async extractStudyProfile(input: { message: string; studentId?: string }) {
// Extract exam, days, minutes, weak topics, and urgency
}
}
This keeps profile extraction separate from schedule creation.
That is a good agent design pattern: one agent, one responsibility.
Agent 2: Study Resources with RAG
The second agent is StudyResourceAgent.
It uses HazelJS RAG to retrieve study methods like:
- active recall
- spaced repetition
- interleaving
- exam review
- healthy study breaks
@Agent({
name: 'StudyResourceAgent',
description: 'Retrieves evidence-based study methods and review tactics.',
enableRAG: true,
})
@Service()
export class StudyResourceAgent {
@Tool({
name: 'searchStudyMethods',
description: 'Search study method guidance for planning, review, recall, and exam preparation.',
})
async searchStudyMethods(input: { query: string; topK?: number }) {
return this.knowledgeBase.answer(input.query, input.topK ?? 3);
}
}
This is better than asking the model to invent study advice. The agent retrieves known study methods and uses those as context.
Agent 3: Study Schedule
The third agent is StudyScheduleAgent.
It creates the actual day-by-day plan.
@Agent({
name: 'StudyScheduleAgent',
description: 'Creates realistic daily study plans with review checkpoints.',
})
@Service()
export class StudyScheduleAgent {
@Tool({
name: 'createStudySchedule',
description: 'Create a day-by-day study schedule from exam constraints and weak topics.',
})
async createStudySchedule(input: {
exam: string;
daysUntilExam: number;
minutesPerDay: number;
weakTopics: string[];
}) {
// Build daily schedule
}
}
The tool returns a structured plan with:
- day number
- focus topic
- study minutes
- activities
- checkpoint
This makes the output easier to use in a real app.
Agent 4: Study Coach Orchestrator
The StudyCoachAgent coordinates the other agents.
It uses HazelJS @Delegate:
@Delegate({
agent: 'StudyIntakeAgent',
description: 'Extract exam, timeline, weak topics, capacity, and urgency.',
inputField: 'input',
})
async analyzeStudyRequest(input: string): Promise<string> {
return '';
}
It can delegate to:
StudyIntakeAgentStudyResourceAgentStudyScheduleAgent
This keeps orchestration clean without putting everything into one giant prompt.
Supervisor Routing
The project also uses HazelJS supervisor routing.
const supervisor = runtime.createSupervisor({
name: 'study-planner-supervisor',
workers: ['StudyIntakeAgent', 'StudyResourceAgent', 'StudyScheduleAgent'],
maxRounds: 4,
});
The supervisor decides which agent should handle the request.
For example, a schedule request is routed to StudyScheduleAgent.
A method question is routed to StudyResourceAgent.
This is useful when the app does not know ahead of time which specialist should run.
Runtime Safety and Observability
The app configures the HazelJS agent runtime with production-friendly settings:
AgentModule.forRoot({
runtime: {
defaultMaxSteps: 8,
defaultTimeout: 15000,
enableObservability: true,
enableMetrics: true,
enableRetry: true,
enableCircuitBreaker: true,
rateLimitPerMinute: 120,
},
})
This gives the agent system:
- step limits
- timeouts
- retries
- circuit breaker protection
- metrics
- rate limiting
- observability
Even for a small demo, these are good habits.
Guardrails
The app enables HazelJS guardrails:
GuardrailsModule.forRoot({
redactPIIByDefault: true,
blockInjectionByDefault: true,
blockToxicityByDefault: true,
})
This helps keep inputs and outputs safer.
For a study planner, guardrails are useful because users may paste personal details, stress-related messages, or unsafe instructions.
Testing with Evals
The project includes golden evals with @hazeljs/eval.
The evals check:
- whether the intake agent calls
extractStudyProfile - whether the resource agent retrieves the right study methods
- whether the schedule agent calls
createStudySchedule
Example:
{
"id": "schedule-biology",
"input": "Build a plan for my biology exam in 14 days with 90 minutes daily. Weak topics are photosynthesis and genetics.",
"expectedOutput": "Study schedule complete",
"expectedToolCalls": ["createStudySchedule"]
}
Run:
npm run eval
Expected result:
[hazeljs/eval] personal-study-planner-agent@2026.06 avg=0.944 passed=true
This proves the project is not only returning text, but also using the right tools.
Running the Project
Install dependencies:
npm install
Run evals:
npm run eval
Start the app:
npm run dev
Test the supervisor endpoint:
curl -s -X POST http://localhost:3000/study/supervisor \
-H 'content-type: application/json' \
-d '{"message":"I have 10 days before my algebra exam. I can study 60 minutes daily. Weak topics are quadratic equations and word problems.","userId":"student-2"}'
Open the HazelJS inspector:
http://localhost:3000/__hazel
You can also inspect registered agents and tools:
http://localhost:3000/study/runtime
Why Use a Local Provider?
The demo uses a deterministic local provider.
That means:
- no API key is required
- outputs are stable
- evals are repeatable
- readers can run the project immediately
In production, you can replace it with a real model provider through HazelJS AI integrations.
Final Thoughts
This project is intentionally simple, but it shows strong HazelJS agent patterns:
- focused agents
- scoped tools
- RAG-backed knowledge
- delegation
- supervisor routing
- guardrails
- runtime observability
- evals
That makes the Personal Study Planner Agent a good beginner-friendly example for learning how to build practical AI agents with HazelJS.
Repo: Personal Study Planner
Top comments (0)