diff --git a/langchain-crash-course/4_rag/logs/rag_application.log b/langchain-crash-course/4_rag/logs/rag_application.log index 2ac8504..8bf56f2 100644 --- a/langchain-crash-course/4_rag/logs/rag_application.log +++ b/langchain-crash-course/4_rag/logs/rag_application.log @@ -139,4 +139,4 @@ 2025-08-26 23:24:09,222 - 8_rag_web_scrape_firecrawl.py - INFO - Querying vector store 'chroma_db_web_scrape_firecrawl'... 2025-08-26 23:24:09,222 - 8_rag_web_scrape_firecrawl.py - INFO - Vector store 'chroma_db_web_scrape_firecrawl' already exists. No need to initialize. 2025-08-26 23:24:10,098 - 8_rag_web_scrape_firecrawl.py - INFO - Relevant documents retrieved successfully. -2025-08-26 23:45:35,451 - 8_rag_web_scrape_firecrawl.py - INFO - User exited conversation \ No newline at end of file +2025-08-26 23:45:35,451 - 8_rag_web_scrape_firecrawl.py - INFO - User exited conversation2025-11-04 21:31:41,119 - rag_with_metadata.py - INFO - ================================================== diff --git a/langchain-crash-course/4_rag/utils/logger.py b/langchain-crash-course/4_rag/utils/logger.py index 534dced..5d2d251 100644 --- a/langchain-crash-course/4_rag/utils/logger.py +++ b/langchain-crash-course/4_rag/utils/logger.py @@ -42,7 +42,7 @@ def setup(module_name: str) -> logging.Logger: # Create formatters and handlers formatter: logging.Formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + "%(asctime)s - %(levelname)s - %(module)s - %(message)s" ) # Rotating file handler diff --git a/langchain-crash-course/5_agents_tools/agent_react_chat.py b/langchain-crash-course/5_agents_tools/agent_react_chat.py index 21fac3b..419e134 100644 --- a/langchain-crash-course/5_agents_tools/agent_react_chat.py +++ b/langchain-crash-course/5_agents_tools/agent_react_chat.py @@ -80,7 +80,7 @@ # Module path -module_path: Path = Path(__file__).resolve().parent +module_path: Path = Path(__file__).resolve() # Set logger logger: Logger = RAGLogger.get_logger(module_name=module_path.name) @@ -178,6 +178,7 @@ async def main() -> None: input={"input": query}, config={"configurable": {"session_id": session_id}}, ) + logger.info(msg=f"Agent: {response['output'][:100]}.....") print(f"Agent: {response['output']}") except (KeyboardInterrupt, EOFError): diff --git a/langchain-crash-course/5_agents_tools/agent_react_rag_context.py b/langchain-crash-course/5_agents_tools/agent_react_rag_context.py new file mode 100644 index 0000000..ed2021e --- /dev/null +++ b/langchain-crash-course/5_agents_tools/agent_react_rag_context.py @@ -0,0 +1,203 @@ +# agent_react_rag_context.py + +# Import standard libraries +import sys +from logging import Logger +from pathlib import Path +from typing import Any + +# Import environment variables +from dotenv import load_dotenv + +# Import langchain modules +from langchain.chains import ( + create_history_aware_retriever, + create_retrieval_chain, +) +from langchain.chains.combine_documents import create_stuff_documents_chain +from langchain_chroma import Chroma +from langchain_core.messages import AIMessage, HumanMessage +from langchain_core.messages.base import BaseMessage +from langchain_core.prompts.chat import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.runnables.base import Runnable +from langchain_core.vectorstores.base import VectorStoreRetriever +from langchain_ollama import ChatOllama +from langchain_ollama.embeddings import OllamaEmbeddings + +# Import custom logger +from utils.logger import RAGLogger + +# Load environment variables +load_dotenv() + +# Module path +module_path: Path = Path(__file__).resolve() + +# Set logger +logger: Logger = RAGLogger.get_logger(module_name=module_path.name) + +# Log application startup +logger.info(msg="=" * 50) +logger.info(msg="Starting Agent ReAct RAG Context Application") +logger.info(msg="=" * 50) + +# Define directories and paths +rag_dir: Path = Path(__file__).parents[1] / "4_rag" +books_dir: Path = rag_dir / "books" +db_dir: Path = rag_dir / "db" +store_name: str = "chroma_db_with_metadata" +persistent_directory: Path = db_dir / store_name + +# Define embeddings models +ollama_embeddings = OllamaEmbeddings( + model="nomic-embed-text:latest", +) + +# Define LLM +llm = ChatOllama(model="gemma3:4b") + +# Check vector store existence +if not persistent_directory.exists(): + logger.error( + msg=f"Vector store '{store_name}' does not exist. Please check the path." + ) + sys.exit(1) + +# Load vector store and create retriever +try: + logger.info(msg=f"Loading vector store '{store_name}'...") + # Load the Chroma vector store + db: Chroma = Chroma( + persist_directory=str(persistent_directory), + embedding_function=ollama_embeddings, + ) + # Create a retriever + retriever: VectorStoreRetriever = db.as_retriever( + search_type="similarity", + search_kwargs={"k": 3}, + ) + logger.info(msg=f"Created retriever from vector store '{store_name}' successfully.") + +except Exception as e: + logger.error(msg=f"Unexpected error querying vector store '{store_name}': {e}") + sys.exit(1) + +# Contextualize question prompt +# System prompt helps the AI understand that it should reformulate the question +# based on the chat history to make it a standalone question +contextualize_q_system_prompt = """ +Given a chat history and the latest user question, this prompt helps the AI reformulate +the question to be standalone. The reformulated question should be understandable +without relying on prior chat context. The AI should not answer the question—only +rephrase it if necessary, or return it unchanged if already standalone. +""" + +# Create contextualize question prompt template +contextualize_q_prompt_template: ChatPromptTemplate = ChatPromptTemplate.from_messages( + messages=[ + ("system", contextualize_q_system_prompt), + MessagesPlaceholder(variable_name="chat_history"), + ("human", "{input}"), + ] +) + +# Create a history-aware retriever +# this users the LLM to help reformulate the question based on chat history +history_aware_retriever: VectorStoreRetriever = create_history_aware_retriever( + llm, + retriever, + contextualize_q_prompt_template, +) + +# Answer question prompt +# This system prompt helps the AI understand that it should provide concise answers +# based on the retrieved context and indicates what to do if the answer is unknown +qa_system_prompt = """ +You are an assistant for question-answering tasks. +Use the following pieces of retrieved context to answer the question. +If you don't know the answer, just say that you don't know. +Limit your response to a maximum of ten sentences and keep the answer concise. +\n\n +{context} +""" + +# Create answer question prompt template +qa_prompt_template: ChatPromptTemplate = ChatPromptTemplate.from_messages( + messages=[ + ("system", qa_system_prompt), + MessagesPlaceholder(variable_name="chat_history"), + ("human", "{input}"), + ] +) + +# Create a chain to combine documents for question answering +# `create_stuff_documents_chain` feeds all retrieved context into the LLM +question_answering_chain: Runnable[dict[str, Any], Any] = create_stuff_documents_chain( + llm=llm, prompt=qa_prompt_template +) + +# Create a RAG chain that combines the history-aware retriever and the question answering chain +rag_chain: Runnable[dict[str, Any], Any] = create_retrieval_chain( + history_aware_retriever, question_answering_chain +) + + +# Run RAG LLM conversation +def main() -> None: + """ + Runs the main conversational loop for the RAG-based chat application. + + This function initializes the chat history and enters an infinite loop to + continuously accept user input. It processes the user's query through the + RAG chain, prints the AI's response, and updates the chat history. + The loop can be exited by typing 'exit', or by sending a + KeyboardInterrupt (Ctrl+C) or EOFError (Ctrl+D). + """ + print("\nStart chatting with AI! Type 'exit' to end the conversation.") + + # Initialize chat history + chat_history: list[BaseMessage] = [] + + while True: + try: + # User query + query: str = input("You: ").strip() + + if not query: + continue + + if query.lower() == "exit": + logger.info(msg="User exited conversation") + print("Exiting...") + break + + # Process user query through RAG chain + logger.info(msg="Processing user query through RAG chain...") + result: Any = rag_chain.invoke( + input={"input": query, "chat_history": chat_history} + ) + + # Display AI response + if result: + logger.info(msg="AI response generated successfully") + print(f"AI: {result['answer']}") + + # Update chat history + chat_history.append(HumanMessage(content=query)) + chat_history.append(AIMessage(content=result["answer"])) + logger.info(msg="Chat history updated successfully") + + except (KeyboardInterrupt, EOFError): + logger.info(msg="Keyboard interrupt or EOF error") + print("Exiting...") + break + + except Exception as e: + logger.error(msg=f"Unexpected error: {e}") + print("Exiting...") + break + + +# Main entry point +if __name__ == "__main__": + main() diff --git a/langchain-crash-course/5_agents_tools/agent_tools_basic.py b/langchain-crash-course/5_agents_tools/agent_tools_basic.py index 2c368b6..c2df724 100644 --- a/langchain-crash-course/5_agents_tools/agent_tools_basic.py +++ b/langchain-crash-course/5_agents_tools/agent_tools_basic.py @@ -73,7 +73,7 @@ # Module path -module_path: Path = Path(__file__).resolve().parent +module_path: Path = Path(__file__).resolve() # Set logger logger: Logger = RAGLogger.get_logger(module_name=module_path.name) @@ -161,7 +161,7 @@ async def main() -> None: # Run agent executor response: Any = await agent_executor.ainvoke(input={"input": query}) - logger.info(msg="Agent response generated successfully") + logger.info(msg=f"Agent: {response['output']}") print(f"Agent: {response['output']}") except (KeyboardInterrupt, EOFError): diff --git a/langchain-crash-course/5_agents_tools/logs/agent_tools.log b/langchain-crash-course/5_agents_tools/logs/agent_tools.log index 32a5787..908157d 100644 --- a/langchain-crash-course/5_agents_tools/logs/agent_tools.log +++ b/langchain-crash-course/5_agents_tools/logs/agent_tools.log @@ -1,8 +1,22 @@ -2025-08-31 22:51:29,350 - 5_agents_tools - INFO - Start Agent Tools Basic Application... -2025-08-31 22:52:03,279 - 5_agents_tools - INFO - Agent response generated successfully -2025-08-31 22:52:11,062 - 5_agents_tools - INFO - User exited conversation -2025-09-04 21:00:41,977 - 5_agents_tools - INFO - Start Agent React Chat Application... -2025-09-04 21:01:15,876 - 5_agents_tools - INFO - Getting current time: 2025-09-04 21:01:15 -2025-09-04 21:01:56,097 - 5_agents_tools - ERROR - Error getting Wikipedia summary: Page id "michael jacks" does not match any pages. Try another id! -2025-09-04 21:02:05,819 - 5_agents_tools - INFO - Getting Wikipedia summary: Michael Joseph Jackson (August 29, 1958 – June 25, 2009) was an American singer, songwriter, dancer,..... -2025-09-04 21:30:59,868 - 5_agents_tools - INFO - User exited conversation +2025-09-25 15:26:55,633 - agent_tools_basic.py - INFO - Start Agent Tools Basic Application... +2025-09-25 15:27:19,931 - agent_tools_basic.py - INFO - Agent response generated successfully +2025-09-25 15:27:35,672 - agent_tools_basic.py - INFO - User exited conversation +2025-09-25 15:52:42,338 - agent_react_chat.py - INFO - Start Agent React Chat Application... +2025-09-25 15:53:17,567 - agent_react_chat.py - INFO - Getting Wikipedia summary: Microsoft Corporation is an American multinational corporation and technology conglomerate headquart..... +2025-09-25 15:53:29,552 - agent_react_chat.py - INFO - Getting Wikipedia summary: Microsoft Corporation is an American multinational corporation and technology conglomerate headquart..... +2025-09-25 15:53:50,242 - agent_react_chat.py - INFO - Agent: Microsoft is a massive technology company with a rich history. It was founded in 1975 by Bill Gates ..... +2025-09-25 15:54:34,323 - agent_react_chat.py - ERROR - Error getting Wikipedia summary: Page id "bill games" does not match any pages. Try another id! +2025-09-25 15:54:45,004 - agent_react_chat.py - INFO - Agent: Bill Gates is a prominent American business magnate, investor, and philanthropist. He co-founded Mic..... +2025-09-25 15:55:00,779 - agent_react_chat.py - INFO - User exited conversation +2025-11-04 22:52:56,368 - INFO - agent_react_rag_context - ================================================== +2025-11-04 22:52:56,368 - INFO - agent_react_rag_context - Starting Agent ReAct RAG Context Application +2025-11-04 22:52:56,368 - INFO - agent_react_rag_context - ================================================== +2025-11-04 22:52:58,444 - INFO - agent_react_rag_context - Loading vector store 'chroma_db_with_metadata'... +2025-11-04 22:52:58,589 - INFO - agent_react_rag_context - Created retriever from vector store 'chroma_db_with_metadata' successfully. +2025-11-04 22:54:46,933 - INFO - agent_react_rag_context - Processing user query through RAG chain... +2025-11-04 22:55:14,195 - INFO - agent_react_rag_context - AI response generated successfully +2025-11-04 22:55:14,198 - INFO - agent_react_rag_context - Chat history updated successfully +2025-11-04 22:57:53,311 - INFO - agent_react_rag_context - Processing user query through RAG chain... +2025-11-04 22:58:41,439 - INFO - agent_react_rag_context - AI response generated successfully +2025-11-04 22:58:41,466 - INFO - agent_react_rag_context - Chat history updated successfully +2025-11-04 22:59:12,397 - INFO - agent_react_rag_context - User exited conversation diff --git a/langchain-crash-course/5_agents_tools/utils/logger.py b/langchain-crash-course/5_agents_tools/utils/logger.py index 0621e4b..36120f6 100644 --- a/langchain-crash-course/5_agents_tools/utils/logger.py +++ b/langchain-crash-course/5_agents_tools/utils/logger.py @@ -42,7 +42,7 @@ def setup(module_name: str) -> logging.Logger: # Create formatters and handlers formatter: logging.Formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + "%(asctime)s - %(levelname)s - %(module)s - %(message)s" ) # Rotating file handler