angkit007 commited on
Commit
2a38383
·
1 Parent(s): 87edae9
.env.example DELETED
@@ -1,3 +0,0 @@
1
- HF_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
2
- HF_GENERATION_MODEL=google/flan-t5-small
3
- CHROMA_DB_DIR=.chroma_db
 
 
 
 
README.md CHANGED
@@ -1,70 +1,32 @@
1
  ---
2
- title: Agentic RAG Demo
3
- emoji: 🤖
4
- colorFrom: blue
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: "6.20.0"
8
- python_version: "3.11"
9
  app_file: app.py
10
- suggested_hardware: cpu-basic
11
  pinned: false
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
15
 
16
- # Agentic RAG Demo
17
 
18
- This project is a compact, interview-friendly agentic retrieval-augmented generation (RAG) assistant. It answers questions over a small document set using a multi-step workflow:
19
-
20
- 1. The agent retrieves candidate passages from a local vector store.
21
- 2. A lightweight reranking step narrows the results.
22
- 3. A free open-source language model answers using the reranked evidence.
23
-
24
- ## Why this architecture
25
-
26
- ### Chunking strategy
27
-
28
- The knowledge base is split with `RecursiveCharacterTextSplitter` at roughly 600 characters with 120 characters of overlap. This balances two goals:
29
-
30
- - keep local context coherent for one business record or ticket
31
- - avoid chunking too aggressively, which would lose important references and reduce answer quality
32
-
33
- ### Retrieval method
34
-
35
- The app uses Chroma as the vector database and Hugging Face embeddings to create dense vector representations of each chunk. This is a good fit for a small demo because it is fast, local, and easy to inspect in the browser or terminal.
36
-
37
- ### Why rerank
38
-
39
- Dense retrieval alone is often noisy. The rerank step gives a second signal by rewarding passages that are not only semantically close but also relevant to the literal question. That makes the final answer more grounded and less likely to hallucinate.
40
-
41
- ## Files
42
-
43
- - `app/main.py` — runs the full demo
44
- - `app/agent.py` — the agent graph and prompt orchestration
45
- - `app/retriever.py` — vector store creation + retrieval + reranking
46
- - `data/knowledge/` — sample documents such as an invoice, resume, and support ticket
47
 
48
  ## Run locally
49
 
50
  ```bash
51
  pip install -r requirements.txt
52
- copy .env.example .env
53
- python app/main.py
54
  ```
55
 
56
- No API key is required for the default free-model path.
57
-
58
- ## Deploy to Hugging Face Space
59
-
60
- 1. Push this repository to a GitHub repo.
61
- 2. Create a new Hugging Face Space.
62
- 3. Choose `Gradio` as the SDK.
63
- 4. Point the Space at the repo.
64
- 5. Keep the Space free-model path only; no API key is needed for the default deployment.
65
-
66
- The default Space behavior uses Hugging Face-hosted free models for both embeddings and generation, which keeps the app portable and cheap to run.
67
-
68
- The included `space.yaml` file tells Hugging Face to launch the Gradio app from `app.py`. The app is intentionally designed to run without any special server-only configuration.
69
 
70
- If the local free-model generation backend is unavailable at runtime, the app gracefully falls back to a grounded evidence summary rather than hard-failing.
 
 
 
1
  ---
2
+ title: Angkit Sarma — Living CV
3
+ emoji: 🟡
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 5.9.1
 
8
  app_file: app.py
 
9
  pinned: false
10
  ---
11
 
12
+ # Angkit Sarma Living CV (Gradio)
13
 
14
+ An interactive résumé with a self-querying "Ask the CV" chat panel.
15
 
16
+ The retrieval engine is a real TF-IDF + cosine-similarity ranker (scikit-learn)
17
+ built directly over this résumé's own content — no external API keys, no
18
+ LLM calls, entirely self-contained in `app.py`. It's a small, visible version
19
+ of the same semantic-search thinking described in the Experience section.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  ## Run locally
22
 
23
  ```bash
24
  pip install -r requirements.txt
25
+ python app.py
 
26
  ```
27
 
28
+ ## Deploy to Hugging Face Spaces
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ 1. Create a new Space SDK: **Gradio**
31
+ 2. Upload `app.py`, `requirements.txt`, and this `README.md`
32
+ 3. The Space builds and launches automatically — no secrets required
agents DELETED
@@ -1 +0,0 @@
1
- Subproject commit 250a96691bb4265cc0d0709cba2a88a701945c8e
 
 
app.py CHANGED
@@ -1,13 +1,239 @@
1
- from __future__ import annotations
 
2
 
3
- from app import demo
 
 
 
 
 
4
 
 
 
 
 
5
 
6
- if __name__ == "__main__":
7
- demo.launch(
8
- server_name="0.0.0.0",
9
- server_port=7860,
10
- ssr_mode=False,
11
- share=False,
12
- show_error=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Angkit Sarma — Living CV (Gradio edition)
3
 
4
+ An interactive resume with a self-querying "Ask the CV" panel. The retrieval
5
+ engine is a real TF-IDF + cosine-similarity ranker over the resume's own
6
+ content (scikit-learn) — the same underlying idea as the semantic-search
7
+ optimization work described in the Experience section below, just visible
8
+ and playable instead of buried in a bullet point.
9
+ """
10
 
11
+ import gradio as gr
12
+ import numpy as np
13
+ from sklearn.feature_extraction.text import TfidfVectorizer
14
+ from sklearn.metrics.pairwise import cosine_similarity
15
 
16
+ # ---------------------------------------------------------------------------
17
+ # Resume content — the single source of truth the retrieval engine indexes.
18
+ # ---------------------------------------------------------------------------
19
+ CORPUS = [
20
+ {"tag": "Experience · Flexday AI", "text": "Built and pitched INFERA, an AI-powered agentic solution that maps sales projects to real-world opportunities, winning 1st place at the Flexday AI Hackathon."},
21
+ {"tag": "Experience · Flexday AI", "text": "Optimized a semantic search system using LLM and embedding based techniques, improving retrieval performance by 50 percent and enhancing user experience."},
22
+ {"tag": "Experience · Flexday AI", "text": "Designed and implemented automated AI assisted workflows across departments, cutting process cycle time by 5 percent and freeing up 10 or more hours per month."},
23
+ {"tag": "Experience · Flexday AI", "text": "Implemented OCR based data extraction pipelines across diverse image collections, improving data extraction accuracy and processing efficiency."},
24
+ {"tag": "Experience · Flexday AI", "text": "Identified and remediated critical application security vulnerabilities, mitigating million dollar risk exposure and strengthening system security."},
25
+ {"tag": "Experience · Flexday AI", "text": "Streamlined CI/CD build pipelines by reducing redundant steps, accelerating deployment times, using Git, GitHub, Jira and Azure DevOps."},
26
+ {"tag": "Experience · Leokraft", "text": "Designed, trained and deployed end to end machine learning models into production, improving model accuracy by 8 percent while cutting infrastructure costs by 20 percent."},
27
+ {"tag": "Experience · Leokraft", "text": "Engineered a key target variable feature that improved model accuracy by 5 percent."},
28
+ {"tag": "Experience · Leokraft", "text": "Developed an end to end application for managing model scores and usage statistics, driving higher stakeholder engagement."},
29
+ {"tag": "Experience · CodingZen", "text": "Taught over 100 students full stack web development with Node.js, from foundational HTML and CSS to advanced backend engineering, while supervising a team of teaching staff."},
30
+ {"tag": "Project · INFERA", "text": "INFERA is an agentic AI system built with large language models that autonomously analyzes sales pipeline data and maps projects to real world business opportunities. Won first place at the Flexday AI Hackathon."},
31
+ {"tag": "Project · Analytica", "text": "Analytica is a full stack data analytics platform built with React, Python, Azure and SQL, showing machine learning model performance and usage statistics."},
32
+ {"tag": "Project · Predictive Allocation", "text": "Predictive Allocation re-engineered an end to end model training and deployment pipeline on Azure SQL and Azure Blob, hardened with Snyk and Wiz security scanning, and modernized legacy code."},
33
+ {"tag": "Skills · AI/ML", "text": "Core AI and machine learning skills include large language models, generative AI, agentic AI and AI agents, prompt engineering, semantic search, classification, regression, decision trees, and SMOTE."},
34
+ {"tag": "Skills · Cloud & MLOps", "text": "Cloud and MLOps skills include Microsoft Azure, Azure SQL, Azure Blob Storage, serverless architecture, virtual machines, and CI/CD pipelines."},
35
+ {"tag": "Skills · Programming", "text": "Programming languages and runtimes include Python, JavaScript and Node.js."},
36
+ {"tag": "Certifications", "text": "Certifications include Building with the Claude API, Generative AI professional certificate, AI Fluency Framework and Foundations, Machine Learning for Leaders, and Analyze Box Office Data with Seaborn and Python."},
37
+ {"tag": "Education", "text": "M.Tech in Information Technology from Tezpur University, graduated with distinction at 8.69 CGPA, with a full time AICTE scholarship. B.Tech in Computer Science and Engineering from KIET, first division."},
38
+ ]
39
+
40
+ _texts = [c["text"] for c in CORPUS]
41
+ _vectorizer = TfidfVectorizer(stop_words="english")
42
+ _doc_matrix = _vectorizer.fit_transform(_texts)
43
+
44
+
45
+ def retrieve(query: str, top_k: int = 3):
46
+ """Rank resume chunks against a query using TF-IDF cosine similarity."""
47
+ if not query or not query.strip():
48
+ return []
49
+ q_vec = _vectorizer.transform([query])
50
+ sims = cosine_similarity(q_vec, _doc_matrix)[0]
51
+ ranked_idx = np.argsort(sims)[::-1]
52
+ results = []
53
+ for i in ranked_idx[:top_k]:
54
+ if sims[i] <= 0:
55
+ continue
56
+ results.append({**CORPUS[i], "score": float(sims[i])})
57
+ return results
58
+
59
+
60
+ def ask_the_cv(query, history):
61
+ history = history or []
62
+ results = retrieve(query)
63
+
64
+ if not results:
65
+ answer = ("No strong match in the résumé index for that — try asking about "
66
+ "experience, projects, skills, or certifications.")
67
+ else:
68
+ top = results[0]
69
+ answer = f"**{top['tag']}** — {top['text']}"
70
+ if len(results) > 1:
71
+ answer += "\n\n**Also relevant:**\n"
72
+ for r in results[1:]:
73
+ answer += f"\n- _{r['tag']}_ ({r['score']*100:.0f}% match): {r['text']}"
74
+
75
+ history.append({"role": "user", "content": query})
76
+ history.append({"role": "assistant", "content": answer})
77
+ return history, ""
78
+
79
+
80
+ EXAMPLE_QUERIES = [
81
+ "What is your experience with LLMs and generative AI?",
82
+ "Tell me about INFERA",
83
+ "What are your cloud and deployment skills?",
84
+ "Have you done any teaching or mentoring?",
85
+ "What certifications do you have?",
86
+ ]
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # Styling — dark "console" theme matching the static-site edition
90
+ # ---------------------------------------------------------------------------
91
+ CUSTOM_CSS = """
92
+ :root {
93
+ --amber: #f5b841;
94
+ --teal: #4fd1c5;
95
+ }
96
+ .gradio-container {
97
+ background: radial-gradient(ellipse 1200px 800px at 20% -10%, #1a2130 0%, #12151c 55%) !important;
98
+ font-family: 'Inter', sans-serif !important;
99
+ }
100
+ #hero-name {
101
+ font-family: 'Space Grotesk', sans-serif !important;
102
+ font-weight: 700 !important;
103
+ font-size: 42px !important;
104
+ margin-bottom: 0 !important;
105
+ }
106
+ #hero-headline { color: var(--amber) !important; font-weight: 600 !important; margin-top: 4px !important; }
107
+ .status-badge {
108
+ display: inline-flex; align-items: center; gap: 8px;
109
+ font-family: monospace; font-size: 12px; letter-spacing: 0.1em;
110
+ color: #9aa3b5; text-transform: uppercase; margin-bottom: 10px;
111
+ }
112
+ .dot {
113
+ width: 8px; height: 8px; border-radius: 50%; background: var(--amber);
114
+ display: inline-block; animation: pulse 2.2s infinite;
115
+ }
116
+ @keyframes pulse {
117
+ 0% { box-shadow: 0 0 0 0 rgba(245,184,65,.55); }
118
+ 70% { box-shadow: 0 0 0 9px rgba(245,184,65,0); }
119
+ 100% { box-shadow: 0 0 0 0 rgba(245,184,65,0); }
120
+ }
121
+ .section-label {
122
+ font-family: monospace !important; color: var(--amber) !important;
123
+ letter-spacing: 0.1em !important; text-transform: uppercase; font-size: 13px !important;
124
+ }
125
+ """
126
+
127
+ with gr.Blocks(title="Angkit Sarma — Living CV") as demo:
128
+
129
+ gr.HTML(
130
+ """
131
+ <div class="status-badge"><span class="dot"></span> resume · live · self-querying</div>
132
+ <div id="hero-name">Angkit Sarma</div>
133
+ <div id="hero-headline">AI/ML Engineer — Agentic Systems &amp; LLM Applications</div>
134
+ <p style="color:#9aa3b5; max-width:680px; margin-top:14px;">
135
+ 4+ years building and shipping ML and generative AI systems — from a 50%-faster semantic
136
+ search pipeline to <b style="color:#eef0f4">INFERA</b>, an agentic AI tool that won 1st place
137
+ at the Flexday AI Hackathon. Ask the panel below anything about my experience — it's a live
138
+ TF-IDF retrieval engine running over this résumé's own content.
139
+ </p>
140
+ <p style="font-family:monospace; font-size:13px; color:#9aa3b5;">
141
+ +91 9990797061 &nbsp;·&nbsp; angkit93@gmail.com &nbsp;·&nbsp;
142
+ <a href="https://www.linkedin.com/in/angkit-s-81b7131b0/" target="_blank" style="color:#4fd1c5;">LinkedIn</a>
143
+ &nbsp;·&nbsp; Hyderabad, India
144
+ </p>
145
+ """
146
  )
147
+
148
+ gr.HTML('<p class="section-label">// ask the cv</p>')
149
+ chatbot = gr.Chatbot(label=None, height=320, show_label=False)
150
+ with gr.Row():
151
+ query_box = gr.Textbox(placeholder="e.g. What's your experience with LLMs?", scale=5, show_label=False, container=False)
152
+ ask_btn = gr.Button("Ask", variant="primary", scale=1)
153
+
154
+ with gr.Row():
155
+ for q in EXAMPLE_QUERIES:
156
+ gr.Button(q, size="sm").click(fn=ask_the_cv, inputs=[gr.State(q), chatbot], outputs=[chatbot, query_box])
157
+
158
+ ask_btn.click(fn=ask_the_cv, inputs=[query_box, chatbot], outputs=[chatbot, query_box])
159
+ query_box.submit(fn=ask_the_cv, inputs=[query_box, chatbot], outputs=[chatbot, query_box])
160
+
161
+ gr.HTML('<p class="section-label">// summary</p>')
162
+ gr.Markdown(
163
+ "Results-driven AI/ML Engineer with 4+ years designing, training, and deploying machine "
164
+ "learning and generative AI systems that solve real business problems. Proven track record "
165
+ "building agentic AI and LLM-powered applications, optimizing semantic search and NLP "
166
+ "pipelines, and automating end-to-end ML workflows from data processing to production "
167
+ "deployment. Combines strong ML engineering fundamentals with cloud deployment, MLOps, and "
168
+ "application security expertise to ship secure, scalable, high-impact AI solutions."
169
+ )
170
+
171
+ gr.HTML('<p class="section-label">// experience</p>')
172
+ with gr.Accordion("Software Developer (AI/ML Focus) — Flexday AI, Hyderabad · Nov 2022 – Present", open=True):
173
+ gr.Markdown(
174
+ "- Built and pitched **INFERA**, an AI-powered agentic solution that maps sales projects "
175
+ "to real-world opportunities — 1st place at the Flexday AI Hackathon\n"
176
+ "- Optimized a semantic search system using LLM/embedding-based techniques, improving "
177
+ "retrieval performance by **50%**\n"
178
+ "- Designed automated, AI-assisted workflows across departments, cutting process cycle "
179
+ "time by 5% and freeing 10+ hours/month\n"
180
+ "- Implemented OCR-based data extraction pipelines across diverse image collections\n"
181
+ "- Identified and remediated critical security vulnerabilities, mitigating million-dollar "
182
+ "risk exposure\n"
183
+ "- Streamlined CI/CD build pipelines using Git, GitHub, Jira, and Azure DevOps"
184
+ )
185
+ with gr.Accordion("Machine Learning Engineer — Leokraft Technologies, Bangalore · Dec 2021 – Dec 2022", open=False):
186
+ gr.Markdown(
187
+ "- Designed, trained, and deployed end-to-end ML models into production, improving "
188
+ "accuracy by **8%** while cutting infrastructure costs by **20%**\n"
189
+ "- Engineered a key target-variable feature that improved model accuracy by 5%\n"
190
+ "- Built an end-to-end application for managing model scores and usage statistics"
191
+ )
192
+ with gr.Accordion("Senior Faculty — CodingZen, Delhi · Jul 2018 – Jul 2019", open=False):
193
+ gr.Markdown(
194
+ "- Taught 100+ students full-stack web development with Node.js\n"
195
+ "- Supervised and mentored a team of teaching staff"
196
+ )
197
+
198
+ gr.HTML('<p class="section-label">// projects</p>')
199
+ with gr.Row():
200
+ with gr.Column():
201
+ gr.Markdown("**INFERA** — Agentic AI Sales-Opportunity Mapper\n\n*LLMs · AI Agents · Python*\n\nAn AI agent that analyzes sales pipeline data and autonomously maps projects to real-world business opportunities.\n\n🏆 1st Place — Flexday AI Hackathon")
202
+ with gr.Column():
203
+ gr.Markdown("**Analytica** — End-to-End ML Analytics Platform\n\n*React · Python · Azure · SQL*\n\nFull-stack analytics tool surfacing ML model performance metrics and usage statistics.")
204
+ with gr.Column():
205
+ gr.Markdown("**Predictive Allocation** — ML Deployment Pipeline\n\n*Python · Azure SQL · Azure Blob · DevOps*\n\nRe-engineered end-to-end training/deployment pipeline; hardened security with Snyk and Wiz.")
206
+
207
+ gr.HTML('<p class="section-label">// skills</p>')
208
+ gr.Markdown(
209
+ "**AI/ML & GenAI:** LLMs · Generative AI · Agentic AI · Prompt Engineering · Semantic Search · "
210
+ "Classification · Regression · Decision Trees · SMOTE · OCR/NLP\n\n"
211
+ "**Programming:** Python · JavaScript · Node.js\n\n"
212
+ "**Cloud & MLOps:** Microsoft Azure · Azure SQL · Azure Blob · Serverless · Virtual Machines · CI/CD\n\n"
213
+ "**Tools & Practices:** Git · GitHub · Jira · Azure DevOps · Snyk · Wiz · Agile"
214
+ )
215
+
216
+ gr.HTML('<p class="section-label">// certifications & education</p>')
217
+ with gr.Row():
218
+ with gr.Column():
219
+ gr.Markdown(
220
+ "**Certifications & Honors**\n\n"
221
+ "- Building with the Claude API\n"
222
+ "- Generative AI (Professional Certificate)\n"
223
+ "- AI Fluency: Framework & Foundations\n"
224
+ "- Machine Learning for Leaders\n"
225
+ "- Analyze Box Office Data with Seaborn and Python\n"
226
+ "- 1st Place — Flexday AI Hackathon (INFERA)"
227
+ )
228
+ with gr.Column():
229
+ gr.Markdown(
230
+ "**Education**\n\n"
231
+ "**M.Tech, Information Technology** — Tezpur University · 2019–2021\n"
232
+ "- Graduated with Distinction — 8.69 CGPA\n"
233
+ "- Full-time AICTE scholarship recipient\n\n"
234
+ "**B.Tech, Computer Science & Engineering** — KIET · 2012–2016\n"
235
+ "- Secured First Division"
236
+ )
237
+
238
+ if __name__ == "__main__":
239
+ demo.launch(css=CUSTOM_CSS, theme=gr.themes.Base(primary_hue="amber", neutral_hue="slate"))
app/__init__.py DELETED
@@ -1,83 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- import re
5
-
6
- import gradio as gr
7
-
8
-
9
- DATA_DIR = Path(__file__).resolve().parent.parent / "data" / "knowledge"
10
-
11
-
12
- def _load_documents() -> list[tuple[str, str]]:
13
- docs: list[tuple[str, str]] = []
14
- for file_path in sorted(DATA_DIR.glob("*.txt")):
15
- content = file_path.read_text(encoding="utf-8").strip()
16
- if content:
17
- docs.append((file_path.name, content))
18
- return docs
19
-
20
-
21
- DOCUMENTS = _load_documents()
22
-
23
-
24
- def _match_lines(question: str, text: str) -> list[str]:
25
- tokens = {token.lower() for token in re.findall(r"[a-zA-Z]+", question) if token}
26
- if not tokens:
27
- return []
28
-
29
- lines = [line.strip() for line in text.splitlines() if line.strip()]
30
- matched: list[str] = []
31
- for line in lines:
32
- line_lower = line.lower()
33
- if any(token in line_lower for token in tokens):
34
- matched.append(line)
35
- return matched[:3]
36
-
37
-
38
- def answer_question(question: str) -> str:
39
- if not question.strip():
40
- return "Please enter a question about the invoice, resume, or support ticket files."
41
-
42
- answers: list[str] = []
43
- for source, text in DOCUMENTS:
44
- matches = _match_lines(question, text)
45
- if matches:
46
- answers.append(f"From {source}:\n" + "\n".join(matches))
47
-
48
- if not answers:
49
- return (
50
- "No strong matches were found in the local knowledge files. "
51
- "Try asking about an invoice, a resume, or a support ticket entry."
52
- )
53
-
54
- return "Grounded answer (evidence from local knowledge files):\n\n" + "\n\n".join(answers[:3])
55
-
56
-
57
- def create_demo() -> gr.Blocks:
58
- with gr.Blocks(title="Agentic RAG Demo") as demo:
59
- gr.Markdown(
60
- """
61
- # Agentic RAG Demo
62
-
63
- Ask a question about the sample invoice, resume, or support ticket set.
64
- This lightweight Spaces version uses the local knowledge files directly and stays compatible with the default CPU runtime.
65
- """
66
- )
67
-
68
- question = gr.Textbox(
69
- label="Question",
70
- placeholder="Example: Which support ticket mentions a missing trailing slash?",
71
- lines=2,
72
- )
73
- submit = gr.Button("Run agent")
74
- answer = gr.Textbox(label="Answer", lines=10)
75
-
76
- submit.click(fn=answer_question, inputs=question, outputs=answer)
77
-
78
- return demo
79
-
80
-
81
- demo = create_demo()
82
-
83
- __all__ = ["answer_question", "create_demo", "demo"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/__pycache__/__init__.cpython-311.pyc DELETED
Binary file (200 Bytes)
 
app/__pycache__/agent.cpython-311.pyc DELETED
Binary file (5.99 kB)
 
app/__pycache__/retriever.cpython-311.pyc DELETED
Binary file (5.11 kB)
 
app/agent.py DELETED
@@ -1,89 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TypedDict
4
-
5
- from dotenv import load_dotenv
6
- from langgraph.graph import END, START, StateGraph
7
-
8
- from app.retriever import build_vectorstore, retrieve_and_rerank
9
-
10
- load_dotenv()
11
-
12
-
13
- class AgentState(TypedDict):
14
- question: str
15
- documents: list
16
- answer: str
17
-
18
-
19
- def _build_vectorstore() -> object:
20
- return build_vectorstore()
21
-
22
-
23
- def retrieve_node(state: AgentState) -> dict:
24
- vectorstore = _build_vectorstore()
25
- docs = retrieve_and_rerank(state["question"], vectorstore, k=5)
26
- return {"documents": docs}
27
-
28
-
29
- def _dedupe_documents(documents: list) -> list:
30
- unique: list = []
31
- seen_sources: set[str] = set()
32
-
33
- for doc in documents:
34
- source = doc.metadata.get("source") if hasattr(doc, "metadata") else None
35
- if source and source in seen_sources:
36
- continue
37
- if source:
38
- seen_sources.add(source)
39
- unique.append(doc)
40
-
41
- return unique
42
-
43
-
44
- def _grounded_summary(question: str, documents: list) -> str:
45
- if not documents:
46
- return "No relevant passages were retrieved for that question."
47
-
48
- question_tokens = {token.lower() for token in question.replace("\n", " ").split() if token.isalpha()}
49
- unique_docs = _dedupe_documents(documents)
50
- selected_parts: list[str] = []
51
-
52
- for doc in unique_docs:
53
- text = doc.page_content.strip()
54
- lines = [line.strip() for line in text.splitlines() if line.strip()]
55
- matched_lines = [
56
- line for line in lines if any(token.lower() in line.lower() for token in question_tokens)
57
- ]
58
- if matched_lines:
59
- selected_parts.append("\n".join(matched_lines))
60
- else:
61
- selected_parts.append("\n".join(lines))
62
-
63
- return (
64
- "Grounded answer (evidence-based summary):\n\n"
65
- + "\n\n".join(selected_parts[:3])
66
- )
67
-
68
-
69
- def answer_node(state: AgentState) -> dict:
70
- evidence = "\n\n".join(doc.page_content for doc in state["documents"])
71
- answer = _grounded_summary(state["question"], state["documents"])
72
-
73
- if not evidence.strip():
74
- answer = "No evidence was retrieved for that question."
75
-
76
- return {"answer": answer}
77
-
78
-
79
- def run_agent(question: str) -> str:
80
- graph = StateGraph(AgentState)
81
- graph.add_node("retrieve", retrieve_node)
82
- graph.add_node("answer", answer_node)
83
- graph.add_edge(START, "retrieve")
84
- graph.add_edge("retrieve", "answer")
85
- graph.add_edge("answer", END)
86
-
87
- app = graph.compile()
88
- result = app.invoke({"question": question, "documents": [], "answer": ""})
89
- return result["answer"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/main.py DELETED
@@ -1,19 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import sys
4
-
5
- from app.agent import run_agent
6
-
7
-
8
- def main() -> None:
9
- question = " ".join(sys.argv[1:])
10
- if not question:
11
- question = "Which ticket mentions a missing trailing slash and what was the resolution?"
12
-
13
- answer = run_agent(question)
14
- print("\nAnswer:\n")
15
- print(answer)
16
-
17
-
18
- if __name__ == "__main__":
19
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/retriever.py DELETED
@@ -1,64 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- from collections import Counter
5
- from pathlib import Path
6
-
7
- from dotenv import load_dotenv
8
- from langchain_chroma import Chroma
9
- from langchain_core.documents import Document
10
- from langchain_huggingface import HuggingFaceEmbeddings
11
- from langchain_text_splitters import RecursiveCharacterTextSplitter
12
-
13
- load_dotenv()
14
-
15
- DATA_DIR = Path(__file__).resolve().parent.parent / "data" / "knowledge"
16
- DB_DIR = Path(os.getenv("CHROMA_DB_DIR", ".chroma_db"))
17
-
18
-
19
- def _load_documents() -> list[Document]:
20
- docs: list[Document] = []
21
- for file_path in sorted(DATA_DIR.glob("*.txt")):
22
- content = file_path.read_text(encoding="utf-8")
23
- docs.append(Document(page_content=content, metadata={"source": file_path.name}))
24
- return docs
25
-
26
-
27
- def build_vectorstore() -> Chroma:
28
- splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=120)
29
- raw_docs = _load_documents()
30
- chunks = splitter.split_documents(raw_docs)
31
-
32
- embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
33
- vectorstore = Chroma.from_documents(
34
- documents=chunks,
35
- embedding=embeddings,
36
- persist_directory=str(DB_DIR),
37
- collection_name="agentic-rag-demo",
38
- )
39
- return vectorstore
40
-
41
-
42
- def _normalize_text(text: str) -> list[str]:
43
- return [token.lower() for token in text.replace("\n", " ").split() if token.isalpha()]
44
-
45
-
46
- def _keyword_overlap_score(question: str, chunk: str) -> float:
47
- question_tokens = Counter(_normalize_text(question))
48
- chunk_tokens = Counter(_normalize_text(chunk))
49
- overlap = sum(min(question_tokens[token], chunk_tokens[token]) for token in question_tokens)
50
- if overlap == 0:
51
- return 0.0
52
- return overlap / max(1, len(question_tokens))
53
-
54
-
55
- def retrieve_and_rerank(question: str, vectorstore: Chroma, k: int = 5) -> list[Document]:
56
- hits = vectorstore.similarity_search(question, k=k)
57
- scored = []
58
- for doc in hits:
59
- score = _keyword_overlap_score(question, doc.page_content)
60
- scored.append((score, doc))
61
-
62
- scored.sort(key=lambda item: item[0], reverse=True)
63
- reranked = [doc for _, doc in scored if doc.page_content.strip()]
64
- return reranked[:3]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/knowledge/invoice.txt DELETED
@@ -1,10 +0,0 @@
1
- Invoice #INV-2048
2
- Vendor: Northwind Data Services
3
- Date: 2026-04-10
4
- Total: $2,480.00
5
- Status: Paid
6
- Line items:
7
- - Managed analytics platform: $1,940.00
8
- - Support retention: $540.00
9
- Notes:
10
- The customer requested quarterly reporting for March and April. The account owner is Dana Lewis.
 
 
 
 
 
 
 
 
 
 
 
data/knowledge/resume.txt DELETED
@@ -1,11 +0,0 @@
1
- Candidate: Maya Thompson
2
- Role: Senior Data Analyst
3
- Experience:
4
- - 7 years in BI and forecasting
5
- - Built a revenue dashboard for a logistics firm
6
- - Led migration from Excel-based reporting to a Snowflake pipeline
7
- Skills:
8
- Python, SQL, dbt, Power BI, stakeholder communication
9
- Availability: Immediate
10
- Reference note:
11
- Maya is a strong fit for roles involving analytics modernization and cross-functional communication.
 
 
 
 
 
 
 
 
 
 
 
 
data/knowledge/support_ticket.txt DELETED
@@ -1,10 +0,0 @@
1
- Support ticket #ST-882
2
- Customer: Harbor Logistics
3
- Priority: High
4
- Issue: The dashboard stopped refreshing after the nightly data import.
5
- Steps taken:
6
- - Verified permissions on the warehouse connector.
7
- - Restarted the ingestion job.
8
- - Confirmed the export path was missing a trailing slash.
9
- Resolution: Updated the path and reran the import. The dashboard became healthy again.
10
- Owner: Omar Patel
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1 +1,3 @@
1
- gradio>=6.20.0
 
 
 
1
+ gradio>=5.0
2
+ scikit-learn>=1.3
3
+ numpy>=1.24
space.yaml DELETED
@@ -1,4 +0,0 @@
1
- sdk: gradio
2
- app_file: app.py
3
- python_version: "3.11"
4
- suggested_hardware: cpu-basic