Model Evaluation
AWS LabsOfficialPublished by AWS Labs
- Skill
- Workflow
- Software development
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured workflow for evaluating SageMaker models, supporting both open-source and Nova models. It guides users through selecting an evaluation strategy—either LLM-as-Judge for qualitative assessment or a Custom Scorer for programmatic validation—and generates the necessary Python code or Jupyter notebooks to execute these benchmarks within the SageMaker environment.
What it does
- Determines the appropriate evaluation type based on model architecture and user requirements.
- Validates compatibility between the chosen evaluation method and the target model.
- Generates Python scripts or Jupyter notebooks for model evaluation.
- Supports LLM-as-Judge for OSS models.
- Facilitates the creation and registration of Lambda-based custom scorers.
- Integrates with SageMaker SDK for streamlined model benchmarking.
When to use it
- When you need to benchmark the performance of a fine-tuned or base SageMaker model.
- When you want to use an LLM to grade model outputs.
- When you require custom programmatic logic to score model responses.
- When you need to compare different model versions or configurations.
Advantages
- Ensures consistent evaluation workflows through guided steps.
- Prevents incompatible configurations by validating model types early.
- Automates the generation of boilerplate code for evaluation tasks.
- Provides flexibility by supporting both automated LLM grading and custom logic.
Usage examples
- “Evaluate my model.”
- The agent will initiate the evaluation workflow, asking you to choose between LLM-as-Judge or Custom Scorer, and verify your model type.
- “How did my model perform?”
- The agent will guide you through setting up a benchmark or custom scorer to generate performance metrics.
- “Run a benchmark on my fine-tuned Llama model.”
- The agent will confirm the model type and guide you through the LLM-as-Judge or Custom Scorer setup process.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/awslabs/model-evaluation/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip model-evaluation.zip -d .agents/skills
Documentation by AWS Labs
Model Evaluation
Generate code that evaluates a SageMaker model.
Prerequisites
The SDK environment has been verified (SDK version, region, execution role). If not done, activate the
sdk-getting-startedskill first.
Principles
One thing at a time. Each response advances exactly one decision. Never combine multiple questions in a single turn.
Confirm before proceeding. Wait for the user to agree before moving to the next step.
Don't read files until you need them. Only read reference files when you've reached the step that requires them.
Don't ask what you already know. If the answer is in conversation history, workflow_state.json, plan.md, or any file you've already read — use it. Confirm if unsure, but don't re-ask.
No narration. Share outcomes and ask questions. Keep responses short.
No repetition. If you said something before a tool call, don't repeat it after.
Scope
This skill supports the evaluation feature for SageMaker Serverless Model Customization. It can evaluate any base or fine-tuned model supported by SageMaker serverless model customization — both OSS models (Llama, Mistral, Qwen, etc.) and Nova models.
Tell the user when the skill is activated:
"I can help evaluate any base or fine-tuned model supported by SageMaker serverless model customization."
If the user requests help evaluating a model that isn't supported by SageMaker serverless model customization, explain that it is not supported by this skill.
Evaluation Types
There are two evaluation types:
LLM-as-Judge — an LLM grades your model's responses. (OSS models only — not supported for Nova.)
Custom Scorer — programmatic evaluation via Lambda function (includes built-in math and code scorers). Works with both OSS and Nova models.
Workflow
Step 1: Determine evaluation type
Do you already know which evaluation type to use?
Check conversation history, plan.md, workflow_state.json, or anything else you've already read.
If yes: confirm with the user.
"It sounds like you want to run [evaluation type]. Is that right?"
⏸ Wait for confirmation. If confirmed → go to Step 2.
If no: ask.
"What kind of evaluation would you like to run? I support:
LLM-as-Judge — an LLM grades your model's responses
Custom Scorer — programmatic scoring (math, code, or your own logic)
Pick one, or say 'help me decide' if you're not sure."
⏸ Wait for user.
If user picks one → go to Step 2.
If user indicates uncertainty, by saying something like "help me decide," "whatever you think," "I'm not sure" → read
references/evaluation-type-guide.mdand follow its instructions. It will guide the user to a choice and then return here. You MUST NEVER make a recommendation to the user on eval type without readingreferences/evaluation-type-guide.md.
Step 2: Validate and hand off to evaluation workflow
Before reading the reference file, validate that the chosen evaluation type is compatible with the user's situation. You may already know these answers from conversation context — don't ask if you don't need to.
LLM-as-Judge validation
What model type are we evaluating? LLM-as-Judge is not supported for Nova models. To determine model type (if you don't already know it):
If you have the training job name or ARN, use the AWS MCP tool
list-tagson the training job ARN and look for thesagemaker-studio:jumpstart-model-idtag. Contains "nova" → Nova. Anything else → OSS.If you have a Model Package ARN, use the AWS MCP tool
describe-model-packageand check the model description or source tags.If neither is available, ask the user.
Does the user have an evaluation dataset? LLM-as-Judge requires one.
Custom Scorer validation
Does the user have an evaluation dataset? Custom Scorer requires one. (Works with both OSS and Nova models, though for Nova only custom lambdas are supported.)
If validation fails, tell the user which requirement(s) aren't met and offer alternatives:
"[Evaluation type] won't work because [reason]."
If the failure reason was lack of an eval dataset, there's nothing we can do. Inform the user:
"Unfortunately all of the supported eval types require an eval dataset. I can't help you with model evaluation."
If the failure reason is something else, offer to help them pick a different evaluation type.
⏸ Wait for user.
If they say they do want help choosing a different eval type → read references/evaluation-type-guide.md.
If validation passes, read the corresponding reference file:
| User chose | Read |
|---|---|
| LLM-as-Judge | references/llmaaj-evaluation.md |
| Custom Scorer | references/custom-scorer-evaluation.md |
Follow the reference file's instructions from the beginning.
Files
- ATTRIBUTION.mdAdded by AutoGineer916 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer89 B
- SKILL.md5.2 KiB
- code_templates/custom_scorer_evaluator.pyScript2.5 KiB
- code_templates/llmaaj_evaluator.pyScript2.4 KiB
- references/code_output_guide.md3.3 KiB
- references/create-reward-function.md3.0 KiB
- references/custom-lambda-scorer.md4.4 KiB
- references/custom-scorer-evaluation.md10.2 KiB
- references/evaluation-type-guide.md8.0 KiB
- references/llmaaj-builtin-evaluation.md5.0 KiB
- references/llmaaj-custom-evaluation.md2.3 KiB
- references/llmaaj-evaluation.md13.7 KiB
- references/supported-judge-models.md2.5 KiB
- scripts/nova_reward_function_source_template.pyScript13.0 KiB
- scripts/reward_function_source_template.pyScript9.3 KiB
- scripts/validate_custom_metrics.pyScript3.6 KiB
scripts/reward_function_source_template.py
"""
Provide your custom reward function code below. Learn about the available libraries and templates that you can use
at: https://docs.aws.amazon.com/sagemaker/latest/dg/customize-model.html.
- You must add your evaluation logic in the reward_function() function
- Do not remove the lambda_handler() function or modify its schema as it is required to create the reward function
"""
import json # For JSON parsing - adjust imports based on your use case
import re # For pattern matching and validation
from typing import Dict, Any, List, Optional # For type hints
# Add any other imports your use case requires
# ========================================================================================
# NOTE: INITIAL SUGGESTION ONLY - MUST BE CUSTOMIZED
#
# YOU MUST:
# 1. Review and update each section per YOUR use case
# 2. Customize the logic for YOUR SPECIFIC requirements
# 3. Replace example values (field names, thresholds, etc.) with your actual values
# 4. Test thoroughly before using
#
# DO NOT use this code as-is. It will not work until you uncomment and customize it.
# =========================================================================================
# =========================================================================================
# SECTION 1: Helper function 1
# =========================================================================================
# TODO: UPDATE or REMOVE the helper function as per YOUR use case
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
def extract_number(text: str) -> Optional[float]:
"""
Extract numerical answer from text.
Looks for numbers after answer keywords, or returns the last number found.
Args:
text: Text containing a numerical answer
Returns:
Extracted number as float, or None if no number found
"""
if not text:
return None
# Try to find numbers after common answer keywords
answer_patterns = [
r'(?:equals|is|answer is|result is|=)\s*(-?\d+\.?\d*)',
r'(?:answer|result|solution):\s*(-?\d+\.?\d*)',
]
for pattern in answer_patterns:
match = re.search(pattern, text, re.IGNORECASE)
if match:
try:
return float(match.group(1))
except ValueError:
pass
# Fallback: find all numbers and return the last one (likely the answer)
pattern = r'-?\d+\.?\d*'
matches = re.findall(pattern, text)
if matches:
try:
return float(matches[-1]) # Return last number instead of first
except ValueError:
return None
return None
# =========================================================================================
# SECTION 2: Helper function 2
# =========================================================================================
# TODO: UPDATE or REMOVE the helper function as per YOUR use case
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
def compute_reasoning_quality(response: str) -> float:
"""
Compute reasoning quality score based on response characteristics.
This is a simple heuristic - customize based on your needs.
Args:
response: The model's response text
Returns:
Quality score between 0.0 and 1.0
"""
if not response:
return 0.0
score = 0.0
# Check for reasoning indicators (customize these for your use case)
reasoning_indicators = [
'because', 'therefore', 'thus', 'since', 'so',
'first', 'second', 'then', 'finally',
'step', 'calculate', 'compute', 'equals'
]
response_lower = response.lower()
# Award points for reasoning indicators (max 0.55)
indicator_count = sum(1 for indicator in reasoning_indicators if indicator in response_lower)
score += min(indicator_count * 0.11, 0.55)
# Award points for response length (indicates detailed reasoning, max 0.25)
if len(response) > 30:
score += 0.05
if len(response) > 60:
score += 0.1
if len(response) > 120:
score += 0.1
# Award points for structured response (max 0.2)
if '\n' in response or '.' in response:
score += 0.2
return min(score, 1.0)
# =========================================================================================
# SECTION 3: Sample reward function
# =========================================================================================
# TODO: UPDATE or REMOVE the reward function as per YOUR use case
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
def reward_function(sample: Dict[str, Any], index: int) -> Dict[str, Any]:
"""
Args:
sample: Dictionary containing messages and reference_answer
index: Sample index in batch
Returns:
Dictionary with reward scores and metrics
"""
# ========================================================================
# SECTION 4: Parse input
# ========================================================================
# TODO: UPDATE logic to parse the input as per YOUR use case
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
#
# The evaluation framework sends each sample with these fields:
# model_response: str — the model's generated text
# query: str — the original prompt sent to the model
# response: str — ground truth from the dataset
# reference_answer: dict {"text": str} OR str — ground truth (type varies)
# id: str — unique sample identifier
response = sample.get('model_response', '')
question = sample.get('query', '')
# reference_answer may be a dict or a plain string — handle both
ref_answer = sample.get('reference_answer', '')
if isinstance(ref_answer, dict):
reference_answer = ref_answer.get('text', '') or sample.get('response', '')
else:
reference_answer = ref_answer or sample.get('response', '')
# Extract numerical answers
predicted = extract_number(response)
expected = extract_number(reference_answer)
# Compute metrics
exact_match = 0.0
answer_present = 0.0
reasoning_quality = compute_reasoning_quality(response)
if predicted is not None and expected is not None:
exact_match = 1.0 if abs(predicted - expected) < 1e-6 else 0.0
answer_present = 1.0
# ========================================================================
# SECTION 5: Compute reward scores
# ========================================================================
# TODO: UPDATE logic to compute aggregate score
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
# Aggregate reward computation
aggregate_reward = 0.7 * exact_match + 0.3 * reasoning_quality
# ========================================================================
# SECTION 6: Form the metrics list
# ========================================================================
# TODO: UPDATE logic to compute metrics list
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
metrics = [
{
'name': 'exact_match',
'value': float(exact_match),
'type': 'Reward'
},
{
'name': 'answer_present',
'value': float(answer_present),
'type': 'Metric'
},
{
'name': 'reasoning_quality',
'value': float(reasoning_quality),
'type': 'Metric'
}
]
# ========================================================================
# SECTION 7: Return output
# ========================================================================
# TODO: UPDATE the return statement to return YOUR outout
# UPDATE the key before creating the evaluator
# Note the below lines of code are examples and will not work for your use case
# You MUST update them to match YOUR use case
return {
'id': str(sample.get('id', f'sample-{index:03d}')), # Use the id from the evaluation framework
'aggregate_reward_score': float(aggregate_reward),
'metrics_list': metrics
}
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
"""
AWS Lambda Handler for reward function.
The evaluation framework invokes this once per sample.
Event is a list containing a single sample dict.
"""
try:
# The framework sends a list with one sample: [{...}]
samples = event if isinstance(event, list) else [event]
sample = samples[0]
result = reward_function(sample, 0)
# body MUST be a JSON string (not a parsed list).
# The container rejects lists with:
# "Lambda response body must be a JSON string, got <class 'list'>"
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps([result])
}
except Exception as e:
return {
'statusCode': 400,
'body': json.dumps({"error": str(e)})
}
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
- Instructions that use the network
- 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
- Published by
- AWS Labs
- Publisher
- AWS Labs (Official)
- Source
- awslabs/agent-plugins · plugins/sagemaker-ai/skills/model-evaluation · commit fbefb37
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by AWS Labs.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download model-evaluation.zip