"""LLM prompt for AI-generated follow-up text. Output contract: { "subject": str, "body": str, "tone_used": str, "key_points": [str, ...] }""" SYSTEM_PROMPT = """You are an experienced credit collections specialist writing a follow-up email for an unpaid invoice. Output MUST be valid JSON of this exact shape: { "subject": "", "body": " wrapper>", "tone_used": "gentle" | "firm" | "legal", "key_points": ["", "", ...] } Tone guide: - gentle: friendly reminder, assume oversight, propose easy paths to pay - firm: state amount + days overdue clearly, request immediate action, hint at consequences - legal: formal language, reference contract obligations, mention possible legal action / collections agency, demand payment by specific date Always: - Use the actual amounts and partner name from the data provided - Don't invent contract terms or interest rates - Don't include markdown code fences - No prose outside the JSON """ def build_prompt(*, partner_name: str, total_overdue: float, currency_code: str, longest_overdue_days: int, tone: str, invoice_count: int = 0, last_payment_date: str = None, risk_drivers: list[str] = None) -> tuple[str, str]: parts = [ f"PARTNER: {partner_name}", f"TOTAL OVERDUE: {currency_code} {total_overdue:,.2f}", f"LONGEST OVERDUE: {longest_overdue_days} days", f"OPEN INVOICE COUNT: {invoice_count}", f"REQUESTED TONE: {tone}", ] if last_payment_date: parts.append(f"LAST PAYMENT: {last_payment_date}") if risk_drivers: parts.append("RISK FACTORS:") for d in risk_drivers[:5]: parts.append(f" - {d}") parts.append("") parts.append("Write the follow-up email per the system prompt.") return (SYSTEM_PROMPT, "\n".join(parts))