← Back to list

Building Your First AI Persona with Python’s Personaut PDK

Give your AI agents a soul — complete with personality, emotions, and psychological grounding.

Anthony Kwiatkowski in Artificial Intelligence in Plain English · 2026-02-25 02:15 · 1 claps · 4.4 min read
#ai-agent #agentic-ai #persona-development #pdks
Open on Medium ↗
Wiki topics: AGT · AI Agents PSY · Psychology 🧘 · Spirituality

Building Your First AI Persona with Python’s Personaut PDK

Give your AI agents a soul — complete with personality, emotions, and psychological grounding.

The Problem with Generic AI Agents

We’ve all been there. You ask ChatGPT to “act like a friendly barista” and it gives you a response that sort of works — but lacks any real personality depth. It doesn’t get nervous when the café is busy. It doesn’t light up when talking about a new roast. It doesn’t remember that you had a rough day yesterday.

That’s because personality isn’t just a system prompt. It’s a dynamic interplay of traits, emotions, and context — and that’s exactly what the Personaut PDK was built to model.

In this article, you’ll learn how to:

  1. Create a psychologically-grounded AI persona
  2. Define personality traits using the 16PF model
  3. Manage a 36-emotion state system
  4. Generate persona-aware LLM prompts that actually feel different

Installation

pip install personaut

That’s it. No API keys needed for everything in this article.

Step 1: Creating Your First Individual

The Individual is the core building block of Personaut. Think of it as a fully modeled character with an identity, personality, and emotional state.

import personaut
# Create a simulated persona — fully tracked with emotions and traits
sarah = personaut.create_individual(
    name="Sarah C",
    traits={"warmth": 0.85, "liveliness": 0.7, "reasoning": 0.6},
    emotional_state={"cheerful": 0.6, "hopeful": 0.4}
)
print(f"Name: {sarah.name}")
print(f"Type: {sarah.individual_type}")
print(f"Traits: {sarah.traits}")
print(f"Emotional State: {sarah.emotional_state}")

Three Types of Individuals

Personaut distinguishes between three types of participants:

# 1. SIMULATED — Full AI persona with state tracking
barista = personaut.create_individual(
    name="Marco",
    traits={"warmth": 0.9, "liveliness": 0.8},
    emotional_state={"cheerful": 0.7}
)
# 2. HUMAN — A real person being tracked (has memory, no simulated emotions)
customer = personaut.create_human(name="Alex")
# 3. NONTRACKED — Background character (no state, no memory)
bystander = personaut.create_nontracked_individual(name="Random Customer")

This distinction is critical for simulations: the system only generates prompts and emotional drift for simulated individuals, while tracking interaction history for human participants.

Step 2: The 17-Factor Personality Model

Personaut’s trait system is inspired by Raymond Cattell’s 16 Personality Factor (16PF) model, extended to 17 factors. Each trait exists on a spectrum from 0.0 to 1.0, where the poles represent opposite behavioral tendencies:

TraitLow Pole (≤ 0.3)High Pole (≥ 0.7)WarmthReserved, DistantFriendly, OutgoingReasoningConcreteAbstract, AnalyticalDominanceAccommodatingAssertive, DirectLivelinessSerious, RestrainedEnthusiastic, ExpressiveEmotional StabilityReactive, ChangeableEven-tempered, CalmOpennessConventionalCurious, Experimental

Here’s how to explore the full trait system:

from personaut import TraitProfile, Trait, WARMTH, DOMINANCE, EMOTIONAL_STABILITY
# Create a detailed personality profile
architect_traits = TraitProfile(traits={
    "warmth": 0.3,              # Reserved
    "reasoning": 0.9,           # Highly analytical
    "emotional_stability": 0.8, # Calm under pressure
    "dominance": 0.7,           # Direct communicator
    "liveliness": 0.2,          # Serious demeanor
    "openness": 0.85,           # Very curious
    "sensitivity": 0.4,         # Utilitarian
    "vigilance": 0.6,           # Somewhat suspicious
})
# Access trait constants for programmatic use
print(f"Warmth constant: {WARMTH}")
print(f"Dominance constant: {DOMINANCE}")

The magic happens when these traits influence how the persona responds to emotions — a concept called trait-emotion coefficients.

Step 3: The 36-Emotion State System

Personaut models 36 discrete emotions organized into 6 categories:

from personaut import EmotionalState, EmotionCategory, Emotion
from personaut import ANXIOUS, CHEERFUL, HOPEFUL, get_emotions_in_category, get_category
# Create an emotional state
state = EmotionalState()
# Set emotions directly (absolute assignment)
state.change_emotion("cheerful", 0.7)
state.change_emotion("hopeful", 0.5)
state.change_emotion("anxious", 0.2)
# Check the dominant emotion — this drives prompt tone
dominant = state.get_dominant_emotion()
print(f"Dominant emotion: {dominant}")  # cheerful
# Explore emotion categories
joy_emotions = get_emotions_in_category("joy")
print(f"Joy category contains: {joy_emotions}")
# Get which category an emotion belongs to
category = get_category("anxious")
print(f"'anxious' belongs to: {category}")  # Fear

How Traits Influence Emotions

This is where Personaut gets interesting. Traits don’t just describe behavior — they mathematically influence how emotions drift over time:

from personaut import TraitProfile
profile = TraitProfile(traits={
    "warmth": 0.9,
    "emotional_stability": 0.3,  # Reactive!
    "sensitivity": 0.8,          # Highly sensitive
})
# Calculate how this personality modifies emotional responses
modifier = profile.calculate_emotion_modifier("anxious")
print(f"Anxiety modifier for sensitive personality: {modifier}")
# A high-sensitivity, low-stability person has amplified anxiety responses

Step 4: Generating Persona-Aware Prompts

This is the payoff. The PromptManager translates all of this psychological modelling into natural language system prompts that you can feed to any LLM:

from personaut.prompts import PromptManager
# Create two very different personas
barista = personaut.create_individual(
    name="Marco",
    traits={"warmth": 0.9, "liveliness": 0.8, "sensitivity": 0.7},
    emotional_state={"cheerful": 0.7, "hopeful": 0.5}
)
architect = personaut.create_individual(
    name="Diana",  
    traits={"warmth": 0.3, "reasoning": 0.9, "dominance": 0.7},
    emotional_state={"confident": 0.6, "curious": 0.4}
)
# Generate conversation prompts
pm = PromptManager()
barista_prompt = pm.generate(barista, template_name="conversation")
architect_prompt = pm.generate(architect, template_name="conversation")
print("=" * 60)
print("BARISTA PROMPT:")
print("=" * 60)
print(barista_prompt)
print()
print("=" * 60)
print("ARCHITECT PROMPT:")
print("=" * 60)
print(architect_prompt)

The output will show dramatically different prompts — the barista’s will emphasize warmth, enthusiasm, and emotional expressiveness, while the architect’s will focus on analytical precision, directness, and reserved emotional display.

Using the PromptBuilder for Fine-Grained Control

For advanced use cases, the PromptBuilder gives you complete control over prompt assembly:

from personaut.prompts import PromptBuilder
builder = PromptBuilder()
prompt = (builder
    .with_individual(barista)
    .with_emotional_state(barista.emotional_state)
    .using_template("conversation")
    .section_order(["identity", "personality", "emotional_state", "guidelines"])
    .build())
print(prompt)

Step 5: Putting It All Together

Let’s build a complete example that creates a persona, modifies their emotional state in response to events, and generates evolving prompts:

import personaut
from personaut.prompts import PromptManager
def main():
    # Create our persona
    alex = personaut.create_individual(
        name="Alex Rivera",
        traits={
            "warmth": 0.7,
            "liveliness": 0.6,
            "emotional_stability": 0.5,
            "sensitivity": 0.7,
            "openness": 0.8,
        },
        emotional_state={"cheerful": 0.5, "curious": 0.4}
    )

    pm = PromptManager()

    # --- Morning: Alex is cheerful and curious ---
    print("🌅 MORNING STATE")
    print(f"   Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
    morning_prompt = pm.generate(alex, template_name="conversation")
    print(f"   Prompt preview: {morning_prompt[:200]}...")
    print()

    # --- Event: Alex receives critical feedback ---
    print("📝 EVENT: Received harsh criticism on their work")
    alex.emotional_state.change_emotion("cheerful", 0.1)
    alex.emotional_state.change_emotion("anxious", 0.7)
    alex.emotional_state.change_emotion("frustrated", 0.5)

    print(f"   Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
    afternoon_prompt = pm.generate(alex, template_name="conversation")
    print(f"   Prompt preview: {afternoon_prompt[:200]}...")
    print()

    # --- Event: A friend offers support ---
    print("💬 EVENT: Friend offers encouragement")
    alex.emotional_state.change_emotion("anxious", 0.3)
    alex.emotional_state.change_emotion("hopeful", 0.6)
    alex.emotional_state.change_emotion("grateful", 0.5)

    print(f"   Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
    evening_prompt = pm.generate(alex, template_name="conversation")
    print(f"   Prompt preview: {evening_prompt[:200]}...")
if __name__ == "__main__":
    main()

What You’ve Learned

In this article, we’ve covered the foundational building blocks of the Personaut PDK:

  • Individuals — Three types of participants with different tracking depths
  • Traits — 17-factor personality model that shapes behavior
  • Emotions — 36 discrete emotions across 6 categories with intensity tracking
  • Trait-Emotion Coefficients — How personality amplifies or dampens emotional responses
  • Prompt Generation — Translating psychological state into natural language LLM instructions

What’s Next

In the next article, we’ll explore how to make personas react dynamically to their environment using Masks (contextual behavior overrides) and Triggers (automated emotional responses) — and use them to generate full multi-turn conversation simulations.

Found this useful? Follow me for the next article in this series, where we build emotion-driven conversation simulations that feel alive.

The Personaut PDK is open-source and available on PyPI and GitHub.


메타데이터
post_id
76ea45c04004
slug
building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
url
https://ai.plainenglish.io/building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
canonical_url
https://ai.plainenglish.io/building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
author_url
https://medium.com/@empadev64
status
ok
fetched_at
2026-07-13 06:23:13