Elaborazione intelligente dei documenti con Amazon Textract, Amazon Bedrock e LangChain

La trasformazione intelligente dei documenti con Amazon Textract, Amazon Bedrock e LangChain

Nell’era dell’informazione di oggi, le enormi quantità di dati contenute in innumerevoli documenti rappresentano sia una sfida che un’opportunità per le aziende. I metodi tradizionali di elaborazione dei documenti spesso risultano poco efficienti e accurati, lasciando spazio a innovazioni, riduzione dei costi e ottimizzazioni. L’elaborazione dei documenti ha registrato significativi progressi con l’avvento dell’Elaborazione Intelligente dei Documenti (IDP). Con l’IDP, le aziende possono trasformare dati non strutturati da diversi tipi di documenti in informazioni strutturate e azionabili, migliorando notevolmente l’efficienza e riducendo gli sforzi manuali. Tuttavia, le potenzialità non finiscono qui. Integrando l’intelligenza artificiale generativa (AI) nel processo, possiamo ulteriormente potenziare le capacità dell’IDP. L’AI generativa non solo introduce capacità avanzate nell’elaborazione dei documenti, ma introduce anche una dinamicità adattabile ai modelli di dati in continuo cambiamento. Questo articolo vi guida alla sinergia tra IDP e AI generativa, svelando come rappresentino la nuova frontiera nell’elaborazione dei documenti.

Approfondiamo l’IDP nei dettagli nella nostra serie sull’Elaborazione Intelligente dei Documenti con i servizi di Intelligenza Artificiale di AWS (Parte 1 e Parte 2). In questo articolo, esamineremo come estendere un’architettura IDP nuova o esistente con i modelli linguistici ampi (LLM). Più specificamente, discuteremo di come integrare Amazon Textract con LangChain come caricatore di documenti e Amazon Bedrock per estrarre dati dai documenti e utilizzare le capacità dell’AI generativa nelle varie fasi dell’IDP.

Amazon Textract è un servizio di apprendimento automatico (ML) che estrae automaticamente testo, scrittura a mano e dati da documenti scansionati. Amazon Bedrock è un servizio completamente gestito che offre una scelta di modelli di base ad alta performance (FM) attraverso API facili da usare.

Panoramica della soluzione

Il seguente diagramma è un’architettura di riferimento ad alto livello che spiega come è possibile potenziare ulteriormente un flusso di lavoro IDP con modelli di base. È possibile utilizzare LLM in una o tutte le fasi dell’IDP a seconda del caso d’uso e del risultato desiderato.

In questa architettura, LLM vengono utilizzati per svolgere compiti specifici all’interno del flusso di lavoro IDP.

  • Classificazione dei documenti – Oltre all’utilizzo di Amazon Comprehend, è possibile utilizzare un LLM per classificare i documenti attraverso pochi esempi. La classificazione a pochi esempi consiste nel fornire al modello linguistico alcuni esempi di diverse classi e una lista di tutte le possibili classi, e chiedere poi al modello di classificare un determinato testo estratto da un documento utilizzando una delle classi.
  • Sommario – È possibile utilizzare i LLM anche per riassumere documenti più estesi al fine di fornire sintesi precise all’interno della fase di estrazione dell’IDP. Ad esempio, un sistema di analisi finanziaria potrebbe prevedere l’analisi di centinaia di pagine di documenti di bilancio di un’azienda. È possibile utilizzare un modello linguistico per riassumere gli aspetti chiave dei risultati, consentendo agli analisti di prendere decisioni aziendali.
  • Standardizzazione e domande e risposte in contesto – Oltre all’estrazione di informazioni esatte dai documenti tramite la funzionalità Analizzare Documento di Amazon Textract, è possibile utilizzare i LLM per estrarre informazioni che potrebbero altrimenti non essere esplicitamente dedotte da un documento. Ad esempio, una sintesi di dimissione di un paziente potrebbe riportare la data di ammissione e di dimissione dell’ospedale del paziente, ma potrebbe non specificare esplicitamente il numero totale di giorni in cui il paziente è stato ricoverato. È possibile utilizzare un LLM per dedurre il numero totale di giorni di ricovero, dati i due date estratte da Amazon Textract. Questo valore può poi essere assegnato con un alias ben noto in un formato chiave-valore normalizzato, rendendo ancora più diretto il consumo e il post-processing.
  • Modellazione e normalizzazione – Un flusso di lavoro IDP genera spesso output che deve conformarsi a uno schema specifico deterministico. Ciò è necessario affinché l’output generato utilizzando il flusso di lavoro IDP possa essere consumato in un sistema downstream, ad esempio un database relazionale. Il vantaggio di definire uno schema deterministico è anche quello di ottenere la normalizzazione chiave in modo che abbiamo un insieme noto di chiavi da elaborare nella nostra logica di post-elaborazione. Ad esempio, potremmo voler definire “DOB” come chiave normalizzata per “data di nascita”, “data di nascita”, “data di compleanno”, “data di nascita” e così via, perché i documenti potrebbero presentare qualsiasi variazione di queste. Utilizziamo i LLM per eseguire tale modellazione e l’estrazione di chiavi e valori normalizzati su qualsiasi documento.
  • Correzione degli errori di ortografia e grammaticali – Sebbene Amazon Textract possa estrarre valori esatti da documenti scansionati (stampati o scritti a mano), è possibile utilizzare un modello linguistico per identificare eventuali errori di ortografia e grammaticali nei dati estratti. Questo è importante nelle situazioni in cui i dati possono essere estratti da documenti di scarsa qualità o scritti a mano e utilizzati per generare materiali di marketing, report istantanei, ecc. Oltre a far esaminare manualmente da un essere umano le estrazioni a basso punteggio di Amazon Textract, è possibile utilizzare un LLM per semplificare ulteriormente il processo di revisione fornendo raccomandazioni di correzione al revisore umano, accelerando così il processo di revisione.

Nelle sezioni seguenti, approfondiamo come Amazon Textract è integrato nei flussi di lavoro generativi AI utilizzando LangChain per elaborare documenti per ciascuno di questi compiti specifici. I blocchi di codice forniti qui sono stati ridotti per brevità. Consulta il nostro repository GitHub per quaderni Python dettagliati e una guida passo-passo.

Caricatore di documenti LangChain di Amazon Textract

L’estrazione del testo dai documenti è un aspetto cruciale per quanto riguarda l’elaborazione dei documenti con LLM. Puoi utilizzare Amazon Textract per estrarre testo grezzo non strutturato dai documenti e preservare gli oggetti semi-strutturati o strutturati originali come coppie chiave-valore e tabelle presenti nel documento. Pacchetti di documenti come reclami sanitari e assicurativi o mutui sono composti da moduli complessi che contengono molte informazioni in formati strutturati, semi-strutturati e non strutturati. L’estrazione dei documenti è un passaggio importante qui perché gli LLM traggono vantaggio dal contenuto ricco per generare risposte più accurate e pertinenti, che altrimenti potrebbero influire sulla qualità dell’output degli LLM.

LangChain è un potente framework open-source per l’integrazione con LLM. Gli LLM in generale sono versatili ma possono avere difficoltà con compiti specifici del dominio in cui sono necessari contesti più approfonditi e risposte sfumate. In scenari simili, LangChain offre agli sviluppatori la possibilità di creare agenti in grado di suddividere compiti complessi in sottocompiti più piccoli. I sottocompiti possono quindi introdurre contesto e memoria negli LLM collegando e concatenando prompt LLM.

LangChain offre caricatori di documenti che possono caricare e trasformare dati dai documenti. Puoi utilizzarli per strutturare i documenti in formati preferiti che possono essere elaborati dagli LLM. Il AmazonTextractPDFLoader è un tipo di caricatore di documenti di tipo servizio che fornisce un modo rapido per automatizzare l’elaborazione dei documenti utilizzando Amazon Textract in combinazione con LangChain. Per ulteriori dettagli su AmazonTextractPDFLoader, consulta la documentazione di LangChain. Per utilizzare il caricatore di documenti di Amazon Textract, inizia importandolo dalla libreria LangChain:

from langchain.document_loaders import AmazonTextractPDFLoader

Puoi caricare un documento da un endpoint URL HTTPS così come documenti ospitati nei Amazon Simple Storage Service (Amazon S3) bucket tramite URL oggetto di Amazon S3 (accesso in stile di percorso):

https_loader = AmazonTextractPDFLoader("https://sample-website.com/sample-doc.pdf")
https_document = https_loader.load()
s3_loader = AmazonTextractPDFLoader("s3://sample-bucket/sample-doc.pdf")
s3_document = s3_loader.load()

Puoi anche archiviare documenti in Amazon S3 e fare riferimento ad essi utilizzando il pattern di URL s3://, come spiegato in Accesso a un bucket usando S3://, e passare questo percorso S3 al caricatore di documenti PDF di Amazon Textract:

import boto3textract_client = boto3.client('textract', region_name='us-east-2')
file_path = "s3://amazon-textract-public-content/langchain/layout-parser-paper.pdf"
loader = AmazonTextractPDFLoader(file_path, client=textract_client)
documents = loader.load()

Un documento multipagina conterrà più pagine di testo, che possono quindi essere accessibili tramite l’oggetto dei documenti, che è una lista di pagine. Il codice seguente scorre le pagine dell’oggetto dei documenti e stampa il testo del documento, disponibile tramite l’attributo page_content:

print(len(documents))
for document in documents:
    print(document.page_content)

Classificazione dei documenti

Amazon Comprehend e LLM possono essere utilizzati efficacemente per la classificazione dei documenti. Amazon Comprehend è un servizio di elaborazione del linguaggio naturale (NLP) che utilizza ML per estrarre informazioni dal testo. Amazon Comprehend supporta anche la formazione di modelli di classificazione personalizzati con consapevolezza del layout su documenti come PDF, Word e formati di immagini. Per ulteriori informazioni sull’utilizzo del classificatore di documenti di Amazon Comprehend, consulta Amazon Comprehend document classifier adds layout support for higher accuracy.

Quando abbinati agli LLM, la classificazione dei documenti diventa un approccio efficace per gestire grandi volumi di documenti. Gli LLM sono utili nella classificazione dei documenti perché possono analizzare il testo, i modelli e gli elementi contestuali nel documento utilizzando la comprensione del linguaggio naturale. È anche possibile regolarli per classi di documenti specifiche. Quando viene introdotto un nuovo tipo di documento nella pipeline IDP che richiede una classificazione, l’LLM può elaborare il testo e categorizzare il documento in base a un insieme di classi. Di seguito è riportato un codice di esempio che utilizza il documento di caricamento del documento LangChain, alimentato da Amazon Textract, per estrarre il testo dal documento e utilizzarlo per classificare il documento. Utilizziamo il modello di Anthropic Claude v2 tramite Amazon Bedrock per eseguire la classificazione.

Nell’esempio seguente, prima estraiamo il testo da un rapporto di dimissioni del paziente e utilizziamo un LLM per classificarlo in base a una lista di tre tipi di documenti diversi: DISCHARGE_SUMMARY, RECEIPT e PRESCRIPTION. La seguente schermata mostra il nostro rapporto.

Utilizziamo il seguente codice:

from langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.llms import Bedrockfrom langchain.prompts import PromptTemplatefrom langchain.chains import LLMChainloader = AmazonTextractPDFLoader("./samples/document.png")document = loader.load()template = """Date una lista di classi, classificate il documento in una di queste classi. Saltate eventuali testi introduttivi e semplicemente date il nome della classe.<classes>DISCHARGE_SUMMARY, RECEIPT, PRESCRIPTION</classes><document>{doc_text}<document><classification>"""prompt = PromptTemplate(template=template, input_variables=["doc_text"])bedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")llm_chain = LLMChain(prompt=prompt, llm=bedrock_llm)class_name = llm_chain.run(document[0].page_content)print(f"Il documento fornito è = {class_name}")

Il codice produce il seguente output:

Il documento fornito è un DISCHARGE_SUMMARY

Sommario del documento

La sommarietà prevede la sintesi di un dato testo o documento in una versione più breve mantenendone le informazioni chiave. Questa tecnica è utile per un recupero efficiente delle informazioni, che consente agli utenti di cogliere rapidamente i punti chiave di un documento senza leggere l’intero contenuto. Sebbene Amazon Textract non effettui direttamente la sintesi del testo, fornisce le capacità fondamentali di estrarre l’intero testo dai documenti. Questo testo estratto serve come input per il nostro modello LLM per svolgere compiti di sintesi del testo.

Utilizzando lo stesso esempio di rapporto di dimissioni, utilizziamo AmazonTextractPDFLoader per estrarre il testo da questo documento. Come prima, utilizziamo il modello Claude v2 tramite Amazon Bedrock e lo inizializziamo con una prompt che contiene le istruzioni su cosa fare con il testo (in questo caso, la sintesi). Infine, eseguiamo la catena LLM passando il testo estratto dal caricatore del documento. Questo avvia un’azione di inferenza sull’LLM con la prompt che consiste delle istruzioni per il riassunto e il testo del documento contrassegnato da Document. Ecco il codice seguente:

from langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.llms import Bedrockfrom langchain.prompts import PromptTemplatefrom langchain.chains import LLMChainloader = AmazonTextractPDFLoader("./samples/discharge-summary.png")document = loader.load()template = """Date un documento completo, datemi un breve riassunto. Saltate eventuali testi introduttivi e semplicemente date il riassunto.<document>{doc_text}</document><summary>"""prompt = PromptTemplate(template=template, input_variables=["doc_text"])bedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")num_tokens = bedrock_llm.get_num_tokens(document[0].page_content)print (f"Il nostro prompt ha {num_tokens} token \n\n=========================\n")llm_chain = LLMChain(prompt=prompt, llm=bedrock_llm)summary = llm_chain.run(document[0].page_content)print(summary.replace("</summary>","").strip())

Il codice genera il riepilogo di una relazione di dimissione di un paziente:

Il nostro prompt ha 797 token =========================35 anni M ammesso per dolore addominale epigastrico, nausea, affaticamento. Riscontrata probabile ulcera. Dimesso con restrizioni di attività, antibiotici, cambiamenti nella dieta e follow-up.

Nell’esempio precedente è stato utilizzato un documento a pagina singola per eseguire la sintesi. Tuttavia, è probabile che tu debba trattare documenti contenenti più pagine che necessitano di sintesi. Un modo comune per eseguire la sintesi su più pagine consiste nel generare prima dei riassunti su porzioni più piccole di testo e poi combinare i riassunti più piccoli per ottenere un riassunto finale del documento. Si noti che questo metodo richiede chiamate multiple a LLM. La logica per questo può essere facilmente creata; tuttavia, LangChain fornisce una catena di sintesi integrata che può sintetizzare testi di grandi dimensioni (da documenti a più pagine). La sintesi può avvenire sia tramite l’opzione map_reduce che con le opzioni stuff, che sono disponibili come opzioni per gestire le chiamate multiple a LLM. Nell’esempio seguente, utilizziamo map_reduce per riassumere un documento a più pagine. La figura seguente illustra il nostro flusso di lavoro.

Iniziamo estraendo il documento e vediamo il conteggio totale dei token per pagina e il numero totale di pagine:

from langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.llms import Bedrockbedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")loader = AmazonTextractPDFLoader(f"s3://{data_bucket}/bedrock-sample/health_plan.pdf")document = loader.load()num_docs = len(document)print(f"Nel documento ci sono {num_docs} pagine")for index, doc in enumerate(document):    num_tokens_first_doc = bedrock_llm.get_num_tokens(doc.page_content)    print(f"La pagina {index+1} ha circa {num_tokens_first_doc} token")Nel documento ci sono 5 pagineLa pagina 1 ha circa 533 tokenLa pagina 2 ha circa 1323 tokenLa pagina 3 ha circa 997 tokenLa pagina 4 ha circa 1643 tokenLa pagina 5 ha circa 867 token

Successivamente, utilizziamo la funzione load_summarize_chain integrata in LangChain per riassumere l’intero documento:

from langchain.chains.summarize import load_summarize_chainsummary_chain = load_summarize_chain(llm=bedrock_llm,                    chain_type='map_reduce')output = summary_chain.run(document)print(output.strip())

Standardizzazione e Q&A

In questa sezione, discutiamo delle attività di standardizzazione e Q&A.

Standardizzazione

La standardizzazione del risultato è un’attività di generazione del testo in cui vengono utilizzati LLM per fornire una formattazione coerente del testo di output. Questa attività è particolarmente utile per l’automazione dell’estrazione delle entità chiave che richiede che l’output sia allineato con i formati desiderati. Ad esempio, possiamo seguire le migliori pratiche di engineering del prompt per ottimizzare un LLM per formattare le date nel formato MM/GG/AAAA, che può essere compatibile con una colonna DATA di un database. Il blocco di codice seguente mostra un esempio di come viene fatto utilizzando un LLM e l’engineering del prompt. Non solo standardizziamo il formato di output per i valori di data, ma chiediamo anche al modello di generare l’output finale in un formato JSON in modo che sia facilmente utilizzabile nelle nostre applicazioni downstream. Utilizziamo il LangChain Expression Language (LCEL) per concatenare due azioni. La prima azione chiede all’LLM di generare un output nel formato JSON solo per le date presenti nel documento. La seconda azione prende l’output JSON e standardizza il formato della data. Si noti che questa azione in due passaggi può anche essere eseguita in un singolo passaggio con un adeguato engineering del prompt, come vedremo nella normalizzazione e nella creazione di template.

from langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.llms import Bedrockfrom langchain.prompts import PromptTemplatefrom langchain.chains import LLMChainloader = AmazonTextractPDFLoader("./samples/discharge-summary.png")document = loader.load()bedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")template1 = """Data un documento completo, rispondi alla domanda e formatta l'output nel formato specificato. Salta ogni testo di introduzione e genera solo il JSON.<formato>{{  "nome_chiave":"valore_chiave"}}</formato><documento>{testo_doc}</documento><domanda>{domanda}</domanda>"""template2 = """Data un documento JSON, formatta le date nei campi dei valori precisamente nel formato fornito. Salta ogni testo di introduzione e genera solo il JSON.<formato>GG/MM/AAAA</formato><documento_json>{json_doc}</documento_json>"""prompt1 = PromptTemplate(template=template1, input_variables=["testo_doc", "domanda"])llm_chain = LLMChain(prompt=prompt1, llm=bedrock_llm, verbose=True)prompt2 = PromptTemplate(template=template2, input_variables=["json_doc"])llm_chain2 = LLMChain(prompt=prompt2, llm=bedrock_llm, verbose=True)chain = (     llm_chain     | {'json_doc': lambda x: x['text'] }      | llm_chain2)std_op = chain.invoke({ "testo_doc": document[0].page_content,                         "domanda": "Puoi darmi le date di ammissione e dimissione del paziente?"})print(std_op['text']){  "data_ammissione":"07/09/2020",  "data_dimissione":"08/09/2020"}

L’output dell’esempio di codice precedente è una struttura JSON con le date 07/09/2020 e 08/09/2020, che sono nel formato DD/MM/YYYY e sono la data di ammissione e di dimissione del paziente dall’ospedale, rispettivamente, secondo il rapporto di dimissione.

Q&A con Generazione Potenziata da Recupero

LLM è noto per conservare informazioni fattuali, spesso definito come la sua conoscenza del mondo o visione del mondo. Quando viene addestrato in modo specifico, può produrre risultati all’avanguardia. Tuttavia, ci sono vincoli su quanto efficacemente un LLM può accedere e manipolare questa conoscenza. Di conseguenza, in compiti che fanno affidamento pesantemente su una conoscenza specifica, le loro prestazioni potrebbero non essere ottimali per determinati casi d’uso. Ad esempio, in scenari di domande e risposte, è essenziale che il modello si attenga strettamente al contesto fornito nel documento senza fare affidamento esclusivamente sulla sua conoscenza del mondo. Deviare da questo può portare a rappresentazioni erronee, imprecisioni o addirittura risposte errate. Il metodo più comunemente utilizzato per affrontare questo problema è noto come Generazione Potenziata da Recupero (RAG). Questo approccio sinergizza i punti di forza dei modelli di recupero e dei modelli linguistici, migliorando la precisione e la qualità delle risposte generate.

LLM può anche imporre limitazioni sui token a causa dei vincoli di memoria e delle limitazioni dell’hardware su cui sono eseguiti. Per affrontare questo problema, vengono utilizzate tecniche come il “chunking” per suddividere i grandi documenti in porzioni più piccole che rientrino nei limiti dei token degli LLM. D’altra parte, gli embedding vengono utilizzati nell’elaborazione del linguaggio naturale principalmente per catturare il significato semantico delle parole e le loro relazioni con altre parole in uno spazio ad alta dimensionalità. Questi embedding trasformano le parole in vettori, consentendo ai modelli di elaborare ed comprendere efficacemente i dati testuali. Comprendendo le sfumature semantiche tra parole e frasi, gli embedding consentono agli LLM di generare output coerenti e pertinenti dal punto di vista contestuale. Si notino i seguenti termini chiave:

  • Chunking – Questo processo suddivide grandi quantità di testo da documenti in porzioni di testo più piccole e significative.
  • Embeddings – Si tratta di trasformazioni vettoriali a dimensione fissa di ciascuna porzione che conservano le informazioni semantiche delle porzioni. Questi embedding vengono successivamente caricati in un database vettoriale.
  • Database vettoriale – Si tratta di un database di embedding o vettori di parole che rappresentano il contesto delle parole. Agisce come una fonte di conoscenza che aiuta i compiti di elaborazione del linguaggio naturale nelle pipeline di elaborazione documenti. Il vantaggio del database vettoriale qui è che consente di fornire solo il contesto necessario agli LLM durante la generazione di testo, come spieghiamo nella sezione seguente.

RAG utilizza la potenza degli embedding per comprendere e recuperare segmenti di documenti pertinenti durante la fase di recupero. In questo modo, RAG è in grado di operare all’interno dei limiti di token degli LLM, garantendo la selezione delle informazioni più pertinenti per la generazione, ottenendo output più accurati e pertinenti dal punto di vista contestuale.

Il seguente diagramma illustra l’integrazione di queste tecniche per creare l’input per gli LLM, migliorando la loro comprensione contestuale e consentendo risposte più rilevanti nel contesto. Un approccio coinvolge la ricerca di similarità, utilizzando sia un database vettoriale che il chunking. Il database vettoriale memorizza gli embedding che rappresentano le informazioni semantiche, e il chunking suddivide il testo in sezioni gestibili. Utilizzando questo contesto della ricerca di similarità, gli LLM possono eseguire compiti come la risposta alle domande e operazioni specifiche del dominio, come la classificazione e l’arricchimento.

In questo post, utilizziamo un approccio basato su RAG per eseguire domande e risposte in contesto con i documenti. Nell’esempio seguente, estraiamo il testo da un documento e poi suddividiamo il documento in porzioni più piccole di testo. Il chunking è richiesto perché potremmo avere documenti di grandi dimensioni su più pagine e i nostri LLM potrebbero avere limiti di token. Queste porzioni vengono quindi caricate nel database vettoriale per eseguire la ricerca di similarità nei passaggi successivi. Nell’esempio seguente, utilizziamo il modello Amazon Titan Embed Text v1, che esegue gli embeddings vettoriali delle porzioni del documento:

from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import BedrockEmbeddingsfrom langchain.vectorstores import FAISSfrom langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.chains import RetrievalQAloader = AmazonTextractPDFLoader("amazon_10k.pdf")document = loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=400,                                               separators=["\n\n", "\n", ".", "!", "?", ",", " ", ""],                                               chunk_overlap=0)texts = text_splitter.split_documents(document)embeddings = BedrockEmbeddings(client=bedrock,                               model_id="amazon.titan-embed-text-v1")db = FAISS.from_documents(documents=texts,                           embedding=embeddings) retriever = db.as_retriever(search_type='mmr', search_kwargs={"k": 3})template = """Rispondi alla domanda in modo veritiero usando esclusivamente il testo fornito e se la risposta non è contenuta nel testo, rispondi "Non lo so". Ometti qualsiasi testo preliminare e considerazioni e fornisci solo la risposta.<text>{context}</text><question>{question}</question><answer>"""# definisci il modello di promptaqa_prompt = PromptTemplate(template=template, input_variables=["context","question"])chain_type_kwargs = { "prompt": qa_prompt, "verbose": False } # cambia verbose su True se vuoi vedere cosa succedebedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")qa = RetrievalQA.from_chain_type(    llm=bedrock_llm,     chain_type="stuff",     retriever=retriever,    chain_type_kwargs=chain_type_kwargs,    verbose=False # cambia verbose su True se vuoi vedere cosa succede)question="Chi è l'amministratore di questo piano?"result = qa.run(question)print(result.strip())

Il codice crea un contesto rilevante per il LLM utilizzando i blocchi di testo restituiti dall’azione di ricerca di similarità dal database dei vettori. Per questo esempio, utilizziamo un [archivio di vettori FAISS open source](https://ai.meta.com/tools/faiss/#:~:text=FAISS%20(Facebook%20AI%20Similarity%20Search,more%20scalable%20similarity%20search%20functions.) come un campione di database vettoriale per memorizzare i vettori incorporati di ogni blocco di testo. Successivamente definiamo il database dei vettori come un retriever LangChain, che viene passato alla catena RetrievalQA. Questo esegue internamente una query di ricerca di similarità sul database dei vettori che restituisce i primi n (dove n=3 nel nostro esempio) blocchi di testo rilevanti alla domanda. Infine, la catena LLM viene eseguita con il contesto rilevante (un gruppo di blocchi di testo rilevanti) e la domanda a cui il LLM deve rispondere. Per una guida dettagliata passo-passo della codifica di Q&A con RAG, fare riferimento al notebook Python su GitHub.

Come alternativa a FAISS, è anche possibile utilizzare le capacità di database vettoriale di Amazon OpenSearch Service, Amazon Relational Database Service (Amazon RDS) per PostgreSQL con l’estensione pgvector come database vettoriali, o il database Chroma open source.

Q&A con dati tabulari

I dati tabulari all’interno dei documenti possono essere sfidanti da elaborare per i LLM a causa della loro complessità strutturale. Amazon Textract può essere potenziato con LLM perché consente di estrarre tabelle dai documenti in un formato nidificato di elementi come pagina, tabella e celle. L’esecuzione di Q&A con dati tabulari è un processo a più passaggi e può essere realizzato tramite auto-interrogazione. Di seguito viene fornuta una panoramica dei passaggi:

  1. Estrarre le tabelle dai documenti utilizzando Amazon Textract. Con Amazon Textract, è possibile estrarre la struttura tabellare (righe, colonne, intestazioni) da un documento.
  2. Memorizzare i dati tabulari in un database vettoriale insieme alle informazioni sui metadati, come i nomi delle intestazioni e la descrizione di ciascuna intestazione.
  3. Utilizzare il prompt per costruire una query strutturata, utilizzando un LLM, per estrarre i dati dalla tabella.
  4. Utilizzare la query per estrarre i dati tabulari rilevanti dal database vettoriale.

Ad esempio, in un estratto conto bancario, dato il prompt “Quali sono le transazioni con più di $1000 in depositi”, il LLM eseguirebbe i seguenti passaggi:

  1. Creare una query, come "Query: transazioni", "filtro: maggiore di (Deposito $)".
  2. Convertire la query in una query strutturata.
  3. Applicare la query strutturata al database vettoriale in cui sono memorizzati i nostri dati tabellari.

Per una guida dettagliata passo-passo della codifica di Q&A con dati tabulari, fare riferimento al notebook Python su GitHub.

Modellazione e normalizzazioni

In questa sezione, vediamo come utilizzare le tecniche di modellazione del prompt e il meccanismo integrato di LangChain per generare un output con estrazioni da un documento in uno schema specificato. Eseguiamo anche una standardizzazione sui dati estratti, utilizzando le tecniche precedentemente discusse. Iniziamo definendo un modello per il nostro output desiderato. Questo servirà come schema e includerà i dettagli su ogni entità che vogliamo estrarre dal testo del documento.

output_template= {    "nome_medico":{ "type": "string", "description": "Il nome completo del medico o del fornitore" },    "id_fornitore":{ "type": "string", "description": "L'ID del medico o del fornitore" },    "nome_paziente":{ "type": "string", "description": "Il nome completo del paziente" },    …}

Note che per ciascuna delle entità, utilizziamo la descrizione per spiegare cosa sia quell’entità e per aiutare il LLM a estrarre il valore dal testo del documento. Nel seguente codice di esempio, utilizziamo questo template per creare il prompt per il LLM insieme al testo estratto dal documento utilizzando AmazonTextractPDFLoader e successivamente eseguiamo l’inferenza con il modello:

from langchain.llms import Bedrockfrom langchain.prompts import PromptTemplatefrom langchain.chains import LLMChaintemplate = """Sei un assistente utile. Estrai i seguenti dettagli dal documento e formatta l'output come JSON utilizzando le chiavi. Ometti qualsiasi testo preliminare e genera la risposta finale.<details>{details}</details><keys>{keys}</keys><document>{doc_text}<document><final_answer>"""details = "\n".join([f"{key}: {value['description']}" for key, value in output_template.items()])keys = "\n".join([f"{key}" for key, value in output_template.items()])prompt = PromptTemplate(template=template, input_variables=["details", "keys", "doc_text"])bedrock_llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")llm_chain = LLMChain(prompt=prompt, llm=bedrock_llm)output = llm_chain.run({"doc_text": full_text, "details": details, "keys": keys})print(output){  "doctor_name": "Mateo Jackson, Phd",  "provider_id": "XA/7B/00338763",   "patient_name": "John Doe",  … }

Come puoi vedere, la parte {keys} del prompt è costituita dalle chiavi del nostro template e la parte {details} sono le chiavi insieme alla loro descrizione. In questo caso, non sollecitiamo esplicitamente il modello con il formato dell’output se non specificando nelle istruzioni di generare l’output in formato JSON. Questo funziona nella maggior parte dei casi; tuttavia, poiché l’output degli LLM è una generazione di testo non deterministica, vogliamo specificare il formato esplicitamente come parte dell’istruzione nel prompt. Per risolvere questo problema, possiamo utilizzare il modulo parser di output strutturato di LangChain per sfruttare la prompt engineering automatizzata che ci aiuta a convertire il nostro template in un’istruzione di formato prompt. Utilizziamo il template definito in precedenza per generare l’istruzione di formato prompt come segue:

from langchain.output_parsers import ResponseSchemafrom langchain.output_parsers import StructuredOutputParserresponse_schems = list()for key, value in output_template.items():    schema = ResponseSchema(name=key, description=value['description'], type=value['type'])    response_schems.append(schema)output_parser = StructuredOutputParser.from_response_schemas(response_schems)format_instructions= output_parser.get_format_instructions()print(format_instructions)

La variabile format_instructions contiene ora l’istruzione di formato prompt:

L'output deve essere un frammento di codice markdown formattato secondo lo schema seguente, inclusi "```json" iniziale e finale:"```json{ "doctor_name": string  // Nome completo del medico o fornitore    "provider_id": string  // ID del medico o fornitore   "patient_name": string  // Nome completo del paziente        …}```

Utilizziamo quindi questa variabile all’interno del nostro prompt originale come istruzione per il LLM in modo che estragga e formatti l’output nello schema desiderato apportando una piccola modifica al nostro prompt:

template = """Sei un assistente utile. Estrai i seguenti dettagli dal documento e segui rigorosamente le istruzioni descritte nelle istruzioni di formato per formattare l'output. Ometti qualsiasi testo preliminare e genera la risposta finale. Non generare risposte incomplete.<details>{details}</details><format_instructions>{format_instructions}</format_instructions><document>{doc_text}<document><final_answer>"""

Fino ad ora, abbiamo solo estratto i dati dal documento in uno schema desiderato. Tuttavia, è ancora necessario eseguire una normalizzazione. Ad esempio, vogliamo che la data di ammissione del paziente e la data di dimissione vengano estratte nel formato GG/MM/AAAA. In questo caso, ampliamo la description della chiave con l’istruzione di formattazione:

new_output_template= {       …     "admitted_date":{ "type": "string",  "description": "Data in cui il paziente è stato ammesso in ospedale, deve essere formattata secondo il formato GG/MM/AAAA." },    "discharge_date":{ "type": "string",  "description": "Data in cui il paziente è stato dimesso dall'ospedale, deve essere formattata secondo il formato GG/MM/AAAA."     …}

Fai riferimento al notebook Python su GitHub per una guida passo passo completa e una spiegazione dettagliata.

Controllo ortografico e correzioni

Le LLM hanno dimostrato notevoli capacità nel comprendere e generare testi simili a quelli umani. Una delle applicazioni meno discusse ma estremamente utili delle LLM è il loro potenziale per il controllo grammaticale e la correzione di frasi in documenti. A differenza dei tradizionali correttori grammaticali che si basano su un insieme di regole predefinite, le LLM utilizzano modelli che hanno identificato da una vasta quantità di dati testuali per determinare cosa costituisce un linguaggio corretto o fluente. Ciò significa che possono rilevare sfumature, contesti e sottilità che i sistemi basati su regole potrebbero non rilevare.

Immagina il testo estratto da una lettera di dimissione del paziente che recita “Paziente Jon Doe, che è stato ammesso con una pnemonia grave, ha mostrato un miglioramento significativo e può essere dimesso in sicurezza. I follow-up sono programmati per la prossima settimana”. Un correttore ortografico tradizionale potrebbe riconoscere “admittd”, “pneumonia”, “improvemnt” e “nex” come errori. Tuttavia, il contesto di questi errori potrebbe portare a ulteriori errori o suggerimenti generici. Una LLM, dotata della sua formazione estesa, potrebbe suggerire: “Paziente John Doe, che è stato ammesso con una polmonite grave, ha mostrato un miglioramento significativo e può essere dimesso in sicurezza. I follow-up sono programmati per la prossima settimana”.

Di seguito è riportato un documento campione scritto male con lo stesso testo spiegato in precedenza.

Estraiamo il documento con un caricatori di documenti Amazon Textract e quindi istruiremo la LLM, tramite l’ingegneria dei prompt, a rettificare il testo estratto per correggere eventuali errori di ortografia o grammatica:

from langchain.document_loaders import AmazonTextractPDFLoaderfrom langchain.llms import Bedrockfrom langchain.prompts import PromptTemplatefrom langchain.chains import LLMChainloader = AmazonTextractPDFLoader("./samples/hand_written_note.pdf")document = loader.load()template = """Dato un dettagliato 'Documento', effettua correzioni grammaticali e di ortografia. Assicurati che l'output sia coerente, curato e privo di errori. Salta qualsiasi testo di introduzione e dai la risposta.<document>{doc_text}</<document><answer>"""prompt = PromptTemplate(template=template, input_variables=["doc_text"])llm = Bedrock(client=bedrock, model_id="anthropic.claude-v2")llm_chain = LLMChain(prompt=prompt, llm=llm)try:    txt = document[0].page_content    std_op = llm_chain.run({"doc_text": txt})        print("Testo estratto")    print("==============")    print(txt)    print("\nTesto corretto")    print("==============")    print(std_op.strip())    print("\n")except Exception as e:    print(str(e))

L’output del codice precedente mostra il testo originale estratto dal caricatore di documenti seguito dal testo corretto generato dalla LLM:

Testo estratto==============Paziente John Doe, che è stato ad mitta con pnequonia severa, ha mostrato Improvemente impression i art & può essere dimesso in sicurezza. Segua w/s sono programmati per nen week. Paziente John Doe, che è stato ad mitta con pnequonia severa, ha mostrato Improvemente impression i art & può essere dimesso in sicurezza. Segua w/s sono programmati per nen week. Testo corretto==============Paziente John Doe, che è stato ammesso con una polmonite grave, ha mostrato un miglioramento significativo e può essere dimesso in sicurezza. Gli appuntamenti di follow-up sono programmati per la prossima settimana.

Tieni presente che, per quanto potenti possano essere le LLM, è essenziale considerare le loro suggerimenti come appunto suggerimenti. Anche se catturano in modo impressionante le sfumature del linguaggio, non sono infallibili. Alcuni suggerimenti potrebbero modificare il significato o il tono del testo originale. Pertanto, è fondamentale che i revisori umani utilizzino le correzioni generate dalle LLM come guida, non come assolute. La collaborazione tra l’intuito umano e le capacità delle LLM promette un futuro in cui la nostra comunicazione scritta sarà non solo priva di errori, ma anche più ricca e sfumata.

Conclusioni

L’IA generativa sta cambiando il modo in cui è possibile elaborare i documenti con IDP per ottenere informazioni. Nel post Enhancing AWS intelligent document processing with generative AI abbiamo discusso delle varie fasi del flusso di lavoro e di come il cliente di AWS Ricoh sta migliorando il proprio flusso di lavoro IDP con LLM tramite Amazon Bedrock. In questo post, abbiamo discusso vari meccanismi per migliorare il flusso di lavoro IDP con LLM tramite Amazon Bedrock, Amazon Textract e il popolare framework LangChain. Puoi iniziare con il nuovo caricatore di documenti Amazon Textract con LangChain oggi utilizzando i notebook di esempio disponibili nel nostro repository GitHub. Per ulteriori informazioni su come lavorare con l’IA generativa su AWS, consulta Announcing New Tools for Building with Generative AI on AWS.