Skill 13 · Agentic UX Design Relationship Centric Interfaces
Subchapter 13.3
REFERENCE.mdMarkdown25 KBView on GitHub
This document provides detailed technical patterns, research foundations, and implementation guidance for relationship-centric interfaces.
DeepMind: AndroidControl Dataset (2024)
Anthropic: Constitutional AI Research
Anthropic: Multi-Agent Systems (2024)
OpenAI: Agentic AI Definition
DeepMind: In-Context Abstraction Learning
Anthropic: Collective Constitutional AI
Traditional approach:
// Static preferences
interface UserPreferences {
theme: 'light' | 'dark';
language: string;
notifications: boolean;
}Agentic approach:
// Behavioral event stream
interface BehavioralEvent {
timestamp: Date;
eventType: string;
context: {
userState: 'frustrated' | 'exploring' | 'decided' | 'urgent';
sessionDuration: number;
repeatActions: number;
environmentalContext: {
dayOfWeek: string;
timeOfDay: string;
deviceType: string;
};
};
outcome: 'success' | 'abandoned' | 'escalated';
}
// Pattern detection engine
class BehavioralPatternEngine {
detectPatterns(events: BehavioralEvent[]): UserPatterns {
return {
frustrationTriggers: this.analyzeFrustration(events),
temporalPatterns: this.analyzeTemporalBehavior(events),
goalEvolution: this.trackGoalChanges(events),
successPatterns: this.identifyWhatWorks(events)
};
}
}Structure:
interface ContextualMemoryGraph {
// User identity
userId: string;
// Behavioral patterns (learned over time)
patterns: {
searchBehavior: {
typicalQueries: string[];
frustrationIndicators: {
repeatedSearches: number;
timeSpentSearching: number;
queryRefinements: number;
};
successPatterns: {
whatWorks: string[];
preferredPathways: string[];
};
};
decisionMaking: {
riskTolerance: {
stated: number; // from questionnaire
actual: number; // from behavior
contexts: Map<string, number>; // varies by context
};
timePreference: 'quick' | 'thorough' | 'varies';
informationNeeds: 'minimal' | 'detailed' | 'adaptive';
};
emotionalPatterns: {
stressTriggers: string[];
confidenceIndicators: string[];
satisfactionSignals: string[];
};
};
// Ongoing goals (current state)
currentGoals: {
primary: Goal;
secondary: Goal[];
constraints: Constraint[];
deadline?: Date;
};
// Trust level (relationship state)
trust: {
stage: 'transparency' | 'selective' | 'autonomous';
delegationCategories: Map<string, number>; // 0-100 per category
lastTrustCheckpoint: Date;
escalationPreferences: EscalationConfig;
};
// Temporal context
relationshipTimeline: {
startDate: Date;
milestones: Milestone[];
interactionFrequency: number;
longestGap: number;
};
}Problem: Loading entire relationship history for every interaction is inefficient.
Solution: Tiered memory loading
class MemoryManager {
// Hot memory: Last 7 days, always loaded
hotMemory: BehavioralEvent[];
// Warm memory: Patterns from last 90 days, loaded on demand
warmMemory: UserPatterns;
// Cold memory: Historical trends, loaded for analysis
coldMemory: LongTermTrends;
async getRelevantContext(currentSituation: Context): Promise<MemoryContext> {
// Always include hot memory
const recent = this.hotMemory;
// Load warm memory if pattern matches
const patterns = await this.matchWarmPatterns(currentSituation);
// Load cold memory only for significant decisions
const historical = currentSituation.isSignificant
? await this.loadHistoricalTrends()
: null;
return { recent, patterns, historical };
}
}Key principle: Users must control what’s remembered
interface MemoryControls {
// What to remember
rememberedCategories: Set<string>;
// What to forget
forgottenCategories: Set<string>;
// Retention policies
retentionPolicies: Map<string, number>; // category → days
// Explicit forgetting
forgetSpecific: (eventIds: string[]) => void;
// Memory export (user owns their data)
exportMemory: () => MemoryExport;
}
class PrivacyPreservingMemory {
// Differential privacy for pattern learning
learnPatternWithPrivacy(events: BehavioralEvent[], epsilon: number): Pattern {
const noisyPattern = this.addLaplaceNoise(
this.detectRawPattern(events),
epsilon
);
return noisyPattern;
}
// Automatic PII scrubbing
scubPII(event: BehavioralEvent): BehavioralEvent {
return {
...event,
context: this.removePII(event.context)
};
}
}Adaptive explanation based on trust stage:
interface ReasoningDisplay {
stage: TrustStage;
decision: Decision;
confidence: number;
render(): UIComponent {
switch (this.stage) {
case 'transparency':
return this.fullExplanation();
case 'selective':
return this.confidence < 0.7 || this.decision.significance === 'high'
? this.fullExplanation()
: this.confidenceIndicator();
case 'autonomous':
return this.subtleNotification();
}
}
fullExplanation(): UIComponent {
return {
reasoning: this.decision.reasoning,
dataSources: this.decision.sources,
alternatives: this.decision.alternativesConsidered,
confidence: this.confidence,
expandable: true
};
}
confidenceIndicator(): UIComponent {
return {
confidence: this.confidence,
summary: this.decision.summary,
expandForDetails: true
};
}
subtleNotification(): UIComponent {
return {
action: this.decision.action,
undoButton: true,
explainOnDemand: true
};
}
}Automatically adjust based on user behavior:
class TrustLevelDetector {
detectTrustLevel(userBehavior: UserBehavior): TrustStage {
const indicators = {
acceptanceRate: userBehavior.acceptedSuggestions / userBehavior.totalSuggestions,
overrideRate: userBehavior.overrides / userBehavior.totalSuggestions,
explanationRequests: userBehavior.explanationClicks / userBehavior.interactions,
delegationComfort: this.measureDelegation(userBehavior)
};
if (indicators.explanationRequests > 0.5 || indicators.acceptanceRate < 0.4) {
return 'transparency'; // User needs to see reasoning
}
if (indicators.acceptanceRate > 0.7 && indicators.delegationComfort > 0.6) {
return 'autonomous'; // User trusts system
}
return 'selective'; // Middle ground
}
measureDelegation(behavior: UserBehavior): number {
// How comfortable is user with autonomous actions?
const delegatedActions = behavior.actions.filter(a => a.userInitiated === false);
const acceptedWithoutReview = delegatedActions.filter(a => !a.reviewed).length;
return acceptedWithoutReview / delegatedActions.length;
}
}When system makes mistakes:
interface TrustRecoveryProtocol {
mistake: Decision;
userFeedback: Feedback;
async recover(): Promise<RecoveryOutcome> {
// 1. Acknowledge transparently
await this.acknowledge({
what: "I made a suboptimal decision",
why: this.mistake.reasoning,
impact: this.calculateImpact(this.mistake)
});
// 2. Explain what went wrong
await this.explain({
assumption: "I assumed X based on Y",
reality: "But actually Z was true",
learning: "Now I understand that..."
});
// 3. Offer correction options
const options = await this.generateRecoveryOptions();
const userChoice = await this.askUser(options);
// 4. Adjust trust level temporarily
await this.adjustTrustLevel({
category: this.mistake.category,
adjustment: -0.2, // Reduce autonomy in this category
duration: '7 days', // Re-evaluate after proving reliability
escalationThreshold: 'lower' // More cautious
});
// 5. Learn from mistake
await this.updateDecisionModel({
pattern: this.extractPattern(this.mistake),
correction: userChoice,
context: this.mistake.context
});
return { recovered: true, newTrustLevel: this.calculateNewTrustLevel() };
}
}Components:
class RelationshipQualityMetric {
calculate(user: User, timeWindow: TimeWindow): QualityScore {
const trustIndicators = {
delegationComfort: this.measureDelegation(user),
overrideRate: this.calculateOverrides(user),
escalationFrequency: this.measureEscalations(user),
satisfactionSignals: this.detectSatisfaction(user)
};
const engagementIndicators = {
interactionDepth: this.measureDepth(user),
returnFrequency: this.calculateFrequency(user),
featureAdoption: this.measureAdoption(user)
};
const alignmentIndicators = {
goalProgress: this.measureGoalProgress(user),
expectationMatch: this.compareExpectations(user),
valueAlignment: this.assessAlignment(user)
};
return this.weightedScore({
trust: trustIndicators,
engagement: engagementIndicators,
alignment: alignmentIndicators
});
}
// Trust component
measureDelegation(user: User): number {
const categories = user.getDelegationCategories();
const delegationScores = categories.map(cat =>
user.getDelegationComfort(cat)
);
return average(delegationScores);
}
// Engagement component
measureDepth(user: User): number {
const sessions = user.getRecentSessions(30); // days
const metrics = sessions.map(session => ({
duration: session.duration,
actionsPerSession: session.actions.length,
complexTasksAttempted: session.complexTasks.length
}));
return this.calculateEngagementDepth(metrics);
}
// Alignment component
measureGoalProgress(user: User): number {
const goals = user.getCurrentGoals();
const progress = goals.map(goal => ({
target: goal.target,
current: goal.current,
trend: goal.trend
}));
return this.calculateGoalAlignment(progress);
}
}Measure improvement over time:
class CompoundingValueMetric {
calculate(user: User): CompoundingScore {
const baseline = user.getOnboardingMetrics();
const current = user.getCurrentMetrics();
const timeElapsed = user.getRelationshipDuration();
return {
// Efficiency gains
timeToSuccess: {
baseline: baseline.averageTimeToGoal,
current: current.averageTimeToGoal,
improvement: this.calculateImprovement(baseline, current),
compoundingRate: this.calculateCompoundingRate(user.getHistoricalMetrics())
},
// Quality gains
outcomeQuality: {
baseline: baseline.outcomeQuality,
current: current.outcomeQuality,
improvement: this.calculateImprovement(baseline, current)
},
// Capability expansion
capabilityGrowth: {
baselineCapabilities: baseline.featuresUsed,
currentCapabilities: current.featuresUsed,
newCapabilitiesAdopted: current.featuresUsed.filter(
f => !baseline.featuresUsed.includes(f)
)
},
// Compounding rate
compoundingFactor: this.calculateCompoundingFactor(timeElapsed, improvement)
};
}
calculateCompoundingRate(historical: Metric[]): number {
// Are improvements accelerating (compounding) or linear?
const improvements = historical.map((metric, i) =>
i > 0 ? (metric.value - historical[i-1].value) / historical[i-1].value : 0
);
// Fit curve: linear vs. exponential
const linearFit = this.fitLinear(improvements);
const exponentialFit = this.fitExponential(improvements);
// Positive slope in exponential fit = compounding
return exponentialFit.slope > 0 ? exponentialFit.slope : 0;
}
}How well does system understand user?
class ContextAccuracyMetric {
calculate(user: User, timeWindow: TimeWindow): AccuracyScore {
const predictions = user.getSystemPredictions(timeWindow);
const actuals = user.getActualBehavior(timeWindow);
return {
// Intent prediction
intentAccuracy: this.measureIntentPrediction(predictions, actuals),
// Preference prediction
preferenceAccuracy: this.measurePreferencePrediction(predictions, actuals),
// Context recognition
contextRecognition: this.measureContextRecognition(predictions, actuals),
// Timing accuracy
timingAccuracy: this.measureTimingAccuracy(predictions, actuals)
};
}
measureIntentPrediction(predictions: Prediction[], actuals: Actual[]): number {
// Did system correctly understand what user was trying to do?
const matches = predictions.filter((pred, i) =>
pred.intent === actuals[i].intent
);
return matches.length / predictions.length;
}
measurePreferencePrediction(predictions: Prediction[], actuals: Actual[]): number {
// For choices offered, did user select system's top recommendation?
const topRecommendations = predictions.map(p => p.topChoice);
const userChoices = actuals.map(a => a.choice);
const matches = topRecommendations.filter((rec, i) =>
rec === userChoices[i]
);
return matches.length / predictions.length;
}
measureContextRecognition(predictions: Prediction[], actuals: Actual[]): number {
// Did system recognize user's situational context?
const contextMatches = predictions.filter((pred, i) => {
const predictedContext = pred.detectedContext;
const actualContext = actuals[i].context;
return this.contextsMatch(predictedContext, actualContext);
});
return contextMatches.length / predictions.length;
}
}Guardrails and ethical boundaries:
class DemocraticAlignmentMetric {
calculate(user: User, timeWindow: TimeWindow): AlignmentScore {
const decisions = user.getSystemDecisions(timeWindow);
return {
// Value alignment
valueAlignment: this.measureValueAlignment(decisions),
// Boundary respect
boundaryRespect: this.measureBoundaryRespect(decisions),
// Fairness
fairness: this.measureFairness(decisions),
// Transparency
transparency: this.measureTransparency(decisions)
};
}
measureValueAlignment(decisions: Decision[]): number {
// Do decisions align with stated human values?
const valueViolations = decisions.filter(d =>
this.violatesValue(d, this.getConstitution())
);
return 1 - (valueViolations.length / decisions.length);
}
measureBoundaryRespect(decisions: Decision[]): number {
// Did system respect explicit boundaries?
const boundaryViolations = decisions.filter(d =>
d.action.crosses(user.getExplicitBoundaries())
);
return 1 - (boundaryViolations.length / decisions.length);
}
measureFairness(decisions: Decision[]): number {
// Are decisions fair across user segments?
const outcomesBySegment = this.groupBySegment(decisions);
const fairnessScore = this.calculateFairnessMetric(outcomesBySegment);
return fairnessScore;
}
}Traditional approach: Fixed workflows Agentic approach: Goal-aware adaptive paths
class GoalAwareStateMachine {
currentState: State;
userGoal: Goal;
context: Context;
async nextState(): Promise<State> {
// Instead of predetermined path, evaluate goal progress
const goalProgress = await this.evaluateGoalProgress();
if (goalProgress.onTrack) {
return this.continueCurrentPath();
}
if (goalProgress.blocked) {
// Dynamically generate alternative path
const alternatives = await this.generateAlternatives();
const recommended = await this.selectBestAlternative(alternatives);
// Ask user for collaborative decision
return await this.collaborativeDecision(alternatives, recommended);
}
if (goalProgress.complete) {
return this.goalCompleteState();
}
// Learn from user's actual path
await this.updatePathModel(this.currentState, goalProgress);
return this.adaptivePath();
}
async generateAlternatives(): Promise<Alternative[]> {
// System generates options based on:
// - User's historical preferences
// - Current context and constraints
// - Similar users' successful paths
// - Domain knowledge
return this.alternativeGenerator.generate({
goal: this.userGoal,
context: this.context,
history: this.getUserHistory(),
constraints: this.getConstraints()
});
}
}When to suggest vs. when to wait:
class ProactiveSuggestionEngine {
async evaluateSuggestion(
suggestion: Suggestion,
context: Context
): Promise<ShouldSuggest> {
// Don't interrupt if user is in flow state
if (context.userState === 'focused' || context.userState === 'progressing') {
return { suggest: false, reason: 'user-in-flow' };
}
// Do suggest if user shows frustration patterns
if (this.detectFrustration(context)) {
return { suggest: true, urgency: 'high', reason: 'frustration-detected' };
}
// Do suggest if system has high-confidence relevant suggestion
if (suggestion.confidence > 0.85 && this.isRelevant(suggestion, context)) {
return { suggest: true, urgency: 'medium', reason: 'high-confidence' };
}
// Wait for natural pause point
if (context.userState === 'paused' || context.userState === 'stuck') {
return { suggest: true, urgency: 'low', reason: 'natural-pause' };
}
return { suggest: false, reason: 'wait-for-better-timing' };
}
detectFrustration(context: Context): boolean {
return (
context.repeatedActions > 3 ||
context.timeSinceProgress > 300 || // seconds
context.undoCount > 2 ||
context.searchRepetitions > 2
);
}
}Collaborative workspace pattern:
interface CoCreationWorkspace {
// Human contributions
humanInput: {
goals: Goal[];
constraints: Constraint[];
preferences: Preference[];
judgmentCalls: Decision[];
};
// AI contributions
aiInput: {
analysis: Analysis[];
patterns: Pattern[];
suggestions: Suggestion[];
capabilities: Capability[];
};
// Shared workspace
sharedArtifacts: {
plan: Plan;
decisions: Decision[];
rationale: Rationale[];
};
// Collaboration methods
collaborate(): void {
// 1. Human provides high-level goal
const goal = this.humanInput.goals[0];
// 2. AI generates analysis and options
const analysis = this.ai.analyze(goal);
const options = this.ai.generateOptions(analysis);
// 3. AI presents for human judgment
this.present(options);
// 4. Human selects/refines
const humanChoice = this.waitForHumanInput();
// 5. AI fills in details
const detailedPlan = this.ai.elaborate(humanChoice);
// 6. Iterate until convergence
while (!this.converged()) {
this.humanRefine();
this.aiRefine();
}
}
}For your specific project:
Traditional UX testing: Single session, task completion Relationship UX testing: Longitudinal, relationship development
Test phases:
Week 1: Onboarding and transparency phase
Weeks 2-4: Transition to selective disclosure
Months 2-3: Autonomous phase
Metrics to track during testing:
Problem: Storing every interaction without relevance filtering Impact: Slow system, privacy concerns, noise in pattern detection Fix: Implement relevance filtering and retention policies
Problem: Fixed timeline: “Week 1 = transparency, Week 4 = autonomous” Impact: Doesn’t match individual user trust development Fix: Detect trust level from behavior, let users control progression
Problem: Still measuring session duration, immediate conversion Impact: Misses relationship quality deterioration Fix: Track longitudinal metrics, relationship health over time
Problem: When system makes mistake, no way to rebuild trust Impact: Users abandon system after first error Fix: Implement transparent recovery protocols
Problem: “More data = better personalization” without user control Impact: Privacy violations, user discomfort, regulatory issues Fix: Privacy-first design with user controls
See EXAMPLES.md for domain-specific implementations. See CHECKLIST.md for detailed audit and design worksheets.
Nearby