Perceived Performance
Actual performance and perceived performance are two different things. While actual performance measures how fast your system responds, perceived performance measures how fast users feel it responds.
Parlant includes a PerceivedPerformancePolicy that controls various UX optimizations designed to make your agent feel more responsive and natural, even when operations take time.
Why Perceived Performance Matters​
LLM-based agents can take several seconds to generate responses, especially when matching guidelines, calling tools, and composing messages. Without perceived performance optimizations, users would stare at a blank screen during this time, leading to frustration and disengagement.
Parlant addresses this through several mechanisms:
- Processing Indicators - Show users that the agent is working
- Preambles - Send quick acknowledgment messages while the full response is being prepared
- Message Splitting - Break long responses into digestible chunks
- Humanized Delays - Add natural-feeling pauses that make the agent feel less robotic
The PerceivedPerformancePolicy Interface​
The policy controls the following behaviors:
| Method | Purpose | Default Behavior |
|---|---|---|
get_processing_indicator_delay() | Delay before showing "thinking..." indicator | 1-2 seconds (randomized) |
get_extended_processing_indicator_delay() | Delay before showing extended processing indicator | 3.5-5 seconds (randomized) |
get_preamble_delay() | Delay before sending a preamble message | 1.5-2 seconds (randomized) |
get_follow_up_delay() | Delay before sending follow-up messages | 0.5-1.5 seconds (randomized) |
is_preamble_required() | Whether to send a preamble for this interaction | Smart logic based on wait times |
is_message_splitting_required() | Whether to split long messages | Always true |
Preamble Logic​
The default BasicPerceivedPerformancePolicy uses intelligent logic to determine when preambles are needed:
- First few interactions: Always sends preambles to engage the customer early
- After long waits: If the last two responses took more than 5 seconds each, sends a preamble
- After a preamble: Won't send another preamble immediately after one was just sent
This adaptive approach keeps users engaged without overwhelming them with unnecessary acknowledgment messages.
Built-in Policies​
Parlant provides three built-in policies:
BasicPerceivedPerformancePolicy​
The default policy with humanized, randomized delays and smart preamble logic. Suitable for most chat-based applications.
NullPerceivedPerformancePolicy​
Disables all perceived performance features. Use this when you want complete control over timing or when building integrations that handle UX separately.
import parlant.sdk as p
async def configure_container(container: p.Container) -> p.Container:
container[p.PerceivedPerformancePolicy] = p.NullPerceivedPerformancePolicy()
return container
VoiceOptimizedPerceivedPerformancePolicy​
Optimized for voice interfaces where immediate acknowledgment is critical. Extends NullPerceivedPerformancePolicy but always requires preambles.
container[p.PerceivedPerformancePolicy] = p.VoiceOptimizedPerceivedPerformancePolicy()
Per-Agent Policies​
You can set different policies for different agents using the PerceivedPerformancePolicyProvider:
async def initialize_container(container: p.Container) -> None:
provider = container[p.PerceivedPerformancePolicyProvider]
# Use voice-optimized policy for a specific agent
provider.set_policy(
agent_id="voice-agent",
policy=p.VoiceOptimizedPerceivedPerformancePolicy(),
)
Custom Policies​
Create your own policy by extending an existing one:
import parlant.sdk as p
class FastResponsePolicy(p.BasicPerceivedPerformancePolicy):
async def get_processing_indicator_delay(
self,
context: p.EngineContext | None = None,
) -> float:
return 0.5 # Show indicator faster
async def get_preamble_delay(
self,
context: p.EngineContext | None = None,
) -> float:
return 0.8 # Send preambles sooner
async def is_preamble_required(
self,
context: p.EngineContext | None = None,
) -> bool:
return True # Always send preambles
Or disable specific features selectively:
class NoPreamblePolicy(p.BasicPerceivedPerformancePolicy):
async def is_preamble_required(
self,
context: p.EngineContext | None = None,
) -> bool:
return False # Never send preambles
async def is_message_splitting_required(
self,
context: p.EngineContext,
message: str,
) -> bool:
return False # Send messages as single blocks