fix(inference): add debugging and error handling for env.step action calls
Browse files- Log each action step with compacted action string for better traceability
- Catch and log RuntimeError exceptions during env.step to identify failures
- Re-raise exceptions after logging to maintain original error flow
- Ensure printed debug information flushes stdout immediately for real-time monitoring
feat(reward): add comprehensive AntiAtropos reward function specification
- Provide detailed Markdown documentation explaining reward function design
- Describe multi-scale control signal architecture for Autonomous SRE
- Explain Lyapunov graph energy, reward composition, sigmoid normalization
- Outline layered action-efficiency penalties and episode grading system
- Highlight theoretical guarantees and practical innovation in reward design
fix(environment): correct idle capacity calculation to prevent negative values
- Adjust idle capacity formula to use max(0, ...) to avoid negative idle counts
- Clarify commented explanation to reflect correction for idle capacity calculation
- Maintain tiered cost computation consistent with three-tier infrastructure model
- REWARD_FUNCTION.md +142 -0
- inference.py +7 -1
- server/AntiAtropos_environment.py +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# The AntiAtropos Reward Function
|
| 2 |
+
|
| 3 |
+
A physics-grounded, multi-scale control signal for Autonomous SRE — built on Lyapunov stability theory, graph-aware energy, three-tier cost economics, and smooth preventive SLA gradients.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Architecture at a Glance
|
| 8 |
+
|
| 9 |
+
The reward operates at three temporal scales:
|
| 10 |
+
|
| 11 |
+
| Scale | Purpose | Consumer |
|
| 12 |
+
|-------|---------|----------|
|
| 13 |
+
| **Per-step** | Immediate feedback on stability, cost, SLA, action quality | LLM agent |
|
| 14 |
+
| **Per-episode** | Holistic grading across uptime, cost, stability | Leaderboard |
|
| 15 |
+
| **Cross-episode** | Sustained-excellence bonuses | Leaderboard |
|
| 16 |
+
|
| 17 |
+
No single term dominates — a healthy cluster with wasted capacity scores poorly, as does a cheap cluster with burning queues.
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## Layer 1: Lyapunov Graph Energy
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
V_graph(s) = Σ w_i · Q_i² + edge_weight · Σ_{(i,j)∈E} |Q_i − Q_j|
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
**Node energy** — Squared queue depths weighted by business importance (node-0 VIP = 2×). Squaring penalizes load concentration: one node at Q=100 is far worse than five at Q=20.
|
| 28 |
+
|
| 29 |
+
**Edge imbalance** — Penalizes flow mismatch across DAG edges. If a parent has deep queues but its child is idle, the edge term fires even though the child's individual energy is zero. This gives the agent gradient signal to **balance load across topology**, not just minimize individual queues.
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## Layer 2: Reward Composition
|
| 34 |
+
|
| 35 |
+
```
|
| 36 |
+
R_t = −(α·ΔV + β·Cost + γ·SLA + δ·Barrier)
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### ΔV — Lyapunov Drift (α = 0.002)
|
| 40 |
+
|
| 41 |
+
One-step change in cluster energy. Negative = stabilizing. Positive = destabilizing. The small weight makes it a **directional nudge**, not a sledgehammer. Grounded in Neely's Drift-Plus-Penalty framework, which guarantees queue stability with bounded average cost.
|
| 42 |
+
|
| 43 |
+
### Cost — Three-Tier Infrastructure Model (β = 1.5)
|
| 44 |
+
|
| 45 |
+
```
|
| 46 |
+
needed = ⌈incoming_rate / 15⌉
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
| Tier | Range | Rate | Rationale |
|
| 50 |
+
|------|-------|------|-----------|
|
| 51 |
+
| **Baseline** | ≤ DEFAULT_CAPACITY (3) | $0.05/u/hr | Already provisioned — sunk cost, no penalty |
|
| 52 |
+
| **Justified** | >3, ≤ needed | $0.20/u/hr (4×) | Extra capacity serving traffic — defensible spend |
|
| 53 |
+
| **Idle waste** | > needed | $1.00/u/hr (20×) | Capacity sitting idle — pure waste |
|
| 54 |
+
|
| 55 |
+
A naive two-tier model (cheap ≤ needed, expensive > needed) penalizes baseline capacity as "overprovisioned" because `needed` can be 1 while DEFAULT_CAPACITY is 3. The three-tier model recognizes that **baseline infrastructure is already paid for** — only agent-added capacity triggers premium pricing.
|
| 56 |
+
|
| 57 |
+
Baseline cost: 5 nodes × 3 units × $0.05 = **$0.75/hr**. Scaling one node to capacity=6 (3 justified + 0 idle) costs $0.75 + $0.60 = $1.35/hr.
|
| 58 |
+
|
| 59 |
+
### SLA — Smooth Preventive Penalty (γ = 4.0)
|
| 60 |
+
|
| 61 |
+
```
|
| 62 |
+
sla = max(σ(latency, threshold=0.20, temp=0.03), σ(errors, threshold=0.05, temp=0.01))
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
Dual sigmoids with **max** operator — worst dimension dominates. Unlike binary penalties (0 or 1), the sigmoid provides **gradient before the violation**, enabling the agent to learn preventive scaling. Asymmetric temperatures reflect operational reality: latency degrades gradually (wide band), error rates spike sharply (narrow band).
|
| 66 |
+
|
| 67 |
+
### Barrier — Control-Barrier Function (δ = 0.1)
|
| 68 |
+
|
| 69 |
+
```
|
| 70 |
+
H(s) = Σ max(0, Q_i − 150)² / 10000
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
Zero below Q=150, quadratic above. Creates a **hard danger zone** near catastrophic failure (Q=200). Architecturally distinct from SLA: SLA says "pay attention," barrier says "act now or the node dies." Layered defense at different urgency levels.
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## Layer 3: Sigmoid Normalization
|
| 78 |
+
|
| 79 |
+
```
|
| 80 |
+
reward_01 = σ(raw_reward, midpoint=−3.0, temperature=2.0)
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
Maps raw reward (always negative) to [0, 1] for the LLM. The **midpoint = −3.0** centers the sigmoid where rewards actually cluster (≈ −1 to −8). Temperature = 2.0 gives visible per-action gradient:
|
| 84 |
+
|
| 85 |
+
| Action | Reward |
|
| 86 |
+
|--------|--------|
|
| 87 |
+
| Baseline NO_OP | ~0.72 |
|
| 88 |
+
| 1× SCALE_UP | ~0.54 |
|
| 89 |
+
| 2× SCALE_UP | ~0.29 |
|
| 90 |
+
| 3× SCALE_UP | ~0.14 |
|
| 91 |
+
| 4× SCALE_UP | ~0.04 |
|
| 92 |
+
|
| 93 |
+
The LLM can read the trend and adjust — each unnecessary scale-up is visibly worse.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## Layer 4: Action-Efficiency Penalties
|
| 98 |
+
|
| 99 |
+
**Cooldown** — Same action on same node within 3 ticks: `reward −= cooldown × 0.1`. Action still executes (emergencies aren't blocked), but the agent learns to wait for boot delay before re-scaling.
|
| 100 |
+
|
| 101 |
+
**Wasted action** (−0.05) — Rejected/invalid actions reduce reward immediately. The LLM sees the consequence in its very next step, not at episode end.
|
| 102 |
+
|
| 103 |
+
---
|
| 104 |
+
|
| 105 |
+
## Layer 5: Episode Grader
|
| 106 |
+
|
| 107 |
+
```
|
| 108 |
+
composite = 0.4·Uptime + 0.2·Stability + 0.4·Cost − invalid_penalty + bonus
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
| Dimension | Formula | Notes |
|
| 112 |
+
|-----------|---------|-------|
|
| 113 |
+
| **Uptime** | Fraction of ticks with SLA met | Latency ≤ 200ms, errors ≤ 5% |
|
| 114 |
+
| **Cost** | `exp(−3.0 × over_ratio)` | Exponential decay from baseline; 2× spend → score 0.05 |
|
| 115 |
+
| **Stability** | `1 / (1 + (avg_energy/2000)²)` | Inverse Lyapunov, no early saturation |
|
| 116 |
+
|
| 117 |
+
**Task-3 coupling** — Cost score zeroed if uptime < 50%. Prevents "cheap but dead" strategies.
|
| 118 |
+
|
| 119 |
+
**Prevention bonuses** (additive, no overlap with step reward):
|
| 120 |
+
- +0.10 zero VIP failures all episode
|
| 121 |
+
- +0.05 < 3 SLA violations all episode
|
| 122 |
+
- +0.05 zero invalid actions all episode
|
| 123 |
+
|
| 124 |
+
---
|
| 125 |
+
|
| 126 |
+
## Why This Is Innovative
|
| 127 |
+
|
| 128 |
+
1. **Dynamical systems, not threshold monitoring** — Lyapunov drift measures *direction of travel*, not just current state. The agent learns whether its actions move the cluster toward or away from equilibrium.
|
| 129 |
+
|
| 130 |
+
2. **Topology-aware energy** — The edge imbalance term captures parent-child queue mismatch that flat per-node metrics miss entirely.
|
| 131 |
+
|
| 132 |
+
3. **Baseline-anchored cost** — The three-tier model separates "infrastructure you already pay for" from "capacity you chose to add," preventing the reward from penalizing the default cluster state.
|
| 133 |
+
|
| 134 |
+
4. **Preventive, not reactive** — Smooth SLA sigmoids give gradient *before* violation. The agent learns the pre-scale window that boot delay demands, rather than waiting for alarms.
|
| 135 |
+
|
| 136 |
+
5. **Layered safety** — SLA + barrier = two-tier defense at different thresholds and urgencies. Not all danger is equally urgent.
|
| 137 |
+
|
| 138 |
+
6. **Action quality as a first-class signal** — Wasted actions, rapid re-scaling, and invalid commands produce immediate penalties. Prevents "spam SCALE_UP and hope."
|
| 139 |
+
|
| 140 |
+
7. **Simulator-to-K8s parity** — Every parameter has a real-world counterpart. DEFAULT_CAPACITY=3 = K8s replicas. Boot delay = pod startup time. Cost tiers = cloud pricing. Trained policies transfer to live infrastructure.
|
| 141 |
+
|
| 142 |
+
8. **Theoretical guarantee** — The reward structure instantiates Neely's Drift-Plus-Penalty optimization, providing formal guarantees of queue stability with bounded average cost. The agent implements a theoretically grounded control policy, not ad-hoc heuristics.
|
|
@@ -343,7 +343,13 @@ async def run_single_task(env: AntiAtroposEnv, client: AsyncOpenAI, task_id: str
|
|
| 343 |
history=history,
|
| 344 |
demo_text=demo_text,
|
| 345 |
)
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
grader.record(result.observation)
|
| 348 |
|
| 349 |
reward = float(result.reward or 0.0)
|
|
|
|
| 343 |
history=history,
|
| 344 |
demo_text=demo_text,
|
| 345 |
)
|
| 346 |
+
action_str = _compact_action(action)
|
| 347 |
+
print(f"[DEBUG] step={step} sending action={action_str}", flush=True)
|
| 348 |
+
try:
|
| 349 |
+
result = await env.step(action)
|
| 350 |
+
except RuntimeError as e:
|
| 351 |
+
print(f"[DEBUG] step={step} FAILED action={action_str} error={e}", flush=True)
|
| 352 |
+
raise
|
| 353 |
grader.record(result.observation)
|
| 354 |
|
| 355 |
reward = float(result.reward or 0.0)
|
|
@@ -419,7 +419,7 @@ class AntiAtroposEnvironment(Environment):
|
|
| 419 |
total_cost += baseline_cap * COST_PER_CAPACITY_UNIT_PER_HOUR
|
| 420 |
above_baseline = capacity - baseline_cap
|
| 421 |
justified = max(0, needed - baseline_cap) # excess that serves traffic
|
| 422 |
-
idle = above_baseline - justified
|
| 423 |
# Tier 2: needed excess at moderate rate (4× base)
|
| 424 |
total_cost += justified * (COST_PER_CAPACITY_UNIT_PER_HOUR * 4.0)
|
| 425 |
# Tier 3: idle excess at penalty rate (20× base)
|
|
|
|
| 419 |
total_cost += baseline_cap * COST_PER_CAPACITY_UNIT_PER_HOUR
|
| 420 |
above_baseline = capacity - baseline_cap
|
| 421 |
justified = max(0, needed - baseline_cap) # excess that serves traffic
|
| 422 |
+
idle = max(0, above_baseline - justified) # excess sitting idle (never negative)
|
| 423 |
# Tier 2: needed excess at moderate rate (4× base)
|
| 424 |
total_cost += justified * (COST_PER_CAPACITY_UNIT_PER_HOUR * 4.0)
|
| 425 |
# Tier 3: idle excess at penalty rate (20× base)
|