Durante un viaggio abbiamo discusso di quanto questo strumento fosse interessante ma non necessariamente utile. Il mio amico poteva inserire i numeri e vedere cosa succedeva, ma ciò non aiutava a rendere più facili le decisioni sul personale, poiché fondamentalmente si trattava di tentativi ed errori istruiti. Ciò che sarebbe più utile sarebbe un sistema in cui potresti impostare uno SLA target per tutto il giorno e il sistema ti fornirebbe la migliore pianificazione dell’agente per corrispondere a tale obiettivo, dati i parametri variabili delle chiamate in entrata e i tempi di gestione dell’agente. Questo è un terzo grande pilastro della OR: ottimizzazione.
Il problema era che con l’ottimizzazione avevi bisogno di una formula per ottimizzare e noi avevamo una simulazione. Tuttavia, mi resi conto che non c’era motivo per cui non potessimo utilizzare la simulazione come funzione obiettivo per un problema di ottimizzazione. Richiede una serie di input e, con un numero sufficiente di esecuzioni della simulazione, fornisce un output sicuro. Fortunatamente, 30 anni fa i ricercatori avevano lo stesso pensiero e da allora hanno studiato il campo dell’ottimizzazione della simulazione. Queste persone intelligenti avevano fatto tutto il duro lavoro per me. (3, 4)
Quindi, ancora una volta, mi sono messo al lavoro. Ora il fulcro dell’ottimizzazione della simulazione è creare una serie di input, eseguirla sulla simulazione e scegliere la serie successiva di input in modo intelligente fino a raggiungere il tuo obiettivo. Il mio problema di ottimizzazione era simile a questo:
Il mio obiettivo è costruire un programma. Gli orari sono costituiti da turni. I turni sono orari prestabiliti durante il “giorno” della simulazione (ad esempio dalle 9:00 alle 17:00, dalle 10:00 alle 16:00) durante i quali un agente può lavorare. Una pianificazione è l’insieme di tutti i turni, con il numero di agenti che lavorano su ciascun turno. Un buon programma è quello che mantiene lo SLA in bilico attorno all’obiettivo e riduce al minimo il numero totale di ore lavorative (numero di lavoratori * durata dei loro turni). Alcuni turni hanno un limite al numero di persone che possono lavorare (ad esempio, solo 10 persone possono lavorare nel turno mattutino di mezza giornata) e ogni turno ha un determinato programma di pause. Lo SLA non dovrebbe mai superare un limite massimo sopra l’obiettivo (ad esempio, il nostro obiettivo è uno SLA di 3 minuti ma nessuno dovrebbe mai aspettare più di 10 minuti).
Ho impostato la struttura in modo da tenere conto di tutti questi vincoli e ora ero pronto per l’ottimizzazione. Alcuni problemi di cui si parla nella ricerca sull’ottimizzazione della simulazione sono difficili perché le loro simulazioni sono scatole nere, il che significa che non si ottiene alcun segnale utile oltre alla risposta della simulazione. Fortunatamente, questa simulazione è diversa. Stiamo misurando il tempo in coda durante il “giorno” della simulazione in modo da poter vedere come si comportano determinati turni rispetto agli altri.
Ad esempio, se il turno mattutino ha più agenti rispetto all’ultimo turno ma quest’ultimo ha più chiamate in entrata, probabilmente vedremo che l’ultimo turno è più fuori obiettivo rispetto al turno mattutino. Nel nostro prossimo tentativo dovremmo probabilmente aumentare il numero di persone nell’ultimo turno.
Non è così semplice: con complicati turni sovrapposti e altri parametri ci sarà interdipendenza, ma possiamo inserire qualsiasi programma nella simulazione e vedere cosa succede.
Questi segnali che otteniamo dalla simulazione mi hanno portato a una strategia chiara, basata sui gradienti.
La ricerca basata sul gradiente può essere intesa come una palla che rotola giù da una collina, dove ad ogni passo si tenta di portare la palla in uno stato più basso finché non raggiunge il fondo. Ciò è complicato dal fatto che non è una collina uniforme. Ci saranno dei falsi fondi in cui qualsiasi direzione scelta risale la collina, ma il vero fondo richiede che tu inizi a rotolare da una posizione diversa. In pratica, non sapremo mai se abbiamo raggiunto il vero fondo, ma esistono modi intelligenti per assicurarci di fare abbastanza test per sapere se siamo abbastanza vicini. Nel nostro caso la palla è un programma e il fondo della collina è la linea perfetta dove tutte le chiamate in arrivo attendono esattamente lo SLA target.
Quindi, qual è il passaggio del gradiente più naturale? Prendi il programma peggiore (quello in media più lontano dall’obiettivo) e aggiungi un agente al turno se la media è superiore al tuo obiettivo, oppure togli un agente se la media è inferiore all’obiettivo. Se sei bloccato dove tutti i cambiamenti di turno peggiorano il programma ma lo SLA non è vicino all’obiettivo, torna a un programma precedentemente buono e ricomincia da lì. Ho provato altri metodi per scegliere un programma successivo, ma questo era il più coerente. Come fai a sapere quando fermarti? Imposta una tolleranza per dire che se ogni spostamento è in media a meno di 30 secondi (ad esempio) dall’obiettivo, considera questo risolto. Fermati se sono stati fatti troppi passi e scegli il programma migliore tra quelli simulati.
In un mondo ideale eseguiremmo questo programma per sempre, provando ogni singolo programma fino a trovare il migliore, ma avevamo bisogno che questo programma scegliesse un programma in meno di 5 minuti. La parte più interessante di questo progetto è stata testare gli algoritmi di ottimizzazione per trovare quello in grado di ottenere una buona soluzione il più rapidamente possibile.
La soluzione con i migliori risultati sfruttava il fatto che è possibile controllare il numero di volte in cui si esegue una simulazione, il compromesso è che meno volte si esegue una simulazione, meno si è sicuri dei suoi risultati. Ecco cosa ho scoperto che funziona:
Inizia con meno simulazioni quando inizi a eseguire l’algoritmo di ottimizzazione e, man mano che l’obiettivo si avvicina, aumenta il numero. Funziona perché all’inizio molti cambi di turno sposteranno il programma nella direzione dell’obiettivo, quindi non è così importante essere così sicuri della direzione che hai scelto. Man mano che una buona soluzione si avvicina, diventa più importante la destinazione del programma. Combina tutto ciò con esecuzioni a più livelli che iniziano tutte dallo stesso punto nullo per garantire una gamma più ampia di soluzioni esplorate.
La cosa fantastica di questo approccio è che sono in grado di controllare la durata dell’esecuzione dell’algoritmo. Posso vedere quanto tempo impiegano le simulazioni e regolare di conseguenza il numero di iterazioni, posso anche scegliere quante volte iniziare dal punto di partenza. Ogni macchina su cui esegui questo algoritmo funzionerà in modo leggermente diverso e un tempo di esecuzione coerente dell’algoritmo è di fondamentale importanza per garantire che questo progetto sia utile nel mondo reale.
#schedule is {(start_time, end_time): num_agents}{'Schedule':
{(0, 540): 5,
(30, 570): 2,
(60, 600): 1,
(90, 630): 1,
(120, 660): 4,
(150, 690): 1,
(180, 720): 0,
(210, 750): 1,
(240, 780): 18,
(0, 660): 0,
(30, 690): 0,
(60, 720): 0,
(90, 750): 0,
(120, 780): 3}
'Average Wait': 0.5738814985851588
'Worker Units': 660.0
'Worst Shift Time In Queue, Relative to Target': 0.5965600329117189}
Fonte: towardsdatascience.com