Project Info
This project did not submit a demo video on Devpost.
Big tech ad networks rely on tracking and personal data to target users. PrivAds is a new kind of ad platform: it predicts what ads users are most likely to engage with — without ever storing their identity, browsing history, or demographics. We use only click feedback and page context to serve relevant, privacy-respecting ads.
Inspiration
We took ideas from many of the challenges at CalHacks. For example, we loved AppLovin’s challenge to extract high-value signals from ads as a way to gain user insights, but we built upon that and pushed it further by asking: “What if we could build an ad recommendation engine that’s smart and privacy first?” We also drew from Y Combinator’s AI native track to imagine what an AI-powered, privacy-centric alternative to existing Y Combinator ad startups, such as Plai, might look like in today’s world. What we do Imagine an ad network that actually respects your privacy. Instead of tracking you with cookies and personal data, our system learns from your interactions like what types of ads you click on, what pages you are interested in, and builds a privacy-first user embedding that never stores your identity. We process ad images or videos using multimodal AI, where we extract visual, textual, and contextual signals from images and videos using a VLM encoder. Then, we use contrastive learning to "match" those embeddings against a user’s embedding space. With Fetch.ai, we simulate autonomous user personas who explore thousands of ads, and Claude AI helps us model click probabilities for each ad-user pair. This all came together into a scalable, privacy-first ad recommendation engine that knows what you like without knowing who you are. What makes us special No user tracking or segments: We use user embeddings, not personas or segments that could be traced back. Multimodal ad understanding: Ads are processed with a vision-language model (Jina CLIP v2) to extract both visual and textual signals. Context-aware serving: Ad selection considers both user embedding and the current page context. Custom and dynamic ads: The system can generate or enhance ads on the fly, tailored to user interests and page content. Less ads: Showing ads to people who are not going to interact with the material is a waste of time and money. For instance, if someone's in a hurry, they won't click on any ads. We detect user interaction patterns and avoid showing ads when we anticipate low performance. Everybody wins!
How we built it
Dataset collection: The contrastive learning model requires data on ads and users interacting with those ads. To do so, we built on AppLovin's provided dataset. We used Google Gemini and Reka to extract key features from image and video ads, respectively. We then used an Anthropic AI agent with BrightData's MCP server and Langchain to build a large dataset of ads. Furthermore, for user dataset generation, we used another agent to build realistic user profiles. Contrastive Learning LLM for Ad-Serving: Unlike OpenAI's CLIP model, instead of a one-to-one relationship amongst data, we have many users interacting with many ads -- aka many-to-many data. We generate embeddings for user profiles, context of the user, and ads. The model crunches out which ads to serve to which users. The model is deployed at an endpoint. Platform for companies: In the grand scheme of things, the ad-serving platform serves companies publishing advertisements. We have already figured out ad-understanding, customer-segmentation, and effective ad-serving. Custom Ads: The platform allows companies to generate custom ads; companies can also opt to enhance their pre-existing ads dynamically on each user's device. The system extracts contextual information about the app / webpage the ad is being deployed on, makes inferences about the user, and specifically tailors the ad to the user's interests to improve click-through rate. Demo website: https://privads-demo.onrender.com/
Challenges we ran into
It was a very complex system to implement because there were several dependent parts. While we doubled down on the research problem of effectively learning from creatives granted high dimensional data, we ideated a lot about what direction to take it in as a product, what problem we were solving for our users (companies), and our unique value proposition. Luckily, we were able to find our direction and niche.
Accomplishments we're proud of
To have built so many agents and a custom LLM for ad-serving that works despite high-dimensional, sparse data.
What we learned
Architecture matters: Separating concerns (frontend, web_ad_service, backend) makes scaling easier, but coordinating them and building them separately at the same time as a team is complex. Environment management: Having one shared miniconda env for multiple services is messy; separate envs or containers (Docker) are better. Privacy-first is hard but valuable - Building without personal data tracking is more complex but more aligned with user interests We learned to test external APIs in isolation. Setting up isolated tests for each part of the frontend customized ad generation pipeline is what helped us catch the real issue in the process not working.
What's next
Comparing the use of contrastive learning to ML models oriented toward high-dimensional sparse data.
PrivAds: Privacy-First AI Ad Recommendation Engine
Why PrivAds?
Big tech ad networks rely on tracking and personal data to target users. PrivAds is a new kind of ad platform: it learns what users like—without ever storing their identity, browsing history, or demographics. We use only click feedback and page context to serve relevant, privacy-respecting ads.
What Makes It Unique
- No user tracking or segments: We use user embeddings, not personas or segments that could be traced back.
- Multimodal ad understanding: Ads are processed with a vision-language model (Jina CLIP v2) to extract both visual and textual signals.
- Context-aware serving: Ad selection considers both user embedding and the current page context.
- Custom and dynamic ads: The system can generate or enhance ads on the fly, tailored to user interests and page content.
Technical Architecture
Ad (text + image) → Jina CLIP v2 (frozen) → z_ad (2048D) → Projector (MLP) → p_ad (512D)
↓
User clicks → Co-click graph → Margin/Contrastive Loss → user_embeddings (512D)
↓
Scoring: cos(user_emb, p_ad)
- Frozen VLM: Jina CLIP v2 is never fine-tuned, ensuring robust, general ad representations.
- Learned Projector: A 2-layer MLP maps ad embeddings into the user space.
- User Embeddings: Learned via margin-based contrastive loss on a co-click graph (users who clicked the same ads).
- All embeddings L2-normalized for cosine similarity.
Quick Start
pip install -r requirements.txtpython main.py- Loads Jina CLIP v2
- Generates synthetic or loads real click data
- Trains user embeddings and projector
- Saves models and embeddings
- Outputs in
models/anddata/(see below)
Outputs
models/
├── user_embeddings.npy # (n_users, 512)
├── global_mean.npy # (512,)
└── projector.pt # Projector weights
data/
├── ad_embeddings_raw.npz # (n_ads, 2048)
└── ad_projected.npz # (n_ads, 512)
Technical Details
- Contrastive Learning: Margin-based loss encourages user embeddings to be closer to ads they clicked than to negatives, by a margin.
- Co-click Graph: Users are connected if they clicked the same ad; this graph is the basis for contrastive training.
- Synthetic & Real Data: Swap between synthetic and real click data with a single line of code.
- Evaluation: Metrics include Recall@100 and AUC for retrieval quality.
- No PII, no history: Only abstract vectors are stored; no user or behavioral data is ever saved.
Component Testing
cd src
python ad_encoder.py # Test ad encoder
python projector.py # Test projector
python click_data.py # Test click data
python train_user_embeddings.py
python train_projector.py
Privacy by Design
- No user metadata, no tracking, no segments
- Embeddings are abstract and cannot be reversed to user data
- GDPR-compliant: delete a user by removing their embedding
Next Steps
- Thompson Sampling for exploration
- Real-time serving API
- Feedback loop for online learning
- Real ad data with images/videos
- Advanced evaluation (NDCG, etc.)
- Fast ANN retrieval (FAISS)
Demo
https://privads-demo.onrender.com/
License
Apache 2.0
Analysis
View
Metric
- 30
- 15
- 4
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- CSSIn code
- FastAPIIn code
- HTMLIn code
- Hugging FaceIn code
- JavaScriptIn code
- LangChainIn code
- PythonIn code
- PyTorchIn code
- ReactIn code
- TypeScriptIn code
- Google GeminiClaimed
11 of 12 appear in the indexed code. 1 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
671 KB
Source files
95
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
ACMCMC/privads
163 files · 105.2 MB · @ d36a4c1
Structure
Interface
7 files · 4%Screens, components and styles rendered to the user.
Application logic
77 files · 47%Domain rules, services and shared utilities.
+6 moreData & schema
3 files · 2%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python41%
- JavaScript28%
- TypeScript13%
- Markdown11%
- CSS6%
- HTML1%
- Other (1)0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
requirements.txt
pypi · 39- beautifulsoup4
- black
- brightdata-sdk
- chromadb
- datasets
- easyocr
- einops
- elasticsearch
- fastapi
- flake8
- google-generativeai
- html5lib
- httpx
- lxml
- matplotlib
- nltk
- numpy
- opencv-python
- +21 more
privads-demo/package.json
npm · 20- @testing-library/dom
- @testing-library/jest-dom
- @testing-library/react
- @testing-library/user-event
- @types/jest
- @types/node
- @types/react
- @types/react-dom
- @xenova/transformers
- axios
- lucide-react
- onnxruntime-web
- react
- react-dom
- react-router-dom
- react-scripts
- recharts
- typescript
- +2 more
web_ad_service/requirements.txt
pypi · 12- beautifulsoup4
- fastapi
- google-generativeai
- numpy
- pip
- pydantic
- python-dotenv
- requests
- scipy
- setuptools
- uvicorn
- wheel
backend/requirements.txt
pypi · 10- chromadb
- fastapi
- groq
- numpy
- pip
- pydantic
- python-dotenv
- setuptools
- uvicorn
- wheel
datagen/package.json
npm · 7- @anthropic-ai/sdk
- @langchain/anthropic
- @langchain/core
- @langchain/langgraph
- @langchain/mcp-adapters
- dotenv
- node-fetch
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.