Skip to main content

Personaut PDK

Python 3.10+ License Hatch

A Python SDK for creating and simulating AI personas with emotional states, personality traits, memories, and relationships.

Overview

Personaut PDK enables you to create rich, psychologically-grounded AI personas that can participate in:

  • Conversations: Multi-party dialogues with realistic emotional dynamics
  • Surveys: Persona-driven questionnaire responses
  • Outcome Analysis: Simulation-based prediction of behavioral outcomes
  • Live Interactions: Real-time chat with modality-specific interfaces

Features

  • 🎭 36 Emotional States - Fine-grained emotional modeling across 6 categories
  • 🧠 17 Personality Traits - Based on the 16PF psychological model
  • 📍 Situational Facts - Structured context with 8 categories and LLM extraction
  • 💾 Vector Memory - Semantic memory retrieval with trust-gated access
  • 🔗 Relationships - Trust-based relationship dynamics
  • 🎯 Triggers & Masks - Context-aware behavioral modifications
  • 🚀 Live Server - FastAPI backend + Flask UI for interactive sessions

Installation

pip install personaut

Optional Dependencies

# For Gemini model provider
pip install personaut[gemini]

# For AWS Bedrock provider
pip install personaut[bedrock]

# For live interaction server
pip install personaut[server]

# For development
pip install personaut[dev]

# Install everything
pip install personaut[all]

Quick Start

Creating an Individual

import personaut

# Create an individual with personality traits and emotional state
sarah = personaut.create_individual(
    name="Sarah",
    traits={"warmth": 0.8, "dominance": 0.4, "sensitivity": 0.7},
    emotional_state={"cheerful": 0.6, "creative": 0.5},
)

# Update emotional state
sarah.change_emotion("anxious", 0.3)

# Access traits
print(sarah.get_high_traits())  # [('warmth', 0.8), ...]

# Get dominant emotion (respects active mask)
print(sarah.get_dominant_emotion())  # ('cheerful', 0.6)

Using Masks for Contextual Behavior

from personaut.masks import PROFESSIONAL_MASK

# Add a professional mask
sarah.add_mask(PROFESSIONAL_MASK)
sarah.activate_mask("professional")

# Emotional state is now filtered through the mask
modified_state = sarah.get_emotional_state()  # Suppresses strong emotions

# Get raw state without mask
raw_state = sarah.get_raw_emotional_state()

Running a Conversation Simulation

import personaut

# Create individuals
sarah = personaut.create_individual(name="Sarah")
mike = personaut.create_individual(name="Mike")

# Create situation
situation = personaut.create_situation(
    modality=personaut.types.modality.TEXT_MESSAGE,
    description='Catching up after a long time'
)

# Create and run simulation
simulation = personaut.create_simulation(
    situation=situation,
    individuals=[sarah, mike],
    type=personaut.simulations.types.CONVERSATION,
    style=personaut.simulations.styles.SCRIPT
)

simulation.run(num=5, dir='./output/')

Situational Facts

from personaut.facts import FactExtractor, LLMFactExtractor

# Regex-based extraction (fast, deterministic)
extractor = FactExtractor()
ctx = extractor.extract(
    "A busy coffee shop in downtown Miami around 3pm. "
    "80% capacity with a line of 5 people."
)
print(ctx.get_value("venue_type"))       # "coffee shop"
print(ctx.get_value("capacity_percent")) # 80

# LLM-based extraction (richer, more nuanced)
llm_extractor = LLMFactExtractor(llm_client=your_client)
ctx = await llm_extractor.extract(
    "We grabbed coffee at the corner spot. Super packed, great vibe."
)

# Generate embedding text
print(ctx.to_embedding_text())

Live Interactive Chat

import personaut
from personaut.server import LiveInteractionServer

# Create server and add individual
server = LiveInteractionServer()
server.add_individual(sarah)

# Start server
server.start(api_port=8000, ui_port=5000)

# Access:
# - UI: http://localhost:5000
# - API: http://localhost:8000/docs

Emotional System

The emotion system models 36 discrete emotions organized into 6 categories:

Category Emotions
Anger/Mad hostile, hurt, angry, selfish, hateful, critical
Sad/Sadness guilty, ashamed, depressed, lonely, bored, apathetic
Fear/Scared rejected, confused, submissive, insecure, anxious, helpless
Joy/Happiness excited, sensual, energetic, cheerful, creative, hopeful
Powerful/Confident proud, respected, appreciated, important, faithful, satisfied
Peaceful/Calm content, thoughtful, intimate, loving, trusting, nurturing
from personaut.emotions import EmotionalState, ANXIOUS, HOPEFUL

state = EmotionalState()
state.change_emotion(ANXIOUS, 0.6)
state.change_emotion(HOPEFUL, 0.8)

# Query dominant emotion
dominant, value = state.get_dominant()  # ('hopeful', 0.8)

Personality Traits

Based on the 16PF model with 17 traits that influence emotional transitions:

import personaut

individual = personaut.create_individual(name="Sarah")

# High warmth = more approachable, friendly
individual.set_trait("warmth", 0.8)

# High emotional stability = less reactive to stress
individual.set_trait("emotional_stability", 0.7)

Memory System

Store and retrieve memories with emotional context and trust-gated access:

from personaut.memory import (
    create_individual_memory,
    create_shared_memory,
    create_private_memory,
    InMemoryVectorStore,
    search_memories,
)

# Individual memory with situational context
memory = create_individual_memory(
    owner_id="sarah_123",
    description="Met Alex at the coffee shop",
    context=ctx,  # SituationalContext from facts extraction
    salience=0.8,
)

# Shared memory between multiple people
shared = create_shared_memory(
    description="Team dinner at the Italian restaurant",
    participant_ids=["sarah_123", "mike_456"],
    perspectives={
        "sarah_123": "Great food, but Mike was late",
        "mike_456": "Traffic was terrible",
    },
)

# Private memory with trust threshold
private = create_private_memory(
    owner_id="sarah_123",
    description="My anxiety about the presentation",
    trust_threshold=0.8,  # Only shared with high-trust individuals
)

# Store and search memories
store = InMemoryVectorStore()
store.store(memory, embedding=[...])  # Vector from embedding model

results = search_memories(
    store=store,
    query="coffee meetings",
    embed_func=my_embed_function,
    trust_level=0.5,  # Filters private memories
)

Masks and Triggers

Masks modify emotional expression based on context. Triggers activate responses when conditions are met:

from personaut.masks import (
    create_mask,
    PROFESSIONAL_MASK,
    STOIC_MASK,
)
from personaut.triggers import (
    create_emotional_trigger,
    create_situational_trigger,
)

# Apply professional mask in office settings
if PROFESSIONAL_MASK.should_trigger("Going to an office meeting"):
    modified_state = PROFESSIONAL_MASK.apply(emotional_state)
    # Suppresses anger, boosts composure

# Create custom mask
interview_mask = create_mask(
    name="interview",
    emotional_modifications={"anxious": -0.3, "content": 0.2},
    trigger_situations=["interview", "formal"],
)

# Emotional trigger: activate stoic mask when anxiety is high
anxiety_trigger = create_emotional_trigger(
    description="High anxiety response",
    rules=[{"emotion": "anxious", "threshold": 0.8, "operator": ">"}],
    response=STOIC_MASK,
)

if anxiety_trigger.check(emotional_state):
    calmed_state = anxiety_trigger.fire(emotional_state)

# Situational trigger: increase anxiety in dark spaces
dark_trigger = create_situational_trigger(
    description="Dark space phobia",
    keywords=["dark", "basement", "cave"],
    response={"anxious": 0.3, "helpless": 0.2},
)

Relationships

Model trust dynamics between individuals and query relationship networks:

from personaut.relationships import (
    create_relationship,
    RelationshipNetwork,
    get_trust_level,
    TrustLevel,
)

# Create relationship with asymmetric trust
rel = create_relationship(
    individual_ids=["sarah", "mike"],
    trust={"sarah": 0.8, "mike": 0.5},  # Sarah trusts Mike more
    history="Roommates in college for 2 years",
    relationship_type="friends",
)

# Query trust
trust = rel.get_trust("sarah", "mike")  # 0.8
mutual = rel.get_mutual_trust("sarah", "mike")  # 0.65

# Update trust after an event
rel.update_trust("mike", "sarah", 0.2, "helped during crisis")

# Check trust level
level = rel.get_trust_level("sarah", "mike")
if level == TrustLevel.HIGH:
    # Sarah will share private memories with Mike

# Build a relationship network
network = RelationshipNetwork()
network.add_relationship(rel)
network.add_relationship(create_relationship(["mike", "carol"], trust={"mike": 0.7, "carol": 0.6}))

# Find connection path
path = network.find_path("sarah", "carol")  # ['sarah', 'mike', 'carol']
path_trust = network.calculate_path_trust(path)  # Trust decays along path

Situations

Define the context for simulations with modality, location, and structured context:

from datetime import datetime
from personaut.situations import (
    create_situation,
    SituationContext,
    create_environment_context,
)
from personaut.types.modality import Modality

# Create a situation
situation = create_situation(
    modality=Modality.IN_PERSON,
    description="Meeting at a coffee shop to discuss a project",
    time=datetime.now(),
    location="Miami, FL",
    context={"atmosphere": "relaxed"},
)

# Query modality characteristics
if situation.is_synchronous():
    # Real-time communication expected
    traits = situation.get_modality_traits()
    print(f"Visual cues: {traits['visual_cues']}")

# Build structured context with validation
ctx = create_environment_context(
    lighting="dim",
    noise_level="quiet",
    indoor=True,
    private=True,
)
result = ctx.validate()
if result.valid:
    # Context data meets schema requirements
    pass

Documentation

Document Description
PERSONAS.md Main agent guidelines
docs/EMOTIONS.md Emotion system reference
docs/TRAITS.md Trait system reference
docs/FACTS.md Situational context and fact extraction
docs/MEMORY.md Memory system and vector storage
docs/PROMPTS.md Prompt generation
docs/SIMULATIONS.md Simulation types
docs/LIVE_INTERACTIONS.md Server architecture
docs/STYLE_GUIDE.md Code conventions

Development

# Clone repository
git clone https://github.com/personaut/python-pdk.git
cd python-pdk

# Install with dev dependencies
pip install hatch
hatch shell

# Run tests
hatch test

# Run linters
hatch fmt

# Type check
hatch run type

See CONTRIBUTING.md for full development guidelines.

Requirements

License

Apache License 2.0 - see LICENSE for details.

Metadata

Release files for personaut 0.3.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for personaut 0.3.5
File Size Uploaded Provenance Actions
personaut-0.3.5.tar.gz 471.0 kB With provenance Details for personaut-0.3.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for personaut 0.3.5
File Interpreter ABI Platform Actions
personaut-0.3.5-py3-none-any.whl Python 3 none any Details for personaut-0.3.5-py3-none-any.whl

Total release size: 865.3 kB

Release files / personaut-0.3.5.tar.gz

Download URL personaut-0.3.5.tar.gz
Size 471.0 kB
Tags Source
SHA-256 checksum
How to use checksums
941fab8487d920c44281d1129ae34ef391e98e7657afb3f86ec97b13968b873b
BLAKE2b-256 checksum
How to use checksums
049364f1a5ae8c9ff63cc05ecb327ea0afcf072312a5f276f3263802d77059d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.

Transparency log

Release files / personaut-0.3.5-py3-none-any.whl

Download URL personaut-0.3.5-py3-none-any.whl
Size 394.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8b49e64c7c223af591be9dea71f9a5235cc7196267804579a95b607bd03ae2b2
BLAKE2b-256 checksum
How to use checksums
4afa78a927b5793143cc491d43bbf994c33f6713bfe3cd051fcecde4fe676905
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.

Transparency log

Security for personaut 0.3.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for personaut-0.3.5.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page