Finetuning
MicrosoftOfficialCreated by Microsoft
- Skill
- Tool integration
- Data and AI
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides the necessary procedures and tools for fine-tuning AI models on Microsoft Foundry. It supports three primary training methodologies: Supervised Fine-Tuning (SFT), Direct Preference Optimization (DPO), and Reinforcement Fine-Tuning (RFT). The skill encompasses the entire lifecycle, including dataset preparation, training job submission, monitoring, model evaluation, and deployment. It also includes specific guidance for agentic RFT, where models are trained to utilize external tools, and provides scripts for data validation, grader calibration, and resource management.
What it does
- Supports SFT, DPO, and RFT training methodologies.
- Provides data validation scripts for SFT, DPO, and RFT formats.
- Facilitates training job submission and monitoring on Microsoft Foundry.
- Enables calibration of RFT graders and pass thresholds.
- Offers tools for model evaluation and deployment via ARM REST API.
- Includes utilities for synthetic data generation and dataset quality scoring.
When to use it
- When you need to adapt a base model to specific tasks or domains.
- When preparing or validating training datasets for SFT, DPO, or RFT.
- When training models for agentic scenarios involving tool calling.
- When you need to evaluate and deploy a fine-tuned model.
- When troubleshooting training jobs or diagnosing poor model performance.
Advantages
- Standardized workflows for different training types.
- Includes built-in validation to prevent common data formatting errors.
- Supports complex agentic RFT training with tool integration.
- Provides robust scripts for monitoring and evaluating training progress.
- Offers clear guidance on grader design and hyperparameter tuning.
Usage examples
- “How do I validate my SFT dataset before training?”
- The agent will instruct you to use the python scripts/validate/validate_sft.py script on your data.jsonl file to ensure it meets the required format.
- “Submit an SFT training job using gpt-4.1-mini.”
- The agent will provide the command using scripts/submit_training.py with the appropriate flags for model, training file, validation file, and type.
- “How can I calibrate my RFT grader?”
- The agent will guide you to use scripts/calibrate_grader.py to find the optimal pass_threshold, aiming for a 25-50% failure rate on the base model.
- “Deploy my fine-tuned model after training.”
- The agent will provide the command to use scripts/deploy_model.py, specifying the model-id and a deployment name.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/microsoft/finetuning/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip finetuning.zip -d .agents/skills
Documentation by Microsoft
Fine-Tuning on Microsoft Foundry
Fine-tune models using SFT (supervised), DPO (preference), or RFT (reinforcement with graders). Covers dataset prep, training, deployment, and evaluation.
When to Use
Use this sub-skill when the user asks about:
Fine-tuning a model (SFT, DPO, or RFT)
Preparing, validating, or formatting training data
Submitting, monitoring, or diagnosing training jobs
Calibrating graders or pass thresholds for RFT
Deploying or evaluating a fine-tuned model
Choosing between training types (SFT vs DPO vs RFT)
Distillation, synthetic data generation, or dataset quality scoring
Large file uploads for training data
Cleaning up fine-tuning resources (files, deployments)
Do NOT use for: General model deployment without fine-tuning (use deploy-model), agent creation (use agents), prompt optimization without training (use prompt-optimizer).
Workflows
| Stage | Guide |
|---|---|
| Quick start | workflows/quickstart.md |
| Full pipeline | workflows/full-pipeline.md |
| Create data | workflows/dataset-creation.md |
| Iterate | workflows/iterative-training.md |
| Diagnose | workflows/diagnose-poor-results.md |
References
| Topic | File |
|---|---|
| SFT vs DPO vs RFT | references/training-types.md |
| Hyperparameters | references/hyperparameters.md |
| Data formats | references/dataset-formats.md |
| Grader design (RFT) | references/grader-design.md |
| Reward hacking | references/reward-hacking.md |
| Agentic RFT (tools) | references/agentic-rft.md |
| Deployment | references/deployment.md |
| Training curves | references/training-curves.md |
| Evaluation | references/evaluation.md |
| Vision fine-tuning | references/vision-fine-tuning.md |
| Large file uploads | references/large-file-uploads.md |
| Platform gotchas | references/platform-gotchas.md |
Scripts
| Script | Purpose |
|---|---|
scripts/submit_training.py | Submit SFT/DPO/RFT jobs |
scripts/monitor_training.py | Poll job until completion |
scripts/calibrate_grader.py | Find optimal RFT pass_threshold |
scripts/check_training.py | Analyze curves, list checkpoints |
scripts/deploy_model.py | Deploy via ARM REST API |
scripts/evaluate_model.py | LLM judge evaluation |
scripts/convert_dataset.py | Convert between SFT/DPO/RFT formats |
scripts/generate_distillation_data.py | Generate synthetic training data |
scripts/score_dataset.py | Quality scoring on training data |
scripts/cleanup.py | Delete old files and deployments |
scripts/validate/ | Data validators (SFT, DPO, RFT) + stats |
Rules
Always baseline first — evaluate the base model before fine-tuning
Validate data before submitting — run
scripts/validate/validate_sft.pyCalibrate RFT graders — target 25-50% failure rate on the base model
Evaluate checkpoints — don't blindly deploy the final one
Measure token cost alongside accuracy when comparing models
Quick Reference
| Task | Command |
|---|---|
| Validate SFT data | python scripts/validate/validate_sft.py data.jsonl |
| Submit SFT job | python scripts/submit_training.py --model gpt-4.1-mini --training-file train.jsonl --validation-file val.jsonl --type sft |
| Monitor job | python scripts/monitor_training.py --job-id ftjob-xxx |
| Analyze curves | python scripts/check_training.py --job-id ftjob-xxx |
| Deploy model | python scripts/deploy_model.py --model-id ft:gpt-4.1-mini:... --name my-eval |
| Evaluate model | python scripts/evaluate_model.py --deployment-name my-eval --test-file test.jsonl |
Error Handling
| Error | Cause | Fix |
|---|---|---|
| "API version not supported" | Older openai SDK on /v1/ endpoint | Upgrade to openai>=1.0 |
| "does not support fine-tuning with Standard TrainingType" | OSS model needs globalStandard | Use --use-rest flag or script auto-falls back |
| Job stuck in post-training eval | Under-provisioned tool endpoint (RFT) | Scale to S2+, enable Always On |
| "DeploymentNotReady" after ARM succeeds | ARM/data-plane race condition | Delete and recreate deployment, wait 5 min |
| Content safety block at deployment | PII-dense training data | Remove problematic document types |
Files
- ATTRIBUTION.mdAdded by AutoGineer998 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md5.4 KiB
- references/agentic-rft.md3.2 KiB
- references/dataset-formats.md3.7 KiB
- references/deployment.md3.4 KiB
- references/evaluation.md5.8 KiB
- references/grader-design.md3.4 KiB
- references/hyperparameters.md3.2 KiB
- references/large-file-uploads.md927 B
- references/platform-gotchas.md2.0 KiB
- references/reward-hacking.md2.9 KiB
- references/training-curves.md4.1 KiB
- references/training-types.md3.1 KiB
- references/vision-fine-tuning.md4.1 KiB
- scripts/calibrate_grader.pyScript9.3 KiB
- scripts/check_training.pyScript7.8 KiB
- scripts/cleanup.pyScript9.5 KiB
- scripts/common.pyScript7.6 KiB
- scripts/convert_dataset.pyScript10.7 KiB
- scripts/deploy_model.pyScript9.4 KiB
- scripts/evaluate_model.pyScript11.4 KiB
- scripts/generate_distillation_data.pyScript10.1 KiB
- scripts/monitor_training.pyScript5.7 KiB
- scripts/score_dataset.pyScript7.7 KiB
- scripts/submit_training.pyScript10.8 KiB
- scripts/validate/__init__.pyScript305 B
- scripts/validate/data_stats.pyScript6.2 KiB
- scripts/validate/validate_dpo.pyScript3.7 KiB
- scripts/validate/validate_rft.pyScript8.6 KiB
- scripts/validate/validate_sft.pyScript4.5 KiB
- workflows/dataset-creation.md3.1 KiB
- workflows/diagnose-poor-results.md2.3 KiB
- workflows/full-pipeline.md3.1 KiB
- workflows/iterative-training.md3.1 KiB
- workflows/quickstart.md4.5 KiB
scripts/deploy_model.py
# /// script
# dependencies = [
# "openai>=1.0",
# "requests",
# "azure-identity",
# ]
# ///
"""
deploy_model.py — Deploy fine-tuned models on Microsoft Foundry via ARM REST API.
Supports all model families with correct format/SKU mapping.
Usage:
python deploy_model.py --model-id "ft:gpt-4.1-mini-2025-04-14:..." --name "my-ft-eval" --capacity 100
python deploy_model.py --model-id "ft:gpt-oss-20b:..." --name "oss-eval" --format Microsoft --sku GlobalStandard
python deploy_model.py --delete --name "my-ft-eval"
python deploy_model.py --list
"""
import os
import subprocess
import sys
try:
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
except (AttributeError, OSError):
pass # Stream not reconfigurable (older Python or non-tty); default encoding is fine
import time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common import HelpOnErrorParser
import requests
def _safe_error_msg(resp):
"""Extract error message from response, handling non-JSON bodies (HTML 502/503)."""
try:
return resp.json().get("error", {}).get("message", resp.text[:200])
except (ValueError, KeyError):
return resp.text[:200] if resp.text else "Unknown error"
# Default Azure resource coordinates — override with env vars or args
DEFAULT_SUB = os.environ.get("AZURE_SUBSCRIPTION_ID", "")
DEFAULT_RG = os.environ.get("AZURE_RESOURCE_GROUP", "")
DEFAULT_ACCOUNT = os.environ.get("AZURE_COGSERVICES_ACCOUNT", "")
AZ_CLI = os.environ.get("AZ_CLI_PATH")
if not AZ_CLI:
import shutil
AZ_CLI = shutil.which("az")
if not AZ_CLI:
# Common Windows paths
for candidate in [
r"C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.cmd",
r"C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin\az.cmd",
]:
if os.path.exists(candidate):
AZ_CLI = candidate
break
if not AZ_CLI:
AZ_CLI = "az" # last resort, hope it's on PATH
# Model format auto-detection rules
FORMAT_RULES = [
(lambda m: "oss-20b" in m.lower() or "oss20b" in m.lower(), "Microsoft", "GlobalStandard"),
(lambda m: "ministral" in m.lower() or "mistral" in m.lower(), "Mistral AI", "GlobalStandard"),
(lambda m: "llama" in m.lower() or "meta" in m.lower(), "Meta", "GlobalStandard"),
(lambda m: "qwen" in m.lower() or "alibaba" in m.lower(), "Alibaba", "GlobalStandard"),
(lambda m: True, "OpenAI", "Standard"), # Default fallback
]
def get_arm_token():
"""Get a fresh ARM token from Azure CLI."""
result = subprocess.run(
[AZ_CLI, "account", "get-access-token", "--query", "accessToken", "-o", "tsv"],
capture_output=True, text=True,
)
token = result.stdout.strip()
if not token:
raise RuntimeError(f"Failed to get ARM token: {result.stderr}")
return token
def arm_url(sub, rg, account, deploy_name=None):
"""Build the ARM REST API URL."""
base = (f"https://management.azure.com/subscriptions/{sub}"
f"/resourceGroups/{rg}"
f"/providers/Microsoft.CognitiveServices/accounts/{account}"
f"/deployments")
if deploy_name:
base += f"/{deploy_name}"
return base + "?api-version=2024-10-01"
def detect_format(model_id):
"""Auto-detect model format and SKU from model ID."""
for check, fmt, sku in FORMAT_RULES:
if check(model_id):
return fmt, sku
return "OpenAI", "Standard"
def create_deployment(sub, rg, account, name, model_id, model_format, sku, capacity):
"""Create a deployment via ARM REST API."""
token = get_arm_token()
url = arm_url(sub, rg, account, name)
body = {
"sku": {"name": sku, "capacity": capacity},
"properties": {
"model": {
"format": model_format,
"name": model_id,
"version": "1",
}
},
}
resp = requests.put(url, headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}, json=body, timeout=(10, 120))
if resp.status_code in (200, 201):
print(f"✅ Deployment '{name}' created (format={model_format}, sku={sku}, capacity={capacity})")
return True
else:
print(f"❌ Deployment failed ({resp.status_code}): {_safe_error_msg(resp)}")
return False
def wait_for_deployment(sub, rg, account, name, timeout=600, poll_interval=15):
"""Wait for deployment to reach 'Succeeded' state."""
url = arm_url(sub, rg, account, name)
start = time.time()
while time.time() - start < timeout:
token = get_arm_token()
try:
resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, timeout=(10, 60))
except requests.exceptions.RequestException as e:
print(f" ⚠️ Polling error: {e} — retrying in {poll_interval}s")
time.sleep(poll_interval)
continue
if resp.status_code == 200:
try:
state = resp.json().get("properties", {}).get("provisioningState", "Unknown")
except (ValueError, KeyError):
state = "Unknown"
print(f" Status: {state}")
if state == "Succeeded":
return True
if state in ("Failed", "Canceled"):
print(f" Deployment {state}.")
return False
time.sleep(poll_interval)
print(f" Timed out after {timeout}s")
return False
def delete_deployment(sub, rg, account, name):
"""Delete a deployment."""
token = get_arm_token()
url = arm_url(sub, rg, account, name)
resp = requests.delete(url, headers={"Authorization": f"Bearer {token}"}, timeout=(10, 60))
if resp.status_code in (200, 202, 204):
print(f"✅ Deployment '{name}' deleted.")
else:
print(f"❌ Delete failed ({resp.status_code}): {_safe_error_msg(resp)}")
def list_deployments(sub, rg, account):
"""List all deployments."""
token = get_arm_token()
url = arm_url(sub, rg, account)
resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, timeout=(10, 60))
if resp.status_code != 200:
print(f"❌ Failed to list deployments ({resp.status_code}): {_safe_error_msg(resp)}")
return
try:
deployments = resp.json().get("value", [])
except (ValueError, KeyError):
print(f"❌ Failed to parse deployment list: {resp.text[:200]}")
return
if not deployments:
print("No deployments found.")
return
print(f"{'Name':<40} {'Model':<40} {'SKU':<15} {'State':<15}")
print("─" * 110)
for d in deployments:
name = d.get("name", "?")
model = d.get("properties", {}).get("model", {}).get("name", "?")
sku = d.get("sku", {}).get("name", "?")
state = d.get("properties", {}).get("provisioningState", "?")
print(f"{name:<40} {model:<40} {sku:<15} {state:<15}")
def main():
parser = HelpOnErrorParser(description="Deploy fine-tuned models on Microsoft Foundry")
parser.add_argument("--sub", default=DEFAULT_SUB, help="Azure subscription ID")
parser.add_argument("--rg", default=DEFAULT_RG, help="Resource group")
parser.add_argument("--account", default=DEFAULT_ACCOUNT, help="Cognitive Services account")
# Actions
parser.add_argument("--list", action="store_true", help="List all deployments")
parser.add_argument("--delete", action="store_true", help="Delete a deployment")
parser.add_argument("--wait", action="store_true", help="Wait for deployment to succeed")
# Deployment config
parser.add_argument("--name", help="Deployment name (max 64 chars, alphanumeric + hyphens)")
parser.add_argument("--model-id", help="Fine-tuned model ID (e.g., ft:gpt-4.1-mini:...)")
parser.add_argument("--format", help="Model format (auto-detected if not specified)")
parser.add_argument("--sku", help="SKU name (auto-detected if not specified)")
parser.add_argument("--capacity", type=int, default=100, help="TPM capacity in thousands")
args = parser.parse_args()
if not all([args.sub, args.rg, args.account]):
print("Error: Set --sub/--rg/--account or AZURE_SUBSCRIPTION_ID/AZURE_RESOURCE_GROUP/AZURE_COGSERVICES_ACCOUNT")
sys.exit(1)
if args.list:
list_deployments(args.sub, args.rg, args.account)
return
if not args.name:
print("Error: --name required for create/delete/wait")
sys.exit(1)
if args.delete:
delete_deployment(args.sub, args.rg, args.account, args.name)
return
if args.wait and not args.model_id:
# Wait-only mode: poll an existing deployment
success = wait_for_deployment(args.sub, args.rg, args.account, args.name)
sys.exit(0 if success else 1)
if not args.model_id:
print("Error: --model-id required for create")
sys.exit(1)
# Auto-detect format/SKU if not specified
model_format = args.format
sku = args.sku
if not model_format or not sku:
auto_fmt, auto_sku = detect_format(args.model_id)
model_format = model_format or auto_fmt
sku = sku or auto_sku
print(f"Auto-detected: format={model_format}, sku={sku}")
created = create_deployment(args.sub, args.rg, args.account, args.name,
args.model_id, model_format, sku, args.capacity)
if args.wait and created:
wait_for_deployment(args.sub, args.rg, args.account, args.name)
if __name__ == "__main__":
main()
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/azure-skills/skills/microsoft-foundry/finetuning · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from .github/plugins/azure-skills/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- microsoft/azure-skills
AutoGineer is not affiliated with or endorsed by Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download finetuning.zip