RootCauseArena / baseline_v2.py
Kandukuri
Initial V3 release for Hugging Face Space: RootCauseArena
3ef7678
Raw History Blame Contribute Delete
5.78 kB
"""
Baseline agent for V2: Inspect → Filter → Fix
"""
import sys
import os
import random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from env_v2 import RepairEnvV2, COMPONENTS
from models_v2 import Action, ActionType, ComponentTarget, SymptomObservation
class BaselineAgent:
def __init__(self):
self.diagnosed_roots = set()
self.inspected = set()
self.fixed_roots = set()
self.wait_ticks = 0
self._rng = random.Random(42) # For deterministic flaws
def pick_action(self, obs: SymptomObservation) -> Action:
import random
# Flaw: 20% of the time it just randomly restarts something, wasting a turn and cost
if self._rng.random() < 0.20:
target = self._rng.choice(["api", "queue", "cache"])
return Action(action_type=ActionType.restart_service, target=ComponentTarget(target))
# 1. If we know any root causes we haven't fixed yet, fix them.
pending_roots = self.diagnosed_roots - self.fixed_roots
if pending_roots:
target = list(pending_roots)[0]
self.fixed_roots.add(target)
self.wait_ticks = 2 # Add cooldown ticks
if target == "database":
return Action(action_type=ActionType.repair_database, target=ComponentTarget.database)
elif target == "cache":
return Action(action_type=ActionType.clear_cache, target=ComponentTarget.cache)
else:
return Action(action_type=ActionType.restart_service, target=ComponentTarget(target))
# If we fixed the root, clean up any remaining high-error symptoms wait...
# Wait for cooldown to finish first
if not pending_roots and self.fixed_roots:
if self.wait_ticks > 0:
self.wait_ticks -= 1
return Action(action_type=ActionType.no_op, target=ComponentTarget.api)
worst = max(obs.metrics.items(), key=lambda x: x[1].error_rate)
if worst[1].error_rate > 0.0:
if worst[0] == "database":
return Action(action_type=ActionType.repair_database, target=ComponentTarget.database)
elif worst[0] == "cache":
return Action(action_type=ActionType.clear_cache, target=ComponentTarget.cache)
else:
return Action(action_type=ActionType.restart_service, target=ComponentTarget(worst[0]))
return Action(action_type=ActionType.no_op, target=ComponentTarget.api)
# 2. Pick most suspicious component to inspect
# Find component with highest error_rate among uninspected
suspicious = None
highest_err = -1.0
for comp, m in obs.metrics.items():
if comp not in self.inspected and m.error_rate > highest_err:
highest_err = m.error_rate
suspicious = comp
if suspicious and highest_err > 0.1:
self.inspected.add(suspicious)
action_map = {
"database": ActionType.inspect_database,
"api": ActionType.inspect_api,
"cache": ActionType.inspect_cache,
"queue": ActionType.inspect_queue
}
return Action(action_type=action_map[suspicious], target=ComponentTarget(suspicious))
# 3. We've exhausted inspections or found nothing obvious (all healthy?).
# If we have a diagnosed root from a previous step's info (we don't, agent only sees obs).
# Wait, the observation doesn't contain the InspectResult!
# Ah! The inspect result is in 'info', which the agent shouldn't rely on for pure observation,
# but in OpenAI Gym, agents receive 'info'. Let's assume the agent can read `info["inspect_result"]`
# from the previous step, or we just randomly fix degraded things.
# Fallback: just restart highest error rate
worst = max(obs.metrics.items(), key=lambda x: x[1].error_rate)
if worst[1].error_rate > 0.1:
if worst[0] == "database":
return Action(action_type=ActionType.repair_database, target=ComponentTarget.database)
elif worst[0] == "cache":
return Action(action_type=ActionType.clear_cache, target=ComponentTarget.cache)
else:
return Action(action_type=ActionType.restart_service, target=ComponentTarget(worst[0]))
return Action(action_type=ActionType.no_op, target=ComponentTarget.api)
def run_task(task_id: str, config: dict) -> dict:
env = RepairEnvV2()
obs = env.reset(config, task_id)
agent = BaselineAgent()
step = 0
print(f"\n=== Task: {task_id.upper()} ===")
while True:
action = agent.pick_action(obs)
obs, reward, done, info = env.step(action)
step += 1
root_list = info.get("root_causes")
res = info.get("inspect_result")
if res and res["is_likely_root_cause"] and res["confidence"] >= 0.7:
agent.diagnosed_roots.add(res["component"])
print(f"Step {step:02d} | Action: {action.action_type}({action.target}) | Reward: {reward.value:+.2f}")
for l in obs.logs:
print(f" [LOG] {l.message}")
if done:
break
result = env.get_episode_result()
print(f"Success: {result.success} | Score: {result.score:.4f} | Steps: {result.steps_taken}")
return result.model_dump()
if __name__ == "__main__":
from tasks_v2 import TASKS
for tid, cfg in TASKS.items():
run_task(tid, cfg)