skills/supplier-selection/SKILL.md
When the user wants to evaluate suppliers, select vendors, or perform supplier scoring and qualification. Also use when the user mentions "vendor selection," "supplier evaluation," "RFP scoring," "supplier qualification," "vendor comparison," "make vs buy," "supplier scorecard," or "bid analysis." For ongoing supplier risk monitoring, see supplier-risk-management. For contract negotiation, see contract-management.
npx skillsauth add kishorkukreja/awesome-supply-chain supplier-selectionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in supplier selection and vendor evaluation. Your goal is to help organizations identify, evaluate, and select the best suppliers through structured, data-driven processes that balance cost, quality, risk, and strategic fit.
Before starting supplier selection, understand:
Sourcing Context
Business Requirements
Selection Criteria
Process & Timeline
1. Need Identification
2. Market Research
3. Supplier Pre-Qualification
4. RFx Development & Issuance
5. Proposal Evaluation
6. Negotiation
7. Final Selection
Most Common Approach:
import pandas as pd
import numpy as np
def weighted_scoring(suppliers, criteria, weights, scores):
"""
Calculate weighted scores for supplier evaluation
Parameters:
- suppliers: list of supplier names
- criteria: list of evaluation criteria
- weights: dict {criterion: weight} (must sum to 1.0)
- scores: dict {(supplier, criterion): score} (0-10 scale)
Returns:
- DataFrame with scores and rankings
"""
# Validate weights sum to 1.0
total_weight = sum(weights.values())
if not np.isclose(total_weight, 1.0):
print(f"Warning: Weights sum to {total_weight}, normalizing...")
weights = {k: v/total_weight for k, v in weights.items()}
results = []
for supplier in suppliers:
weighted_score = 0
criterion_scores = {}
for criterion in criteria:
score = scores.get((supplier, criterion), 0)
weight = weights.get(criterion, 0)
weighted_value = score * weight
criterion_scores[criterion] = score
weighted_score += weighted_value
results.append({
'Supplier': supplier,
**criterion_scores,
'Weighted_Score': round(weighted_score, 2)
})
# Create DataFrame and rank
df = pd.DataFrame(results)
df['Rank'] = df['Weighted_Score'].rank(ascending=False, method='min')
df = df.sort_values('Weighted_Score', ascending=False)
return df
# Example usage
suppliers = ['Supplier_A', 'Supplier_B', 'Supplier_C']
criteria = ['Price', 'Quality', 'Delivery', 'Service', 'Innovation']
weights = {
'Price': 0.30,
'Quality': 0.25,
'Delivery': 0.20,
'Service': 0.15,
'Innovation': 0.10
}
scores = {
('Supplier_A', 'Price'): 8,
('Supplier_A', 'Quality'): 9,
('Supplier_A', 'Delivery'): 7,
('Supplier_A', 'Service'): 8,
('Supplier_A', 'Innovation'): 9,
('Supplier_B', 'Price'): 9,
('Supplier_B', 'Quality'): 7,
('Supplier_B', 'Delivery'): 8,
('Supplier_B', 'Service'): 7,
('Supplier_B', 'Innovation'): 6,
('Supplier_C', 'Price'): 7,
('Supplier_C', 'Quality'): 9,
('Supplier_C', 'Delivery'): 9,
('Supplier_C', 'Service'): 9,
('Supplier_C', 'Innovation'): 8,
}
results = weighted_scoring(suppliers, criteria, weights, scores)
print(results)
Beyond Price:
class TCOCalculator:
"""Total Cost of Ownership calculator for supplier comparison"""
def __init__(self, supplier_name):
self.supplier_name = supplier_name
self.costs = {}
def add_purchase_cost(self, unit_price, annual_volume):
"""Direct purchase cost"""
self.costs['purchase'] = unit_price * annual_volume
return self
def add_logistics_cost(self, cost_per_unit, annual_volume):
"""Transportation, duties, handling"""
self.costs['logistics'] = cost_per_unit * annual_volume
return self
def add_quality_cost(self, defect_rate, cost_per_defect, annual_volume):
"""Quality issues, returns, rework"""
self.costs['quality'] = defect_rate * cost_per_defect * annual_volume
return self
def add_inventory_cost(self, lead_time_days, unit_cost,
annual_volume, carrying_rate=0.25):
"""Inventory carrying cost based on lead time"""
avg_inventory = (lead_time_days / 365) * annual_volume
inventory_value = avg_inventory * unit_cost
self.costs['inventory'] = inventory_value * carrying_rate
return self
def add_admin_cost(self, annual_admin_cost):
"""Administrative overhead (POs, invoicing, etc.)"""
self.costs['admin'] = annual_admin_cost
return self
def add_risk_cost(self, disruption_probability, disruption_cost):
"""Expected cost of supply disruptions"""
self.costs['risk'] = disruption_probability * disruption_cost
return self
def calculate_tco(self):
"""Calculate total TCO and cost breakdown"""
total_tco = sum(self.costs.values())
return {
'supplier': self.supplier_name,
'total_tco': round(total_tco, 2),
'breakdown': {k: round(v, 2) for k, v in self.costs.items()},
'breakdown_pct': {
k: round(v/total_tco*100, 1)
for k, v in self.costs.items()
}
}
# Example: Compare two suppliers
annual_volume = 100000 # units
# Supplier A: Lower price, longer lead time, higher defect rate
supplier_a = TCOCalculator('Supplier_A')
supplier_a.add_purchase_cost(unit_price=10.00, annual_volume=annual_volume)
supplier_a.add_logistics_cost(cost_per_unit=1.50, annual_volume=annual_volume)
supplier_a.add_quality_cost(defect_rate=0.02, cost_per_defect=50, annual_volume=annual_volume)
supplier_a.add_inventory_cost(lead_time_days=45, unit_cost=10.00, annual_volume=annual_volume)
supplier_a.add_admin_cost(annual_admin_cost=25000)
supplier_a.add_risk_cost(disruption_probability=0.10, disruption_cost=200000)
tco_a = supplier_a.calculate_tco()
# Supplier B: Higher price, shorter lead time, lower defect rate
supplier_b = TCOCalculator('Supplier_B')
supplier_b.add_purchase_cost(unit_price=10.50, annual_volume=annual_volume)
supplier_b.add_logistics_cost(cost_per_unit=1.00, annual_volume=annual_volume)
supplier_b.add_quality_cost(defect_rate=0.005, cost_per_defect=50, annual_volume=annual_volume)
supplier_b.add_inventory_cost(lead_time_days=21, unit_cost=10.50, annual_volume=annual_volume)
supplier_b.add_admin_cost(annual_admin_cost=20000)
supplier_b.add_risk_cost(disruption_probability=0.03, disruption_cost=200000)
tco_b = supplier_b.calculate_tco()
# Compare
print(f"\n{tco_a['supplier']} TCO: ${tco_a['total_tco']:,.0f}")
print(f"{tco_b['supplier']} TCO: ${tco_b['total_tco']:,.0f}")
print(f"\nDifference: ${abs(tco_a['total_tco'] - tco_b['total_tco']):,.0f}")
For Complex Decisions:
import numpy as np
from numpy.linalg import eig
def ahp_pairwise_matrix(comparisons):
"""
Create pairwise comparison matrix for AHP
comparisons: dict of tuples {(criterion_a, criterion_b): importance}
importance scale: 1=equal, 3=moderate, 5=strong, 7=very strong, 9=extreme
"""
# Extract unique criteria
criteria = set()
for (a, b) in comparisons.keys():
criteria.add(a)
criteria.add(b)
criteria = sorted(list(criteria))
n = len(criteria)
# Build matrix
matrix = np.ones((n, n))
for i, crit_i in enumerate(criteria):
for j, crit_j in enumerate(criteria):
if i != j:
if (crit_i, crit_j) in comparisons:
matrix[i, j] = comparisons[(crit_i, crit_j)]
elif (crit_j, crit_i) in comparisons:
matrix[i, j] = 1.0 / comparisons[(crit_j, crit_i)]
return matrix, criteria
def ahp_weights(matrix):
"""Calculate priority weights from pairwise comparison matrix"""
# Calculate eigenvector of maximum eigenvalue
eigenvalues, eigenvectors = eig(matrix)
max_eigenvalue_idx = np.argmax(eigenvalues.real)
principal_eigenvector = eigenvectors[:, max_eigenvalue_idx].real
# Normalize to get weights
weights = principal_eigenvector / principal_eigenvector.sum()
# Calculate consistency ratio
n = len(matrix)
max_eigenvalue = eigenvalues[max_eigenvalue_idx].real
ci = (max_eigenvalue - n) / (n - 1)
# Random index values
ri_values = {1: 0, 2: 0, 3: 0.58, 4: 0.90, 5: 1.12,
6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45, 10: 1.49}
ri = ri_values.get(n, 1.49)
cr = ci / ri if ri > 0 else 0
return weights, cr
# Example: Compare evaluation criteria
comparisons = {
('Quality', 'Price'): 3, # Quality is moderately more important than Price
('Quality', 'Delivery'): 5, # Quality is strongly more important than Delivery
('Quality', 'Service'): 7, # Quality is very strongly more important than Service
('Price', 'Delivery'): 3, # Price is moderately more important than Delivery
('Price', 'Service'): 5, # Price is strongly more important than Service
('Delivery', 'Service'): 3, # Delivery is moderately more important than Service
}
matrix, criteria = ahp_pairwise_matrix(comparisons)
weights, consistency_ratio = ahp_weights(matrix)
print("AHP Criteria Weights:")
for criterion, weight in zip(criteria, weights):
print(f" {criterion}: {weight:.3f} ({weight*100:.1f}%)")
print(f"\nConsistency Ratio: {consistency_ratio:.3f}")
if consistency_ratio < 0.10:
print(" ✓ Acceptable consistency (CR < 0.10)")
else:
print(" ✗ Inconsistent judgments (CR >= 0.10), review needed")
Key Metrics:
Red Flags:
def assess_financial_health(financials):
"""
Assess supplier financial health
financials: dict with financial metrics
Returns: risk score (0-10, higher is better)
"""
score = 10.0
flags = []
# Years in business
years = financials.get('years_in_business', 0)
if years < 2:
score -= 3
flags.append("Limited operating history")
elif years < 5:
score -= 1
# Revenue trend
revenue_growth = financials.get('revenue_growth_yoy', 0)
if revenue_growth < -0.20:
score -= 2
flags.append("Significant revenue decline")
elif revenue_growth < 0:
score -= 1
# Profitability
profit_margin = financials.get('profit_margin', 0)
if profit_margin < 0:
score -= 2
flags.append("Unprofitable")
elif profit_margin < 0.05:
score -= 1
# Liquidity
current_ratio = financials.get('current_ratio', 0)
if current_ratio < 1.0:
score -= 2
flags.append("Liquidity concerns")
elif current_ratio < 1.5:
score -= 0.5
# Leverage
debt_to_equity = financials.get('debt_to_equity', 0)
if debt_to_equity > 2.0:
score -= 1.5
flags.append("High leverage")
score = max(0, score) # Floor at 0
risk_level = 'Low' if score >= 7 else 'Medium' if score >= 4 else 'High'
return {
'score': round(score, 1),
'risk_level': risk_level,
'flags': flags
}
Assessment Areas:
Evaluation Methods:
Quality Indicators:
Compliance Requirements:
Key Metrics:
Evaluation Factors:
Document Structure:
Introduction & Overview
Requirements Specification
Commercial Terms
Evaluation Criteria
Supplier Information Required
Instructions to Bidders
import pandas as pd
class RFPScorer:
"""Automated RFP response scoring system"""
def __init__(self, criteria_weights):
"""
Initialize with weighted criteria
criteria_weights: dict {category: {criterion: weight}}
"""
self.criteria_weights = criteria_weights
self.supplier_scores = {}
def add_supplier_response(self, supplier_name, responses):
"""
Add supplier's scored responses
responses: dict {category: {criterion: score}}
scores on 0-10 scale
"""
self.supplier_scores[supplier_name] = responses
def calculate_scores(self):
"""Calculate weighted scores for all suppliers"""
results = []
for supplier, responses in self.supplier_scores.items():
category_scores = {}
total_weighted_score = 0
total_weight = 0
for category, criteria in self.criteria_weights.items():
category_score = 0
category_weight = sum(criteria.values())
for criterion, weight in criteria.items():
score = responses.get(category, {}).get(criterion, 0)
category_score += score * weight
total_weighted_score += score * weight
total_weight += weight
if category_weight > 0:
category_scores[category] = category_score / category_weight
overall_score = total_weighted_score / total_weight if total_weight > 0 else 0
results.append({
'Supplier': supplier,
**category_scores,
'Overall_Score': round(overall_score, 2)
})
df = pd.DataFrame(results)
df['Rank'] = df['Overall_Score'].rank(ascending=False, method='min')
df = df.sort_values('Overall_Score', ascending=False)
return df
def generate_report(self):
"""Generate detailed scoring report"""
df = self.calculate_scores()
report = []
report.append("=" * 80)
report.append("RFP EVALUATION SUMMARY")
report.append("=" * 80)
report.append("")
for _, row in df.iterrows():
report.append(f"Rank #{int(row['Rank'])}: {row['Supplier']}")
report.append(f" Overall Score: {row['Overall_Score']:.2f}/10")
report.append("")
return "\n".join(report)
# Example usage
criteria = {
'Technical': {
'Specifications_Met': 0.15,
'Quality_Certifications': 0.10,
'Technical_Capability': 0.10
},
'Commercial': {
'Price_Competitiveness': 0.20,
'Payment_Terms': 0.05,
'Volume_Flexibility': 0.05
},
'Operational': {
'Lead_Time': 0.10,
'Capacity': 0.08,
'Geographic_Location': 0.07
},
'Strategic': {
'Innovation': 0.05,
'Sustainability': 0.03,
'References': 0.02
}
}
scorer = RFPScorer(criteria)
# Add supplier responses
scorer.add_supplier_response('Supplier_A', {
'Technical': {'Specifications_Met': 9, 'Quality_Certifications': 8, 'Technical_Capability': 9},
'Commercial': {'Price_Competitiveness': 7, 'Payment_Terms': 8, 'Volume_Flexibility': 7},
'Operational': {'Lead_Time': 8, 'Capacity': 9, 'Geographic_Location': 7},
'Strategic': {'Innovation': 9, 'Sustainability': 8, 'References': 9}
})
scorer.add_supplier_response('Supplier_B', {
'Technical': {'Specifications_Met': 8, 'Quality_Certifications': 9, 'Technical_Capability': 8},
'Commercial': {'Price_Competitiveness': 9, 'Payment_Terms': 7, 'Volume_Flexibility': 8},
'Operational': {'Lead_Time': 7, 'Capacity': 8, 'Geographic_Location': 9},
'Strategic': {'Innovation': 6, 'Sustainability': 7, 'References': 8}
})
results_df = scorer.calculate_scores()
print(results_df)
print("\n" + scorer.generate_report())
For Complex Trade-offs:
def utility_function(value, min_val, max_val, risk_aversion=0.5):
"""
Calculate utility for a value
risk_aversion: 0 = risk neutral, <0 = risk seeking, >0 = risk averse
"""
if max_val == min_val:
return 1.0
normalized = (value - min_val) / (max_val - min_val)
# Power utility function
if risk_aversion != 0:
utility = normalized ** (1 - risk_aversion)
else:
utility = normalized
return max(0, min(1, utility))
When There's Uncertainty in Scores:
import numpy as np
def monte_carlo_supplier_selection(suppliers, criteria,
score_distributions,
n_simulations=10000):
"""
Monte Carlo simulation for supplier selection under uncertainty
score_distributions: dict {(supplier, criterion): (mean, std)}
"""
results = {supplier: 0 for supplier in suppliers}
for _ in range(n_simulations):
scores = {}
for supplier in suppliers:
weighted_score = 0
for criterion, weight in criteria.items():
mean, std = score_distributions.get((supplier, criterion), (5, 1))
score = np.random.normal(mean, std)
score = max(0, min(10, score)) # Clip to 0-10
weighted_score += score * weight
scores[supplier] = weighted_score
# Winner of this simulation
winner = max(scores, key=scores.get)
results[winner] += 1
# Convert to probabilities
probabilities = {
supplier: count / n_simulations
for supplier, count in results.items()
}
return probabilities
# Example with uncertainty
suppliers = ['Supplier_A', 'Supplier_B', 'Supplier_C']
criteria = {'Price': 0.4, 'Quality': 0.4, 'Delivery': 0.2}
# (mean, std) for each supplier-criterion pair
score_distributions = {
('Supplier_A', 'Price'): (8.0, 0.5),
('Supplier_A', 'Quality'): (9.0, 0.8),
('Supplier_A', 'Delivery'): (7.0, 1.0),
('Supplier_B', 'Price'): (9.0, 1.2),
('Supplier_B', 'Quality'): (7.0, 0.6),
('Supplier_B', 'Delivery'): (8.0, 0.8),
('Supplier_C', 'Price'): (7.0, 0.8),
('Supplier_C', 'Quality'): (9.0, 0.5),
('Supplier_C', 'Delivery'): (9.0, 0.7),
}
probabilities = monte_carlo_supplier_selection(
suppliers, criteria, score_distributions
)
print("Probability of being best choice:")
for supplier, prob in sorted(probabilities.items(), key=lambda x: x[1], reverse=True):
print(f" {supplier}: {prob*100:.1f}%")
Data Analysis & Scoring:
pandas: Data manipulation and analysisnumpy: Numerical computationsscipy: Scientific computing, optimizationscikit-learn: Machine learning for predictive scoringOptimization:
pulp: Linear programming for multi-sourcing optimizationcvxpy: Convex optimizationpyomo: Mathematical optimization modelingVisualization:
matplotlib, seaborn: Statistical visualizationsplotly: Interactive dashboardsnetworkx: Supplier network visualizationSourcing Platforms:
Supplier Management:
Analytics:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Executive Summary:
Evaluation Summary:
| Rank | Supplier | Overall Score | Price | Quality | Delivery | Service | Total Cost (Annual) | Recommendation | |------|----------|---------------|-------|---------|----------|---------|---------------------|----------------| | 1 | Supplier B | 8.5 | 9 | 9 | 8 | 8 | $2.1M | Award 60% | | 2 | Supplier C | 8.2 | 7 | 9 | 9 | 9 | $2.3M | Award 40% | | 3 | Supplier A | 7.8 | 8 | 7 | 8 | 7 | $2.0M | Backup |
Detailed Scoring:
Supplier B: 8.5/10
Technical (35%): 8.8
✓ Specifications Met: 9/10
✓ Quality Certifications: 9/10 (ISO 9001, AS9100)
✓ Technical Capability: 8/10
Commercial (30%): 8.5
✓ Price Competitiveness: 9/10
✓ Payment Terms: 8/10 (Net 60)
✓ Volume Flexibility: 8/10
Operational (25%): 8.0
✓ Lead Time: 8/10 (21 days)
✓ Capacity: 8/10 (150% of requirement)
✓ Geographic Coverage: 8/10
Strategic (10%): 9.0
✓ Innovation: 9/10 (strong R&D)
✓ Sustainability: 9/10 (ISO 14001)
✓ References: 9/10 (excellent)
Strengths:
- Strong quality certifications and processes
- Competitive pricing with good payment terms
- Excellent references from industry leaders
- Commitment to sustainability and innovation
Weaknesses:
- Moderate lead time (21 days vs. 14 days for Supplier C)
- Geographic concentration (single plant location)
Risks:
- Capacity constraints if demand exceeds 150% of current forecast
- Currency exposure (EUR-based pricing)
Total Cost of Ownership Comparison:
| Cost Component | Supplier A | Supplier B | Supplier C | |----------------|------------|------------|------------| | Purchase Price | $1,800,000 | $2,000,000 | $2,150,000 | | Logistics | $150,000 | $100,000 | $80,000 | | Quality Costs | $100,000 | $50,000 | $40,000 | | Inventory Carrying | $80,000 | $60,000 | $50,000 | | Administrative | $30,000 | $25,000 | $25,000 | | Risk Premium | $40,000 | $20,000 | $15,000 | | Total TCO | $2,200,000 | $2,255,000 | $2,360,000 |
Recommendation:
Implementation Plan:
If you need more context:
documentation
When the user wants to optimize yard operations, manage trailer parking, or improve dock door utilization. Also use when the user mentions "yard management," "trailer tracking," "yard jockey," "drop trailer program," "trailer pool," "dock scheduling," or "gate management." For cross-dock operations, see cross-docking. For warehouse design, see warehouse-design.
tools
When the user wants to optimize workforce scheduling, create shift plans, or balance labor demand. Also use when the user mentions "staff scheduling," "labor planning," "shift optimization," "crew scheduling," "roster optimization," or "employee scheduling." For task assignment, see task-assignment-problem. For wave planning labor, see wave-planning-optimization.
testing
When the user wants to optimize pick wave planning, schedule warehouse operations, or improve order fulfillment efficiency. Also use when the user mentions "wave management," "batch picking," "pick wave scheduling," "order release optimization," "wave design," or "pick wave strategy." For order batching, see order-batching-optimization. For workforce scheduling, see workforce-scheduling.
testing
When the user wants to optimize warehouse slot assignments, improve pick efficiency, or design warehouse layouts. Also use when the user mentions "slotting optimization," "slot assignment," "ABC slotting," "pick path optimization," "storage location assignment," "warehouse layout optimization," or "forward pick locations." For picker routing, see picker-routing-optimization. For warehouse design, see warehouse-design.