executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
personalized-behaviorImplements personalized AI agent behavior by learning and adapting to individual user preferences, communication styles, expertise levels, and interaction history for tailored responses.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: personalized-behavior description: Implements personalized AI agent behavior by learning and adapting to individual user preferences, communication styles, expertise levels, and interaction history for tailored responses. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: agent triggers: personalized behavior, adaptive agent, user preferences, communication style, expertise level, tailored responses, how do i customize ai agent, user profiling archetypes:
Implements personalized AI agent behavior by adapting responses to individual users based on learned preferences, communication styles, expertise levels, and interaction history. The model acts as a user-aware assistant that continuously refines its output format, tone, depth, and complexity to match each user's evolving needs and expectations.
User Request
↓
┌───────────────────────────────────────────┐
│ Load/Create UserProfile │
│ (from persistence or default profile) │
└──────────────┬────────────────────────────┘
↓
┌───────────────────────────────────────────┐
│ Classify Communication Style │
│ (direct / explanatory / visual / structured) │
│ │
│ <3 messages? ──► Use stored/default │
│ Enough data? ──► Run classifier │
└──────────────┬────────────────────────────┘
↓
┌───────────────────────────────────────────┐
│ Assess Expertise Level │
│ (beginner → expert scale) │
│ │
│ Uncertain? ──► Assume intermediate │
│ Clear signal? ──► Set detected level │
└──────────────┬────────────────────────────┘
↓
┌───────────────────────────────────────────┐
│ Generate Tailored Response │
│ (apply tone + depth + style filters) │
│ │
│ Validation fail? ──► Re-generate │
│ Passes check? ──► Return to user │
└──────────────┬────────────────────────────┘
↓
┌───────────────────────────────────────────┐
│ Update Profile from Interaction │
│ (refine style/expertise from feedback) │
│ │
│ Changes detected? ──► Persist snapshot │
│ No changes? ──► Skip write │
└───────────────────────────────────────────┘
Use this skill when:
Avoid this skill for:
Initialize or Load User Profile — Fetch the user's existing profile from storage, or create a new one with default settings if none exists. Default to conservative assumptions: intermediate expertise, explanatory communication style, and standard formatting preferences. Checkpoint: Validate that all required profile fields are populated; fill missing fields with documented defaults before proceeding.
Classify Communication Style — Analyze recent user messages to determine the dominant communication pattern:
direct — short sentences, action-oriented, minimal preambleexplanatory — asks "why" questions, wants reasoning and contextvisual — requests diagrams, charts, or structured layoutsstructured — prefers numbered lists, tables, and categorized output
Checkpoint: If fewer than 3 messages exist for classification, fall back to the user's stored preference or the default style.Assess Expertise Level — Determine the user's domain expertise on a 5-level scale:
beginner — needs definitions, step-by-step guidance, no jargon without explanationintermediate — understands fundamentals, wants best practices and reasoningadvanced — knows core concepts, seeks edge cases, performance tradeoffs, internalsexpert — expects minimal scaffolding, prefers raw technical detail and references
Checkpoint: If expertise cannot be confidently assessed from interaction history, conservatively assume intermediate and log the uncertainty.Generate Tailored Response — Compose the output by applying the user's profile filters:
Update Profile from Interaction — After generating the response, update the user's profile with new observations:
directPersist with Guard — Save the profile snapshot back to storage only if meaningful changes occurred (avoid unnecessary write amplification). Checkpoint: Verify persistence succeeded before considering the interaction complete. Log any failures for retry.
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Dict, List, Optional
class CommunicationStyle(Enum):
DIRECT = "direct"
EXPLANATORY = "explanatory"
VISUAL = "visual"
STRUCTURED = "structured"
class ExpertiseLevel(Enum):
BEGINNER = 1
INTERMEDIATE = 2
ADVANCED = 3
EXPERT = 4
class PreferenceTone(Enum):
FORMAL = "formal"
CASUAL = "casual"
NEUTRAL = "neutral"
@dataclass
class UserProfile:
"""Immutable user profile snapshot.
All fields are immutable once created. Updates produce new instances,
following Law 3 (Atomic Predictability) — no in-place mutation of shared state.
Attributes:
user_id: Unique identifier for the user
communication_style: Detected or explicitly set communication preference
expertise_level: Domain expertise scale for response depth tuning
tone_preference: Preferred response formality level
formatting_preferences: Structured formatting overrides (e.g., use_markdown_tables)
interaction_history: Recent interactions used for style classification
created_at: Timestamp when this profile was first created
updated_at: Timestamp of the last profile update
"""
user_id: str
communication_style: CommunicationStyle = CommunicationStyle.EXPLANATORY
expertise_level: ExpertiseLevel = ExpertiseLevel.INTERMEDIATE
tone_preference: PreferenceTone = PreferenceTone.NEUTRAL
formatting_preferences: Dict[str, bool] = field(default_factory=dict)
interaction_history: List[Dict] = field(default_factory=list)
created_at: datetime = field(
default_factory=lambda: datetime.now(timezone.utc)
)
updated_at: datetime = field(
default_factory=lambda: datetime.now(timezone.utc)
)
def with_updated_history(
self,
new_interaction: Dict,
max_history: int = 50
) -> "UserProfile":
"""Return a new profile with the interaction appended and trimmed.
Does not mutate the original instance (Law 3).
Args:
new_interaction: Dict containing user_message, assistant_response, timestamp
max_history: Maximum number of interactions to retain for classification
Returns:
New UserProfile instance with updated history
"""
if not new_interaction or "user_message" not in new_interaction:
raise ValueError("interaction must contain 'user_message' key")
trimmed = self.interaction_history[-(max_history - 1):] if len(self.interaction_history) >= max_history else []
return UserProfile(
user_id=self.user_id,
communication_style=self.communication_style,
expertise_level=self.expertise_level,
tone_preference=self.tone_preference,
formatting_preferences=dict(self.formatting_preferences),
interaction_history=trimmed + [new_interaction],
created_at=self.created_at,
updated_at=datetime.now(timezone.utc),
)
def to_snapshot(self) -> Dict:
"""Serialize profile to a plain dict for storage.
Returns:
Flat dictionary suitable for JSON serialization or database insertion.
"""
return {
"user_id": self.user_id,
"communication_style": self.communication_style.value,
"expertise_level": self.expertise_level.value,
"tone_preference": self.tone_preference.value,
"formatting_preferences": dict(self.formatting_preferences),
"interaction_history_count": len(self.interaction_history),
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
# ❌ BAD: Uses string matching without normalization; fragile to casing and typos
def bad_classify_style(messages: List[str]) -> str:
counts = {"direct": 0, "explanatory": 0}
for msg in messages:
if "why" in msg:
counts["explanatory"] += 1
elif len(msg.split()) < 10:
counts["direct"] += 1
return max(counts, key=counts.get)
# ✅ GOOD: Normalized token analysis with confidence scoring and explicit enum output
def classify_communication_style(
messages: List[str],
fallback: CommunicationStyle = CommunicationStyle.EXPLANATORY,
) -> CommunicationStyle:
"""Classify the dominant communication style from user message history.
Analyzes word choice, sentence length patterns, and question types to
determine whether a user prefers direct, explanatory, visual, or structured responses.
Args:
messages: List of recent user messages (last N messages from history)
fallback: Style to return if classification is inconclusive
Returns:
Detected CommunicationStyle enum value
Raises:
ValueError: If messages list is empty and no fallback provided
"""
# Law 1: Early exit for invalid input
if not messages:
raise ValueError("messages list must contain at least one message")
scores: Dict[str, float] = {"direct": 0.0, "explanatory": 0.0, "visual": 0.0, "structured": 0.0}
total_signals = 0
for msg in messages:
lower = msg.lower()
words = msg.split()
sent_len = len(words)
# Direct signals: short sentences, imperative verbs
if sent_len < 8:
scores["direct"] += 1.0
total_signals += 1
# Explanatory signals: question words, "why", "how does"
explanatory_markers = ["why", "how does", "explain", "tell me about", "what is"]
if any(marker in lower for marker in explanatory_markers):
scores["explanatory"] += 1.5
total_signals += 1
# Visual signals: requests for diagrams, charts, layout
visual_markers = ["diagram", "chart", "visual", "graph", "layout", "draw"]
if any(marker in lower for marker in visual_markers):
scores["visual"] += 2.0
total_signals += 1
# Structured signals: requests for lists, tables, categories
structured_markers = ["list", "table", "categorize", "compare", "structured"]
if any(marker in lower for marker in structured_markers):
scores["structured"] += 2.0
total_signals += 1
# If no signals detected, return fallback (Law 4: explicit default)
if total_signals == 0:
return fallback
best_style = max(scores, key=scores.get)
confidence = scores[best_style] / total_signals
# Law 4: Fail fast — only commit if classification has reasonable confidence
if confidence < 0.25:
return fallback
return CommunicationStyle(best_style)
def generate_toned_response(
content: str,
profile: UserProfile,
include_code_examples: bool = True,
) -> str:
"""Generate a response tailored to the user's communication style and expertise.
Adjusts tone, depth markers, and output formatting based on UserProfile fields.
Follows Law 1 (Early Exit) by returning early for edge cases.
Args:
content: The core response text to adapt
profile: Current user profile controlling adaptation parameters
include_code_examples: Whether to wrap code in detailed explanations
Returns:
Formatted, personalized response string
"""
# Law 1: Guard clauses at top
if not content or not isinstance(content, str):
raise ValueError("content must be a non-empty string")
if not profile:
raise ValueError("profile is required for personalization")
parts = []
# Tone prefix
if profile.tone_preference == PreferenceTone.FORMAL:
prefix = "Here is the detailed response:\n\n"
elif profile.tone_preference == PreferenceTone.CASUAL:
prefix = "Sure — here's what you need to know:\n\n"
else:
prefix = ""
parts.append(prefix)
# Depth adjustment based on expertise
if profile.expertise_level == ExpertiseLevel.BEGINNER:
# Insert definitions and step-by-step markers for beginners
parts.append(_add_beginner_markers(content))
elif profile.expertise_level == ExpertiseLevel.EXPERT:
# Strip explanatory padding for experts — get to the point
parts.append(_strip_explanations(content))
else:
# Intermediate / Advanced: keep standard depth
parts.append(content)
# Format according to communication style
formatted = _apply_style_formatting("\n".join(parts), profile.communication_style)
return formatted
def _add_beginner_markers(text: str) -> str:
"""Wrap technical terms in explanatory brackets for beginners."""
# Simple heuristic: wrap known pattern names and technical identifiers
markers = ["function", "class", "method", "property", "attribute"]
result = text
for marker in markers:
result = result.replace(
f"{marker} ",
f"**{marker}** (a named block of logic) ",
)
return result
def _strip_explanations(text: str) -> str:
"""Remove verbose explanations, returning only essential technical content."""
removal_patterns = [
"In other words,", "To put it simply,", "Essentially,",
"The key takeaway is that", "It's important to understand that",
]
result = text
for pattern in removal_patterns:
if result.startswith(pattern):
result = result[len(pattern):].lstrip()
return result
def _apply_style_formatting(text: str, style: CommunicationStyle) -> str:
"""Apply communication-style-specific formatting to response text."""
# Law 2: Parse at boundary — validate style enum
if not isinstance(style, CommunicationStyle):
raise TypeError(f"Expected CommunicationStyle, got {type(style).__name__}")
if style == CommunicationStyle.STRUCTURED:
# Wrap in categorized sections
return f"[Structured]\n{text}\n[End Structured]"
elif style == CommunicationStyle.VISUAL:
# Add ASCII visual markers
return f"[Visual Layout]\n{text}\n[End Visual Layout]"
else:
# Direct and Explanatory pass through unchanged (they control depth, not format)
return text
class PersonalizationService:
"""Orchestrates user profiling, style classification, and response personalization.
Acts as the central service that ties UserProfile management, CommunicationStyle
classification, and AdaptiveResponse generation together into a cohesive pipeline.
Follows Law 5 (Intentional Naming) — every method name describes its full responsibility.
"""
def __init__(self, profile_store: Optional[Dict] = None):
"""Initialize service with optional profile storage.
Args:
profile_store: Mutable dict simulating a database of user profiles.
Keyed by user_id (str) → UserProfile.
"""
self._store: Dict[str, UserProfile] = profile_store or {}
def get_or_create_profile(self, user_id: str) -> UserProfile:
"""Retrieve an existing profile or create a fresh one with defaults.
Args:
user_id: Unique user identifier
Returns:
UserProfile instance (existing or newly minted with conservative defaults)
"""
if not user_id or not isinstance(user_id, str):
raise ValueError("user_id must be a non-empty string")
if user_id not in self._store:
self._store[user_id] = UserProfile(user_id=user_id)
return self._store[user_id]
def record_interaction_and_adapt(
self,
user_id: str,
user_message: str,
assistant_response: str,
) -> Dict:
"""Record a full interaction, reclassify the user's style, and adapt.
This is the main entry point for the personalization pipeline:
1. Load profile
2. Record interaction into history
3. Reclassify communication style from updated history
4. Return adaptation results for the caller to use in response generation.
Args:
user_id: Unique identifier of the interacting user
user_message: The message sent by the user
assistant_response: The message generated by the assistant
Returns:
Dict with keys: profile_snapshot, detected_style, expertise_level, adaptation_applied
"""
# Step 1: Load or create
profile = self.get_or_create_profile(user_id)
# Step 2: Record interaction
new_interaction = {
"user_message": user_message,
"assistant_response": assistant_response,
"timestamp": datetime.now(timezone.utc).isoformat(),
}
updated_profile = profile.with_updated_history(new_interaction)
# Step 3: Reclassify from full history
message_texts = [
hist["user_message"] for hist in updated_profile.interaction_history
]
detected_style = classify_communication_style(message_texts)
# Step 4: Build adaptation result
return {
"profile_snapshot": updated_profile.to_snapshot(),
"detected_style": detected_style.value,
"expertise_level": updated_profile.expertise_level.name,
"adaptation_applied": True,
}
def get_personalized_response(
self, user_id: str, raw_content: str
) -> str:
"""Generate a fully personalized response for the given user.
Args:
user_id: Unique identifier of the user
raw_content: The unpersonalized response content to adapt
Returns:
Personalized response string matching this user's style, tone, and expertise level
"""
profile = self.get_or_create_profile(user_id)
return generate_toned_response(raw_content, profile)
When applying this skill, produce:
| Skill | Purpose |
|---|---|
personal-workflow-framework | Manages recurring workflows that can be personalized per user |
conversation-memory | Provides the memory infrastructure for storing interaction history |
hierarchical-agent-memory | Adds hierarchical memory layers for long-term preference retention across sessions |
Authoritative documentation links for this domain. The model follows markdown links at load time to resolve external references and inline content.