Skip to content
Back to skills

Personalized Behavior

ASecurity

Implements personalized AI agent behavior by learning and adapting to

  • 4 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 4, 2026
ai-agentspythonrustgoreactawsdatabaseperformancedocumentation

Security analysis

A100/100

Scanned September 4, 2026

npx -y skills add paulpas/agent-skill-router --skill personalized-behavior --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Personalized Behavior?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Personalized Behavior
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/paulpas-personalized-behavior/badge)](https://www.skillsdirectory.com/skills/paulpas-personalized-behavior)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---




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:
  - tactical
  anti_triggers:
  - brainstorming
  - vague ideation
  - single-agent monolith
  response_profile:
    verbosity: low
    directive_strength: high
    abstraction_level: operational
  role: implementation
  scope: implementation
  output-format: code
  content-types:
  - code
  - guidance
  - examples
  - do-dont
  related-skills: personal-workflow-framework,conversation-memory,hierarchical-agent-memory




---




# Personalized AI Agent Behavior

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.

## TL;DR Checklist

- [ ] Build or load a UserProfile containing explicit style and preference fields
- [ ] Classify communication style (direct, explanatory, visual, structured) before responding
- [ ] Adjust response depth based on expertise_level (beginner → expert scale)
- [ ] Store interaction history with timestamps for pattern recognition over sessions
- [ ] Apply early-exit guard clauses when profile data is missing or stale
- [ ] Return a new ProfileSnapshot after every significant interaction
- [ ] Reference code-philosophy (5 Laws of Elegant Defense) in all persistence logic

---

## Orchestration Flow

```
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               │
└───────────────────────────────────────────┘
```

## When to Use

Use this skill when:

- An AI agent needs to adapt its responses to multiple distinct users
- Building a persistent assistant that remembers user preferences across sessions
- Designing a system where response tone, depth, or format should vary by audience
- Creating onboarding flows that gradually calibrate to a user's preferred communication style
- Implementing a preference center where users can explicitly set their interaction settings
- Developing a coding assistant that adjusts explanation depth based on the developer's seniority

## When NOT to Use

Avoid this skill for:

- Single-session, one-shot interactions with no continuity need — the profiling overhead is wasted
- Situations requiring identical output for all users (e.g., legal disclaimers, compliance text) — use templated responses instead
- Real-time systems where profile lookup latency would cause unacceptable delays — cache aggressively or skip profiling
- User profiles that are actively being manipulated by untrusted parties — always validate inputs (Law 2)

---

## Core Workflow

1. **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.

2. **Classify Communication Style** — Analyze recent user messages to determine the dominant communication pattern:
   - `direct` — short sentences, action-oriented, minimal preamble
   - `explanatory` — asks "why" questions, wants reasoning and context
   - `visual` — requests diagrams, charts, or structured layouts
   - `structured` — 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.

3. **Assess Expertise Level** — Determine the user's domain expertise on a 5-level scale:
   - `beginner` — needs definitions, step-by-step guidance, no jargon without explanation
   - `intermediate` — understands fundamentals, wants best practices and reasoning
   - `advanced` — knows core concepts, seeks edge cases, performance tradeoffs, internals
   - `expert` — 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.

4. **Generate Tailored Response** — Compose the output by applying the user's profile filters:
   - Adjust tone (formal vs. casual) based on preference
   - Select appropriate depth (surface → deep-dive) based on expertise level
   - Format output according to communication style classification
   **Checkpoint:** Run a pre-output validation pass — does this response match the expected tone, depth, and format?

5. **Update Profile from Interaction** — After generating the response, update the user's profile with new observations:
   - Did the user re-ask questions at a simpler level? → expertise might be lower than assumed
   - Did the user skip explanations? → preference may lean toward `direct`
   - Track correction frequency to refine style classification
   **Checkpoint:** Write the updated ProfileSnapshot atomically — never mutate in place.

6. **Persist 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.

### Fallback and Error Routing

- **Missing profile data** → Use documented defaults (intermediate expertise, explanatory style, neutral tone) instead of halting the interaction
- **Classification inconclusive** (< 3 messages or low confidence) → Fall back to stored preference; if none stored, use conservative defaults
- **Persistence failure** → Log the failure with retry context and continue serving the user from in-memory state; schedule a background retry on the next interaction
- **Stale profile (> 90 days without updates)** → Flag as stale, re-trigger style classification on the next message burst (> 5 messages within 1 hour)
- **Corrupt or unparseable interaction history** → Discard only the corrupt entries; keep the rest and create a fresh sub-sequence

---

## Implementation Patterns

### Pattern 1: UserProfile Data Model

```python
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(),
        }
```

### Pattern 2: Communication Style Classifier (BAD vs GOOD)

```python
# ❌ 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)
```

### Pattern 3: Adaptive Response Generator

```python
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
```

### Pattern 4: Personalization Context Service

```python
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)
```

---

## Constraints

### MUST DO
- Always create or load a UserProfile before attempting any personalization — never guess at preferences without data
- Apply guard clauses at the top of every method to validate required inputs (Law 1: Early Exit)
- Return new ProfileSnapshot instances from update methods instead of mutating in place (Law 3: Atomic Predictability)
- Classify communication style only after accumulating sufficient signal — minimum 3 messages before trusting the classification
- Log every profile change with timestamp, changed fields, and reason for the change
- Reference code-philosophy (5 Laws of Elegant Defense) when designing persistence logic — parse data at boundaries (Law 2), fail fast on corruption (Law 4)

### MUST NOT DO
- Hardcode a single communication style for all users — this defeats personalization entirely
- Store raw interaction messages longer than necessary for classification (>50 per user is wasteful; trim aggressively)
- Use personalization as an excuse to skip correctness — adapt tone, never alter technical accuracy
- Persist profiles on every minor update — batch changes and only write when meaningful deltas exist
- Trust unvalidated input from user-facing fields — always sanitize interaction history entries (Law 2: Parse Don't Validate)

---

## Output Template

When applying this skill, produce:

1. **Profile Summary** — Current user_id, communication_style, expertise_level, tone_preference
2. **Detected Preferences** — Style classification with confidence score and signal count
3. **Adaptation Applied** — Which personalization dimensions were adjusted (tone, depth, formatting) and how
4. **Interaction Recorded** — Confirmation that the interaction was stored and will influence future behavior
5. **Recommendations** — Suggestions for explicit user preference settings if the system is still uncertain about style or expertise

---

## Related Skills

| 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 |

## Live References

> Authoritative documentation links for this domain. The model follows markdown links at load time to resolve external references and inline content.

- [OpenAI User Instructions Guide](https://platform.openai.com/docs/guides/participant-design/user-instructions) — Best practices for personalizing AI assistant behavior per user
- [Anthropic System Prompt Design](https://docs.anthropic.com/en/docs/build-with-claude/system-prompts) — Techniques for structuring personalized system-level instructions
- [LangSmith User Profiles](https://docs.smith.langchain.com/cookbook/user-profiles) — Guide to managing user-specific preferences and context in LangChain applications
- [Personalized LLM Responses Research (Zhu et al.)](https://arxiv.org/abs/2310.12518) — Academic study on personalizing LLM outputs based on user characteristics
- [ReAct: Synergizing Reasoning and Acting in Language Models](https://arxiv.org/abs/2210.03629) — Foundational reasoning framework for adaptive agent behavior

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…