Instructions to use ykshrestha/personalmail-rl-qwen with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use ykshrestha/personalmail-rl-qwen with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("unsloth/qwen2.5-1.5b-instruct-unsloth-bnb-4bit") model = PeftModel.from_pretrained(base_model, "ykshrestha/personalmail-rl-qwen") - Transformers
How to use ykshrestha/personalmail-rl-qwen with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ykshrestha/personalmail-rl-qwen") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ykshrestha/personalmail-rl-qwen", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use ykshrestha/personalmail-rl-qwen with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ykshrestha/personalmail-rl-qwen" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ykshrestha/personalmail-rl-qwen", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/ykshrestha/personalmail-rl-qwen
- SGLang
How to use ykshrestha/personalmail-rl-qwen with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "ykshrestha/personalmail-rl-qwen" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ykshrestha/personalmail-rl-qwen", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "ykshrestha/personalmail-rl-qwen" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ykshrestha/personalmail-rl-qwen", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Unsloth Desktop
- Docker Model Runner
How to use ykshrestha/personalmail-rl-qwen with Docker Model Runner:
docker model run hf.co/ykshrestha/personalmail-rl-qwen
- π§ PersonalMail-RL: Teaching LLMs to Handle Personal Emails Through Reinforcement Learning
- π― The Problem
- ποΈ Architecture & Flow
- π Environment Design
- π 7 Independent Reward Functions
- π‘οΈ Anti-Reward-Hacking Measures
- π¬ Training Pipeline
- π Evaluation Results β Before vs After Training
- π₯οΈ Live Demo Screenshots
- β
Hackathon Requirements Checklist
- π Deployment
- π Repository Structure
- π Links
- π― The Problem
π§ PersonalMail-RL: Teaching LLMs to Handle Personal Emails Through Reinforcement Learning
Theme: #3.2 Personalized Tasks Β· OpenEnv Hackathon Apr 2026
Stack: OpenEnv Β· TRL GRPO Β· Unsloth Β· Qwen2.5-1.5B-Instruct
Demo: π Live on HuggingFace Spaces
Model: π€ ykshrestha/personalmail-rl-qwen
Code: π» GitHub
Blog link: π» Blog
Youtube Video: π»Youtube Link
PPT link: Link
Training Scripts link: Link
Blog link: Link
β οΈ To view live inference results in the demo, please select Nvidia A100 Large GPU in HuggingFace Space Settings before running.
π― The Problem
Personal emails are not equal. A message from your manager about a missed deadline is completely different from a dinner invite from a friend. Yet most LLMs treat every email the same β they reply generically, miss urgency signals, use the wrong tone, and sometimes reply to emails that should never be replied to (like spam).
What we built: A 2-step RL environment where a model learns to first understand an email, then respond to it appropriately β rewarded by 6 independent verifiable functions.
Why RL? You cannot write ideal replies for every scenario in advance. But you can verify whether a reply is good: Does it have a proper greeting? Does it address the key points? Is the tone right? RL with verifiable rewards is the perfect fit.
ποΈ Architecture & Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PersonalMail-RL β
β β
β Email Scenario (25 scenarios Γ 3 difficulties) β
β β β
β βΌ β
β βββββββββββββββ Step 1 Action ββββββββββββββββββββ β
β β Agent β ββββββββββββββββββΊ β Classification β β
β β (Qwen2.5 β β Reward (2 fns) β β
β β 1.5B-RL) β ββββββββββββββββββ β Score: 0-1 β β
β β β Reward + Obs ββββββββββββββββββββ β
β β β β
β β β Step 2 Action ββββββββββββββββββββ β
β β β ββββββββββββββββββΊ β Reply Quality β β
β β β β Reward (5 fns) β β
β β β ββββββββββββββββββ β Score: 0-1 β β
β βββββββββββββββ Reward + Done ββββββββββββββββββββ β
β β
β Episode Total = 0.30 Γ Step1 + 0.70 Γ Step2 β
β β
β OpenEnv (FastAPI) β TRL GRPO β Unsloth QLoRA β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1 β Email Classification
The agent receives the email and outputs:
{
"urgency": "high | medium | low",
"category": "work | personal | social | spam",
"requires_reply": true | false,
"reason": "2-3 sentence explanation"
}
Step 2 β Reply Drafting
Using its own classification as context, the agent drafts:
{
"tone": "professional | friendly | assertive | apologetic | none",
"reply_text": "Full email reply here"
}
π Environment Design
Built with OpenEnv + FastAPI β fully OpenEnv spec compatible.
OpenEnv Interface
env.reset(scenario_id=None) # Start new episode, receive email
env.step(action) # Submit classification or reply β get reward
env.state() # Current episode state for monitoring
Scenario Dataset
25 handcrafted real-world email scenarios Γ 3 difficulty levels:
| Difficulty | Count | Examples |
|---|---|---|
| π’ Easy | 10 | Clear work emails, obvious spam, simple social invites |
| π‘ Medium | 8 | Ambiguous tone, mixed urgency, passive-aggressive messages |
| π΄ Hard | 7 | Conflict emails, emotional situations, multi-intent messages |
Curriculum Learning
Training starts with easy scenarios only β progressively adds medium β then hard. This ensures the model sees successful trajectories early and avoids zero-reward stalls.
π 7 Independent Reward Functions
Using multiple independent reward functions is the core defense against reward hacking. Here is every function implemented:
Step 1 Rewards β Classification (30% of Episode)
Reward 1: classification_accuracy β 21% of Episode Total
β
Urgency correct (high/medium/low) β +0.34
β
Category correct (work/personal/social/spam) β +0.33
β
Requires_reply correct (true/false) β +0.33
Reward 2: classification_format β 9% of Episode Total
β
All 4 required JSON fields present β base score
β
Reason field β₯ 5 words β +0.10 bonus
Step 2 Rewards β Reply Quality (70% of Episode)
Reward 3: reply_relevance β 28% of Episode Total
β
Must-include keywords matched in reply
Score = keywords_matched / total_keywords
β
Spam emails auto-score 1.0 (correct to not reply)
Reward 4: reply_format β 24.5% of Episode Total
β
Greeting present (Dear/Hi/Hello...) β +0.30
β
Body has β₯ 2 substantive sentences β +0.35
β
Closing present (Best/Regards/Thanks...) β +0.35
Reward 5: reply_length β 17.5% of Episode Total
Target: 40β150 words
β
40-150 words β 1.0 (perfect)
β οΈ 25-40 words β 0.7 (short)
β οΈ 150-250 words β 0.7 (long)
β <25 words β 0.3 (too short)
β >250 words β 0.4 (too long)
Reward 6: tone_matching β 15% of Episode Total
Tone detected via lexical signals:
professional β "please", "kindly", "regards", "sincerely"
friendly β "hey", "cheers", "sounds great", "would love"
assertive β "I expect", "immediately", "by end of"
apologetic β "I apologize", "deeply sorry", "I take full responsibility"
Reward 7: reply_clarity β 15% of Episode Total
β
Actionable signals (timelines, action verbs, coordination phrases)
β Penalizes vague filler ("noted", "will revert", "as soon as possible")
β
Unique word ratio β₯ 0.55 (not repetitive)
Episode Total Formula
Episode = 0.30 Γ Step1_total + 0.70 Γ Step2_total
Reply quality is weighted 70% because drafting a contextually appropriate reply is the harder, more valuable task.
π‘οΈ Anti-Reward-Hacking Measures
1. 7 Independent Signals β Cannot game any one without the others catching it.
2. Timeout Penalty β Episodes exceeding 120 seconds receive β0.5 penalty.
3. Duplicate Action Detection β Repeated identical actions are penalized.
4. Spam Safeguard β Spam emails reward not replying. Replying to spam scores 0.
5. Reply Clarity Guard β Penalizes vague "polite-only" hacks like "noted, will revert."
6. Real Discovery During Training:
During GRPO training, we discovered the model learned to output
"reason": ["some text"](a list) instead of"reason": "some text"(a string) to exploit our format reward. The field was "present" but not actually a valid string. We identified this as reward hacking and fixed reward functions to be type-safe β a real RL failure mode caught and resolved.
π¬ Training Pipeline
Stack
Qwen2.5-1.5B-Instruct (base)
β
Unsloth 4-bit QLoRA (memory efficiency)
β
TRL GRPOTrainer
β
PersonalMail-RL Environment (live reward scoring)
β
LoRA Adapter β ykshrestha/personalmail-rl-qwen
Training Configuration
model: Qwen2.5-1.5B-Instruct
lora_rank: 16
quantization: 4-bit QLoRA
rollouts: 6 per step
steps: 200
lr: 5e-5
save: adapter (no naive merge)
Training Script
# Run in Google Colab
PYTHONPATH=./personalmail-rl-opt \
python training/train_grpo.py
π Evaluation Results β Before vs After Training
Evaluated on 12 scenarios comparing Baseline Qwen2.5-1.5B vs our GRPO-trained adapter.
Colab Evaluation Output
{
"num_scenarios": 12,
"baseline": {
"classification": 0.4738,
"reply_total": 0.3874,
"reply_format": 0.4542,
"reply_relevance": 0.3611,
"reply_length": 0.4500,
"tone_matching": 0.1949,
"episode_total": 0.4133
},
"trained": {
"classification": 0.2905,
"reply_total": 0.7664,
"reply_format": 0.8917,
"reply_relevance": 0.7223,
"reply_length": 0.8333,
"tone_matching": 0.6207,
"episode_total": 0.6237
},
"improvement": {
"reply_format": "+0.4375 β
",
"tone_matching": "+0.4258 β
",
"reply_relevance": "+0.3612 β
",
"reply_length": "+0.3833 β
",
"reply_total": "+0.3790 β
",
"episode_total": "+0.2104 β
"
}
}
Results Summary Table
| Metric | Baseline | Trained | Improvement |
|---|---|---|---|
| Reply Format | 0.454 | 0.892 | +0.438 β |
| Tone Matching | 0.195 | 0.621 | +0.426 β |
| Reply Length | 0.450 | 0.833 | +0.383 β |
| Reply Relevance | 0.361 | 0.722 | +0.361 β |
| Reply Total | 0.387 | 0.766 | +0.379 β |
| Episode Total | 0.413 | 0.624 | +0.210 β |
| Classification | 0.474 | 0.291 | -0.183 β οΈ |
What Improved and Why
Reply quality improved dramatically across all metrics:
- +44% reply format β Model learned to always include greeting + body + closing
- +43% tone matching β Model learned to match professional/friendly/apologetic tone correctly
- +38% reply length β Model learned to write replies of appropriate length
- +36% reply relevance β Model learned to address actual content of the email
- +21% overall episode β Significant improvement in end-to-end email handling
Classification slightly degraded (expected tradeoff): The base Qwen model already classifies emails reasonably. GRPO focused reward signal on reply quality (70% weight) so the model optimized for the harder, more valuable task. This is an expected behavior in weighted RL.
Evaluation Command
PYTHONPATH=./personalmail-rl-opt \
python training/evaluate_before_after.py \
--baseline_model "Qwen/Qwen2.5-1.5B-Instruct" \
--trained_model "ykshrestha/personalmail-rl-qwen" \
--num_scenarios 12 \
--out_json eval_results.json
π₯οΈ Live Demo Screenshots
Live Demo β Step 1: Email Classification
The agent classifies the email and receives immediate reward feedback. Step 1 Reward: 0.762 on a conflict resolution email.
Live Demo β Step 2: Reply Drafting
The agent drafts a contextually appropriate reply.
Episode Complete with Total Reward: 0.681

Before vs After Comparison
Side-by-side comparison of Baseline vs GRPO-Trained model on the same email.

Megha: Conflict email β Meeting overlaps with client call
| Baseline | Trained (GRPO) | |
|---|---|---|
| Reply | "Hi Megha, I understand there's an overlap... Could we possibly rearrange things..." | "Hello Megha, I understand your concern about overlapping commitments. Let's try to find a mutually convenient time... If it's possible, we could adjust the design sync to another day or hour that doesn't clash with the client call. Please let me know if there's anything else I can assist with regarding scheduling adjustments. Best regards, [Your Name]" |
| Episode Reward | 0.614 | 0.694 |
| Improvement | +0.080 β |
The trained model's reply is more structured, uses proper closing ("Best regards"), is more specific about solutions, and scores higher on clarity and format.
Environment Info Tab
Full checklist of all hackathon requirements β all green β

β Hackathon Requirements Checklist
| Requirement | Status |
|---|---|
| OpenEnv-compatible FastAPI server | β |
| TRL GRPO training script | β |
| Unsloth QLoRA efficiency | β |
| Google Colab notebook | β |
| 7 independent reward functions | β |
| Clarity guardrail against vague "polite-only" hacks | β |
| Anti-reward-hacking measures | β |
| Process supervision (2-step) | β |
| Interactive demo UI | β |
| Before/After comparison with real rewards | β |
| Docker deployment ready | β |
| HuggingFace Spaces deployed | β |
| Mini blog post | β |
π Deployment
HuggingFace Space
- URL: https://huggingface.co/spaces/ykshrestha/personalmail-rl-demo
- Hardware: Nvidia A100 GPU (required for inference)
- Backend: FastAPI (OpenEnv compatible)
- Frontend: Interactive HTML UI
β οΈ Important: Select Nvidia A100 Large in Space Settings to enable live model inference. CPU mode will not support float16 inference.
Run Locally
git clone https://github.com/YASHASWINIKSHRESTHA/personalmail-rl-opt
cd personalmail-rl-opt/personalmail-rl-opt
pip install -r requirements.txt
uvicorn server:app --host 0.0.0.0 --port 7863
Docker
docker build -t personalmail-rl .
docker run -p 7863:7863 personalmail-rl
π Repository Structure
personalmail-rl-opt/
βββ env/
β βββ environment.py # OpenEnv reset/step/state
β βββ rewards.py # 7 independent reward functions
β βββ scenarios.py # 25 email scenarios Γ 3 difficulties
β βββ models.py # Pydantic schemas
βββ training/
β βββ train_grpo.py # GRPO training script
β βββ evaluate_before_after.py # Before/after evaluation
β βββ PersonalMailRL_Colab.ipynb # Full training notebook
βββ server.py # FastAPI OpenEnv server
βββ ui/index.html # Interactive demo UI
βββ Dockerfile # HuggingFace Space deployment
βββ openenv.yaml # OpenEnv configuration
π Links
| Resource | Link |
|---|---|
| π€ Trained Model | ykshrestha/personalmail-rl-qwen |
| π Live Demo Space | ykshrestha/personalmail-rl-demo |
| π» GitHub Code | YASHASWINIKSHRESTHA/personalmail-rl-opt |
| π Eval Results | eval_v5.json β +0.21 episode total improvement |
- Downloads last month
- -



