
workflow-metrics
Track, evaluate, and analyze multi-agent AI workflows using Azure AI Foundry cloud evaluations. Use
Workflow Metrics Skill
Folder Contents
| File | Type | Description |
|---|---|---|
SKILL.md | Documentation | Main skill documentation with 14 evaluation metrics, trajectory format, and decorator usage |
PRD.md | Documentation | Product Requirements Document for the skill |
.env.sample | Configuration | Sample environment variables (AZURE_AI_PROJECT_ENDPOINT) |
requirements.txt | Dependencies | Python package dependencies (azure-ai-projects, azure-identity) |
| scripts/workflow_metrics/ | ||
scripts/workflow_metrics/__init__.py | Module | Package initializer with exports for all classes and decorators |
scripts/workflow_metrics/trajectory.py | Logging | WorkflowTrajectoryLog class for multi-step agent trajectory logging in Foundry-compatible format |
scripts/workflow_metrics/metrics.py | Metrics | TrajectoryMetrics class for calculating metrics from trajectory files |
scripts/workflow_metrics/foundry_evals.py | Evaluator | FoundryEvalsClient for Azure AI Foundry cloud evaluation API with 14 evaluation metrics |
scripts/workflow_metrics/telemetry.py | Decorators | @track_workflow decorator and context functions (log_plan, log_step, auto_log_step) |
scripts/workflow_metrics/trajectory_manager.py | Manager | EvaluationManager singleton for daily metrics files management |
scripts/workflow_metrics/tool_definitions_helper.py | Helper | Generate tool definitions from Python functions with optional LLM enrichment |
scripts/workflow_metrics/utils.py | Utilities | Utility functions for workflow execution and common operations |
Track, evaluate, and analyze multi-agent AI workflows using Azure AI Foundry cloud evaluations.
Table of Contents
- Overview
- Module Location
- Quick Start: Cloud Eval Runs
- Available Cloud Metrics
- FoundryEvalsClient: Complete API Reference
- Creating Cloud Eval Runs Step-by-Step
- Data Format for Cloud Evaluations
- Metric Configurations
- Result Parsing
- Trajectory Logging (for Cloud Evaluation)
- Environment Variables
- Local Metrics (Only If Specifically Requested)
- Files Reference
- Related Resources
Overview
This skill provides cloud-based evaluation of AI agent workflows via Azure AI Foundry Evals API.
CRITICAL: All evaluations (13 LLM-as-judge metrics) MUST be run in Azure AI Foundry cloud. Local SDK-based evaluations are NOT supported. Always use
FoundryEvalsClientto create cloud eval runs.
The evaluation flow:
- Log trajectories in Foundry-compatible format
- Create an eval group with testing criteria (metrics)
- Submit an eval run with inline JSONL data
- Poll for completion and retrieve results
Module Location
.github/skills/workflow-metrics/scripts/workflow_metrics/
Quick Start: Cloud Eval Runs
from workflow_metrics import FoundryEvalsClient, WorkflowTrajectoryLog
# 1. Log a trajectory
trajectory = WorkflowTrajectoryLog(
user_query="What's the status of order W123456?",
system_prompt="You are a helpful customer service agent."
)
trajectory.log_plan("Check order status", ["get_order_status"])
trajectory.log_step(tool_name="get_order_status", arguments={"order_id": "W123456"}, result={"status": "shipped"})
trajectory.finalize("Your order W123456 has been shipped!")
# 2. Create cloud eval run
client = FoundryEvalsClient()
# Create eval group with metrics
eval_id = client.create_eval(
name="order_workflow_eval",
metrics=["coherence", "task_completion", "tool_call_accuracy"]
)
# Submit the workflow for cloud evaluation
workflow_data = trajectory.to_dict()
run_id = client.run_eval(
eval_id=eval_id,
items=[client._prepare_workflow_data(workflow_data)],
run_name="order_eval_run"
)
# Wait for cloud completion and get results
results = client.wait_for_completion(eval_id=eval_id, run_id=run_id)
print(results)
Available Cloud Metrics
All 13 metrics below run in Azure AI Foundry cloud as LLM-as-judge evaluations:
Text Quality Metrics
| Metric | Evaluator | Description | Required Fields |
|---|---|---|---|
coherence | builtin.coherence | Logical flow and consistency | query, response |
fluency | builtin.fluency | Grammar and readability | response |
relevance | builtin.relevance | Response relevance to query | query, response |
groundedness | builtin.groundedness | Factual accuracy based on context | response (context, query, tool_definitions optional) |
Agent Quality Metrics
| Metric | Evaluator | Description | Required Fields |
|---|---|---|---|
intent_resolution | builtin.intent_resolution | How well agent understood intent | query, response |
task_adherence | builtin.task_adherence | Following task requirements | query, response |
task_completion | builtin.task_completion | Successfully completing tasks | query, response |
response_completeness | builtin.response_completeness | Completeness of response | ground_truth, response |
Tool Usage Metrics
| Metric | Evaluator | Description | Required Fields |
|---|---|---|---|
tool_call_accuracy | builtin.tool_call_accuracy | Correct tool selection | query, tool_definitions |
tool_input_accuracy | builtin.tool_input_accuracy | Correct tool inputs | query, response, tool_definitions |
tool_output_utilization | builtin.tool_output_utilization | Effective use of tool results | query, response |
tool_selection | builtin.tool_selection | Appropriate tool choices | query, response, tool_definitions |
tool_success | builtin.tool_call_success | Tool execution success rate | response |
FoundryEvalsClient: Complete API Reference
The FoundryEvalsClient class in foundry_evals.py is the primary interface for cloud evaluations.
Initialization
from workflow_metrics import FoundryEvalsClient
client = FoundryEvalsClient(
endpoint=None, # Uses AZURE_AI_PROJECT_ENDPOINT env var
deployment_name=None, # Uses AZURE_AI_MODEL_DEPLOYMENT_NAME env var (default: "gpt-4.1")
use_reasoning_model=False, # Set True for o-series models
threshold=3.0, # Score threshold for pass/fail (1-5 scale)
matching_mode="in_order_match" # For task_navigation_efficiency
)
Core Methods
create_eval(name, metrics) -> str
Creates an evaluation group with specified metrics (testing criteria).
eval_id = client.create_eval(
name="my_agent_eval",
metrics=["coherence", "task_completion", "tool_call_accuracy"]
)
# Returns: "eval_abc123..."
What happens internally:
# Builds data_source_config schema based on required fields
data_source_config = {
"type": "custom",
"item_schema": {
"type": "object",
"properties": {...}, # Fields needed by selected metrics
"required": [...]
},
"include_sample_schema": True
}
# Builds testing_criteria for each metric
testing_criteria = [
{
"type": "azure_ai_evaluator",
"name": "coherence",
"evaluator_name": "builtin.coherence",
"initialization_parameters": {
"deployment_name": "gpt-4.1"
},
"data_mapping": {
"query": "{{item.query}}",
"response": "{{item.response}}"
}
},
# ... more criteria
]
eval_obj = client.evals.create(
name=name,
data_source_config=data_source_config,
testing_criteria=testing_criteria
)
run_eval(eval_id, items, run_name, metadata) -> str
Submits items for cloud evaluation using inline JSONL data.
run_id = client.run_eval(
eval_id=eval_id,
items=prepared_items, # List of workflow data dicts
run_name="my_run_001",
metadata={"source": "production", "version": "1.0"}
)
# Returns: "run_xyz789..."
Critical: The API call structure:
from openai.types.evals.create_eval_jsonl_run_data_source_param import (
CreateEvalJSONLRunDataSourceParam,
SourceFileContent,
SourceFileContentContent,
)
# Build content items (filter out internal tracking fields)
content_items = []
for item in items:
eval_item = {k: v for k, v in item.items() if not k.startswith("_")}
content_items.append(SourceFileContentContent(item=eval_item))
# Create the eval run with inline JSONL data
eval_run = client.evals.runs.create(
eval_id=eval_id,
name=run_name or f"run_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
metadata=metadata or {"source": "FoundryEvalsClient"},
data_source=CreateEvalJSONLRunDataSourceParam(
type="jsonl",
source=SourceFileContent(
type="file_content", # Inline content, not file reference
content=content_items, # List of SourceFileContentContent
),
),
)
wait_for_completion(eval_id, run_id, timeout, poll_interval) -> Dict
Polls until evaluation completes and retrieves results.
result = client.wait_for_completion(
eval_id=eval_id,
run_id=run_id,
timeout=300, # Max wait in seconds
poll_interval=5 # Seconds between polls
)
# Returns: {
# "status": "completed" | "failed" | "timeout",
# "output_items": [...],
# "report_url": "https://...",
# "eval_id": "...",
# "run_id": "..."
# }
evaluate_workflows(workflows, metrics, batch_size, timeout) -> Dict
High-level method that handles the full evaluation flow.
results = client.evaluate_workflows(
workflows=workflow_list, # List of workflow dicts with foundry_format
metrics=["coherence", "task_completion"],
batch_size=50, # Max workflows per eval run
timeout=300 # Max wait per batch
)
# Returns: {
# "workflow_run_id_1": {
# "coherence": {"score": 4.0, "pass": True, "reason": "..."},
# "task_completion": {"score": 5.0, "pass": True, "reason": "..."}
# },
# ...
# }
evaluate_single(workflow, metrics, timeout) -> Dict
Evaluate a single workflow.
result = client.evaluate_single(
workflow=workflow_dict,
metrics=["coherence", "tool_call_accuracy"],
timeout=60
)
Creating Cloud Eval Runs Step-by-Step
Step 1: Prepare Workflow Data
Workflows must have a foundry_format section:
workflow = {
"workflow_id": "wf_001",
"workflow_run_id": "run_001",
"user_request": "Check order status",
"foundry_format": {
"query": "What is the status of order W123?",
"response": [
{"role": "assistant", "content": "Let me check that for you."},
{"role": "assistant", "content": [
{
"type": "tool_call",
"tool_call_id": "tc_1",
"name": "get_order_status",
"arguments": {"order_id": "W123"}
}
]},
{"role": "tool", "content": [
{
"type": "tool_result",
"tool_call_id": "tc_1",
"tool_result": {"status": "shipped", "tracking": "1Z999"}
}
]},
{"role": "assistant", "content": "Your order W123 has been shipped! Tracking: 1Z999"}
],
"tool_definitions": [
{
"name": "get_order_status",
"description": "Get the status of an order",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "Order ID"}
},
"required": ["order_id"]
}
}
],
"ground_truth": ["get_order_status"] # Expected tool sequence
}
}
Step 2: Create Eval Group
client = FoundryEvalsClient()
# Select metrics to evaluate
metrics = [
"coherence", # Text quality
"task_completion", # Did agent complete the task?
"tool_call_accuracy", # Did agent call the right tools?
"tool_input_accuracy" # Were tool inputs correct?
]
eval_id = client.create_eval(
name=f"workflow_eval_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
metrics=metrics
)
print(f"Created eval group: {eval_id}")
Step 3: Submit Eval Run
# Prepare items for the API
prepared_items = [client._prepare_workflow_data(w) for w in workflows]
# Submit to Azure AI Foundry cloud
run_id = client.run_eval(
eval_id=eval_id,
items=prepared_items,
run_name="batch_1",
metadata={"environment": "production", "version": "2.0"}
)
print(f"Submitted eval run: {run_id}")
Step 4: Wait for Results
# Poll until completion (runs in Azure cloud)
completion = client.wait_for_completion(
eval_id=eval_id,
run_id=run_id,
timeout=300,
poll_interval=5
)
if completion["status"] == "completed":
print(f"Eval completed! Report: {completion['report_url']}")
# Parse results
results = client._parse_results(
completion["output_items"],
prepared_items,
metrics
)
for workflow_id, metric_results in results.items():
print(f"\nWorkflow: {workflow_id}")
for metric, result in metric_results.items():
print(f" {metric}: {result['score']:.1f} ({'PASS' if result['pass'] else 'FAIL'})")
else:
print(f"Eval {completion['status']}: {completion.get('error', '')}")
Data Format for Cloud Evaluations
Required Fields by Metric
| Metric | query | response | tool_definitions | ground_truth | context |
|---|---|---|---|---|---|
| coherence | ✓ | ✓ | |||
| fluency | ✓ | ||||
| relevance | ✓ | ✓ | |||
| groundedness | ✓ | (opt) | (opt) | ||
| intent_resolution | ✓ | ✓ | (opt) | ||
| task_adherence | ✓ | ✓ | (opt) | ||
| task_completion | ✓ | ✓ | (opt) | ||
| response_completeness | ✓ | ✓ | |||
| tool_call_accuracy | ✓ | (opt) | ✓ | ||
| tool_input_accuracy | ✓ | ✓ | ✓ | ||
| tool_output_utilization | ✓ | ✓ | (opt) | ||
| tool_selection | ✓ | ✓ | ✓ | ||
| tool_success | ✓ | (opt) |
Response Format (Conversation Array)
The response field must be an array of message objects:
response = [
# Assistant text message
{"role": "assistant", "content": "I'll help you with that."},
# Assistant tool call
{"role": "assistant", "content": [
{
"type": "tool_call",
"tool_call_id": "call_001",
"name": "search_orders",
"arguments": {"customer_id": "C123"}
}
]},
# Tool result
{"role": "tool", "content": [
{
"type": "tool_result",
"tool_call_id": "call_001",
"tool_result": {"orders": [{"id": "W123", "status": "shipped"}]}
}
]},
# Final assistant response
{"role": "assistant", "content": "I found your order W123. It has been shipped!"}
]
Tool Definitions Format
tool_definitions = [
{
"name": "search_orders",
"description": "Search for customer orders",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer ID to search"
},
"status": {
"type": "string",
"enum": ["pending", "shipped", "delivered"],
"description": "Filter by status"
}
},
"required": ["customer_id"]
}
}
]
Metric Configurations
The FOUNDRY_METRICS dictionary in foundry_evals.py defines each metric's configuration:
FOUNDRY_METRICS = {
"coherence": {
"evaluator_name": "builtin.coherence",
"data_mapping": {
"query": "{{item.query}}",
"response": "{{item.response}}",
},
"requires": ["query", "response"],
},
"tool_call_accuracy": {
"evaluator_name": "builtin.tool_call_accuracy",
"data_mapping": {
"query": "{{item.query}}",
"tool_definitions": "{{item.tool_definitions}}",
"tool_calls": "{{item.tool_calls}}",
"response": "{{item.response}}",
},
"requires": ["query", "tool_definitions"],
},
# ... 11 more metrics
}
Testing Criteria Structure
Each metric becomes a testing criterion:
{
"type": "azure_ai_evaluator",
"name": "tool_call_accuracy",
"evaluator_name": "builtin.tool_call_accuracy",
"initialization_parameters": {
"deployment_name": "gpt-4.1",
# "is_reasoning_model": True # For o-series models
},
"data_mapping": {
"query": "{{item.query}}",
"tool_definitions": "{{item.tool_definitions}}",
"tool_calls": "{{item.tool_calls}}",
"response": "{{item.response}}"
}
}
Result Parsing
Azure AI Foundry returns different result formats depending on the metric:
Score Format (1-5)
Used by: coherence, fluency, relevance, groundedness, intent_resolution, response_completeness, tool_selection
{"score": 4, "explanation": "The response is coherent and well-structured..."}
Flagged Format
Used by: task_adherence
{"flagged": False, "reasoning": "The agent followed all task requirements..."}
# flagged=False means PASS (score=5), flagged=True means FAIL (score=1)
Success Boolean Format
Used by: task_completion, tool_success
{"success": True, "explanation": "The task was completed successfully..."}
# success=True -> score=5, success=False -> score=1
Tool Level Format
Used by: tool_call_accuracy
{"tool_calls_success_level": 4, "chain_of_thought": "The agent selected appropriate tools..."}
Pass/Fail String Format
Used by: tool_input_accuracy
{"result": "pass", "chain_of_thought": "All tool inputs were accurate..."}
Used by: tool_output_utilization
{"label": "pass", "reason": "The agent effectively used tool outputs..."}
Trajectory Logging (for Cloud Evaluation)
Use WorkflowTrajectoryLog to create Foundry-compatible trajectory data:
from workflow_metrics import WorkflowTrajectoryLog
trajectory = WorkflowTrajectoryLog(
user_query="Cancel order W123",
system_prompt="You are a customer service agent.",
tool_definitions=[{
"name": "cancel_order",
"description": "Cancel a customer order",
"parameters": {...}
}]
)
# Log plan
trajectory.log_plan("Cancel the order", ["cancel_order"])
# Log tool execution
trajectory.log_step(
tool_name="cancel_order",
arguments={"order_id": "W123", "reason": "Customer request"},
result={"success": True, "refund_amount": 99.99},
success=True
)
# Finalize
trajectory.finalize("Order W123 has been cancelled. Refund of $99.99 will be processed.")
# Get dict for cloud evaluation
workflow_dict = trajectory.to_dict()
# Or save to file
trajectory.save("trajectories/cancellation.jsonl")
Decorator-Based Logging
from workflow_metrics import track_workflow, log_plan, auto_log_step
@track_workflow(output_dir="trajectories")
async def my_workflow(user_request: str):
log_plan(user_request, ["tool_a", "tool_b"])
result = await tool_a()
auto_log_step(result)
result = await tool_b()
auto_log_step(result)
return "Done"
Environment Variables
# REQUIRED for Azure AI Foundry cloud evaluations
AZURE_AI_PROJECT_ENDPOINT=https://your-project.api.azureml.ms
# Optional: Model deployment for LLM-as-judge (default: gpt-4.1)
AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4.1
Local Metrics (Only If Specifically Requested)
Note: Only use local metrics if the user EXPLICITLY requests them. Always default to cloud evaluations.
The only metric available locally is task_navigation_efficiency:
from workflow_metrics import TrajectoryMetrics
# ONLY if user specifically asks for local metrics
metrics = TrajectoryMetrics("trajectories/workflow.jsonl")
metrics.print_summary() # Shows task_navigation_efficiency only
This metric compares planned vs actual tool sequences using simple code logic (no LLM required).
Files Reference
| File | Description |
|---|---|
foundry_evals.py | Primary file - FoundryEvalsClient for Azure AI Foundry cloud API |
trajectory.py | WorkflowTrajectoryLog class for Foundry-compatible logging |
metrics.py | TrajectoryMetrics for loading trajectories and orchestrating evaluation |
telemetry.py | @track_workflow decorator |
trajectory_manager.py | EvaluationManager for daily file management |
tool_definitions_helper.py | Tool definition generation utilities |
__init__.py | Module exports |