Introduction
Agentic Artificial Intelligence (Agentic AI) represents a new paradigm in AI system design where intelligent agents are capable of making decisions, performing tasks, and adapting to different contexts with minimal human intervention. Unlike traditional AI models that passively respond to user inputs, Agentic AI systems can actively plan, retrieve relevant information, execute multi-step reasoning, and integrate external tools to achieve specific goals.
fastapi
uvicorn[standard]
sentence-transformers
faiss-cpu
pydantic
python-multipart
requests
openai
python-docx
pdfplumber
tqdm
Simple RAG app: upload docs, build FAISS index, ask queries via a simple web UI.
cd backend
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -r requirements.txt
Put your sample PDFs/TXT/DOCX into backend/sample_docs/
python -m backend.vectorstore build --docs_dir backend/sample_docs --index_path backend/faiss_index.pkl
uvicorn backend.main
--reload --port 8000Open frontend/index.html in browser (or serve it). The frontend talks to http://localhost:8000
Use the demo_script.txt
to record a 2-3 minute demo video.
import os
import pickle
from typing import List, Tuple
import numpy as np
import faiss
from backend.embedder import embed_texts
from backend.utils import chunk_text
class SimpleFAISS:
def init(self, dim: int, index_path: str = None):
self.dim = dim
self.index = faiss.IndexFlatIP(dim) # cosine via normalized vectors
self.metadatas = [] # list of dicts for each vector
self.index_path = index_path
def add(self, vectors: np.ndarray, metas: List[dict]):
assert vectors.shape[0] == len(metas)
# normalize for cosine similarity
faiss.normalize_L2(vectors)
self.index.add(vectors)
self.metadatas.extend(metas)
def search(self, vector: np.ndarray, k: int = 5) -> List[Tuple[dict, float]]:
faiss.normalize_L2(vector)
D, I = self.index.search(vector, k)
results = []
for idx, score in zip(I[0], D[0]):
if idx == -1:
continue
results.append((self.metadatas[idx], float(score)))
return results
def save(self, path: str):
data = {
"index": faiss.serialize_index(self.index),
"metadatas": self.metadatas,
"dim": self.dim,
}
with open(path, "wb") as f:
pickle.dump(data, f)
@classmethod
def load(cls, path: str):
with open(path, "rb") as f:
data = pickle.load(f)
obj = cls(dim=data["dim"], index_path=path)
obj.index = faiss.deserialize_index(data["index"])
obj.metadatas = data["metadatas"]
return obj
def build_index_from_docs(docs: List[dict], index_path: str):
"""
docs: list of {"id": str, "text": str, "source": str}
"""
# chunk all documents
all_chunks = []
metas = []
texts = []
for doc in docs:
chunks = chunk_text(doc["text"], chunk_size=500, overlap=50)
for i, c in enumerate(chunks):
texts.append(c)
metas.append({
"source": doc.get("source"),
"doc_id": doc.get("id"),
"chunk_id": i,
"text": c[:200],
})
vecs = embed_texts(texts)
vecs = np.array(vecs).astype("float32")
dim = vecs.shape[1]
store = SimpleFAISS(dim=dim, index_path=index_path)
store.add(vecs, metas)
store.save(index_path)
return store
from sentence_transformers import SentenceTransformer
MODEL_NAME = "all-MiniLM-L6-v2"
_model = None
def get_model():
global _model
if _model is None:
_model = SentenceTransformer(MODEL_NAME)
return _model
def embed_texts(texts):
"""Return list of embeddings for the given list of texts."""
model = get_model()
return model.encode(texts, show_progress_bar=False)
def embed_text(text):
return embed_texts([text])[0]
from backend.vectorstore import SimpleFAISS
from backend.embedder import embed_text
import json
class RAGPipeline:
def init(self, index: SimpleFAISS):
self.index = index
def retrieve(self, query: str, k: int = 5):
qv = embed_text(query).reshape(1, -1).astype("float32")
results = self.index.search(qv, k)
return results
def build_prompt(self, query: str, hits, max_context_chars=1500):
contexts = []
total = 0
for meta, score in hits:
snippet = meta.get("text", "")
add = f"SOURCE: {meta.get('source')} | CHUNK: {meta.get('chunk_id')}\n{snippet}\n"
contexts.append(add)
total += len(add)
if total > max_context_chars:
break
context_block = "\n---\n".join(contexts)
prompt = f"You are a helpful assistant. Use ONLY the context below to answer the question. If answer is not in the context say 'I don't know'.\n\nCONTEXT:\n{context_block}\nQUESTION: {query}\n\nAnswer concisely in Hindi (Roman) and then give a 1-line English summary. Cite sources like [source.pdf#chunk_id]."
return prompt
def postprocess(self, llm_output: str):
return llm_output
import os
from typing import List
import pdfplumber
from docx import Document
def load_txt(path):
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return f.read()
def load_pdf(path):
text = []
with pdfplumber.open(path) as pdf:
for page in pdf.pages:
text.append(page.extract_text() or "")
return "\n".join(text)
def load_docx(path):
doc = Document(path)
return "\n".join([p.text for p in doc.paragraphs])
def read_documents_from_dir(dir_path: str):
docs = []
for fname in sorted(os.listdir(dir_path)):
path = os.path.join(dir_path, fname)
if fname.lower().endswith(".pdf"):
text = load_pdf(path)
elif fname.lower().endswith(".docx"):
text = load_docx(path)
elif fname.lower().endswith(".txt"):
text = load_txt(path)
else:
continue
docs.append({"id": fname, "text": text, "source": fname})
return docs
def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> List[str]:
words = text.split()
chunks = []
i = 0
while i < len(words):
chunk = words[i
This project aims to design and implement a RAG-Powered AI Document Assistant that integrates prompting strategies, embeddings, and vector databases to create a question-answering system capable of handling domain-specific queries with high accuracy. The system will leverage the principles learned in Weeks 1–4, including core Agentic AI concepts, prompt engineering, embeddings, retrieval workflows, and end-to-end project deployment.