Updated Agentic RAG math tutor

This commit is contained in:
ShubhamSaboo 2025-05-04 15:57:35 -05:00
parent 6d0e5fd7a3
commit 25c36f9182
24 changed files with 11 additions and 24 deletions

View file

@ -2,24 +2,19 @@
This project implements an **Agentic-RAG architecture** to simulate a math professor that solves **JEE-level math questions** with step-by-step explanations. The system smartly routes queries between a vector database and web search, applies input/output guardrails, and incorporates human feedback for continuous learning. This project implements an **Agentic-RAG architecture** to simulate a math professor that solves **JEE-level math questions** with step-by-step explanations. The system smartly routes queries between a vector database and web search, applies input/output guardrails, and incorporates human feedback for continuous learning.
---
## 📌 Features ## 📌 Features
- ✅ **Input Guardrails** (DSPy): Accepts only academic math questions. - ✅ **Input Guardrails** (DSPy): Accepts only academic math questions.
- 📚 **Knowledge Base Search**: Uses **Qdrant Vector DB** with OpenAI Embeddings to match known questions. - 📚 **Knowledge Base Search**: Uses **Qdrant Vector DB** with OpenAI Embeddings to match known questions.
- 🌐 **Web Fallback**: Integrates **Tavily API** when no good match is found. - 🌐 **Web Fallback**: Integrates **Tavily API** when no good match is found.
- ✍️ **GPT-3.5 Turbo Explanations**: Generates step-by-step math solutions. - ✍️ **GPT-4.1 Explanations**: Generates step-by-step math solutions.
- 🛡️ **Output Guardrails**: Filters for correctness and safety. - 🛡️ **Output Guardrails**: Filters for correctness and safety.
- 👍 **Human-in-the-Loop Feedback**: Users rate answers (Yes/No), logged for future learning. - 👍 **Human-in-the-Loop Feedback**: Users rate answers (Yes/No), logged for future learning.
- 📊 **Benchmarking**: Evaluated on **JEEBench** dataset with adjustable question limits. - 📊 **Benchmarking**: Evaluated on **JEEBench** dataset with adjustable question limits.
- 💻 **Streamlit UI**: Interactive dashboard with multiple tabs. - 💻 **Streamlit UI**: Interactive dashboard with multiple tabs.
---
## 🚀 Architecture Flow ## 🚀 Architecture Flow
![image](https://github.com/user-attachments/assets/9197a918-d14e-4759-9b28-8a90dadd1baf)
## 📚 Knowledge Base ## 📚 Knowledge Base
@ -27,36 +22,29 @@ This project implements an **Agentic-RAG architecture** to simulate a math profe
- **Vector DB:** Qdrant (with OpenAI Embeddings) - **Vector DB:** Qdrant (with OpenAI Embeddings)
- **Storage:** Built with `llama-index` to persist embeddings and perform top-1 similarity search - **Storage:** Built with `llama-index` to persist embeddings and perform top-1 similarity search
---
## 🌐 Web Search ## 🌐 Web Search
- Uses **Tavily API** for fallback search when the KB doesn't contain a good match - Uses **Tavily API** for fallback search when the KB doesn't contain a good match
- Fetched content is piped into **GPT-3.5 Turbo** for clean explanation - Fetched content is piped into **GPT-4o** for clean explanation
---
## 🔐 Guardrails ## 🔐 Guardrails
- **Input Guardrail (DSPy):** Accepts only math-related academic questions - **Input Guardrail (DSPy):** Accepts only math-related academic questions
- **Output Guardrail (DSPy):** Blocks hallucinated or off-topic content - **Output Guardrail (DSPy):** Blocks hallucinated or off-topic content
---
## 👨‍🏫 Human-in-the-Loop Feedback ## 👨‍🏫 Human-in-the-Loop Feedback
- Streamlit UI allows students to give 👍 / 👎 after seeing the answer - Streamlit UI allows students to give 👍 / 👎 after seeing the answer
- Feedback is logged to a local JSON file for future improvement - Feedback is logged to a local JSON file for future improvement
---
## 📊 Benchmarking ## 📊 Benchmarking
- Evaluated on **50 random JEEBench Math Questions** - Evaluated on **50 random JEEBench Math Questions**
- **Current Accuracy:** 66% - **Current Accuracy:** 66%
- Benchmark results saved to: `benchmark/results.csv` - Benchmark results saved to: `benchmark/results.csv`
---
## 🚀 Demo ## 🚀 Demo
@ -65,8 +53,6 @@ To run the app with Streamlit:
```bash ```bash
streamlit run app/streamlit.py streamlit run app/streamlit.py
----

View file

@ -63,5 +63,10 @@
"question": "Let \ud835\udc53 ( \ud835\udc65 ) = \ud835\udc65 3 \u2212 3 \ud835\udc65 + 1 f(x)=x 3 \u22123x+1. The number of real solutions of the equation \ud835\udc53 ( \ud835\udc53 ( \ud835\udc65 ) ) = 0 f(f(x))=0 is: Options: (A) 3 (B) 5 (C) 7 (D) 9", "question": "Let \ud835\udc53 ( \ud835\udc65 ) = \ud835\udc65 3 \u2212 3 \ud835\udc65 + 1 f(x)=x 3 \u22123x+1. The number of real solutions of the equation \ud835\udc53 ( \ud835\udc53 ( \ud835\udc65 ) ) = 0 f(f(x))=0 is: Options: (A) 3 (B) 5 (C) 7 (D) 9",
"answer": "To find the number of real solutions of the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0, we first need to understand what the function \ud835\udc53(\ud835\udc65) = \ud835\udc65\u00b3 - 3\ud835\udc65 + 1 represents.\n\nThe function \ud835\udc53(\ud835\udc65) = \ud835\udc65\u00b3 - 3\ud835\udc65 + 1 is a cubic function, which means it has at most 3 real roots. To find the real roots of \ud835\udc53(\ud835\udc65), we set \ud835\udc53(\ud835\udc65) = 0 and solve for \ud835\udc65:\n\n\ud835\udc65\u00b3 - 3\ud835\udc65 + 1 = 0\n\nThis equation may have 1, 2, or 3 real roots. Let's assume it has 3 real roots for now.\n\nNow, we need to find the number of real solutions of the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0. This equation can be rewritten as:\n\n\ud835\udc53(\ud835\udc53(\ud835\udc65)) = \ud835\udc53(\ud835\udc65)\u00b3 - 3\ud835\udc53(\ud835\udc65) + 1 = 0\n\nSince \ud835\udc53(\ud835\udc65) has 3 real roots, each of these roots contributes 3 solutions to the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0. Therefore, the total number of real solutions of \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0 is 3 * 3 = 9.\n\nTherefore, the correct answer is (D) 9, as stated in the web content provided.", "answer": "To find the number of real solutions of the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0, we first need to understand what the function \ud835\udc53(\ud835\udc65) = \ud835\udc65\u00b3 - 3\ud835\udc65 + 1 represents.\n\nThe function \ud835\udc53(\ud835\udc65) = \ud835\udc65\u00b3 - 3\ud835\udc65 + 1 is a cubic function, which means it has at most 3 real roots. To find the real roots of \ud835\udc53(\ud835\udc65), we set \ud835\udc53(\ud835\udc65) = 0 and solve for \ud835\udc65:\n\n\ud835\udc65\u00b3 - 3\ud835\udc65 + 1 = 0\n\nThis equation may have 1, 2, or 3 real roots. Let's assume it has 3 real roots for now.\n\nNow, we need to find the number of real solutions of the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0. This equation can be rewritten as:\n\n\ud835\udc53(\ud835\udc53(\ud835\udc65)) = \ud835\udc53(\ud835\udc65)\u00b3 - 3\ud835\udc53(\ud835\udc65) + 1 = 0\n\nSince \ud835\udc53(\ud835\udc65) has 3 real roots, each of these roots contributes 3 solutions to the equation \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0. Therefore, the total number of real solutions of \ud835\udc53(\ud835\udc53(\ud835\udc65)) = 0 is 3 * 3 = 9.\n\nTherefore, the correct answer is (D) 9, as stated in the web content provided.",
"feedback": "positive" "feedback": "positive"
},
{
"question": "Let \ud835\udc53 ( \ud835\udc65 ) = ln \u2061 ( sin \u2061 \ud835\udc65 ) + ln \u2061 ( tan \u2061 \ud835\udc65 ) f(x)=ln(sinx)+ln(tanx). Find the domain of the function \ud835\udc53 ( \ud835\udc65 ) f(x) in the interval ( 0 , \ud835\udf0b ) (0,\u03c0).",
"answer": "To find the domain of the function \\( f(x) = \\ln(\\sin x) + \\ln(\\tan x) \\) in the interval \\( (0, \\pi) \\), we need to determine where both \\(\\ln(\\sin x)\\) and \\(\\ln(\\tan x)\\) are defined.\n\n### Step-by-Step Explanation:\n\n1. **Understanding the Natural Logarithm:**\n - The natural logarithm function \\(\\ln(y)\\) is defined only for \\(y > 0\\).\n\n2. **Analyzing \\(\\ln(\\sin x)\\):**\n - \\(\\sin x > 0\\) in the interval \\( (0, \\pi) \\).\n - Therefore, \\(\\ln(\\sin x)\\) is defined for all \\(x\\) in \\( (0, \\pi) \\).\n\n3. **Analyzing \\(\\ln(\\tan x)\\):**\n - \\(\\tan x = \\frac{\\sin x}{\\cos x}\\).\n - For \\(\\ln(\\tan x)\\) to be defined, \\(\\tan x > 0\\), which means \\(\\sin x > 0\\) and \\(\\cos x > 0\\).\n - \\(\\cos x > 0\\) in the interval \\( (0, \\frac{\\pi}{2}) \\).\n - \\(\\tan x\\) is undefined at \\(x = \\frac{\\pi}{2}\\) because \\(\\cos x = 0\\) there.\n\n4. **Combining the Conditions:**\n - Both \\(\\ln(\\sin x)\\) and \\(\\ln(\\tan x)\\) are defined when \\(x\\) is in \\( (0, \\frac{\\pi}{2}) \\) and \\( (\\frac{\\pi}{2}, \\pi) \\).\n - At \\(x = \\frac{\\pi}{2}\\), \\(\\tan x\\) is undefined, so \\(x = \\frac{\\pi}{2}\\) is excluded from the domain.\n\n5. **Conclusion:**\n - The domain of \\( f(x) = \\ln(\\sin x) + \\ln(\\tan x) \\) in the interval \\( (0, \\pi) \\) is \\( (0, \\frac{\\pi}{2}) \\cup (\\frac{\\pi}{2}, \\pi) \\).\n\nThis step-by-step analysis confirms that the domain of the function \\( f(x) \\) is indeed \\( (0, \\frac{\\pi}{2}) \\cup (\\frac{\\pi}{2}, \\pi) \\), excluding \\(x = \\frac{\\pi}{2}\\) where \\(\\tan x\\) is undefined.",
"feedback": "positive"
} }
] ]

View file

@ -8,7 +8,7 @@ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
print("🔐 Loaded OPENAI_API_KEY:", "✅ Found" if OPENAI_API_KEY else "❌ Missing") print("🔐 Loaded OPENAI_API_KEY:", "✅ Found" if OPENAI_API_KEY else "❌ Missing")
# Configure LM # Configure LM
lm = dspy.LM(model="gpt-3.5-turbo", api_key=OPENAI_API_KEY) lm = dspy.LM(model="gpt-4o", api_key=OPENAI_API_KEY)
dspy.configure(lm=lm) dspy.configure(lm=lm)
# ✅ Signature for Input Guard # ✅ Signature for Input Guard

View file

@ -6,7 +6,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
import os import os
import requests import requests
import openai # ✅ now using the real OpenAI SDK import openai
import json import json
import inspect import inspect
from llama_index.core import StorageContext,load_index_from_storage from llama_index.core import StorageContext,load_index_from_storage
@ -78,7 +78,7 @@ Web Content:
Now write a clear, accurate, and step-by-step explanation of the student's question. Now write a clear, accurate, and step-by-step explanation of the student's question.
Only include valid math steps — do not guess or make up answers. Only include valid math steps — do not guess or make up answers.
""" """
llm = OpenAI(api_key=OPENAI_API_KEY, model="gpt-3.5-turbo") llm = OpenAI(api_key=OPENAI_API_KEY, model="gpt-4o")
response = llm.complete(prompt) response = llm.complete(prompt)
return response.text return response.text
@ -118,7 +118,7 @@ Do not change the final answer. You are only allowed to explain what is already
Use the KB content as your only source. Do not guess or recalculate. Use the KB content as your only source. Do not guess or recalculate.
""" """
llm = OpenAI(api_key=OPENAI_API_KEY, model="gpt-3.5-turbo") llm = OpenAI(api_key=OPENAI_API_KEY, model="gpt-4o")
answer = llm.complete(prompt).text answer = llm.complete(prompt).text
from_kb = True from_kb = True
else: else:

View file

@ -1 +0,0 @@
{"graph_dict": {}}

View file

@ -1 +0,0 @@
{"embedding_dict": {}, "text_id_to_ref_doc_id": {}, "metadata_dict": {}}

View file

@ -1 +0,0 @@
{"index_store/data": {"b8b2a959-cac2-4df4-808b-9edf8eda98ff": {"__type__": "vector_store", "__data__": "{\"index_id\": \"b8b2a959-cac2-4df4-808b-9edf8eda98ff\", \"summary\": null, \"nodes_dict\": {}, \"doc_id_dict\": {}, \"embeddings_dict\": {}}"}}}