RAG QA con autovalutazione II
Per questa variazione, apportiamo una modifica alla procedura di valutazione. Oltre alla coppia domanda-risposta, passiamo anche il contesto recuperato al valutatore LLM.
Per ottenere ciò, aggiungiamo un'ulteriore funzione itemgetter nel secondo RunnableParallel per raccogliere la stringa di contesto e passarla al nuovo modello di prompt qa_eval_prompt_with_context.
rag_chain = (
RunnableParallel(context = retriever | format_docs, question = RunnablePassthrough() ) |
RunnableParallel(answer= qa_prompt | llm | retrieve_answer, question = itemgetter("question"), context = itemgetter("context") ) |
qa_eval_prompt_with_context |
llm_selfeval |
json_parser
)
Diagramma di flusso dell'implementazione:
Uno dei punti critici comuni nell'utilizzo di un'implementazione a catena come LCEL è la difficoltà nell'accesso alle variabili intermedie, che è importante per il debug delle pipeline. Esaminiamo alcune opzioni in cui possiamo ancora accedere a qualsiasi variabile intermedia che ci interessa utilizzando le manipolazioni dell'LCEL
Utilizzo di RunnableParallel per portare avanti gli output intermedi
Come abbiamo visto in precedenza, RunnableParallel ci consente di portare avanti più argomenti al passaggio successivo della catena. Quindi utilizziamo questa capacità di RunnableParallel per portare avanti i valori intermedi richiesti fino alla fine.
Nell'esempio seguente, modifichiamo la catena RAG di autovalutazione originale per produrre il testo di contesto recuperato insieme all'output finale di autovalutazione. La modifica principale è che aggiungiamo un oggetto RunnableParallel a ogni fase del processo per portare avanti la variabile di contesto.
Inoltre, utilizziamo anche la funzione itemgetter per specificare chiaramente gli input per i passaggi successivi. Ad esempio, per gli ultimi due oggetti RunnableParallel, utilizziamo itemgetter('input') per garantire che solo l'argomento di input del passaggio precedente venga trasmesso agli oggetti parser LLM/Json.
rag_chain = (
RunnableParallel(context = retriever | format_docs, question = RunnablePassthrough() ) |
RunnableParallel(answer= qa_prompt | llm | retrieve_answer, question = itemgetter("question"), context = itemgetter("context") ) |
RunnableParallel(input = qa_eval_prompt, context = itemgetter("context")) |
RunnableParallel(input = itemgetter("input") | llm_selfeval , context = itemgetter("context") ) |
RunnableParallel(input = itemgetter("input") | json_parser, context = itemgetter("context") )
)
L'output di questa catena è simile al seguente:
Una variazione più concisa:
rag_chain = (
RunnableParallel(context = retriever | format_docs, question = RunnablePassthrough() ) |
RunnableParallel(answer= qa_prompt | llm | retrieve_answer, question = itemgetter("question"), context = itemgetter("context") ) |
RunnableParallel(input = qa_eval_prompt | llm_selfeval | json_parser, context = itemgetter("context"))
)
Utilizzo delle variabili globali per salvare i passaggi intermedi
Questo metodo utilizza essenzialmente il principio di un logger. Introduciamo una nuova funzione che salva il suo input in una variabile globale, permettendoci così di accedere alla variabile intermedia attraverso la variabile globale
global contextdef save_context(x):
global context
context = x
return x
rag_chain = (
RunnableParallel(context = retriever | format_docs | save_context, question = RunnablePassthrough() ) |
RunnableParallel(answer= qa_prompt | llm | retrieve_answer, question = itemgetter("question") ) |
qa_eval_prompt |
llm_selfeval |
json_parser
)
Qui definiamo una variabile globale chiamata contesto e una funzione chiamata salva_contesto che salva il suo valore di input nel globale contesto variabile prima di restituire lo stesso input. Nella catena aggiungiamo il salva_contesto funzionare come l'ultimo passaggio della fase di recupero del contesto.
Questa opzione consente di accedere a eventuali passaggi intermedi senza apportare modifiche importanti alla catena.
Utilizzo dei callback
Allegare callback alla catena è un altro metodo comune utilizzato per registrare valori di variabili intermedie. C'è molto da trattare sull'argomento dei callback in LangChain, quindi lo tratterò in dettaglio in un post diverso.
Fonte: towardsdatascience.com