from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough, RunnableLambda, RunnableParallel from langchain_openai import ChatOpenAI from langchain.prompts import PromptTemplate from langchain_community.utilities import GoogleSerperAPIWrapper from typing import List from operator import itemgetter CUSTOM_RAG_PROMPT = """ Використай наступні **надійні** елементи, для того, щоб вибрати відповідь на питання з запропонованих. Якщо вони не містять відповіді, зверни увагу на інформацію з інтернету. Якщо ти не знаєш відповіді, використаши всі свої джерела, то просто скажи про це, не потрібно вигадувати відповідь. Напиши у відповіді номер правильного варіанту відповіді. Якщо серед варантів немає правильної відповіді, напиши коротко відповідь самостійно. {context} Відповідь з інтернету: {internet} Приклад: Чия типологія поділяється на традиційні, харизматичні й раціональні системи? 1) Вебер 2) Ленін 3) Сталін 4) Обама Правильна відповідь: 1 - Вебер. Питання: {question} Варіанти відповіді: {options} Правильна відповідь:""" CUSTOM_RAG_PROMPT = PromptTemplate.from_template(CUSTOM_RAG_PROMPT) VERIFICATION_PROMPT = """ Вам було задано наступне питання: {question} З варіантами відповіді: {options} На яку було запропоновано відповідь: {answer} Повторіть відповідь, якщо вона правильна. Інакше, скажіть "відповідь відсутня". Відповідь: """ VERIFICATION_PROMPT = PromptTemplate.from_template(VERIFICATION_PROMPT) def documents_parser(docs): return "\n\n".join(doc.page_content for doc in docs) def prepare_options(options): return "\n".join([f"{i + 1}) {option}" for i, option in enumerate(options)]) def flatten_input(d): ret = d.pop('input') ret.update(d) return ret class PdfAndGoogleChain: def use_google_search(self, query): try: return self.search.run(query) except Exception as ex: return 'NONE' def retrieve_multiple(self, query_dict): query = query_dict['query'] options = query_dict['options'] ret = self.retriever.get_relevant_documents(query) for option in options: ret.extend(self.retriever.get_relevant_documents(option)[:2]) return ret def __init__(self, retriever, llm_name: str = "gpt-3.5-turbo-0125"): self.search = GoogleSerperAPIWrapper() self.retriever = retriever self.llm = ChatOpenAI(model=llm_name) self.rag_chain = ( {"context": RunnableLambda(self.retrieve_multiple) | documents_parser, "internet": itemgetter("query") | RunnableLambda(self.use_google_search), "question": itemgetter("query") | RunnablePassthrough(), "options": itemgetter("options") | RunnableLambda(prepare_options)} | CUSTOM_RAG_PROMPT | self.llm | StrOutputParser() ) self.verification_chain = ( {"question": itemgetter("query") | RunnablePassthrough(), "options": itemgetter("options") | RunnableLambda(prepare_options), "answer": itemgetter("answer") | RunnablePassthrough()} | VERIFICATION_PROMPT | self.llm | StrOutputParser() ) self.global_chain = (RunnableParallel(input=RunnablePassthrough(), answer=self.rag_chain) | RunnableLambda(flatten_input) | self.verification_chain) def answer(self, query: str, options: List[str]): options = list(filter(lambda x: x is not None and len(x) > 0, options)) return self.global_chain.invoke({"query": query, "options": options})