Callback e strutture pipeline in LangChain |  di Roshan Santhosh |  Aprile 2024

 | Intelligenza-Artificiale

Iniziamo con una semplice catena fittizia composta da 3 componenti: 2 prompt e una funzione personalizzata per unirli. Mi riferisco a questo come un esempio fittizio perché è molto improbabile che siano necessari due prompt separati per interagire tra loro, ma costituisce un esempio più semplice con cui iniziare per comprendere i callback e le pipeline LangChain.

Esempio 1: struttura di base della pipeline LangChain

L'implementazione di questo nel codice sarebbe simile a:

Implementazione della pipeline per l'esempio 1

Il codice sopra è roba da manuale. L'unico pezzo forse complesso è il recuperare_testo E EseguibileLambda funzione utilizzata qui. Il motivo per cui ciò è necessario è perché il formato dell'output da qa_prompt1 non è compatibile con il formato dell'output richiesto da qa_prompt2.

Definizione della richiamata personalizzata

Per la nostra callback personalizzata, definiamo una nuova sottoclasse di BaseCallbackHandler chiamata CustomCallback1 che definisce il on_chain_start metodo. La definizione del metodo è semplice in quanto prende semplicemente i valori di input che gli vengono passati e li salva in 2 variabili specifiche: catena_input E input_serializzato

Richiamo della richiamata personalizzata

Esempio 1: invocazione con pipeline con callback personalizzato

Il codice precedente mostra uno dei modi possibili per passare il callback personalizzato alla pipeline: come elenco di oggetti callback come valore a una chiave corrispondente di “callback”. Questo rende anche facile indovinarlo puoi passare più callback alla tua pipeline LangChain.

Decodificare la struttura di callback/pipeline

Ora arriva la parte interessante. Dopo aver definito i callback e averli passati alla nostra pipeline, eseguiamo ora un'analisi approfondita degli output dei callback

Per prima cosa esaminiamo i valori memorizzati in catena_input

Esempio 1: contenuto della variabile chain_input del gestore callback

Osservazioni:

  1. Anche se ci sono 3 componenti nella nostra catena, ci sono 4 valori dentro catena_input. Che corrisponde a on_chain_start il metodo viene attivato 4 volte invece di 3.
  2. Per i primi due catena_input valori/trigger on_chain_start, l'input è lo stesso fornito dall'utente.

Successivamente esamineremo gli output di input_serializzato

Osservazioni:

  1. Il primo componente è a Sequenza eseguibile che è un componente che non è stato aggiunto dall'utente ma è stato aggiunto automaticamente da LangChain. Il resto dei componenti corrisponde direttamente ai componenti definiti dall'utente nella pipeline.
  2. Il contenuto completo di serialized_input è ampio! Sebbene esista una struttura definita per quel contenuto, è decisamente fuori dall'ambito di questo post e probabilmente non ha molte implicazioni pratiche per l'utente finale.

Come interpretiamo questi risultati

Per la maggior parte, gli output visti in catena_input E input_serializzato ha senso. Che si tratti dei valori di input o dei nomi/ID dei componenti. L'unica parte in gran parte sconosciuta è il Sequenza eseguibile componente, quindi diamo un'occhiata più da vicino a questo.

Come ho detto in precedenza, l'intero contenuto di input_serializzato è abbondante e non facile da digerire. Quindi, per rendere le cose più semplici, esamineremo solo gli attributi di alto livello descritti in input_serializzato e provare a interpretare i risultati attraverso questi attributi. Per questo, utilizziamo una funzione di debug personalizzata chiamata getChainBreakdown (codice sul taccuino).

Noi chiamiamo getChainBreakdown su tutti i valori di input_serializzato e osservare l'output. Specificamente per il primo Sequenza eseguibile elemento, guardiamo le chiavi del dict kwargs: primo, mezzo, cognome, nome.

Ad un esame più attento dell'argomento kwargs e dei loro valori, vediamo che hanno la stessa struttura dei nostri precedenti componenti della pipeline. Infatti, il primo, il medio e l'ultimo componente corrispondono esattamente ai componenti definiti dall'utente della pipeline.

Ispezione più approfondita dei valori kwargs di RunnableSequence

I dettagli di cui sopra costituiscono la base della conclusione finale che traiamo qui. La struttura della pipeline è quella mostrata di seguito:

Esempio 1: struttura della pipeline LangChain

Facciamo un piccolo salto qui poiché il diagramma di flusso sopra è stato confermato dopo aver esaminato una serie di esempi e osservato il formato in cui questi componenti vengono creati internamente da LangChain. Quindi abbi pazienza mentre esaminiamo questi altri esempi che consolideranno la conclusione che traiamo qui.

Con la struttura sopra definita, gli altri pezzi del puzzle si incastrano abbastanza bene. Concentrandosi sui valori chain_input, mappateli ai componenti (con il loro ordinamento) definiti sopra.

Esempio 1: mappatura dei valori chain_input ai componenti della pipeline

Osservazioni:

  1. Per RunnableSequence, poiché agisce come un wrapper per l'intera pipeline, l'input dell'utente funge anche da input per il componente RunnableSequence.
  2. Per il primo ChatPromptTemplate (qa_prompt1), in quanto primo componente “vero” della pipeline, riceve l'input diretto dall'utente
  3. Per RunnableLambda (retrieve_text), riceve come input l'output da qa_prompt1, che è un oggetto Message
  4. Per l'ultimo ChatPromptTemplate (qa_prompt2), riceve come input l'output da retrieve_text, che è un dict con “prompt” come unica chiave

La suddivisione sopra riportata mostra come la struttura della pipeline sopra descritta si adatti perfettamente ai dati visti in input_serializzato E catena_input

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *