Spaces:
Sleeping
Sleeping
Add full RAG pipeline: agent, rag_engine, generator, knowledge_base, full Gradio UI
Browse files- agent.py +6 -9
- generator.py +4 -1
- rag_engine.py +3 -2
agent.py
CHANGED
|
@@ -22,7 +22,6 @@ from rag_engine import MLOpsRAGEngine
|
|
| 22 |
logger = logging.getLogger(__name__)
|
| 23 |
|
| 24 |
MAX_ITERATIONS = 2
|
| 25 |
-
RELEVANCE_SCORE_THRESHOLD = 0.30
|
| 26 |
|
| 27 |
|
| 28 |
@dataclass
|
|
@@ -96,21 +95,19 @@ class MLOpsRAGAgent:
|
|
| 96 |
logger.error(f"Retrieval failed: {e}")
|
| 97 |
return [], []
|
| 98 |
|
| 99 |
-
#
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
]
|
| 104 |
|
| 105 |
-
# Self-reflection relevance check on remaining nodes
|
| 106 |
relevant = []
|
| 107 |
-
for node in
|
| 108 |
text = self.rag.get_node_text(node)
|
| 109 |
try:
|
| 110 |
if self.gen.check_relevance(query, text):
|
| 111 |
relevant.append(node)
|
| 112 |
except Exception:
|
| 113 |
-
# On error, include by score only
|
| 114 |
relevant.append(node)
|
| 115 |
|
| 116 |
logger.info(f"Nodes after relevance filtering: {len(relevant)}/{len(nodes)}")
|
|
|
|
| 22 |
logger = logging.getLogger(__name__)
|
| 23 |
|
| 24 |
MAX_ITERATIONS = 2
|
|
|
|
| 25 |
|
| 26 |
|
| 27 |
@dataclass
|
|
|
|
| 95 |
logger.error(f"Retrieval failed: {e}")
|
| 96 |
return [], []
|
| 97 |
|
| 98 |
+
# ChromaDB returns cosine *distance* (lower = more similar).
|
| 99 |
+
# A score threshold on distance would silently discard the best chunks,
|
| 100 |
+
# so we pass all retrieved nodes to the Flan-T5 relevance check instead.
|
| 101 |
+
scores = [self.rag.get_node_score(n) for n in nodes]
|
| 102 |
+
logger.info(f"Raw node distances: {[round(s, 3) for s in scores]}")
|
| 103 |
|
|
|
|
| 104 |
relevant = []
|
| 105 |
+
for node in nodes:
|
| 106 |
text = self.rag.get_node_text(node)
|
| 107 |
try:
|
| 108 |
if self.gen.check_relevance(query, text):
|
| 109 |
relevant.append(node)
|
| 110 |
except Exception:
|
|
|
|
| 111 |
relevant.append(node)
|
| 112 |
|
| 113 |
logger.info(f"Nodes after relevance filtering: {len(relevant)}/{len(nodes)}")
|
generator.py
CHANGED
|
@@ -95,7 +95,10 @@ class FlanT5Generator:
|
|
| 95 |
f"Answer:"
|
| 96 |
)
|
| 97 |
result = self._generate(prompt, max_new_tokens=5).lower()
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
def reflect_on_answer(self, query: str, answer: str) -> tuple[bool, str]:
|
| 101 |
"""Self-reflect on whether the generated answer adequately addresses the query."""
|
|
|
|
| 95 |
f"Answer:"
|
| 96 |
)
|
| 97 |
result = self._generate(prompt, max_new_tokens=5).lower()
|
| 98 |
+
# Lenient: include the chunk unless the model explicitly says "no".
|
| 99 |
+
# "yes" in result is too strict — Flan-T5 sometimes outputs synonyms or
|
| 100 |
+
# fuller sentences; requiring an explicit "no" avoids false negatives.
|
| 101 |
+
return "no" not in result
|
| 102 |
|
| 103 |
def reflect_on_answer(self, query: str, answer: str) -> tuple[bool, str]:
|
| 104 |
"""Self-reflect on whether the generated answer adequately addresses the query."""
|
rag_engine.py
CHANGED
|
@@ -16,8 +16,9 @@ from llama_index.vector_stores.chroma import ChromaVectorStore
|
|
| 16 |
logger = logging.getLogger(__name__)
|
| 17 |
|
| 18 |
EMBED_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
|
| 19 |
-
|
| 20 |
-
|
|
|
|
| 21 |
COLLECTION_NAME = "mlops_knowledge_base"
|
| 22 |
CHUNK_SIZE = 512
|
| 23 |
CHUNK_OVERLAP = 64
|
|
|
|
| 16 |
logger = logging.getLogger(__name__)
|
| 17 |
|
| 18 |
EMBED_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
|
| 19 |
+
_HERE = Path(__file__).parent
|
| 20 |
+
KNOWLEDGE_BASE_PATH = str(_HERE / "knowledge_base")
|
| 21 |
+
CHROMA_DB_PATH = str(_HERE / "chroma_db")
|
| 22 |
COLLECTION_NAME = "mlops_knowledge_base"
|
| 23 |
CHUNK_SIZE = 512
|
| 24 |
CHUNK_OVERLAP = 64
|