-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path07-decomposition.py
158 lines (125 loc) · 5.3 KB
/
07-decomposition.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import dotenv
import bs4
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from operator import itemgetter
from langchain import hub
def retrieve_and_rag(question, prompt_rag, sub_question_generator_chain, retriever):
"""RAG on each sub-question"""
# Use our decomposition
sub_questions = sub_question_generator_chain.invoke({"question": question})
# Initialize a list to hold RAG chain results
rag_results = []
for sub_question in sub_questions:
# Retrieve documents for each sub-question
retrieved_docs = retriever.invoke(sub_question)
# Use retrieved documents and sub-question in RAG chain
answer = (
prompt_rag
| ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0)
| StrOutputParser()
).invoke({"context": retrieved_docs, "question": sub_question})
rag_results.append(answer)
return rag_results, sub_questions
def format_qa_pairs(questions, answers):
"""Format Q and A pairs"""
formatted_string = ""
for i, (question, answer) in enumerate(zip(questions, answers), start = 1):
formatted_string += f"Question {i}: {question}\nAnswer {i}: {answer}\n\n"
return formatted_string.strip()
if __name__ == "__main__":
dotenv.load_dotenv()
loader = WebBaseLoader(
web_paths=(
"https://lilianweng.github.io/posts/2023-06-23-agent/",
),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
)
)
blog_docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300,
chunk_overlap=50
)
splits = text_splitter.split_documents(blog_docs)
vectorstore = Chroma.from_documents(
documents=splits,
embedding=GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
)
retriever = vectorstore.as_retriever()
# Decomposition
template = """You are a helpful assistant that generates multiple sub-questions related to an input question. \n
The goal is to break down the input into a set of sub-problems / sub-questions that can be answers in isolation. \n
Generate multiple search queries related to: {question} \n
Output with interrogative sentence (3 queries):""" # gemini가 하도 의문문으로 질문을 생성 안해서 의문문으로 만들어달라고 추가함
prompt_decomposition = ChatPromptTemplate.from_template(template)
# decomposition chain
generate_queries_decomposition = (
prompt_decomposition
| ChatGoogleGenerativeAI(model="gemini-1.5-pro", temperature=0)
| StrOutputParser()
| (lambda x: x.strip().split("\n"))
)
# Run
question = "What are the main components of an LLM-powered autonomous agent system?"
questions = generate_queries_decomposition.invoke({"question": question})
print(questions)
"""Answer Resursively"""
# Prompt
template = """Here is the question you need to answer:
\n --- \n {question} \n --- \n
Here is any available background question + answer pairs:
\n --- \n {q_a_pairs} \n --- \n
Here is additional context relevant to the question:
\n --- \n {context} \n --- \n
Use the above context and any background question + answer pairs to answer the question: \n {question}
"""
decomposition_prompt = ChatPromptTemplate.from_template(template)
q_a_pairs = ""
for q in questions:
rag_chain = (
dict(
context=itemgetter("question") | retriever,
question=itemgetter("question"),
q_a_pairs=itemgetter("q_a_pairs")
)
| decomposition_prompt
| ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0)
| StrOutputParser()
)
answer = rag_chain.invoke({
"question": q,
"q_a_pairs": q_a_pairs
})
q_a_pair = f"Question: {q}\nAnswer: {answer}\n\n".strip()
q_a_pairs = q_a_pairs + "\n---\n" + q_a_pair
print("Answer Resursively")
print(answer)
print("---")
"""Answer Individually"""
# RAG prompt
prompt_rag = hub.pull("rlm/rag-prompt")
# Wrap the retrieval and RAG process in a RunnableLambda for integration into a chain
answers, questions = retrieve_and_rag(question, prompt_rag, generate_queries_decomposition, retriever)
context = format_qa_pairs(questions, answers)
# Prompt
template = """Here is a set of Q+A pairs:
{context}
Use these to synthesize an answer to the question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
final_rag_chain = (
prompt
| ChatGoogleGenerativeAI(model="gemini-1.5-flash")
| StrOutputParser()
)
print("Answer Individually")
print(final_rag_chain.invoke({"context": context, "question": question}))
print("----")