Introduction
The code you've provided defines an AI assistant called AgenticRAG (Agentic Retrieval-Augmented Generation). This assistant can:
Let's break down how this code works in simple terms.
1. Importing Necessary Libraries
At the beginning, the code imports several libraries:
2. Setting Up Logging
logging.basicConfig(level=logging.DEBUG)This sets the logging level to DEBUG, which means all debug messages will be shown. This helps track what's happening in the code.
3. Defining the AgenticRAG Class
class AgenticRAG:
def __init__(self, api_key: str):
...The AgenticRAG class contains all the functions needed for the assistant to work. When you create an instance of this class, it:
4. Initializing the OpenAI Client
self.client = OpenAI(api_key=api_key)This creates a client to interact with OpenAI's language models using your API key.
5. Loading the Embedding Model
self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')This loads a pre-trained model that can convert sentences into numerical vectors (embeddings) that capture their meanings.
6. Preparing the Knowledge Base
self.chunks = []
self.embeddings = []
self.load_knowledge_base()7. Loading and Processing the Knowledge Base
def load_knowledge_base(self):
...Reading the File: Opens and reads the knowledge.txt file.
knowledge_path = Path(__file__).parent / "knowledge.txt"
with open(knowledge_path, 'r', encoding='utf-8') as f:
content = f.read()Splitting into Sentences: Breaks the text into individual sentences.
sentences = content.split(". ")Creating Chunks: Groups sentences into chunks of about 100 sentences.
chunk_size = 100
self.chunks = ['. '.join(sentences[i:i + chunk_size]) for i in range(0, len(sentences), chunk_size)]Generating Embeddings: Converts each chunk into a numerical vector.
self._generate_embeddings()8. Generating Embeddings
def _generate_embeddings(self):
self.embeddings = self.embedding_model.encode(self.chunks, convert_to_tensor=True, normalize_embeddings=True)
logging.debug("Generated embeddings for all chunks.")9. Retrieving Relevant Chunks
def retrieve_relevant_chunks(self, query: str) -> str:
...This function finds the most relevant pieces of the knowledge base to answer a question.
Embedding the Query: Converts the user's question into an embedding.
query_embedding = self.embedding_model.encode(query, convert_to_tensor=True, normalize_embeddings=True)Preparing Embeddings: Moves data to the CPU and converts it to a NumPy array for processing.
chunk_embeddings = self.embeddings.cpu().numpy()
query_embedding_np = query_embedding.cpu().numpy().reshape(1, -1)Calculating Similarities: Measures how similar each chunk is to the query.
similarities = cosine_similarity(chunk_embeddings, query_embedding_np).flatten()Selecting Top Chunks: Picks the top 3 most similar chunks.
top_indices = np.argsort(similarities)[-3:][::-1]Combining Chunks: Gathers the relevant chunks into a single string.
relevant_chunks = [self.chunks[i] for i in top_indices]
return "\n\n".join(relevant_chunks)10. Performing Calculations
def calculator(self, expression: str) -> str:
...This function safely evaluates mathematical expressions.
Allowed Operations: Defines which mathematical operations are permitted.
allowed_operators = {ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.USub}Evaluating the Expression: Parses and evaluates the expression step by step, ensuring only allowed operations are used.
node = ast.parse(expression, mode='eval').body
result = eval_node(node)
return str(result)11. Running the Agentic RAG Process
def run_agentic_rag(self, question: str) -> str:
...This is where the assistant answers the question by deciding whether to retrieve information, perform calculations, or both.
Setting Up the Conversation: Starts a conversation with a system prompt that instructs the assistant on how to behave.
conversation = []
system_prompt = """You are an AI assistant that can access a knowledge base using the 'retriever' tool and perform calculations using the 'calculator' tool.
...
"""
conversation.append({"role": "system", "content": system_prompt})
conversation.append({"role": "user", "content": f"Question: {question}"})Agent Loop: Allows the assistant to think and act in several steps (up to 5 iterations).
max_iterations = 5
for _ in range(max_iterations):
...Getting the Assistant's Response: Sends the conversation to the OpenAI model to get the next response.
response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=conversation,
max_tokens=500,
temperature=0.7
)
agent_reply = response.choices[0].message.content.strip()
conversation.append({"role": "assistant", "content": agent_reply})Parsing the Response: Checks if the assistant provided an answer or wants to perform an action.
answer_match = re.search(r"Answer:\s*(.*)", agent_reply, re.DOTALL)
if answer_match:
final_answer = answer_match.group(1).strip()
return final_answeraction_match = re.search(r"Action:\s*(\w+)\s*Action Input:\s*(.*?)(?:(?:\nThought:)|$)", agent_reply, re.DOTALL)Performing the Action:
if action == 'retriever':
observation = self.retrieve_relevant_chunks(action_input)
observation_text = f"Observation: {observation}"elif action == 'calculator':
observation = self.calculator(action_input)
observation_text = f"Observation: {observation}"else:
observation_text = f"Observation: Invalid action '{action}'."conversation.append({"role": "assistant", "content": observation_text})Handling Format Issues: If the assistant doesn't follow the expected format, it informs the assistant.
else:
conversation.append({"role": "assistant", "content": "Observation: The assistant did not follow the required format."})Ending the Loop: If no answer is provided after maximum iterations, it returns a message.
12. Running the Standard RAG Process
def run_standard_rag(self, question: str) -> str:
...This function answers the question by retrieving relevant information but without the step-by-step reasoning.
Retrieving Context: Gets relevant information from the knowledge base.
context = self.retrieve_relevant_chunks(question)Creating the Prompt: Prepares the prompt to send to the assistant.
prompt = f"""Use the following context to answer the question.
If the context doesn't contain relevant information, use your general knowledge.
Context:
{context}
Question: {question}
Answer:"""Getting the Response: Sends the prompt to the assistant and gets the answer.
messages = [
{"role": "system", "content": "You are a helpful AI assistant. Answer questions based on the provided context."},
{"role": "user", "content": prompt}
]
response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
max_tokens=150,
temperature=0.7
)
return response.choices[0].message.content.strip()13. How to Use the AgenticRAG Class
Initialize the Assistant:
api_key = "your_openai_api_key"
agentic_rag = AgenticRAG(api_key=api_key)Ask a Question with Agentic RAG:
question = "If I have 5 apples and buy 7 more, how many apples do I have in total?"
response = agentic_rag.run_agentic_rag(question)
print("Agentic RAG Response:")
print(response)Ask a Question with Standard RAG:
response = agentic_rag.run_standard_rag(question)
print("Standard RAG Response:")
print(response)14. Key Points to Remember
knowledge.txt) to find relevant information."gpt-4o-mini" with the model you have access to, such as "gpt-3.5-turbo".15. Final Notes
knowledge.txt with relevant information for the assistant to use.This assistant combines advanced AI techniques to provide accurate and helpful answers by both retrieving information and performing calculations as needed. It's designed to think through the problem and decide the best way to help you.