Local LLMs are well-suited to email drafting tasks — they generate contextually appropriate text quickly, run privately without sending your emails to external servers, and can be prompted with specific tone, length, and style requirements. This guide covers practical email drafting with Ollama using Python, with patterns for common email scenarios.
Basic Email Drafting
import ollama
def draft_email(context: str, tone: str = "professional", length: str = "concise") -> str:
prompt = f"""Draft a {tone} email that is {length}.
Context: {context}
Return only the email body (no subject line). Do not add sign-off or greeting placeholder text."""
response = ollama.chat(
model="llama3.2",
messages=[{"role": "user", "content": prompt}],
options={"temperature": 0.4}
)
return response["message"]["content"]
# Examples
print(draft_email("Follow up on a meeting about Q3 budget review. Ask for the presentation slides."))
print(draft_email("Decline a job offer politely, keeping the door open for future opportunities.", tone="warm"))
print(draft_email("Request a 2-week deadline extension for a project due Friday.", tone="professional", length="brief"))
Subject Line Generation
def generate_subject(email_body: str) -> str:
response = ollama.chat(
model="llama3.2",
messages=[{
"role": "user",
"content": f"Write a concise email subject line (under 60 characters) for this email:\n\n{email_body}\n\nReturn only the subject line, no quotes."
}],
options={"temperature": 0.3}
)
return response["message"]["content"].strip()
body = draft_email("Thank a client for their business this year and wish them happy holidays.")
subject = generate_subject(body)
print(f"Subject: {subject}\n\n{body}")
Reply to an Existing Email
def draft_reply(original_email: str, intent: str, tone: str = "professional") -> str:
response = ollama.chat(
model="llama3.2",
messages=[{
"role": "user",
"content": f"""Draft a {tone} reply to this email.
Original email:
{original_email}
My reply should: {intent}
Return only the reply body."""
}],
options={"temperature": 0.4}
)
return response["message"]["content"]
original = """Hi,
I noticed your invoice #1234 is 30 days overdue. Could you please provide an update on payment?
Thanks,
Accounts Team"""
reply = draft_reply(original, "apologise for the delay and confirm payment within 5 business days")
Batch Email Personalisation
def personalise_email(template: str, recipient: dict) -> str:
response = ollama.chat(
model="llama3.2",
messages=[{
"role": "user",
"content": f"""Personalise this email template for the recipient below.
Make it feel personal and relevant. Keep the same length and structure.
Template:
{template}
Recipient:
Name: {recipient['name']}
Company: {recipient['company']}
Role: {recipient['role']}
Recent news: {recipient.get('news', 'None')}
Return only the personalised email body."""
}],
options={"temperature": 0.5}
)
return response["message"]["content"]
template = "I wanted to reach out about how our product could help your team save time on reporting..."
recipients = [
{"name": "Sarah Chen", "company": "Acme Corp", "role": "Head of Analytics",
"news": "Recently launched a new data platform"},
{"name": "James Okafor", "company": "TechStart", "role": "CTO",
"news": "Company just raised Series A"},
]
for r in recipients:
print(f"--- {r['name']} ---")
print(personalise_email(template, r))
print()
Email Tone and Style Options
Prompting for specific email characteristics produces reliably different outputs. For tone: professional, friendly, assertive, apologetic, enthusiastic, diplomatic. For length: one sentence, brief (2–3 sentences), concise (1 paragraph), detailed (2–3 paragraphs). For formality: formal, semi-formal, casual. Combining these parameters in the prompt gives fine-grained control over the output style — more reliably than trying to describe the style in natural language without explicit parameter labels.
Why Local LLMs for Email
Email drafting is a strong use case for local LLMs for privacy reasons: emails often contain sensitive business information, client names, financial figures, or personal details that should not be sent to cloud AI services. Processing email content locally ensures no sensitive information leaves your infrastructure. The quality bar for email drafting is also well within reach of 7–13B models — unlike complex reasoning tasks that benefit from frontier models, drafting a professional follow-up email or declining a meeting request is straightforward for any capable instruction-following model. Llama 3.2 7B handles 90% of common email drafting tasks with quality indistinguishable from GPT-4o for most business purposes.
Email Rewriting and Improvement
def improve_email(draft: str, instructions: str) -> str:
"""Improve an existing draft email based on specific instructions."""
response = ollama.chat(
model='llama3.2',
messages=[{
'role': 'user',
'content': f"""Improve this email draft based on the instructions below.
Return only the improved email body.
Original draft:
{draft}
Instructions: {instructions}"""
}],
options={'temperature': 0.4}
)
return response['message']['content']
# Examples
draft = "Hey, just checking in about that thing we talked about last week."
print(improve_email(draft, "Make it professional and specific — mention the Q3 budget proposal"))
print(improve_email(draft, "Make it shorter and add a clear call to action"))
Meeting Request and Scheduling Emails
def draft_meeting_request(
recipient: str,
purpose: str,
duration_minutes: int = 30,
proposed_times: list[str] | None = None
) -> dict:
times_text = (
'Proposed times: ' + ', '.join(proposed_times)
if proposed_times else 'Ask them to suggest a time that works'
)
response = ollama.chat(
model='llama3.2',
messages=[{
'role': 'user',
'content': f"""Draft a professional meeting request email.
Recipient: {recipient}
Purpose: {purpose}
Duration: {duration_minutes} minutes
{times_text}
Return JSON with keys: subject, body"""
}],
format={'type': 'object',
'properties': {'subject': {'type': 'string'}, 'body': {'type': 'string'}},
'required': ['subject', 'body']},
options={'temperature': 0.3}
)
import json
return json.loads(response['message']['content'])
result = draft_meeting_request(
recipient='Sarah Chen, Head of Analytics',
purpose='Discuss Q3 data pipeline requirements',
proposed_times=['Tuesday 2pm', 'Wednesday 10am', 'Thursday 3pm']
)
print(f"Subject: {result['subject']}\n\n{result['body']}")
CLI Tool for Daily Use
#!/usr/bin/env python3
# email_draft.py — quick CLI for email drafting
import argparse, ollama
def main():
parser = argparse.ArgumentParser(description='Draft an email with a local LLM')
parser.add_argument('context', help='What the email should say/do')
parser.add_argument('--tone', default='professional',
choices=['professional','friendly','assertive','apologetic','formal'])
parser.add_argument('--length', default='concise',
choices=['one sentence','brief','concise','detailed'])
parser.add_argument('--subject', action='store_true', help='Also generate subject line')
args = parser.parse_args()
body = draft_email(args.context, args.tone, args.length)
if args.subject:
subj = generate_subject(body)
print(f'Subject: {subj}\n')
print(body)
if __name__ == '__main__':
main()
# Usage:
# python email_draft.py "Follow up on unpaid invoice #1234" --tone assertive --subject
# python email_draft.py "Decline meeting request politely" --length brief
Model Choice for Email Tasks
For email drafting, model size matters less than for complex reasoning tasks. Llama 3.2 3B handles simple emails (short replies, brief acknowledgements) quickly and with acceptable quality. Llama 3.2 7B is the recommended default — it handles nuanced tone requirements, complex meeting scheduling emails, and personalisation prompts reliably. Mistral Nemo 12B produces noticeably better outputs for delicate situations (delivering bad news, negotiating, apologising) where tone precision matters most. The batch personalisation and meeting request patterns in this article benefit most from the larger model’s better instruction following. For a daily-use CLI tool where speed matters, llama3.2:3b is worth evaluating alongside the 7B default — the quality difference for straightforward email types is smaller than the latency difference.
Integration with Email Clients
For daily use, connecting the email drafting functions to your actual email client saves the copy-paste step. The Gmail API, Microsoft Graph API, and IMAP/SMTP libraries all allow reading and sending emails from Python, enabling pipelines like: read an email thread from Gmail, draft a contextual reply with Ollama, present it for approval, and send. For privacy-conscious teams, the local-processing model means the email content is processed by a local model without being sent to cloud AI services, while the email itself travels through its normal delivery path:
def draft_reply_from_thread(thread_text: str, intent: str) -> str:
"""Draft a reply given a full email thread for context."""
# Keep recent context only (last ~2000 chars)
context = thread_text[-2000:] if len(thread_text) > 2000 else thread_text
response = ollama.chat(
model='llama3.2',
messages=[{
'role': 'user',
'content': f"""Given this email thread, draft a reply.
Thread (most recent at end):
{context}
My reply should: {intent}
Return only the reply body."""
}],
options={'temperature': 0.4}
)
return response['message']['content']
# With Gmail API (requires google-auth, gmail API setup):
# thread = gmail_service.users().threads().get(userId='me', id=thread_id).execute()
# thread_text = extract_text_from_thread(thread)
# reply = draft_reply_from_thread(thread_text, 'confirm receipt and ask for timeline')
Building a Personal Email Assistant
Combine the patterns from this article into a personal email assistant script that you run daily. A practical workflow: load your unread emails from the past 24 hours, classify each as action-required, informational, or can-ignore, draft responses for action-required emails using the thread context, and present them to you in a simple terminal interface for review and send. This workflow processes your email locally with no data leaving your machine, runs in a few seconds per email on a 7B model, and produces drafts you edit and approve before sending. The model handles the tedious part (drafting professional language for routine responses) while you retain full control over what actually gets sent. It is one of the most practically valuable daily-use applications of local LLMs for knowledge workers.
Privacy and Data Handling
Local email drafting preserves the privacy guarantees that cloud AI cannot: the email content, recipient names, and business context are processed entirely on your machine and are never transmitted to an external service. For individuals and organisations with data handling obligations — legal, medical, financial, or simply a preference for keeping business communications private — this is a meaningful advantage. The drafting quality from a local 7B model is sufficient for most routine business emails, and the privacy guarantee is absolute rather than contractual. As local model quality continues to improve, the quality gap with cloud services for email-class tasks will continue to narrow, while the privacy advantage of local processing remains constant.
Prompting Techniques for Better Email Drafts
Several prompting patterns consistently improve email draft quality. First, provide explicit outcome context rather than just the topic — instead of “write an email about the invoice”, use “write an email requesting payment of overdue invoice #1234, due 30 days ago, in a firm but professional tone that preserves the business relationship”. The additional context helps the model calibrate both content and tone. Second, specify what NOT to include — “do not include a formal sign-off” or “do not use phrases like ‘I hope this email finds you well'” prevents the model from generating formulaic filler text. Third, for replies, include a key excerpt from the original email in the prompt so the model can reference it accurately — models hallucinate email content when asked to reply to something they have not seen. Fourth, ask for multiple variants when the situation is delicate — three versions of a difficult email (direct, diplomatic, apologetic) give you options to review rather than having to re-prompt if the first version misses the mark. These four practices together produce significantly better draft quality than bare prompts and apply across all the email drafting functions in this article.
Getting Started
Install the ollama Python package, pull llama3.2, copy the draft_email and generate_subject functions from this article, and run them from a Python script or the CLI tool. Evaluate the output on 10–15 real email scenarios that represent your typical workload — the quality you observe on your actual use cases is more relevant than any benchmark. Adjust the model (try llama3.2:3b for speed, mistral-nemo for better nuance) and prompt parameters based on what you observe. Once you have a working script that produces acceptable drafts, integrate it into your workflow — whether as a terminal command, a VS Code extension, a simple web form, or a full email client integration. The core logic is the same regardless of the interface.
Email Drafting as a Gateway to Broader Local AI Adoption
Email drafting is one of the most immediately valuable local AI use cases for knowledge workers because the use case is universal, the quality bar is achievable with 7B models, and the privacy benefit of local processing is concrete and easy to explain. Once a developer or team has Ollama running for email drafting, they have the infrastructure and familiarity to explore other local AI use cases: document summarisation, meeting note generation, code review assistance, customer support response drafting, and content writing. Email drafting is frequently the entry point that leads to broader local AI adoption, because it demonstrates the privacy and quality combination tangibly in a daily-use context. The patterns in this article — prompt design for tone and length, structured JSON output, CLI tooling, email client integration — transfer directly to all of these adjacent use cases with minimal adaptation. If you are new to local AI, start with email drafting: the setup is quick, the results are immediately useful, and the learning transfers to every other application you build on the same local infrastructure.
Structured Email Extraction
Beyond drafting, local LLMs can extract structured information from incoming emails — useful for parsing orders, support requests, or invoice data without sending sensitive content to cloud services:
from pydantic import BaseModel
from typing import Optional
import ollama, json
class EmailExtract(BaseModel):
sender_intent: str # What the sender wants
urgency: str # high / medium / low
action_required: bool
key_dates: list[str]
reply_tone: str # suggested tone for reply
def extract_email_info(email_text: str) -> EmailExtract:
response = ollama.chat(
model='llama3.2',
messages=[{'role':'user','content':
f'Analyse this email and extract key information:\n\n{email_text}'}],
format=EmailExtract.model_json_schema(),
options={'temperature': 0}
)
return EmailExtract.model_validate_json(response['message']['content'])
email = """
Hi,
Our contract renewal is due on Friday and I haven't received the updated terms.
This is quite urgent as our legal team needs 48 hours to review.
Please send them today if possible.
Best,
Sarah
"""
info = extract_email_info(email)
print(f'Intent: {info.sender_intent}')
print(f'Urgency: {info.urgency}')
print(f'Action needed: {info.action_required}')
print(f'Key dates: {", ".join(info.key_dates)}')
print(f'Reply in: {info.reply_tone} tone')
Batch Processing an Email Archive
For processing a backlog of emails — categorising, summarising, or extracting data from a large archive — the batch personalisation pattern extends naturally to large volumes. Process emails concurrently using concurrent.futures.ThreadPoolExecutor (since Ollama calls are I/O-bound) with a concurrency limit that respects Ollama’s parallel inference capacity. Write results to a CSV or database for downstream processing. On a machine with a mid-range GPU running a 7B model, expect 30–120 emails per hour depending on email length and the complexity of the extraction task — more than sufficient for overnight batch processing of typical email archives.
The Broader Value of Local Email AI
Email drafting sits at an interesting intersection of high daily use, achievable quality bar, and clear privacy benefit — which makes it one of the most universally applicable local AI use cases. Unlike code generation (requires coding skills) or document summarisation (requires relevant documents), email drafting is relevant to virtually everyone with an email inbox. The combination of Ollama’s local processing and the patterns in this article makes professional email drafting assistance available without subscription costs, API key management, or data privacy trade-offs. For teams evaluating whether local AI has practical value in their workflows, email drafting is the use case to start with — it demonstrates the value proposition concretely, the implementation is approachable, and the skills transfer directly to every other local AI application they build afterward.
Quality Evaluation for Email Drafts
Before relying on any model for production email drafting, evaluate it against a representative sample of your actual email types. Create a test set of 20–30 typical email scenarios — meeting requests, follow-ups, declines, complaints, updates — and run each through your drafting function. Evaluate each output on three dimensions: accuracy (does it address the right topic with the right intent?), tone appropriateness (does it match the requested tone consistently?), and naturalness (does it read as something you would actually send?). Track the failure modes — models that hallucinate facts, produce generic filler text, or consistently misread tone requirements need either prompt adjustment or a model upgrade. This evaluation takes 2–3 hours and produces a clear picture of where the model succeeds and fails for your specific use cases, which is far more informative than any benchmark score. Run the same evaluation again after switching models or significantly changing prompts to verify that quality has improved rather than regressed on your actual use cases — an investment of a few hours that pays back every time you confidently use or recommend the model for a new email type.
Email drafting is one of those daily tasks where a local LLM’s speed and quality are both good enough that the tool becomes genuinely useful rather than a novelty. The patterns in this article — basic drafting, reply generation, batch personalisation, and the CLI tool — give you a complete toolkit for integrating AI into your email workflow while keeping all content fully private on your own machine.