{"id":7549,"date":"2025-01-15T08:00:00","date_gmt":"2025-01-15T07:00:00","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=7549"},"modified":"2025-01-15T14:46:45","modified_gmt":"2025-01-15T13:46:45","slug":"amazon-bedrock-sorry-im-unable-to-assist-you-with-this-request-indagine-e-risoluzione-del-misterioso-errore","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/amazon-bedrock-sorry-im-unable-to-assist-you-with-this-request-indagine-e-risoluzione-del-misterioso-errore\/","title":{"rendered":"Amazon Bedrock: \u201cSorry, I\u2019m unable to assist you with this request\u201d. Indagine e risoluzione del misterioso errore"},"content":{"rendered":"\n
“Quando hai eliminato l’impossibile, ci\u00f2 che rimane, per quanto improbabile, deve essere la verit\u00e0.”<\/em><\/p>\n\n\n\n Sherlock Holmes<\/p>\n\n\n\n Avere un assistente (lo chiameremo Watson!) che ci aiuti a recuperare informazioni e che risponda alle nostre domande \u00e8 sempre utile. Si sa, cercare all’interno di vaste documentazioni (Knowledge Base) pu\u00f2 essere un compito molto impegnativo in termini di tempo.<\/p>\n\n\n\n Per fortuna, oggi i Large Language Model (LLM) sono qui per aiutarci… Almeno fino a che non si rifiutano di fare il loro lavoro!<\/p>\n\n\n\n Recentemente, ci siamo imbattuti nel misterioso errore \u201cSorry, I\u2019m unable to assist you with this request\u201d dato dal modello quando si esegue programmaticamente una query \u201cretrieve&generate\u201d utilizzando Amazon Bedrock e la libreria Python boto3.<\/p>\n\n\n\n Utilizzare la query boto3 Amazon Bedrock \u201cretrieve&generate\u201d fornendo un prompt template con il placeholder $output_format_instructions$ gi\u00e0 valutato e creare un parser XML.<\/p>\n\n\n\n In questo modo si otterr\u00e0 sempre il testo in uscita che, se sintatticamente corretto, potr\u00e0 essere analizzato con il proprio parser XML personalizzato per estrarre il reale testo. Inoltre, questo fa rimanere ancora attivo il parser XML, integrato nel metodo boto3, per cercare i riferimenti ed estrarre le citazioni dei documenti della knowledge-base.<\/p>\n\n\n\n Altrimenti, se non \u00e8 allineato con la struttura XML definita, \u00e8 sufficiente sanificare i tag XML per estrarre il testo in uscita.<\/p>\n\n\n\n Ci \u00e8 stato affidato il compito di implementare un sistema di ricerca basato su una Knowledge Base di una azienda farmaceutica. Abbiamo scelto Amazon Bedrock poich\u00e9 la sintesi e la risposta alle query in linguaggio naturale sono facili da ottenere.<\/p>\n\n\n\n Purtroppo, le cose non vanno sempre come vorremmo; cos\u00ec, quando abbiamo implementato la soluzione, abbiamo scoperto che l’uso programmatico di boto3 per eseguire una query di retrieve and generate ci dava saltuariamente e in modo imprevedibile l’errore \u201cSorry, I\u2019m unable to assist you with this request\u201d<\/strong> senza informazioni aggiuntive o log.<\/p>\n\n\n\n Inutile dire che non \u00e8 disponibile documentazione, n\u00e9 articoli su Internet, quindi abbiamo iniziato la nostra avventura per risolvere il problema. Di seguito potrete leggere come \u00e8 andato il nostro viaggio e, se state avendo lo stesso problema, potrete trovare la soluzione.<\/p>\n\n\n\n Continuate a leggere!<\/p>\n\n\n\n Facciamo una breve introduzione al caso d’uso per capire meglio il contesto e la soluzione. Sar\u00e0 molto importante per capire, verifica per verifica, dove si trova il problema.<\/p>\n\n\n\n Il nostro compito era quello di implementare un sistema di ricerca per trovare facilmente i foglietti illustrativi dei farmaci tra tutta la documentazione del catalogo dei prodotti. Il testo del foglietto illustrativo doveva essere accessibile tramite API, in modo che la funzione potesse essere integrata all’interno dell’applicazione web del cliente e utilizzata dalla sua base di utenti per facilitarne l’esperienza (UX).<\/p>\n\n\n\n Il sistema complessivo pu\u00f2 essere descritto attraverso 3 macro-aree:<\/p>\n\n\n\n Partendo dalle fondamenta della nostra infrastruttura, il sistema di archiviazione<\/strong> supporta l’applicazione utilizzando un bucket S3, uno database vettoriale, Pinecone, e una Bedrock Knowledge Base.<\/p>\n\n\n\n Il bucket S3 contiene tutti i documenti della nostra Knowledge Base, insieme ai relativi file di metadati associati.<\/p>\n\n\n\n Il database vettoriale, o vector storage, \u00e8 il cuore pulsante dell’infrastruttura di archiviazione. Contiene gli embeddings di tutti i pezzi dei nostri documenti, insieme ai metadati associati. In altre parole, questo \u00e8 solo un modo complicato per dire che contiene l’intero testo della Knowledge base, diviso in pezzi (chunks), ognuno dei quali trasformato con una rappresentazione vettoriale (embedding) che \u00e8 molto efficace ed efficiente per le attivit\u00e0 di ricerca e recupero, poich\u00e9 codifica le informazioni semantiche del testo al suo interno. Date le dimensioni complessive dei documenti, abbiamo scelto Pinecone per la nostra soluzione poich\u00e9 si integra facilmente con i pezzi della nostra infrastruttura AWS ed \u00e8 molto efficiente dal punto di vista dei costi per il caso d’uso.<\/p>\n\n\n\n I documenti del bucket S3 vengono elaborati e trasferiti nel database vettoriale utilizzando la Knowledge Base di Bedrock. Si tratta di un elemento infrastrutturale che prepara e carica i documenti da varie fonti di dati configurate all\u2019interno del database vettoriale. Inoltre, la Knowledge Base di Bedrock gestisce anche il recupero dei documenti significativi in base a un’interrogazione dell’utente, elemento chiave della soluzione.<\/p>\n\n\n\n Il sistema di caricamento<\/strong> automatizza l’elaborazione e il popolamento del database vettoriale. Quando un utente amministratore carica un nuovo documento all’interno del bucket S3, viene avviata una funzione Lambda che genera un file di metadati associato al documento di origine. L’amministratore sincronizzer\u00e0 i nuovi documenti della knowledge base nel database vettoriale Pinecone utilizzando la gi\u00e0 citata funzione della Bedrock Knowledge Base.<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Infine, il sistema di recupero<\/strong> \u00e8 composto da un API Gateway che gestisce le richieste provenienti dall’applicazione del cliente utilizzando una funzione Lambda. La funzione prende in input una domanda dell’utente e interroga la base di conoscenza, utilizzando la chiamata API retrieve&generate della libreria Python boto3, per reperire il testo del foglietto illustrativo richiesto. La chiamata API retrieve&generate utilizza il LLM Anthropic Claude 3 Sonnet, il migliore per il nostro caso d’uso al momento del progetto.<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Per completare il contesto, abbiamo caricato l’intera documentazione del catalogo, circa 70 documenti, all’interno del bucket S3 e abbiamo avviato il processo di sincronizzazione. Una volta terminato, abbiamo iniziato a testare la soluzione e ci siamo imbattuti nel famigerato errore: \u201cSorry, I’m unable to assist you with this request\u201d, un codice di stato 200 OK e nessun altro indizio.<\/p>\n\n\n\n La ricerca della vera fonte di questo errore \u00e8 stata molto dura. La strada era piena di trappole: pericoli logici che cercavano di portarci fuori strada. Rivediamo il processo di ricerca in modo pi\u00f9 strutturato e ordinato passo dopo passo:<\/p>\n\n\n\n Passo molto semplice e basilare: abbiamo provato la soluzione con vari prompt e domande degli utenti per capire se ci fossero pattern colpevoli dell’errore.<\/p>\n\n\n\n L’errore sembrava per\u00f2 verificarsi in momenti casuali. Per risolvere il problema, abbiamo fissato alcune variabili, generato alcune ipotesi ed eseguito ulteriori test.<\/p>\n\n\n\n Abbiamo testato alcuni prompt per la parte di generazione e abbiamo definito il migliore da utilizzare nella successiva serie di test. Una volta che la variabile prompt \u00e8 stata definita, abbiamo potuto creare alcune ipotesi per capire se il problema si nascondesse nei documenti. Definiamole:<\/p>\n\n\n\n Abbiamo iniziato a testare la prima ipotesi ponendo una serie di domande su farmaci specifici e alcune su farmaci inesistenti. L’esito del test ha invalidato l’ipotesi: il testo in uscita per i farmaci inesistenti a volte era corretto, a volte affetto da errore.<\/p>\n\n\n\n Successivamente, abbiamo eseguito un test simile per la seconda ipotesi, raccogliendo le domande precedenti degli utenti, insieme ai loro output. Abbiamo eseguito il test comprendendo che il problema sembra essere legato a documenti specifici, mentre non c’era correlazione tra i documenti \u201cdifettosi\u201d.<\/p>\n\n\n\n Qui il mistero si \u00e8 infittito. L’errore era causato da un insieme di documenti specifici, molto diversi tra loro. Inoltre, alcuni documenti difettosi erano molto simili a quelli corretti!<\/p>\n\n\n\n Stavamo comunque facendo dei passi avanti per avvicinarci alla soluzione. Il passo successivo \u00e8 stato capire cosa rendesse un documento \u201cdifettoso\u201d. Abbiamo limitato l’entropia restringendo la nostra Knowledge base solo a quest’ultima tipologia di documenti e abbiamo eseguito la chiamata API \u201cretrieve\u201d per vedere le differenze nei pezzi recuperati tra i documenti buoni e quelli cattivi.<\/p>\n\n\n\n Abbiamo creato un ambiente isolato che riflettesse la soluzione e abbiamo caricato nella Knowledge base solo i documenti dell’ultimo test, cercando di capire le differenze tra loro.<\/p>\n\n\n\n Abbiamo creato un notebook per eseguire alcune analisi sul nostro database vettoriale e ricavarne alcuni spunti.<\/p>\n\n\n\n In primo luogo, abbiamo esaminato il numero di chunk all’interno del database vettoriale: Il numero di chunk era paragonabile al numero di documenti originali. Ci\u00f2 non sorprende, in quanto i foglietti illustrativi dei farmaci sono documenti di piccole dimensioni e quasi tutti possono essere contenuti nella dimensione dei chunk.<\/p>\n\n\n\n Quindi, abbiamo eseguito alcune operazioni di recupero utilizzando la chiamata API boto3 retrieve e le domande del nostro ultimo test. Ed ecco un risultato inaspettato: tutti i documenti erano stati recuperati!<\/p>\n\n\n\n Con queste informazioni, il problema doveva trovarsi nella parte di generazione della chiamata API boto3 retrieve&generate. Qui abbiamo solo due variabili: il prompt e le domande.<\/p>\n\n\n\n Ci stiamo avvicinando alla soluzione, ma come arrivarci? Ovviamente\u2026 pi\u00f9 log!<\/p>\n\n\n\n Avevamo bisogno di ulteriori informazioni per capire meglio come funzionasse la chiamata API retrieve&generate delle libreria Python boto3, come venissero gestiti i prompt e se domande specifiche potessero innescare qualche condizione inaspettata.<\/p>\n\n\n\n Per ottenere maggiori dettagli, abbiamo attivato il logging di Bedrock. <\/p>\n\n\n\n Per farlo, serve entrare nelle Impostazioni, sotto la voce Configurazioni di Bedrock, e attivare il logging delle invocazioni dei modelli.<\/p>\n\n\n\n \u00c8 necessario specificare per quali tipi di modelli si desidera registrare le invocazioni e specificare un ruolo IAM per salvare i log alle destinazioni proposte: CloudWatch, S3 o entrambi. Poich\u00e9 si trattava di documenti di tipo testuale, abbiamo registrato solo gli embedding e le invocazioni dei modelli di testo. In questo caso, i log di CloudWatch sono stati sufficienti. Ecco un esempio di configurazione:<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Una nota rapida, ma importante: abilitando il logging, ogni singola invocazione di un modello viene registrata. Questi log possono crescere molto velocemente e, con essi, anche la vostra bolletta! Per evitare fatture inaspettate alla fine del mese, attivate questi log solo quando ne avete veramente bisogno.<\/p>\n\n\n\n Abbiamo rieseguito alcune chiamate retrieve&generate per capire cosa succedesse al prompt, alle domande e all’output.<\/p>\n\n\n\n Dai log delle invocazioni, vediamo finalmente la luce! <\/p>\n\n\n\n Il problema era all’interno del prompt e riguardava in particolare l’uso del placeholder $output_format_instructions$.<\/p>\n\n\n\nTL;DR<\/h2>\n\n\n\n
Il problema<\/h2>\n\n\n\n
Il nostro caso d\u2019uso e implementazione<\/h2>\n\n\n\n
\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
Alla ricerca dell\u2019errore<\/h2>\n\n\n\n
Step 1: Generare l’errore<\/h4>\n\n\n\n
\n
Step 2: Test delle ipotesi sui documenti<\/h4>\n\n\n\n
Step 3: Ritorno alle origini, pochi documenti, solo recupero<\/h4>\n\n\n\n
Step 4: Bedrock Logging<\/h4>\n\n\n\n
<\/figure><\/div>\n\n\n
Cosa abbiamo trovato (il tassello mancante)<\/h2>\n\n\n\n