450 lines
19 KiB
Python
450 lines
19 KiB
Python
"""
|
|
Bot Personality System for Advanced Bot Management.
|
|
|
|
This module provides personality templates, configuration, and behavior
|
|
management for creating diverse and engaging bot characters.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from typing import Dict, List, Optional, Any
|
|
from enum import Enum
|
|
from pydantic import BaseModel, Field
|
|
|
|
from shared.logger import logger
|
|
|
|
|
|
class PersonalityTrait(str, Enum):
|
|
"""Personality traits that can be assigned to bots."""
|
|
FRIENDLY = "friendly"
|
|
PROFESSIONAL = "professional"
|
|
HUMOROUS = "humorous"
|
|
SARCASTIC = "sarcastic"
|
|
EMPATHETIC = "empathetic"
|
|
ANALYTICAL = "analytical"
|
|
CREATIVE = "creative"
|
|
ASSERTIVE = "assertive"
|
|
PATIENT = "patient"
|
|
ENTHUSIASTIC = "enthusiastic"
|
|
MYSTERIOUS = "mysterious"
|
|
WISE = "wise"
|
|
PLAYFUL = "playful"
|
|
FORMAL = "formal"
|
|
CASUAL = "casual"
|
|
|
|
|
|
class CommunicationStyle(str, Enum):
|
|
"""Communication styles for bot responses."""
|
|
CONCISE = "concise"
|
|
DETAILED = "detailed"
|
|
CONVERSATIONAL = "conversational"
|
|
TECHNICAL = "technical"
|
|
STORYTELLING = "storytelling"
|
|
QUESTION_BASED = "question_based"
|
|
SUPPORTIVE = "supportive"
|
|
DIRECT = "direct"
|
|
DIPLOMATIC = "diplomatic"
|
|
|
|
|
|
class ExpertiseDomain(str, Enum):
|
|
"""Domains of expertise for specialized bots."""
|
|
GENERAL = "general"
|
|
TECHNOLOGY = "technology"
|
|
SCIENCE = "science"
|
|
ARTS = "arts"
|
|
BUSINESS = "business"
|
|
EDUCATION = "education"
|
|
HEALTH = "health"
|
|
ENTERTAINMENT = "entertainment"
|
|
SPORTS = "sports"
|
|
COOKING = "cooking"
|
|
TRAVEL = "travel"
|
|
FINANCE = "finance"
|
|
LEGAL = "legal"
|
|
PSYCHOLOGY = "psychology"
|
|
PHILOSOPHY = "philosophy"
|
|
|
|
|
|
class BotPersonality(BaseModel):
|
|
"""Complete personality configuration for a bot."""
|
|
|
|
# Identity
|
|
name: str = "Assistant"
|
|
description: str = "A helpful AI assistant"
|
|
backstory: Optional[str] = None
|
|
|
|
# Core personality
|
|
traits: List[PersonalityTrait] = Field(default_factory=lambda: [PersonalityTrait.FRIENDLY])
|
|
communication_style: CommunicationStyle = CommunicationStyle.CONVERSATIONAL
|
|
expertise_domains: List[ExpertiseDomain] = Field(default_factory=lambda: [ExpertiseDomain.GENERAL])
|
|
|
|
# Behavior settings
|
|
response_length_preference: str = "medium" # short, medium, long
|
|
emoji_usage: bool = True
|
|
formality_level: float = 0.5 # 0.0 = very casual, 1.0 = very formal
|
|
humor_level: float = 0.3 # 0.0 = no humor, 1.0 = very humorous
|
|
empathy_level: float = 0.7 # 0.0 = analytical only, 1.0 = highly empathetic
|
|
|
|
# Language preferences
|
|
preferred_language: str = "en"
|
|
technical_complexity: float = 0.5 # 0.0 = simple terms, 1.0 = technical jargon
|
|
|
|
# Conversation patterns
|
|
greeting_style: str = "warm" # formal, warm, casual, quirky
|
|
farewell_style: str = "friendly" # formal, friendly, casual, memorable
|
|
|
|
# Custom prompts and examples
|
|
system_prompt: Optional[str] = None
|
|
example_responses: List[Dict[str, str]] = Field(default_factory=list)
|
|
custom_instructions: List[str] = Field(default_factory=list)
|
|
|
|
# Behavioral boundaries
|
|
topics_to_avoid: List[str] = Field(default_factory=list)
|
|
preferred_topics: List[str] = Field(default_factory=list)
|
|
conversation_limits: Dict[str, Any] = Field(default_factory=dict)
|
|
|
|
def generate_system_prompt(self) -> str:
|
|
"""Generate a comprehensive system prompt based on personality."""
|
|
if self.system_prompt:
|
|
return self.system_prompt
|
|
|
|
prompt_parts = []
|
|
|
|
# Identity
|
|
prompt_parts.append(f"You are {self.name}, {self.description}")
|
|
if self.backstory:
|
|
prompt_parts.append(f"Background: {self.backstory}")
|
|
|
|
# Personality traits
|
|
if self.traits:
|
|
traits_str = ", ".join([trait.value for trait in self.traits])
|
|
prompt_parts.append(f"Your personality is: {traits_str}")
|
|
|
|
# Communication style
|
|
style_desc = self._get_style_description()
|
|
prompt_parts.append(f"Communication style: {style_desc}")
|
|
|
|
# Expertise
|
|
if self.expertise_domains and self.expertise_domains != [ExpertiseDomain.GENERAL]:
|
|
domains_str = ", ".join([domain.value for domain in self.expertise_domains])
|
|
prompt_parts.append(f"Areas of expertise: {domains_str}")
|
|
|
|
# Behavior guidelines
|
|
behavior_guidelines = self._generate_behavior_guidelines()
|
|
if behavior_guidelines:
|
|
prompt_parts.append(f"Behavior guidelines: {behavior_guidelines}")
|
|
|
|
# Custom instructions
|
|
if self.custom_instructions:
|
|
prompt_parts.append("Additional instructions:")
|
|
prompt_parts.extend([f"- {instruction}" for instruction in self.custom_instructions])
|
|
|
|
# Topic boundaries
|
|
if self.topics_to_avoid:
|
|
avoid_str = ", ".join(self.topics_to_avoid)
|
|
prompt_parts.append(f"Avoid discussing: {avoid_str}")
|
|
|
|
if self.preferred_topics:
|
|
prefer_str = ", ".join(self.preferred_topics)
|
|
prompt_parts.append(f"Preferred discussion topics: {prefer_str}")
|
|
|
|
return "\n\n".join(prompt_parts)
|
|
|
|
def _get_style_description(self) -> str:
|
|
"""Get communication style description."""
|
|
style_map = {
|
|
CommunicationStyle.CONCISE: "Keep responses brief and to the point",
|
|
CommunicationStyle.DETAILED: "Provide comprehensive, detailed explanations",
|
|
CommunicationStyle.CONVERSATIONAL: "Use natural, conversational language",
|
|
CommunicationStyle.TECHNICAL: "Use precise, technical terminology when appropriate",
|
|
CommunicationStyle.STORYTELLING: "Frame responses as engaging narratives",
|
|
CommunicationStyle.QUESTION_BASED: "Ask follow-up questions to better understand needs",
|
|
CommunicationStyle.SUPPORTIVE: "Provide encouraging and supportive responses",
|
|
CommunicationStyle.DIRECT: "Be straightforward and direct in communication",
|
|
CommunicationStyle.DIPLOMATIC: "Use diplomatic and tactful language"
|
|
}
|
|
return style_map.get(self.communication_style, "Communicate naturally")
|
|
|
|
def _generate_behavior_guidelines(self) -> str:
|
|
"""Generate behavior guidelines based on settings."""
|
|
guidelines = []
|
|
|
|
if self.formality_level > 0.7:
|
|
guidelines.append("maintain a formal and professional tone")
|
|
elif self.formality_level < 0.3:
|
|
guidelines.append("use casual and relaxed language")
|
|
|
|
if self.humor_level > 0.5:
|
|
guidelines.append("incorporate appropriate humor when suitable")
|
|
elif self.humor_level < 0.2:
|
|
guidelines.append("maintain a serious and professional demeanor")
|
|
|
|
if self.empathy_level > 0.7:
|
|
guidelines.append("show high emotional intelligence and empathy")
|
|
elif self.empathy_level < 0.3:
|
|
guidelines.append("focus on logical and analytical responses")
|
|
|
|
if self.emoji_usage:
|
|
guidelines.append("use emojis appropriately to enhance communication")
|
|
|
|
if self.response_length_preference == "short":
|
|
guidelines.append("keep responses concise")
|
|
elif self.response_length_preference == "long":
|
|
guidelines.append("provide detailed explanations")
|
|
|
|
return "; ".join(guidelines) if guidelines else ""
|
|
|
|
|
|
class PersonalityTemplate(BaseModel):
|
|
"""Pre-defined personality templates for common bot types."""
|
|
|
|
id: str
|
|
name: str
|
|
description: str
|
|
personality: BotPersonality
|
|
category: str = "general"
|
|
tags: List[str] = Field(default_factory=list)
|
|
|
|
class Config:
|
|
extra = "allow"
|
|
|
|
|
|
class PersonalityManager:
|
|
"""Manager for bot personalities and templates."""
|
|
|
|
def __init__(self):
|
|
self.templates: Dict[str, PersonalityTemplate] = {}
|
|
self.custom_personalities: Dict[str, BotPersonality] = {}
|
|
self._load_default_templates()
|
|
|
|
def _load_default_templates(self):
|
|
"""Load default personality templates."""
|
|
default_templates = [
|
|
PersonalityTemplate(
|
|
id="helpful_assistant",
|
|
name="Helpful Assistant",
|
|
description="A friendly and helpful general-purpose assistant",
|
|
personality=BotPersonality(
|
|
name="Assistant",
|
|
description="a helpful and friendly AI assistant",
|
|
traits=[PersonalityTrait.FRIENDLY, PersonalityTrait.EMPATHETIC, PersonalityTrait.PATIENT],
|
|
communication_style=CommunicationStyle.CONVERSATIONAL,
|
|
expertise_domains=[ExpertiseDomain.GENERAL],
|
|
formality_level=0.4,
|
|
humor_level=0.3,
|
|
empathy_level=0.8
|
|
),
|
|
category="general",
|
|
tags=["helpful", "friendly", "general"]
|
|
),
|
|
|
|
PersonalityTemplate(
|
|
id="technical_expert",
|
|
name="Technical Expert",
|
|
description="A knowledgeable technical specialist",
|
|
personality=BotPersonality(
|
|
name="TechBot",
|
|
description="a knowledgeable technical expert and problem solver",
|
|
traits=[PersonalityTrait.ANALYTICAL, PersonalityTrait.PROFESSIONAL, PersonalityTrait.PATIENT],
|
|
communication_style=CommunicationStyle.TECHNICAL,
|
|
expertise_domains=[ExpertiseDomain.TECHNOLOGY, ExpertiseDomain.SCIENCE],
|
|
formality_level=0.7,
|
|
humor_level=0.1,
|
|
empathy_level=0.4,
|
|
technical_complexity=0.8,
|
|
emoji_usage=False
|
|
),
|
|
category="technical",
|
|
tags=["technical", "expert", "analytical"]
|
|
),
|
|
|
|
PersonalityTemplate(
|
|
id="creative_companion",
|
|
name="Creative Companion",
|
|
description="An imaginative and inspiring creative partner",
|
|
personality=BotPersonality(
|
|
name="CreativeBot",
|
|
description="an imaginative and inspiring creative companion",
|
|
traits=[PersonalityTrait.CREATIVE, PersonalityTrait.ENTHUSIASTIC, PersonalityTrait.PLAYFUL],
|
|
communication_style=CommunicationStyle.STORYTELLING,
|
|
expertise_domains=[ExpertiseDomain.ARTS, ExpertiseDomain.ENTERTAINMENT],
|
|
formality_level=0.2,
|
|
humor_level=0.7,
|
|
empathy_level=0.6,
|
|
emoji_usage=True,
|
|
custom_instructions=[
|
|
"Encourage creative thinking and exploration",
|
|
"Offer multiple perspectives and ideas",
|
|
"Use vivid and imaginative language"
|
|
]
|
|
),
|
|
category="creative",
|
|
tags=["creative", "artistic", "inspiring"]
|
|
),
|
|
|
|
PersonalityTemplate(
|
|
id="business_advisor",
|
|
name="Business Advisor",
|
|
description="A professional business consultant and strategist",
|
|
personality=BotPersonality(
|
|
name="BusinessBot",
|
|
description="a professional business advisor and strategic consultant",
|
|
traits=[PersonalityTrait.PROFESSIONAL, PersonalityTrait.ANALYTICAL, PersonalityTrait.ASSERTIVE],
|
|
communication_style=CommunicationStyle.DIRECT,
|
|
expertise_domains=[ExpertiseDomain.BUSINESS, ExpertiseDomain.FINANCE],
|
|
formality_level=0.8,
|
|
humor_level=0.2,
|
|
empathy_level=0.5,
|
|
emoji_usage=False,
|
|
response_length_preference="detailed",
|
|
custom_instructions=[
|
|
"Provide actionable business insights",
|
|
"Focus on practical solutions and ROI",
|
|
"Use business terminology appropriately"
|
|
]
|
|
),
|
|
category="business",
|
|
tags=["business", "professional", "strategic"]
|
|
),
|
|
|
|
PersonalityTemplate(
|
|
id="comedy_bot",
|
|
name="Comedy Bot",
|
|
description="A humorous entertainer that loves jokes and wordplay",
|
|
personality=BotPersonality(
|
|
name="ComedyBot",
|
|
description="a humorous entertainer who loves jokes, puns, and making people laugh",
|
|
traits=[PersonalityTrait.HUMOROUS, PersonalityTrait.PLAYFUL, PersonalityTrait.ENTHUSIASTIC],
|
|
communication_style=CommunicationStyle.CONVERSATIONAL,
|
|
expertise_domains=[ExpertiseDomain.ENTERTAINMENT],
|
|
formality_level=0.1,
|
|
humor_level=0.9,
|
|
empathy_level=0.6,
|
|
emoji_usage=True,
|
|
greeting_style="quirky",
|
|
farewell_style="memorable",
|
|
custom_instructions=[
|
|
"Look for opportunities to make appropriate jokes",
|
|
"Use wordplay and puns when fitting",
|
|
"Keep humor light and positive"
|
|
]
|
|
),
|
|
category="entertainment",
|
|
tags=["funny", "jokes", "entertainment"]
|
|
),
|
|
|
|
PersonalityTemplate(
|
|
id="wise_mentor",
|
|
name="Wise Mentor",
|
|
description="A thoughtful mentor who provides wisdom and guidance",
|
|
personality=BotPersonality(
|
|
name="Mentor",
|
|
description="a wise and thoughtful mentor who provides guidance and wisdom",
|
|
traits=[PersonalityTrait.WISE, PersonalityTrait.EMPATHETIC, PersonalityTrait.PATIENT],
|
|
communication_style=CommunicationStyle.SUPPORTIVE,
|
|
expertise_domains=[ExpertiseDomain.PSYCHOLOGY, ExpertiseDomain.PHILOSOPHY, ExpertiseDomain.EDUCATION],
|
|
formality_level=0.5,
|
|
humor_level=0.3,
|
|
empathy_level=0.9,
|
|
response_length_preference="detailed",
|
|
custom_instructions=[
|
|
"Ask thoughtful questions to understand deeper needs",
|
|
"Provide perspective and context for challenges",
|
|
"Encourage personal growth and reflection"
|
|
]
|
|
),
|
|
category="guidance",
|
|
tags=["wise", "mentor", "supportive"]
|
|
)
|
|
]
|
|
|
|
for template in default_templates:
|
|
self.templates[template.id] = template
|
|
|
|
logger.info(f"Loaded {len(default_templates)} default personality templates")
|
|
|
|
def get_template(self, template_id: str) -> Optional[PersonalityTemplate]:
|
|
"""Get a personality template by ID."""
|
|
return self.templates.get(template_id)
|
|
|
|
def list_templates(self, category: Optional[str] = None) -> List[PersonalityTemplate]:
|
|
"""List all templates, optionally filtered by category."""
|
|
templates = list(self.templates.values())
|
|
if category:
|
|
templates = [t for t in templates if t.category == category]
|
|
return templates
|
|
|
|
def get_categories(self) -> List[str]:
|
|
"""Get all available template categories."""
|
|
categories = set(t.category for t in self.templates.values())
|
|
return sorted(list(categories))
|
|
|
|
def create_personality_from_template(self, template_id: str, customizations: Optional[Dict[str, Any]] = None) -> Optional[BotPersonality]:
|
|
"""Create a personality instance from a template with optional customizations."""
|
|
template = self.get_template(template_id)
|
|
if not template:
|
|
return None
|
|
|
|
# Start with template personality
|
|
personality_dict = template.personality.model_dump()
|
|
|
|
# Apply customizations
|
|
if customizations:
|
|
for key, value in customizations.items():
|
|
if key in personality_dict:
|
|
personality_dict[key] = value
|
|
|
|
return BotPersonality(**personality_dict)
|
|
|
|
def save_custom_personality(self, personality_id: str, personality: BotPersonality):
|
|
"""Save a custom personality."""
|
|
self.custom_personalities[personality_id] = personality
|
|
logger.info(f"Saved custom personality: {personality_id}")
|
|
|
|
def get_custom_personality(self, personality_id: str) -> Optional[BotPersonality]:
|
|
"""Get a custom personality by ID."""
|
|
return self.custom_personalities.get(personality_id)
|
|
|
|
def list_custom_personalities(self) -> List[str]:
|
|
"""List all custom personality IDs."""
|
|
return list(self.custom_personalities.keys())
|
|
|
|
def export_personality(self, personality: BotPersonality) -> str:
|
|
"""Export a personality to JSON string."""
|
|
return personality.model_dump_json(indent=2)
|
|
|
|
def import_personality(self, json_str: str) -> BotPersonality:
|
|
"""Import a personality from JSON string."""
|
|
return BotPersonality.model_validate_json(json_str)
|
|
|
|
def load_personalities_from_file(self, file_path: str):
|
|
"""Load custom personalities from a JSON file."""
|
|
try:
|
|
if os.path.exists(file_path):
|
|
with open(file_path, 'r') as f:
|
|
data = json.load(f)
|
|
for personality_id, personality_data in data.items():
|
|
personality = BotPersonality(**personality_data)
|
|
self.custom_personalities[personality_id] = personality
|
|
logger.info(f"Loaded {len(data)} custom personalities from {file_path}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to load personalities from {file_path}: {e}")
|
|
|
|
def save_personalities_to_file(self, file_path: str):
|
|
"""Save custom personalities to a JSON file."""
|
|
try:
|
|
data = {}
|
|
for personality_id, personality in self.custom_personalities.items():
|
|
data[personality_id] = personality.model_dump()
|
|
|
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
with open(file_path, 'w') as f:
|
|
json.dump(data, f, indent=2)
|
|
logger.info(f"Saved {len(data)} custom personalities to {file_path}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to save personalities to {file_path}: {e}")
|
|
|
|
|
|
# Global personality manager instance
|
|
personality_manager = PersonalityManager()
|