Hackathon Problem ID: SBM02
A fully offline, privacy-first cold outreach system that generates hyper-personalized, tone-matched messages across multiple channels using a locally hosted LLM.
- Overview
- The Problem
- Our Solution
- Key Features
- System Architecture
- Knowledge Reuse Mechanism
- Multi-Channel Generation
- Reply Likelihood Scoring
- User Interface
- Tech Stack
- Installation & Setup
- Usage Guide
- Privacy & Ethics
- Hackathon Evaluation Criteria
- Future Enhancements
- Demo Screenshots
Cold outreach today is generic, noisy, and ineffective.
This project solves that by using a local/offline LLM to generate deeply personalized, human-like outreach messages based on publicly available profile data — without relying on any cloud AI APIs.
Traditional Outreach → Our Offline Engine
─────────────────────────────────────────────────────────
❌ Generic templates → ✅ AI-generated personalization
❌ Cloud API dependencies → ✅ 100% offline operation
❌ Privacy concerns → ✅ Local processing only
❌ One-size-fits-all → ✅ Multi-channel, multi-tone
❌ No learning mechanism → ✅ Knowledge reuse across profiles
Modern cold outreach faces critical challenges:
- Generic Messaging - Templates feel robotic and impersonal
- Privacy Risks - Cloud AI services process sensitive business data
- No Adaptation - Same message across different channels/audiences
- No Learning - Each outreach starts from scratch
- Poor Conversion - Low reply rates due to lack of personalization
SBM02 Challenge: Build an intelligent, offline solution that demonstrates true personalization and knowledge reuse.
An offline-first outreach engine that:
| Component | Implementation |
|---|---|
| LLM Backend | Ollama + LLaMA 3.2 3B (fully local) |
| Personalization | Profile parsing + dynamic prompt engineering |
| Channel Adaptation | Email / LinkedIn / WhatsApp formats |
| Tone Matching | Formal / Casual variants with scoring |
| Knowledge Reuse | JSON-based memory of past outreach patterns |
| Privacy | Zero external API calls, all processing local |
|
|
|
|
graph TD
A[User Input] -->|Profile Text| B[React Frontend Vite]
B -->|1. Stream Request SSE| C[FastAPI Backend]
C -->|2. Parse Metadata| D{Logic Engine}
D -->|Search Patterns| F[(Local Memory JSON)]
F -->|Context Injection| G[Knowledge Reuse]
G -->|3. Construct Prompt| H[Prompt Engineer]
H -->|4. Trigger Offline LLM| J[Ollama - Llama 3.2]
J -->|Neural Token Stream| C
C -->|5. Real-time Chunks| B
C -->|6. Self-Reflection| L[Reply Scorer]
L -->|Hidden Score Tag| B
style A fill:#e1f5ff,stroke:#007acc
style J fill:#fff4e1,stroke:#d4a017
style F fill:#f0f0f0,stroke:#333
style B fill:#e8f5e9,stroke:#2e7d32
style L fill:#fce4ec,stroke:#c2185b
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ React Frontend (Vite + TS) │ │
│ │ - Glassmorphic UI (Tailwind + Framer) │ │
│ │ - Real-time Typewriter Rendering │ │
│ │ - Stream Parsing (||SCORE:|| separation) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ Server-Sent Events (SSE)
┌─────────────────────────────────────────────────────────────┐
│ BUSINESS LOGIC LAYER │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ FastAPI Backend (main.py) │ │
│ │ ┌─────────────────┐ ┌──────────────────┐ │ │
│ │ │ StreamingEngine │ │ Context Injector │ │ │
│ │ └─────────────────┘ └──────────────────┘ │ │
│ │ ┌─────────────────┐ ┌──────────────────┐ │ │
│ │ │ Memory JSON │ │ Reply Scorer │ │ │
│ │ └─────────────────┘ └──────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ Local Network (Loopback)
┌─────────────────────────────────────────────────────────────┐
│ LLM LAYER │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Ollama Inference Server │ │
│ │ (llama3.2:3b - Fully Offline Runtime) │ │
│ │ - Token Streaming │ │
│ │ - Zero-Latency Handoff │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ File I/O
┌─────────────────────────────────────────────────────────────┐
│ PERSISTENCE LAYER │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Local Memory Store (memory.json) │ │
│ │ { │ │
│ │ "role": "Software Engineer", │ │
│ │ "channel": "email", │ │
│ │ "tone": "formal" │ │
│ │ } │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ ┌────────┐
│ Profile │───▶│ Parse │───▶│ Memory │───▶│ Prompt │───▶│ LLM │
│ Input │ │ Metadata │ │ Check │ │ Engineer │ │ Call │
└─────────┘ └──────────┘ └─────────┘ └──────────┘ └────────┘
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│ Store │◀─────────────────│ Score & │
│ Pattern │ │ Format │
└─────────┘ └──────────┘
Step 1: New Profile Arrives
↓
Step 2: Extract Metadata (Role, Industry, Channel, Tone)
↓
Step 3: Query Local Memory for Similar Patterns
↓
┌───────────────────────┐
│ Found Similar Match? │
└─────────┬─────────────┘
│
┌─────┴─────┐
│ │
YES NO
│ │
▼ ▼
Add Context Standard
to Prompt Prompt
│ │
└─────┬─────┘
│
▼
Generate Message
↓
Store New Pattern
{
"outreach_history": [
{
"timestamp": "2026-02-06T18:17:18",
"profile_metadata": {
"role": "Software Engineer",
"industry": "AI/Tech",
"seniority": "Mid-level",
"interests": ["Machine Learning", "Open Source"]
},
"channel": "email",
"tone": "formal",
"reply_score": 72,
"successful_patterns": {
"mentioned_interests": true,
"referenced_work": true,
"clear_cta": true
}
}
]
}| Without Knowledge Reuse | With Knowledge Reuse |
|---|---|
|
```
"I came across your profile and
thought you might be interested
in..."
```
❌ Generic |
```
"We've been connecting with several
ML engineers in the fintech space
recently, and your work on XYZ
caught our attention..."
```
✅ Shows research |
┌────────────────────────────────────────────────────────────────┐
│ CHANNEL ADAPTATION │
├──────────────┬─────────────┬──────────────┬────────────────────┤
│ Channel │ Length │ Purpose │ Key Features │
├──────────────┼─────────────┼──────────────┼────────────────────┤
│ 📧 Email │ 100-120 w │ Detailed │ • Subject line │
│ │ │ outreach │ • Formal structure │
│ │ │ │ • Clear CTA │
├──────────────┼─────────────┼──────────────┼────────────────────┤
│ 💼 LinkedIn │ 40-60 w │ Professional │ • Concise intro │
│ │ │ quick connect│ • Value prop │
│ │ │ │ • Soft ask │
├──────────────┼─────────────┼──────────────┼────────────────────┤
│ 💬 WhatsApp │ 25-35 w │ Casual & │ • Conversational │
│ │ │ direct │ • Emoji-friendly │
│ │ │ │ • Ultra-brief │
└──────────────┴─────────────┴──────────────┴────────────────────┘
| Profile: Senior Data Scientist @ FinTech Startup | ||
|
📧 Email (Formal) Score: 78/100 |
💼 LinkedIn DM Score: 72/100 |
Score: 68/100 |
def calculate_reply_score(message, profile):
"""
Scores message on 0-100 scale based on:
- Personalization depth
- Clarity of value proposition
- Call-to-action strength
- Natural language quality
"""
score = 0
# Personalization (40 points)
if mentions_specific_work(message, profile):
score += 20
if references_interests(message, profile):
score += 10
if uses_name_naturally(message):
score += 10
# Value Proposition (30 points)
if has_clear_value_prop(message):
score += 15
if shows_mutual_benefit(message):
score += 15
# CTA Quality (20 points)
if has_specific_cta(message):
score += 10
if low_friction_ask(message):
score += 10
# Language Quality (10 points)
if natural_tone(message):
score += 5
if appropriate_length(message):
score += 5
return min(score, 100)┌────────────────────────────────────────────────┐
│ REPLY LIKELIHOOD │
├──────────┬─────────────────────────────────────┤
│ 0-30 │ ❌ Poor - Major revisions needed │
│ 31-50 │ ⚠️ Fair - Needs improvement │
│ 51-70 │ ✅ Good - Likely to get response │
│ 71-85 │ 🎯 Great - High engagement chance │
│ 86-100 │ ⭐ Excellent - Optimized message │
└──────────┴─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ REACT GLASS INTERFACE │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Step 1: Paste Neural Context │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Paste LinkedIn Bio, Twitter feed, or raw │ │ │
│ │ │ profile text into the Glass Input Container. │ │ │
│ │ │ │ │ │
│ │ │ Example: "Senior Dev @ Netflix, Loves Rust..." │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Step 2: Intelligent Configuration │ │
│ │ Lang: [ Hinglish ▼ ] Mode: [ Professional ▼ ] │ │
│ │ [ 🚀 Execute Neural Generation ] │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Step 3: Real-Time Neural Streaming │ │
│ │ ┌─────────────────────┬─────────────────────┐ │ │
│ │ │ EMAIL VARIANT │ LINKEDIN VARIANT │ │ │
│ │ ├─────────────────────┼─────────────────────┤ │ │
│ │ │ Generating... │ Generating... │ │ │
│ │ │ [Streaming Tokens] │ [Streaming Tokens] │ │ │
│ │ │ │ │ │ │
│ │ │ "Hey Sarvesh, I saw"│ "Sarvesh, let's con"│ │ │
│ │ │ │ │ │ │
│ │ │ 📊 Score: Calc... │ 📊 Score: Calc... │ │ │
│ │ │ 📋 [Copy] │ 📋 [Copy] │ │ │
│ │ └─────────────────────┴─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 📊 Knowledge Reuse & Context Indicator | │
│ │ ✅ Vector match found in memory.json (AI/ML Tier) │ │
│ │ 💡 Optimizing prompt using proven success patterns │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
react>=18.2.0 framer-motion>=12.0.0 lenis>=1.1.18 three>=0.172.0
typewriter-effect>=2.21.0 axios>=1.7.0
lucide-react>=0.473.0 tailwind-merge>=3.0.0 clsx>=2.1.1
# System Requirements
- Python 3.10 or higher
- 8GB RAM minimum (16GB recommended)
- 10GB free disk space for LLM model
- macOS, Linux, or Windows with WSL2macOS
```bash brew install ollama ```Linux
```bash curl -fsSL https://ollama.com/install.sh | sh ```Windows (WSL2)
```bash curl -fsSL https://ollama.com/install.sh | sh ```# Start Ollama service
ollama serve
# In a new terminal, pull the model
ollama pull llama3.2:3b
# Verify installation
ollama list# Clone repository
git clone
cd offline-outreach-engine
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt# Create empty memory file
echo '{"outreach_history": []}' > memory.json# Terminal 1: Wake up the local LLM
ollama serve
# Terminal 2: Ignite the FastAPI backend
cd backend
source venv/bin/activate
uvicorn main:app --reload --port 8000
# Terminal 3: Launch the React Frontend
cd frontend
npm run dev🌐 Frontend UI: http://localhost:5173
🔧 Backend API: http://localhost:8000
📚 API Documentation: http://localhost:8000/docs
# Example Profile Input
"""
Senior Machine Learning Engineer at Netflix
- 8+ years in ML/AI
- Specializes in recommendation systems
- Published papers on collaborative filtering
- Interested in large-scale distributed systems
"""
# System generates:
# ✅ Formal Email
# ✅ Casual Email
# ✅ LinkedIn DM
# ✅ WhatsApp Message
# ✅ Reply scores for each# Generate outreach via API
curl -X POST "http://localhost:8000/generate" \
-H "Content-Type: application/json" \
-d '{
"profile_text": "Senior ML Engineer at Google...",
"channel": "email",
"tone": "formal"
}'┌─────────────────────────────────────────────────────────┐
│ PRIVACY-FIRST DESIGN │
├─────────────────────────────────────────────────────────┤
│ ✅ 100% Offline Processing │
│ ✅ No External API Calls │
│ ✅ No Data Transmission │
│ ✅ Local Storage Only │
│ ✅ No PII Stored (only metadata) │
│ ✅ User-Controlled Data Deletion │
│ ✅ GDPR Compliant by Design │
└─────────────────────────────────────────────────────────┘
| ✅ Stored (Anonymous Metadata) | ❌ NOT Stored |
|---|---|
| Role (e.g., "Engineer") | Full names |
| Industry (e.g., "Tech") | Email addresses |
| Channel preference | Phone numbers |
| Tone preference | Company names |
| Timestamp | Generated message content |
| Success patterns | Profile URLs |
⚠️ This tool is designed for:
✅ Legitimate business outreach
✅ Educational demonstrations
✅ Personal networking
❌ NOT intended for:
❌ Spam or unsolicited mass messaging
❌ Deceptive practices
❌ Harassment
❌ Data scraping without consent
| Criterion | Implementation | Evidence |
|---|---|---|
| Offline LLM Usage | ✅ Ollama + LLaMA 3.2 (3B) | Fully air-gapped; zero external network egress post-setup. |
| Personalization | ✅ Dynamic Prompt Engineering | Context-aware extraction of role, industry, and niche interests. |
| Multi-Channel | ✅ Adaptive Formatting Engine | Context-specific outputs for Email (SMTP), LinkedIn, and WhatsApp. |
| Tone Adaptation | ✅ Dual-Mode Neural Generation | Formal vs. Casual variants with side-by-side UI comparison. |
| Knowledge Reuse | ✅ Local JSON Vector-Pattern Store | Uses memory.json to inject successful past patterns into prompts. |
| Scoring Mechanism | ✅ Self-Reflection Agent (0-100) | LLM-driven probability scoring for reply likelihood. |
| Demo Quality | ✅ React + SSE Streaming UI | High-fidelity Glassmorphic UI with token-by-token streaming. |
| Privacy | ✅ Localhost Handoff Logic | All data stays on MacOS local storage; inherently GDPR compliant. |
1. 🔄 Knowledge Reuse
- Demonstrates learning from past interactions
- Improves personalization over time
- Shows in generated message text
2. 📊 Multi-Dimensional Scoring
- Not just "good/bad"
- Quantified 0-100 scale
- Helps users make informed decisions
3. 🎯 Channel-Specific Optimization
- Not one-size-fits-all
- Adapts length, tone, format
- Platform-appropriate messaging
4. 🔒 Privacy-First Architecture
- Offline-by-default
- No vendor lock-in
- User data sovereignty
Phase 1 (Current) ✅
├── Offline Llama 3.2 (3B) Neural Engine
├── FastAPI Real-time SSE Token Streaming
├── React Glassmorphic UI & Smooth Motion
├── Multi-lingual Neural Support (English + Hindi + Hinglish)
└── Local JSON-based Knowledge Reuse
Phase 2 (Next Sprint) 🔨
├── Chrome Extension (Direct LinkedIn Integration)
├── CSV Batch Processing Engine for Bulk Outreach
├── Advanced A/B Testing & Response Heuristics
└── Local Vector DB (ChromaDB/FAISS) for Semantic Memory
Phase 3 (Future) 📋
├── Multi-Modal Support (Image/Audio Outreach Analysis)
├── Private Local CRM & Interaction Analytics
├── WASM-based Edge Inference for Browser-only Mode
└── Voice-to-Outreach (Local Whisper Integration)
| Feature | Description | Priority |
|---|---|---|
| Bulk Processing | Local CSV/JSON engine for high-volume offline outreach batches. | High |
| Chrome Extension | Scrape & Generate directly from the LinkedIn DOM without context-switching. | High |
| SSE Optimization | Concurrent multi-model streaming for near-instant side-by-side variants. | High |
| Vector Memory | Transitioning from memory.json to a local ChromaDB instance for semantic search. |
Medium |
| A/B Testing | Comparison dashboard to track response rate heuristics and tone effectiveness. | Medium |
| Reply Simulation | Practice handling objections with a local "Prospect Agent" simulator. | Low |
| Local CRM | An air-gapped, SQLite-based database to track sent messages and follow-up cycles. | Low |
Glassmorphic Neural UI with intelligent input parsing.

Real-time token streaming of Formal vs. Casual variants with Reply Probability scoring.

On-device pattern matching triggering the local history optimization toast.

Built for XENIA - SBM02 Challenge
For questions or demo requests:
📧 Email : sarvesh.bijawe24@vit.edu
🔗 LinkedIn : www.linkedin.com/in/sarvesh-bijawe
🐙 GitHub : https://github.com/Sarvesh5273
MIT License - Feel free to use for educational purposes
Not intended for production spam/unsolicited outreach