The problem isn't that you're too nice, it's that your first conversation doesn't have a consequence attached to it. A sub who shows up short-handed and gets a friendly heads up learns one thing: this costs me nothing. So the next three-day shortage is alredy baked in before you've finished your coffee. What fixed it for me was treating every correction as step one of a short, boring ladder. First time, private conversation with the foreman, plain words: you're two guys short, I need the full crew by tomorrow or I'm backfilling the labor and backcharging you. Then actually write it in your daily log. Date, who you talked to, what was said. Second time, same conversation but with your PM or their company guy copied on an email. Third time, you're just executing what you already told them, not punishing anyone.
The script for that first talk is short on purpose. 'Hey, you've been running three guys when your schedule shows six. I need the full crew tomorrow. If it happens again I'll cover the labor and it comes off your pay app.' Then shake the guy's hand and move on with your day. No raising your voice, no lecture, no dressing anyone down in front of the crew. Being calm and being serious can live in the same sentence.
And here's the part that matters most: follow through the first time it repeats. Your blowups happen because everyone has learned your words are optional. Once you backcharge a sub for one day of missing manpower, word travels, and you'll notice you need fewer conversations, not more. That's not being a jerk, that's just doing what the contract already says. Keep a small notebook or a notes file on your phone with every verbal correction, date and all. When a sub's PM says 'this is the first I'm hearing of it,' you can read it back to him. That one habit has saved me more arguments than anything else I do on site.