system-prompts-and-models-o.../personal-brand-engine/agents/email/responder.py
VoXc2 4bb2442313
Add Personal Brand Engine - 7 AI Agents Automation System
Complete AI-powered personal brand automation for Sami Assiri.\n\n7 agents: LinkedIn, Email, Social Media, WhatsApp, CV Optimizer, Content Strategist, Opportunity Scout.\nInfra: FastAPI + APScheduler + Docker + Ollama/Groq LLM + GitHub Pages landing page.\n83 files, ~10K lines. Cost: $0-5/month.
2026-03-30 11:45:48 +03:00

181 lines
6.2 KiB
Python

"""LLM-powered email response drafter for Sami Assiri."""
from __future__ import annotations
import re
from pathlib import Path
import yaml
from utils.logger import get_logger
logger = get_logger(__name__)
_TEMPLATES_PATH = Path(__file__).parent / "prompts" / "reply_templates.yaml"
def _load_templates() -> dict:
"""Load reply templates from the YAML file."""
if not _TEMPLATES_PATH.exists():
return {}
with open(_TEMPLATES_PATH, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
def _detect_language(text: str) -> str:
"""Detect whether the text is primarily Arabic or English.
Uses a simple heuristic: if the text contains Arabic Unicode characters
above a threshold, treat it as Arabic.
"""
if not text:
return "en"
arabic_chars = len(re.findall(r"[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]", text))
total_alpha = len(re.findall(r"[a-zA-Z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]", text))
if total_alpha == 0:
return "en"
return "ar" if (arabic_chars / total_alpha) > 0.3 else "en"
def _build_system_prompt(brand_profile: dict, language: str, classification: str) -> str:
"""Construct the system prompt for the response drafter."""
personal = brand_profile.get("personal", {})
employment = brand_profile.get("employment", {})
current_job = employment.get("current", {})
links = brand_profile.get("links", {})
if language == "ar":
name = personal.get("name_ar", "سامي محمد العسيري")
title = current_job.get("title_ar", "مهندس خدمات ميدانية")
company = current_job.get("company_ar", "ميتكو - خدمات الشرق الأوسط")
location = current_job.get("location_ar", "مطار الملك خالد الدولي - الرياض")
else:
name = personal.get("name_en", "Sami Mohammed Assiri")
title = current_job.get("title", "Field Services Engineer")
company = current_job.get("company", "METCO - Middle East Services")
location = current_job.get("location", "King Khalid International Airport, Riyadh")
calcom_url = links.get("calcom", "")
linkedin_url = links.get("linkedin", "")
templates = _load_templates()
template_guidance = ""
if classification in templates:
tpl = templates[classification]
lang_key = "ar" if language == "ar" else "en"
if lang_key in tpl:
template_guidance = f"\n\nUse this template as a starting guide:\n{tpl[lang_key]}"
lang_instruction = (
"Write the reply entirely in Arabic."
if language == "ar"
else "Write the reply entirely in English."
)
meeting_instruction = ""
if classification == "urgent" and calcom_url:
meeting_instruction = (
f"\nIf the email involves a meeting request, suggest booking via "
f"the Cal.com link: {calcom_url}"
)
return (
f"You are drafting a professional email reply on behalf of {name}, "
f"{title} at {company}, based in {location}.\n\n"
f"LinkedIn: {linkedin_url}\n"
f"Email: {personal.get('email', 'sami.assiri11@gmail.com')}\n\n"
f"Guidelines:\n"
f"- {lang_instruction}\n"
f"- Maintain a professional, courteous, and confident tone.\n"
f"- Keep the response concise and actionable.\n"
f"- When relevant, mention Sami's role at {company} and his engineering background.\n"
f"- Do NOT fabricate information. If you're unsure, suggest Sami will follow up.\n"
f"- Sign off with Sami's name and title.{meeting_instruction}"
f"{template_guidance}"
)
async def draft_response(
llm_client,
email_subject: str,
email_body: str,
brand_profile: dict,
classification: str,
) -> str:
"""Draft a professional email response using the LLM.
Parameters
----------
llm_client:
An :class:`LLMClient` instance.
email_subject:
Subject line of the incoming email.
email_body:
Body text of the incoming email.
brand_profile:
Parsed brand profile dictionary.
classification:
The email classification (``urgent``, ``reply_needed``, etc.).
Returns
-------
str
The drafted reply text, ready for review or sending.
"""
language = _detect_language(email_body)
system_prompt = _build_system_prompt(brand_profile, language, classification)
if language == "ar":
user_prompt = (
f"الرد على البريد الإلكتروني التالي:\n\n"
f"الموضوع: {email_subject}\n\n"
f"المحتوى:\n{email_body[:2500]}\n\n"
f"اكتب رداً مهنياً مناسباً."
)
else:
user_prompt = (
f"Draft a reply to the following email:\n\n"
f"Subject: {email_subject}\n\n"
f"Body:\n{email_body[:2500]}\n\n"
f"Write an appropriate professional response."
)
try:
response = await llm_client.generate(
prompt=user_prompt,
system_prompt=system_prompt,
temperature=0.5,
max_tokens=1500,
)
draft = response.text.strip()
logger.info(
"email_response_drafted",
subject=email_subject[:80],
language=language,
classification=classification,
length=len(draft),
)
return draft
except Exception as exc:
logger.error(
"email_response_error",
error=str(exc),
subject=email_subject[:80],
)
# Return a safe fallback so the email isn't left without a draft
if language == "ar":
return (
"شكراً لتواصلك. سأراجع رسالتك وأرد عليك في أقرب وقت ممكن.\n\n"
"مع أطيب التحيات،\n"
"سامي محمد العسيري\n"
"مهندس خدمات ميدانية - ميتكو"
)
return (
"Thank you for reaching out. I will review your message and get back "
"to you as soon as possible.\n\n"
"Best regards,\n"
"Sami Mohammed Assiri\n"
"Field Services Engineer - METCO"
)