mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 13:52:43 +08:00
182 lines
5.8 KiB
Python
182 lines
5.8 KiB
Python
"""
|
|
System prompt experiment handler.
|
|
|
|
This module contains the handler for the system prompt experiment that uses
|
|
the PostHog variant as the system prompt filename.
|
|
"""
|
|
|
|
import copy
|
|
|
|
import posthog
|
|
from experiments.constants import EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT
|
|
from server.constants import IS_FEATURE_ENV
|
|
from storage.experiment_assignment_store import ExperimentAssignmentStore
|
|
|
|
from openhands.core.config.openhands_config import OpenHandsConfig
|
|
from openhands.core.logger import openhands_logger as logger
|
|
|
|
|
|
def _get_system_prompt_variant(user_id, conversation_id):
|
|
"""
|
|
Get the system prompt variant for the experiment.
|
|
|
|
Args:
|
|
user_id: The user ID
|
|
conversation_id: The conversation ID
|
|
|
|
Returns:
|
|
str or None: The PostHog variant name or None if experiment is not enabled or error occurs
|
|
"""
|
|
# No-op if the specific experiment is not enabled
|
|
if not EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT:
|
|
logger.info(
|
|
'experiment_manager_002:ab_testing:skipped',
|
|
extra={
|
|
'convo_id': conversation_id,
|
|
'reason': 'experiment_not_enabled',
|
|
'experiment': EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT,
|
|
},
|
|
)
|
|
return None
|
|
|
|
# Use experiment name as the flag key
|
|
try:
|
|
enabled_variant = posthog.get_feature_flag(
|
|
EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT, conversation_id
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
'experiment_manager:get_feature_flag:failed',
|
|
extra={
|
|
'convo_id': conversation_id,
|
|
'experiment': EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT,
|
|
'error': str(e),
|
|
},
|
|
)
|
|
return None
|
|
|
|
# Store the experiment assignment in the database
|
|
try:
|
|
experiment_store = ExperimentAssignmentStore()
|
|
experiment_store.update_experiment_variant(
|
|
conversation_id=conversation_id,
|
|
experiment_name='system_prompt_experiment',
|
|
variant=enabled_variant,
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
'experiment_manager:store_assignment:failed',
|
|
extra={
|
|
'convo_id': conversation_id,
|
|
'experiment': EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT,
|
|
'variant': enabled_variant,
|
|
'error': str(e),
|
|
},
|
|
)
|
|
# Fail the experiment if we cannot track the splits - results would not be explainable
|
|
return None
|
|
|
|
# Log the experiment event
|
|
# If this is a feature environment, add "FEATURE_" prefix to user_id for PostHog
|
|
posthog_user_id = f'FEATURE_{user_id}' if IS_FEATURE_ENV else user_id
|
|
|
|
try:
|
|
posthog.capture(
|
|
distinct_id=posthog_user_id,
|
|
event='system_prompt_set',
|
|
properties={
|
|
'conversation_id': conversation_id,
|
|
'variant': enabled_variant,
|
|
'original_user_id': user_id,
|
|
'is_feature_env': IS_FEATURE_ENV,
|
|
},
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
'experiment_manager:posthog_capture:failed',
|
|
extra={
|
|
'convo_id': conversation_id,
|
|
'experiment': EXPERIMENT_SYSTEM_PROMPT_EXPERIMENT,
|
|
'error': str(e),
|
|
},
|
|
)
|
|
# Continue execution as this is not critical
|
|
|
|
logger.info(
|
|
'posthog_capture',
|
|
extra={
|
|
'event': 'system_prompt_set',
|
|
'posthog_user_id': posthog_user_id,
|
|
'is_feature_env': IS_FEATURE_ENV,
|
|
'conversation_id': conversation_id,
|
|
'variant': enabled_variant,
|
|
},
|
|
)
|
|
|
|
return enabled_variant
|
|
|
|
|
|
def handle_system_prompt_experiment(
|
|
user_id, conversation_id, config: OpenHandsConfig
|
|
) -> OpenHandsConfig:
|
|
"""
|
|
Handle the system prompt experiment for OpenHands config.
|
|
|
|
Args:
|
|
user_id: The user ID
|
|
conversation_id: The conversation ID
|
|
config: The OpenHands configuration
|
|
|
|
Returns:
|
|
Modified OpenHands configuration
|
|
"""
|
|
enabled_variant = _get_system_prompt_variant(user_id, conversation_id)
|
|
|
|
# If variant is None, experiment is not enabled or there was an error
|
|
if enabled_variant is None:
|
|
return config
|
|
|
|
# Deep copy the config to avoid modifying the original
|
|
modified_config = copy.deepcopy(config)
|
|
|
|
# Set the system prompt filename based on the variant
|
|
if enabled_variant == 'control':
|
|
# Use the long-horizon system prompt for the control variant
|
|
agent_config = modified_config.get_agent_config(modified_config.default_agent)
|
|
agent_config.system_prompt_filename = 'system_prompt_long_horizon.j2'
|
|
agent_config.enable_plan_mode = True
|
|
elif enabled_variant == 'interactive':
|
|
modified_config.get_agent_config(
|
|
modified_config.default_agent
|
|
).system_prompt_filename = 'system_prompt_interactive.j2'
|
|
elif enabled_variant == 'no_tools':
|
|
modified_config.get_agent_config(
|
|
modified_config.default_agent
|
|
).system_prompt_filename = 'system_prompt.j2'
|
|
else:
|
|
logger.error(
|
|
'system_prompt_experiment:unknown_variant',
|
|
extra={
|
|
'user_id': user_id,
|
|
'convo_id': conversation_id,
|
|
'variant': enabled_variant,
|
|
'reason': 'no explicit mapping; returning original config',
|
|
},
|
|
)
|
|
return config
|
|
|
|
# Log which prompt is being used
|
|
logger.info(
|
|
'system_prompt_experiment:prompt_selected',
|
|
extra={
|
|
'user_id': user_id,
|
|
'convo_id': conversation_id,
|
|
'system_prompt_filename': modified_config.get_agent_config(
|
|
modified_config.default_agent
|
|
).system_prompt_filename,
|
|
'variant': enabled_variant,
|
|
},
|
|
)
|
|
|
|
return modified_config
|