Skip to content
Learn Agentic AI
Learn Agentic AI15 min read0 views

Build a Fitness Coaching Agent: Workout Planning, Progress Tracking, and Nutrition Advice

Build a complete fitness coaching AI agent that generates personalized workout plans, tracks exercise progress over time, and provides nutrition advice — a personal trainer powered by Python and the OpenAI Agents SDK.

Why Build a Fitness Coaching Agent

Personal trainers cost between fifty and two hundred dollars per hour. Most fitness apps give you static workout templates that ignore your progress, equipment availability, and dietary preferences. A fitness coaching agent bridges this gap: it generates personalized workout plans based on your goals and available equipment, tracks your progress across sessions, adjusts difficulty over time, and provides nutrition advice tailored to your training.

This tutorial builds a complete fitness coaching system with an exercise database, plan generator, progress tracker, and nutrition advisor.

Project Setup

mkdir fitness-agent && cd fitness-agent
python -m venv venv && source venv/bin/activate
pip install openai-agents pydantic
mkdir -p src
touch src/__init__.py src/exercises.py src/planner.py
touch src/progress.py src/nutrition.py src/agent.py

Step 1: Exercise Database

# src/exercises.py
from pydantic import BaseModel

class Exercise(BaseModel):
    name: str
    muscle_group: str
    equipment: str  # "none", "dumbbells", "barbell", "machine"
    difficulty: str  # "beginner", "intermediate", "advanced"
    calories_per_set: float

EXERCISES: list[Exercise] = [
    Exercise(name="Push-ups", muscle_group="chest",
             equipment="none", difficulty="beginner", calories_per_set=8),
    Exercise(name="Bench Press", muscle_group="chest",
             equipment="barbell", difficulty="intermediate", calories_per_set=10),
    Exercise(name="Squats", muscle_group="legs",
             equipment="none", difficulty="beginner", calories_per_set=10),
    Exercise(name="Barbell Squats", muscle_group="legs",
             equipment="barbell", difficulty="intermediate", calories_per_set=14),
    Exercise(name="Deadlifts", muscle_group="back",
             equipment="barbell", difficulty="advanced", calories_per_set=15),
    Exercise(name="Pull-ups", muscle_group="back",
             equipment="none", difficulty="intermediate", calories_per_set=9),
    Exercise(name="Dumbbell Rows", muscle_group="back",
             equipment="dumbbells", difficulty="beginner", calories_per_set=8),
    Exercise(name="Shoulder Press", muscle_group="shoulders",
             equipment="dumbbells", difficulty="intermediate", calories_per_set=9),
    Exercise(name="Plank", muscle_group="core",
             equipment="none", difficulty="beginner", calories_per_set=5),
    Exercise(name="Lunges", muscle_group="legs",
             equipment="none", difficulty="beginner", calories_per_set=8),
    Exercise(name="Bicep Curls", muscle_group="arms",
             equipment="dumbbells", difficulty="beginner", calories_per_set=6),
    Exercise(name="Tricep Dips", muscle_group="arms",
             equipment="none", difficulty="intermediate", calories_per_set=7),
    Exercise(name="Romanian Deadlifts", muscle_group="legs",
             equipment="dumbbells", difficulty="intermediate", calories_per_set=12),
    Exercise(name="Lat Pulldown", muscle_group="back",
             equipment="machine", difficulty="beginner", calories_per_set=8),
    Exercise(name="Leg Press", muscle_group="legs",
             equipment="machine", difficulty="beginner", calories_per_set=11),
]

def find_exercises(
    muscle_group: str | None = None,
    equipment: list[str] | None = None,
    difficulty: str | None = None,
) -> list[Exercise]:
    results = EXERCISES
    if muscle_group:
        results = [
            e for e in results
            if e.muscle_group.lower() == muscle_group.lower()
        ]
    if equipment:
        equip_lower = [eq.lower() for eq in equipment]
        results = [
            e for e in results
            if e.equipment.lower() in equip_lower
        ]
    if difficulty:
        results = [
            e for e in results
            if e.difficulty.lower() == difficulty.lower()
        ]
    return results

Step 2: Workout Plan Generator

# src/planner.py
from src.exercises import find_exercises, Exercise

SPLIT_TEMPLATES = {
    "full_body": ["chest", "back", "legs", "shoulders", "core", "arms"],
    "upper_lower": {
        "upper": ["chest", "back", "shoulders", "arms"],
        "lower": ["legs", "core"],
    },
    "push_pull_legs": {
        "push": ["chest", "shoulders"],
        "pull": ["back", "arms"],
        "legs": ["legs", "core"],
    },
}

def generate_workout(
    split_type: str,
    day_name: str,
    equipment: list[str],
    difficulty: str,
    exercises_per_group: int = 2,
) -> str:
    if split_type == "full_body":
        groups = SPLIT_TEMPLATES["full_body"]
    else:
        template = SPLIT_TEMPLATES.get(split_type, {})
        groups = template.get(day_name.lower(), [])

    if not groups:
        return f"Invalid split/day combination: {split_type}/{day_name}"

    lines = [f"=== {day_name.upper()} DAY ({split_type}) ===\n"]
    total_calories = 0.0

    for group in groups:
        exercises = find_exercises(group, equipment, difficulty)
        if not exercises:
            exercises = find_exercises(group, ["none"], None)

        selected = exercises[:exercises_per_group]
        for ex in selected:
            sets, reps = _get_sets_reps(difficulty)
            cals = ex.calories_per_set * sets
            total_calories += cals
            lines.append(
                f"  {ex.name} ({ex.muscle_group})"
            )
            lines.append(
                f"    {sets} sets x {reps} reps | "
                f"~{cals:.0f} cal | Equipment: {ex.equipment}"
            )

    lines.append(f"\nEstimated calories burned: {total_calories:.0f}")
    return "\n".join(lines)

def _get_sets_reps(difficulty: str) -> tuple[int, int]:
    if difficulty == "beginner":
        return 3, 10
    elif difficulty == "intermediate":
        return 4, 10
    else:
        return 4, 8

Step 3: Progress Tracker

# src/progress.py
from datetime import datetime
from pydantic import BaseModel

class WorkoutLog(BaseModel):
    date: str
    exercises: dict[str, dict]  # name -> {sets, reps, weight}
    duration_min: int
    notes: str = ""

class ProgressTracker:
    def __init__(self):
        self.logs: list[WorkoutLog] = []

    def log_workout(
        self, exercises: dict[str, dict],
        duration: int, notes: str = "",
    ) -> str:
        log = WorkoutLog(
            date=datetime.now().strftime("%Y-%m-%d"),
            exercises=exercises,
            duration_min=duration,
            notes=notes,
        )
        self.logs.append(log)
        return f"Logged workout: {len(exercises)} exercises, {duration}min"

    def get_summary(self, last_n: int = 5) -> str:
        if not self.logs:
            return "No workouts logged yet."
        recent = self.logs[-last_n:]
        lines = [f"Last {len(recent)} workouts:\n"]
        for log in recent:
            lines.append(f"Date: {log.date} | Duration: {log.duration_min}min")
            for name, details in log.exercises.items():
                lines.append(
                    f"  {name}: {details.get('sets', 0)}x"
                    f"{details.get('reps', 0)} @ "
                    f"{details.get('weight', 'bodyweight')}"
                )
            if log.notes:
                lines.append(f"  Notes: {log.notes}")
            lines.append("")
        total_sessions = len(self.logs)
        total_time = sum(l.duration_min for l in self.logs)
        lines.append(
            f"Total: {total_sessions} sessions, {total_time} minutes"
        )
        return "\n".join(lines)

progress = ProgressTracker()

Step 4: Nutrition Advisor

# src/nutrition.py

MEAL_SUGGESTIONS = {
    "muscle_gain": {
        "breakfast": "4 eggs, oatmeal with banana, protein shake (600 cal, 45g protein)",
        "lunch": "Grilled chicken breast, brown rice, steamed broccoli (650 cal, 50g protein)",
        "dinner": "Salmon fillet, sweet potato, mixed greens (600 cal, 40g protein)",
        "snacks": "Greek yogurt, almonds, protein bar (400 cal, 30g protein)",
    },
    "fat_loss": {
        "breakfast": "2 eggs, spinach, whole wheat toast (350 cal, 25g protein)",
        "lunch": "Turkey wrap with veggies, side salad (400 cal, 35g protein)",
        "dinner": "Grilled fish, quinoa, roasted vegetables (450 cal, 35g protein)",
        "snacks": "Apple with peanut butter, cottage cheese (250 cal, 15g protein)",
    },
    "maintenance": {
        "breakfast": "3 eggs, toast with avocado, fruit (500 cal, 30g protein)",
        "lunch": "Chicken stir fry with rice and vegetables (550 cal, 40g protein)",
        "dinner": "Lean steak, baked potato, green beans (550 cal, 40g protein)",
        "snacks": "Trail mix, banana, protein shake (350 cal, 25g protein)",
    },
}

def get_meal_plan(goal: str) -> str:
    goal_key = goal.lower().replace(" ", "_")
    plan = MEAL_SUGGESTIONS.get(goal_key)
    if not plan:
        available = ", ".join(MEAL_SUGGESTIONS.keys())
        return f"Unknown goal. Available: {available}"
    lines = [f"=== Meal Plan ({goal}) ===\n"]
    total_cal = 0
    for meal, description in plan.items():
        lines.append(f"  {meal.title()}: {description}")
        cal_str = description.split("(")[1].split(" cal")[0]
        total_cal += int(cal_str)
    lines.append(f"\nEstimated daily total: ~{total_cal} calories")
    return "\n".join(lines)

Step 5: Assemble the Agent

# src/agent.py
import asyncio
import json
from agents import Agent, Runner, function_tool
from src.planner import generate_workout
from src.progress import progress
from src.nutrition import get_meal_plan

@function_tool
def create_workout(
    split_type: str = "full_body",
    day_name: str = "full_body",
    equipment: str = "none",
    difficulty: str = "beginner",
) -> str:
    """Generate a workout plan."""
    equip_list = [e.strip() for e in equipment.split(",")]
    return generate_workout(
        split_type, day_name, equip_list, difficulty,
    )

@function_tool
def log_exercise(
    exercises_json: str, duration_min: int, notes: str = "",
) -> str:
    """Log a completed workout. exercises_json format:
    {"Push-ups": {"sets": 3, "reps": 10, "weight": "bodyweight"}}"""
    exercises = json.loads(exercises_json)
    return progress.log_workout(exercises, duration_min, notes)

@function_tool
def view_progress(last_n: int = 5) -> str:
    """View recent workout history."""
    return progress.get_summary(last_n)

@function_tool
def get_nutrition_plan(goal: str) -> str:
    """Get a meal plan for a fitness goal."""
    return get_meal_plan(goal)

fitness_agent = Agent(
    name="Fitness Coach",
    instructions="""You are a personal fitness coaching agent.
Generate workouts based on the user's equipment, experience,
and goals. Track their progress and provide nutrition advice.
Always encourage consistency and progressive overload.
Warn about proper form for advanced exercises.""",
    tools=[create_workout, log_exercise, view_progress, get_nutrition_plan],
)

async def main():
    result = await Runner.run(
        fitness_agent,
        "I'm a beginner with dumbbells at home. Create a "
        "full body workout and suggest a meal plan for muscle gain.",
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

FAQ

How does progressive overload work with this agent?

Add a get_personal_records tool that retrieves the user's best weight and reps for each exercise from the progress log. When generating new workouts, the planner checks these records and increases weight by 2.5 to 5 percent or adds one rep. This systematic progression is what drives muscle adaptation over time.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

flowchart TD
    START["Build a Fitness Coaching Agent: Workout Planning,…"] --> A
    A["Why Build a Fitness Coaching Agent"]
    A --> B
    B["Project Setup"]
    B --> C
    C["Step 1: Exercise Database"]
    C --> D
    D["Step 2: Workout Plan Generator"]
    D --> E
    E["Step 3: Progress Tracker"]
    E --> F
    F["Step 4: Nutrition Advisor"]
    F --> G
    G["Step 5: Assemble the Agent"]
    G --> H
    H["FAQ"]
    H --> DONE["Key Takeaways"]
    style START fill:#4f46e5,stroke:#4338ca,color:#fff
    style DONE fill:#059669,stroke:#047857,color:#fff

Can the agent adjust workouts based on soreness or injury?

Yes. Add a report_condition tool that takes a muscle group and severity level. The planner then excludes or substitutes exercises targeting that area. For example, if the user reports shoulder soreness, the agent replaces overhead presses with lateral raises or skips shoulder exercises entirely for that session.

How do I make the nutrition advice more precise?

Integrate a food database API like Nutritionix or USDA FoodData Central. Replace the static meal suggestions with calculated macronutrient plans based on the user's body weight, activity level, and goal. The agent can then generate meals that hit specific protein, carb, and fat targets rather than providing generic templates.


#FitnessCoaching #AIAgent #Python #WorkoutPlanning #Nutrition #AgenticAI #LearnAI #AIEngineering

Share
C

Written by

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

AI Interview Prep

7 AI Coding Interview Questions From Anthropic, Meta & OpenAI (2026 Edition)

Real AI coding interview questions from Anthropic, Meta, and OpenAI in 2026. Includes implementing attention from scratch, Anthropic's progressive coding screens, Meta's AI-assisted round, and vector search — with solution approaches.

Learn Agentic AI

Creating an AI Email Assistant Agent: Triage, Draft, and Schedule with Gmail API

Build an AI email assistant that reads your inbox, classifies urgency, drafts context-aware responses, and schedules sends using OpenAI Agents SDK and Gmail API.

Learn Agentic AI

Building a Multi-Agent Data Pipeline: Ingestion, Transformation, and Analysis Agents

Build a three-agent data pipeline with ingestion, transformation, and analysis agents that process data from APIs, CSVs, and databases using Python.

Learn Agentic AI

Building a Research Agent with Web Search and Report Generation: Complete Tutorial

Build a research agent that searches the web, extracts and synthesizes data, and generates formatted reports using OpenAI Agents SDK and web search tools.

Learn Agentic AI

OpenAI Agents SDK in 2026: Building Multi-Agent Systems with Handoffs and Guardrails

Complete tutorial on the OpenAI Agents SDK covering agent creation, tool definitions, handoff patterns between specialist agents, and input/output guardrails for safe AI systems.

Learn Agentic AI

LangGraph Agent Patterns 2026: Building Stateful Multi-Step AI Workflows

Complete LangGraph tutorial covering state machines for agents, conditional edges, human-in-the-loop patterns, checkpointing, and parallel execution with full code examples.