tune(dispatch): VRP prioritizes distance over specialty (rank_weight 6→2)

Skill capability stays a hard filter, so a multi-skill tech (e.g. an
installer who also has the repair skill) can take those jobs. But the
specialist bias (skill-order rank) was too strong (6 virtual min/rank),
so a far repair-specialist beat a nearby installer-who-repairs. Lower
rank_weight to 2 → distance dominates; specialty only breaks near-ties.

Verified: repair job near a rank-3 multi-skill installer (4 min) vs a
far rank-0 specialist (14 min) — old weight picked the far specialist
(bug), new weight picks the nearby installer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-02 13:53:46 -04:00
parent 96929bed6f
commit bc157588d1
2 changed files with 5 additions and 2 deletions

View File

@ -4103,7 +4103,7 @@ async function optimizeSuggestion () {
}
if (!vehicles.length) { out.push(...entries); continue } // aucun tech éligible (ex. week-end sans quart) garder l'assignation greedy
let res
try { res = await roster.optimizeRoutes({ jobs, vehicles, max_seconds: 8, rank_weight: 6 }) } catch (e) { out.push(...entries); continue }
try { res = await roster.optimizeRoutes({ jobs, vehicles, max_seconds: 8, rank_weight: 2 }) } catch (e) { out.push(...entries); continue } // rank_weight faible = DISTANCE prioritaire, spécialité = simple départage
if (!res || res.status !== 'OK') { out.push(...entries); continue } // solveur absent/erreur/infaisable on GARDE l'assignation greedy (ne jamais perdre de jobs)
const placed = new Set()
for (const rt of (res.routes || [])) {

View File

@ -46,7 +46,10 @@ def solve_route(req: dict) -> dict:
speed = float(req.get("speed_kmh") or 45.0)
default_service = int(req.get("default_service_min") or 60)
rank_w = float(req.get("rank_weight") or 5.0) # « minutes virtuelles » par cran d'ordre de compétence (spécialiste=0)
# DISTANCE PRIORITAIRE : la spécialité (ordre de compétence) ne fait que DÉPARTAGER à trajet ~égal.
# rank_w = « minutes virtuelles » par cran d'ordre (spécialiste=0). Petit → un tech multi-compétences PROCHE
# passe avant un spécialiste LOIN (ex. un installateur qui a aussi la réparation prend une réparation voisine).
rank_w = float(req.get("rank_weight") if req.get("rank_weight") is not None else 2.0)
base_pen = int(req.get("drop_penalty") or 100000)
max_seconds = float(req.get("max_seconds") or 10)