SupportOps / adversary /policy_tracker.py
prathvi9607's picture
done phase 1
9c1645f
Raw History Blame Contribute Delete
6.63 kB
"""
Policy tracker for adaptive adversary system.
Tracks agent behavior across episodes to identify patterns and weaknesses.
"""
from collections import defaultdict, deque
from typing import Any
from env.state import ActionRecord
class PolicyTracker:
"""
Tracks agent behavior across episodes.
Maintains a sliding window of last N episodes for analysis.
"""
def __init__(self, window_size: int = 20):
"""
Initialize policy tracker.
Args:
window_size: Number of recent episodes to track
"""
self.window_size = window_size
self.episodes = deque(maxlen=window_size)
self.action_counts = defaultdict(int)
self.total_actions = 0
self.root_cause_scores = defaultdict(list)
self.escalation_steps = []
self.first_actions = []
def record_episode(
self,
episode_id: str,
task_level: str,
root_cause_category: str,
root_cause_id: str,
actions: list[ActionRecord],
score: float
) -> None:
"""
Record completed episode into tracker.
Args:
episode_id: Unique episode identifier
task_level: Difficulty level
root_cause_category: Category of root cause
root_cause_id: Specific root cause ID
actions: List of actions taken
score: Episode score (0-1)
"""
episode_data = {
"episode_id": episode_id,
"task_level": task_level,
"root_cause_category": root_cause_category,
"root_cause_id": root_cause_id,
"actions": actions,
"score": score,
"num_actions": len(actions)
}
self.episodes.append(episode_data)
# Update action counts
for action in actions:
self.action_counts[action.action_name] += 1
self.total_actions += 1
# Track root cause performance
self.root_cause_scores[root_cause_category].append(score)
# Track escalation patterns
for i, action in enumerate(actions):
if action.action_name == "escalate_ticket":
self.escalation_steps.append(i + 1) # 1-indexed
break
# Track first action
if actions:
self.first_actions.append(actions[0].action_name)
def get_action_frequencies(self, task_level: str | None = None) -> dict[str, float]:
"""
Get normalized frequency of each action type.
Args:
task_level: Optional filter by task level
Returns:
Dictionary mapping action names to frequencies (0-1)
"""
if not self.total_actions:
return {}
# Filter episodes if task level specified
if task_level:
filtered_actions = defaultdict(int)
total = 0
for ep in self.episodes:
if ep["task_level"] == task_level:
for action in ep["actions"]:
filtered_actions[action.action_name] += 1
total += 1
if total == 0:
return {}
return {
action: count / total
for action, count in filtered_actions.items()
}
# Return overall frequencies
return {
action: count / self.total_actions
for action, count in self.action_counts.items()
}
def get_first_action_distribution(self) -> dict[str, float]:
"""
Analyze what agent does first.
Returns:
Dictionary mapping action names to probability of being first
"""
if not self.first_actions:
return {}
counts = defaultdict(int)
for action in self.first_actions:
counts[action] += 1
total = len(self.first_actions)
return {
action: count / total
for action, count in counts.items()
}
def get_escalation_step_distribution(self) -> list[int]:
"""
Get list of steps at which agent escalated.
Returns:
List of step numbers (useful for statistical analysis)
"""
return self.escalation_steps.copy()
def get_score_by_root_cause_category(self) -> dict[str, float]:
"""
Get average score per root cause category.
Returns:
Dictionary mapping categories to average scores
"""
return {
category: sum(scores) / len(scores) if scores else 0.0
for category, scores in self.root_cause_scores.items()
}
def get_average_score(self, task_level: str | None = None) -> float:
"""
Get average score across episodes.
Args:
task_level: Optional filter by task level
Returns:
Average score (0-1)
"""
if task_level:
scores = [
ep["score"] for ep in self.episodes
if ep["task_level"] == task_level
]
else:
scores = [ep["score"] for ep in self.episodes]
return sum(scores) / len(scores) if scores else 0.0
def get_episode_count(self) -> int:
"""Get number of recorded episodes."""
return len(self.episodes)
def get_weakness_categories(self, threshold: float = 0.40) -> list[str]:
"""
Identify root cause categories where agent performs poorly.
Args:
threshold: Score threshold below which category is "weak"
Returns:
List of category names with average score < threshold
"""
scores_by_category = self.get_score_by_root_cause_category()
return [
category for category, score in scores_by_category.items()
if score < threshold
]
def uses_action_frequently(self, action_name: str, threshold: float = 0.40) -> bool:
"""
Check if agent uses a specific action frequently.
Args:
action_name: Name of action to check
threshold: Frequency threshold (0-1)
Returns:
True if action frequency >= threshold
"""
frequencies = self.get_action_frequencies()
return frequencies.get(action_name, 0.0) >= threshold