Spaces:
Sleeping
Sleeping
Divyansh Agrawal commited on
Upload folder using huggingface_hub
Browse files- .gitattributes +4 -0
- Blog.md +606 -0
- Experimental/ProofOfConcept.ipynb +0 -0
- README.md +124 -7
- images/Agent Actions.png +3 -0
- images/Cost comparison.png +0 -0
- images/DAG.svg +4 -0
- images/HF Grafana.png +3 -0
- images/RL_training_metrics.png +3 -0
- images/VM_Grafana.png +3 -0
- inference.py +1 -1
- logs/non_record_grpo_run/NON_RECORD_GRPO.log +0 -0
- logs/non_record_grpo_run/README.md +31 -0
- logs/submission_run/README.md +41 -0
- logs/submission_run/SUBMISSION_TRAINING_RUN.log +0 -0
- logs/submission_run/run_info.json +65 -0
- logs/submission_run/step_metrics.jsonl +0 -0
- training/config.yaml +1 -1
- training/launch_train.py +2 -2
.gitattributes
CHANGED
|
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
images/Agent[[:space:]]Actions.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
images/HF[[:space:]]Grafana.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
images/RL_training_metrics.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
images/VM_Grafana.png filter=lfs diff=lfs merge=lfs -text
|
Blog.md
ADDED
|
@@ -0,0 +1,606 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: "AntiAtropos: The Physics of Autonomous Infrastructure Control"
|
| 3 |
+
thumbnail: /blog/assets/antiatropos/thumbnail.png
|
| 4 |
+
authors:
|
| 5 |
+
- user: PranavKK
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
# AntiAtropos: The Physics of Autonomous Infrastructure Control
|
| 9 |
+
|
| 10 |
+
> **Hackathon Submission:** We are building for **"Theme #3: World Modelling for Professional Tasks."**
|
| 11 |
+
> AntiAtropos represents a leap in World Modeling for the SRE domain. By treating a microservice cluster as a physical environment with its own laws of motion—modeled via Lyapunov stability and fluid dynamics—we enable AI agents to "reason" through the physics of infrastructure, maintaining equilibrium where traditional heuristics fail.
|
| 12 |
+
|
| 13 |
+
### **Project Links**
|
| 14 |
+
* **Demo Video:** [youtu.be/46SX0HocpSs](https://youtu.be/46SX0HocpSs)
|
| 15 |
+
* **Live Space:** [keshav051-antiatropos.hf.space](https://keshav051-antiatropos.hf.space/)
|
| 16 |
+
* **Models & Training Logs:** [huggingface.co/Keshav051/antiatropos-qlora](https://huggingface.co/Keshav051/antiatropos-qlora)
|
| 17 |
+
* **Source Code:** [huggingface.co/Keshav051/AntiAtropos](https://huggingface.co/Keshav051/AntiAtropos)
|
| 18 |
+
|
| 19 |
+
## Table of Contents
|
| 20 |
+
- [The Problem](#the-problem-infrastructure-automation-cannot-solve)
|
| 21 |
+
- [Two Opposing Equations](#two-opposing-equations-one-equilibrium)
|
| 22 |
+
- [The Evolution: SFT to QLoRA](#the-evolution-from-sft-to-qlora)
|
| 23 |
+
- [Architecture: HF + VM](#the-dual-plane-architecture-hf--cloud-vm)
|
| 24 |
+
- [Reward Shaping](#how-the-reward-is-shaped)
|
| 25 |
+
- [The Road Not Taken: GRPO](#the-road-not-taken-grpo-experimentation)
|
| 26 |
+
- [Quick Start](#quick-start)
|
| 27 |
+
|
| 28 |
+
---
|
| 29 |
+
|
| 30 |
+
## The Problem Infrastructure Automation Cannot Solve
|
| 31 |
+
|
| 32 |
+
When a production service saturates at 3 AM, the sequence is predictable. Somewhere, an engineer wakes up, opens a terminal, and types `kubectl scale deployment --replicas=10`. Traffic gets rerouted. The alert clears. The engineer goes back to sleep.
|
| 33 |
+
|
| 34 |
+
In the best case, this workflow is partially automated. Horizontal Pod Autoscalers watch CPU and bump replica counts when a threshold crosses 80%. But thresholds are reactive and they fire after congestion has already built. They have no concept of boot delay, no awareness of topology, no ability to anticipate a surge before it arrives. And when an edge case appears — a side-channel burst, a cascading dependency failure — the automation falls silent and the pager goes off.
|
| 35 |
+
|
| 36 |
+
More importantly, **no existing system provides a mathematical guarantee**. There is no production autoscaler today that can prove it will keep queues bounded. There is no incident response tool that can prove its actions are cost-optimal. The industry runs on heuristics — rules of thumb encoded as YAML, battle-tested through painful outages, but ultimately ad-hoc.
|
| 37 |
+
|
| 38 |
+
AntiAtropos is the first infrastructure control environment to replace SRE heuristics with **RL-based physics control**. It models the cluster as a fluid queue network, defines equilibrium through a Lyapunov energy function, and trains a Qwen3.5-4B model via **QLoRA REINFORCE** to minimize a Drift-Plus-Penalty objective—producing a policy that keeps queues bounded at minimum cost.
|
| 39 |
+
|
| 40 |
+
### The Bottom Line: 50% Cost Savings at Perfect SLA
|
| 41 |
+
|
| 42 |
+
This is not theoretical. In head-to-head benchmarks against a production-grade heuristic autoscaler, AntiAtropos maintained **perfect SLA compliance** while spending **half the infrastructure cost**. The agent learned to scale predictively rather than reactively, eliminating the "panic-scaling" that traditional threshold-based systems rely on. By understanding the physics of the cluster, it anticipates load before it arrives.
|
| 43 |
+
|
| 44 |
+

|
| 45 |
+
*Figure 1: AntiAtropos (Blue) maintains stability at half the resource cost of a production-grade heuristic scaler (Orange).*
|
| 46 |
+
|
| 47 |
+
---
|
| 48 |
+
|
| 49 |
+
## Two Opposing Equations, One Equilibrium
|
| 50 |
+
|
| 51 |
+
Every natural system is governed by opposing forces. A pendulum swings because gravity and tension pull in different directions.
|
| 52 |
+
|
| 53 |
+
AntiAtropos is built on the same principle. Two equations — one pushing toward stability, the other pulling toward efficiency — work in tandem to define the agent's objective. Neither dominates. Neither is allowed to win alone. The system reaches equilibrium only when both are satisfied.
|
| 54 |
+
|
| 55 |
+
### The Arena: A Directed Acyclic Graph
|
| 56 |
+
|
| 57 |
+
Before the equations, the structure they operate on. AntiAtropos models a 5-node microservice cluster as a **directed acyclic graph (DAG)** — the standard topology of real service dependencies. Nodes are services. Edges are request flows from upstream to downstream.
|
| 58 |
+
|
| 59 |
+
The cluster has five services with a fixed dependency chain:
|
| 60 |
+
|
| 61 |
+
- **node-0** (Payment Gateway — VIP, 2× importance): the primary ingress. Splits its outflow evenly between node-1 and node-2.
|
| 62 |
+
- **node-1** (Checkout Service): a leaf node receiving half of node-0's traffic.
|
| 63 |
+
- **node-2** (Catalog Service): receives the other half of node-0's traffic and passes everything downstream to node-3.
|
| 64 |
+
- **node-3** (Inventory Service): depends entirely on node-2.
|
| 65 |
+
- **node-4** (Auth Service): an independent ingress with no downstream dependencies.
|
| 66 |
+
|
| 67 |
+
External traffic enters at two points — node-0 and node-4 — splitting the cluster-wide load equally. Every tick, the simulator traverses the graph in topological order (Kahn's BFS), computing each node's actual outflow as the minimum of incoming traffic and available service rate, then routing that outflow to downstream children according to fixed edge weights. A failed node has service rate zero, so its outflow stops and its children starve — a causal failure chain the agent must learn to route around.
|
| 68 |
+
|
| 69 |
+

|
| 70 |
+
|
| 71 |
+
This topology is the substrate for every feature that follows. The edge imbalance term, backpressure propagation, cascading failure detection, and upstream pressure signals all derive their meaning from this specific graph structure.
|
| 72 |
+
|
| 73 |
+
### The First Equation: Lyapunov Graph Energy
|
| 74 |
+
|
| 75 |
+
This is the **stabilizing force**. It measures how much disorder has accumulated in the cluster and how unevenly it is distributed across the topology.
|
| 76 |
+
|
| 77 |
+
$$ V_{\text{graph}}(s) = \sum w_i \cdot Q_i^2 + \lambda_{\text{edge}} \cdot \sum |Q_i - Q_j| $$
|
| 78 |
+
|
| 79 |
+
**Node energy ($\sum w_i \cdot Q_i^2$).** Each node's queue depth is squared, then weighted by its business criticality — the payment gateway (node-0) carries $2\times$ gravity. Squaring penalizes concentration: one node at queue depth $100$ contributes the same energy as five nodes at depth $20$. The system cares about how load clusters, not just how much exists.
|
| 80 |
+
|
| 81 |
+
**Edge imbalance ($\sum |Q_i - Q_j|$).** This is the term that makes the energy function graph-aware. If node-0 is drowning in requests but node-1 — its direct child — is completely idle, per-node metrics see nothing wrong with node-1. The edge term sees the mismatch and fires. It tells the agent: the problem is not node-0's capacity; the problem is the distribution of traffic across the topology.
|
| 82 |
+
|
| 83 |
+
Together, the node and edge terms form a single scalar that captures the **entire structural health of the cluster** — both how much load exists and how poorly or well it is spread across the service graph.
|
| 84 |
+
|
| 85 |
+
### The Second Equation: Drift-Plus-Penalty (Neely Framework)
|
| 86 |
+
|
| 87 |
+
This is the **efficiency force**. It pulls against the first equation by demanding that stability be achieved at minimum cost.
|
| 88 |
+
|
| 89 |
+
$$ R(t) = -(\alpha \cdot \Delta V + \beta \cdot \text{Cost} + \gamma \cdot \text{SLA} + \delta \cdot \text{Barrier}) $$
|
| 90 |
+
|
| 91 |
+
The four terms represent four separate, sometimes competing, demands on the agent:
|
| 92 |
+
|
| 93 |
+
- **$\Delta V$ — Lyapunov Drift ($\alpha = 0.002$).** The one-tick change in cluster energy. Negative drift means the system moved toward equilibrium; positive drift means it destabilized. The coefficient is deliberately small — it is a directional nudge, grading the agent on *where the system is going*, not just where it sits.
|
| 94 |
+
|
| 95 |
+
- **Cost — Three-Tier Infrastructure Model ($\beta = 1.5$).** Not all capacity costs the same. Baseline capacity at $3$ replicas is already provisioned — a sunk cost charged at the cheap base rate. Capacity the agent adds above baseline to serve real traffic costs $4\times$ more — defensible. Capacity sitting idle above what traffic actually needs costs **$20\times$ more** — pure operational waste. This prevents the trivial policy of "scale everything to max."
|
| 96 |
+
|
| 97 |
+
- **SLA — Dual Sigmoid Gradients ($\gamma = 4.0$).** Traditional SRE tools use binary thresholds: latency below $200$ ms is fine, above $200$ ms is a violation. This gives zero gradient *before* the threshold — the agent has no signal to act preventively. AntiAtropos replaces the binary cliff with two smooth sigmoids, one for latency and one for error rate. The curve provides measurable feedback well before a violation occurs, giving the agent time to scale ahead of the surge.
|
| 98 |
+
|
| 99 |
+
- **Barrier — Control-Barrier Function ($\delta = 0.1$).** Architecturally distinct from the SLA term. The sigmoid says "pay attention — you are approaching a problem." The barrier fires quadratically when any node exceeds queue depth $150$, creating a hard danger zone that says "act now or this node collapses."
|
| 100 |
+
|
| 101 |
+
### How They Balance
|
| 102 |
+
|
| 103 |
+
Imagine an agent that only cares about stability. It scales every node to maximum capacity at the first sign of traffic. Queues stay empty. Energy stays low. But the cost term destroys the reward — idle capacity at $20\times$ rates. The agent learns to scale down.
|
| 104 |
+
|
| 105 |
+
Now imagine an agent that only cares about cost. It never scales up. Queues accumulate. The drift term goes sharply positive. Nodes approach the barrier ceiling. Then the SLA sigmoid ramps. The agent learns to scale up — but only when the gradient signals it is necessary and only to the degree the traffic demands.
|
| 106 |
+
|
| 107 |
+
The opposing equations create a **domain of viable operation** — a region in the space of (stability, cost, latency) where neither force dominates. The agent is trained to find and stay inside that region.
|
| 108 |
+
|
| 109 |
+
This is the Drift-Plus-Penalty framework, formalized by Neely et al. [1] for stochastic network optimization. The theory provides a mathematical guarantee: minimizing this expression at every step produces a policy that keeps queues bounded with minimum average cost. The agent is not learning ad-hoc heuristics. It is learning a control policy with provable stability properties.
|
| 110 |
+
|
| 111 |
+
### The Evolution: From SFT to QLoRA
|
| 112 |
+
The path to autonomous SRE was not linear. We initially experimented with **Supervised Fine-Tuning (SFT)**, using the scripts found in `Experimental/ProofOfConcept.ipynb` to teach the model basic "if-then" operational logic from a dataset of expert rollouts.
|
| 113 |
+
|
| 114 |
+
However, we quickly hit the ceiling of supervised learning: an agent can only be as good as its teacher. To achieve true **autonomous stability**, we moved to a Reinforcement Learning paradigm using **QLoRA Reinforce**. This allowed us to leverage high-rank (64) adapters for deep reasoning while keeping the model memory-efficient enough to run 60-step rollouts on a single GPU. The transition from mimicking experts to **minimizing the Lyapunov Energy Function** is what allowed the agent to discover novel stabilization strategies that a human operator would miss.
|
| 115 |
+
|
| 116 |
+

|
| 117 |
+
*Figure 2: QLoRA Training Progress — showing the steady rise in episodic reward as the agent learns to govern the cluster's Lyapunov energy.*
|
| 118 |
+
|
| 119 |
+

|
| 120 |
+
*Figure 3: Strategy Evolution — The model learns to prioritize dynamic Rerouting and proactive Scaling, moving away from passive observation (NO_OP) as training converges.*
|
| 121 |
+
|
| 122 |
+
---
|
| 123 |
+
|
| 124 |
+
## The Dual-Plane Architecture: HF + Cloud VM
|
| 125 |
+
Our observability stack is split into a **Dual-Control Plane** to balance high-fidelity simulation with real-world infrastructure monitoring.
|
| 126 |
+
|
| 127 |
+
1. **The Simulation & Grading Plane (Hugging Face):**
|
| 128 |
+
The heavy lifting of QLoRA Reinforce training and the primary OpenEnv simulator runs on Hugging Face. This plane exposes a **FastAPI server** that handles the complex reward calculations, Lyapunov energy tracking, and advantage normalization.
|
| 129 |
+
|
| 130 |
+

|
| 131 |
+
*Figure 4: The HF-side telemetry focuses on the "Brain"—tracking how the agent's reward correlates with the draining of systemic potential energy ($V$).*
|
| 132 |
+
|
| 133 |
+
2. **The Infrastructure Plane (Cloud VM):**
|
| 134 |
+
For real-world validation, we maintain a lighter control plane on a dedicated Cloud VM running a **Live K8s Cluster**. This environment is instrumented with **Prometheus and Grafana** to capture raw node observability: CPU saturation, pod restart counts, and network jitter.
|
| 135 |
+
|
| 136 |
+

|
| 137 |
+
*Figure 5: The VM-side telemetry tracks the "Body"—monitoring the physical health of the containers being governed by the agent.*
|
| 138 |
+
|
| 139 |
+
This hybrid approach ensures that the agent is not just "playing a game" in a simulator, but is being trained on a world model that has 1:1 parity with production-grade monitoring stacks.
|
| 140 |
+
|
| 141 |
+
---
|
| 142 |
+
|
| 143 |
+
## How the Reward Is Shaped
|
| 144 |
+
|
| 145 |
+
The reward is not a single monolithic number. It is computed through a multi-stage pipeline where each stage transforms raw cluster physics into progressively more informative learning signals.
|
| 146 |
+
|
| 147 |
+
### Stage 1: Compute Lyapunov Graph Energy
|
| 148 |
+
|
| 149 |
+
At every tick, the simulator computes $V_{\text{graph}}(s)$ from ground-truth node states — the weighted squared queues plus the edge imbalance penalty. This scalar captures the total structural disorder in the cluster. A rising $V_{\text{graph}}$ means the system is destabilizing; a falling $V_{\text{graph}}$ means the agent's actions are working.
|
| 150 |
+
|
| 151 |
+
### Stage 2: Compute $\Delta V$ — Lyapunov Drift
|
| 152 |
+
|
| 153 |
+
The one-step change in energy:
|
| 154 |
+
|
| 155 |
+
$$ \Delta V(t) = V_{\text{graph}}(s_t) - V_{\text{graph}}(s_{t-1}) $$
|
| 156 |
+
|
| 157 |
+
Negative drift is good — energy decreased, stability improved. Positive drift is a warning: the system is sliding away from equilibrium. The critical insight: the agent is graded on **direction of travel**, not absolute position. Two clusters can have identical queue depths, but the one with negative drift is on a healing trajectory.
|
| 158 |
+
|
| 159 |
+
### Stage 3: Compute Cost — Three-Tier Infrastructure Economics
|
| 160 |
+
|
| 161 |
+
The cost function separates capacity into three buckets:
|
| 162 |
+
|
| 163 |
+
$$ \text{needed} = \lceil \text{incoming\_request\_rate} / 15 \rceil $$
|
| 164 |
+
$$ \text{cost} = \text{baseline\_at\_cheap\_rate} + (\text{needed} - \text{baseline}) \times 4\times + (\text{active} - \text{needed}) \times 20\times $$
|
| 165 |
+
|
| 166 |
+
Capacity at or below $DEFAULT\_CAPACITY$ ($3$ replicas) is charged at the base rate — it is already provisioned, a sunk cost. Agent-added capacity that serves real traffic costs $4\times$ the base rate — justified. Active capacity sitting idle above what traffic needs costs **$20\times$** the base rate — pure operational waste. Pending capacity (still booting) is always charged at the justified tier — the agent cannot control boot delay and should not be penalized for it.
|
| 167 |
+
|
| 168 |
+
### Stage 4: Compute SLA — Smooth Dual Sigmoids
|
| 169 |
+
|
| 170 |
+
Latency and error rates are passed through two independent sigmoid functions, then the maximum is taken:
|
| 171 |
+
|
| 172 |
+
$$ \text{sla\_penalty} = \max\left( \frac{1}{1 + e^{-(\text{lat\_norm} - 0.20) / 0.03}}, \frac{1}{1 + e^{-(\text{err\_rate} - 0.05) / 0.01}} \right) $$
|
| 173 |
+
|
| 174 |
+
Each sigmoid produces a smooth value in $[0, 1]$. When latency is comfortably below $200$ ms ($lat_{\text{norm}} < 0.20$), the penalty is near zero. As latency approaches the threshold, the penalty rises continuously. The temperature parameters ($0.03$ for latency, $0.01$ for errors) control how sharp the transition is — smaller values create steeper curves that give stronger gradient near the boundary.
|
| 175 |
+
|
| 176 |
+
This is fundamentally different from binary SLA counters. A counter increments by $1$ when latency crosses $200$ ms — the gradient is zero until the moment of violation. The smooth sigmoid provides **preventive gradient** — the agent senses the metric approaching the boundary and adjusts before the violation occurs.
|
| 177 |
+
|
| 178 |
+
### Stage 5: Compute the Barrier Penalty
|
| 179 |
+
|
| 180 |
+
The Control-Barrier Function fires separately from SLA:
|
| 181 |
+
|
| 182 |
+
$$ \text{barrier} = \sum \max(0, Q_i - 150)^2 $$
|
| 183 |
+
|
| 184 |
+
This is zero for all queues below $150$. Above $150$, it grows quadratically. The architectural separation between SLA and barrier is intentional:
|
| 185 |
+
|
| 186 |
+
- **SLA sigmoid** says: "pay attention — you are approaching suboptimal performance."
|
| 187 |
+
- **Barrier function** says: "act now — this node is about to collapse."
|
| 188 |
+
|
| 189 |
+
The barrier fires closer to catastrophic failure ($Q = 200$), creating a distinct urgency gradient that the agent must learn to distinguish from normal SLA pressure.
|
| 190 |
+
|
| 191 |
+
### Stage 6: Combine into Raw Reward
|
| 192 |
+
|
| 193 |
+
All four terms are combined into a single scalar:
|
| 194 |
+
|
| 195 |
+
$$ \text{raw\_reward} = -(0.002 \times \Delta V + 1.5 \times \text{Cost} + 4.0 \times \text{SLA} + 0.1 \times \text{Barrier}_{\text{normalized}}) $$
|
| 196 |
+
|
| 197 |
+
At baseline — a stable 5-node cluster with no SLA violations and no drift — the raw reward sits around **−1 to −3**, dominated by the cost term ($\beta \times \$0.75/hr$ baseline $\approx -1.1$). It deepens toward **−6 to −12** as SLA pressure builds, queues accumulate, or the agent wastes capacity. The weight tuning is deliberate:
|
| 198 |
+
|
| 199 |
+
- **$\Delta V$ at $0.002$** — a directional nudge, not a sledgehammer. Stability drift should inform, not dominate.
|
| 200 |
+
- **Cost at $1.5$** — makes over-provisioning painful enough that the agent cannot ignore it.
|
| 201 |
+
- **SLA at $4.0$** — the strongest term. SLA violations matter more than either drift or cost individually.
|
| 202 |
+
- **Barrier at $0.1$** — after normalization, comparable in magnitude to drift.
|
| 203 |
+
|
| 204 |
+
### Stage 7: Sigmoid Normalization to [0, 1]
|
| 205 |
+
|
| 206 |
+
The raw reward is not directly usable for the LLM — negative numbers are harder to interpret and compare across episodes. We map it through a sigmoid:
|
| 207 |
+
|
| 208 |
+
$$ \text{normalized\_reward} = \frac{1}{1 + e^{-(\text{raw\_reward} - \text{midpoint}) / \text{temperature}}} $$
|
| 209 |
+
|
| 210 |
+
With midpoint = $−6.0$ and temperature = $2.0$, a healthy NO-OP ($raw \approx -1$ to $-3$) maps to a normalized reward near $0.8$, a single wasteful SCALE_UP pulls it toward $0.6$, and a cluster approaching the barrier threshold ($raw \approx -12$) collapses near $0.0$. The LLM sees these values alongside the observation text and learns to associate specific action patterns with specific reward ranges — each unnecessary scale-up is visibly worse than the last.
|
| 211 |
+
|
| 212 |
+
### Stage 8: Post-Processing Penalties
|
| 213 |
+
|
| 214 |
+
Two final adjustments are applied to the normalized reward:
|
| 215 |
+
|
| 216 |
+
- **Cooldown penalty** — scaling the same node within 3 ticks subtracts up to 0.1 from the reward. The action still executes (emergencies must not be blocked), but the agent receives negative reinforcement for oscillatory behavior.
|
| 217 |
+
- **Invalid-action penalty** — attempting SHED_LOAD on a critical node subtracts 0.05. The validator hard-rejects the action first; this penalty ensures the agent sees the rejection in its reward history.
|
| 218 |
+
|
| 219 |
+
Most RL environments compute reward in a single step: state → reward. AntiAtropos decomposes it into eight stages because each stage teaches a distinct concept:
|
| 220 |
+
|
| 221 |
+
This decomposition is not arbitrary. It mirrors the way a human SRE reasons about an incident: "Is the situation getting worse? What am I spending? How close am I to violating SLA? Is any service about to die?" AntiAtropos encodes each of these questions into a separate, tunable, inspectable stage of the reward pipeline.
|
| 222 |
+
|
| 223 |
+
---
|
| 224 |
+
|
| 225 |
+
## The Realism of Interactions
|
| 226 |
+
|
| 227 |
+
Physics gives the agent its objective. But to learn that objective, the agent must interact with an environment that behaves like a real production cluster. AntiAtropos models the following features — each drawn from operational reality, each contributing gradient signal that shapes learned behavior.
|
| 228 |
+
|
| 229 |
+
### Delay and Finite Deployment Time
|
| 230 |
+
|
| 231 |
+
Scaling in Kubernetes is not instantaneous. New pods take $30$-$90$ seconds to pull images, start containers, and register with the load balancer. AntiAtropos models this as a **$5$-tick boot delay**. A SCALE_UP command adds capacity to a pending queue; that capacity only becomes active five ticks later.
|
| 232 |
+
|
| 233 |
+
This creates a fundamental requirement for **predictive action**. The agent cannot wait until queues are overflowing to scale — by the time the new capacity boots, the node has already failed. It must read the early signal in its observations and act before the surge arrives.
|
| 234 |
+
|
| 235 |
+
### Jitter and Non-Determinism
|
| 236 |
+
|
| 237 |
+
Production traffic is never perfectly predictable. AntiAtropos introduces **jitter** into every critical event. The Task 3 surge has a nominal window of $10$ ticks, but each episode randomizes the start and end points by $\pm 10$ ticks. The domain parameters — initial load, ramp slope, failure timing — are randomized per episode using a configurable seed. The agent cannot memorize timing; it must learn to detect the *signal* of an impending event from the cluster's state.
|
| 238 |
+
|
| 239 |
+
### Sensor Noise and Partial Observability
|
| 240 |
+
|
| 241 |
+
Real observability pipelines are imperfect. Metrics scrape intervals, network delays, and transient sensor failures all contribute noise. AntiAtropos introduces a **$5\%$ sensor dropout probability**: on any given tick, any node's reported queue depth may read as zero and its latency may report $-1.0$ — simulating a failed scrape or lost sample. The agent must learn to operate under partial information, cross-referencing adjacent nodes and recent history to infer the true state.
|
| 242 |
+
|
| 243 |
+
### Backpressure Propagation
|
| 244 |
+
|
| 245 |
+
A node is not an island. When a downstream service is congested, it cannot accept forwarded requests fast enough — and that congestion travels backward through the Directed acyclic graph (DAG). AntiAtropos models **backpressure**: when any child node's queue exceeds the backpressure threshold ($Q > 60$), its parent's effective service rate is reduced proportionally, up to a cap of $40\%$.
|
| 246 |
+
|
| 247 |
+
This means the health of any node affects every node upstream of it. A problem at node-3 eventually starves node-2, which radiates back to node-0. The agent must see the graph as a connected system, not a collection of independent services.
|
| 248 |
+
|
| 249 |
+
### Cascading Failure Propagation
|
| 250 |
+
|
| 251 |
+
In a real incident, one node's failure can cascade — the remaining nodes absorb its traffic, become overwhelmed, and fail in sequence. AntiAtropos implements **bounded cascade detection**: when a node fails from overload ($queue > 200$), adjacent nodes (upstream parents and downstream children) are monitored for a window of $3$ ticks. If an adjacent node also approaches the failure threshold during that window, it degrades. The cascade does not spread infinitely — it is bounded to one graph hop — but it captures the precise chaining dynamic that SRE teams fear during high-severity incidents.
|
| 252 |
+
|
| 253 |
+
### Priority Queues and Business Criticality
|
| 254 |
+
|
| 255 |
+
Not every node matters equally. The payment gateway (node-0) carries **double the importance weight** of any other service. Its queue growth contributes twice as much to the Lyapunov energy. Its failure counts as a separate VIP failure metric. SHED_LOAD — the action that drops a percentage of incoming traffic — is **hard-blocked** on critical nodes. The agent receives immediate rejection for attempting it.
|
| 256 |
+
|
| 257 |
+
These guardrails are not learned. They are designed into the action validator as safety constraints, mirroring the production guardrails that real platform engineering teams enforce: you cannot shed the payment gateway. Ever.
|
| 258 |
+
|
| 259 |
+
### Edge Traversal and Topological Routing
|
| 260 |
+
|
| 261 |
+
Request flow is not random. It follows a specific directed acyclic graph (DAG) with fixed edges: node-0 pipes half its outflow to node-1 and half to node-2; node-2 pipes all of its outflow to node-3. Every tick, the simulator traverses this graph in **topological order** (computed via Kahn's BFS), processing parent nodes before their children, computing each node's outflow as min(incoming_traffic, service_rate) and routing it downstream via the edge weights.
|
| 262 |
+
|
| 263 |
+
This models the precise path a request takes through a microservice architecture. A failed node has $service\_rate = 0$, so its outflow stops and its children are starved — a causal failure chain the agent must learn to route around using REROUTE_TRAFFIC.
|
| 264 |
+
|
| 265 |
+
### Hockey-Stick Latency (M/M/1 Queueing)
|
| 266 |
+
|
| 267 |
+
Latency is not linear. Under light load, increasing traffic by 10% produces negligible latency growth. But as utilization crosses 90%, latency enters the **hockey-stick region** — small increases in traffic produce exponential growth in response time.
|
| 268 |
+
|
| 269 |
+
AntiAtropos uses a hybrid model:
|
| 270 |
+
|
| 271 |
+
$$ \text{latency} = \frac{\text{BASE\_LATENCY}}{1 - \text{utilization}} + \text{queue\_depth} \times \text{STEEPNESS} $$
|
| 272 |
+
|
| 273 |
+
The first term is the M/M/1 queuing formula — it spikes exponentially as $utilization$ approaches $1.0$. The second term ensures that even at the capped $utilization$ of $0.99$, the raw $queue\_backlog$ still contributes meaningful signal. Without this dual structure, the agent would see flattening latency at the cap and lose gradient for further action.
|
| 274 |
+
|
| 275 |
+
### Cooldown and Action Discipline
|
| 276 |
+
|
| 277 |
+
Rapid repeated scaling is destabilizing in production — it causes pod churn, wastes scheduler cycles, and indicates a policy that oscillates rather than stabilizes. AntiAtropos enforces a **soft cooldown**: if the agent issues a SCALE_UP or SCALE_DOWN on the same node within $3$ ticks, the action still executes (emergency scaling must not be blocked), but a linear penalty is subtracted from the normalized reward. The agent learns to scale decisively and then wait.
|
| 278 |
+
|
| 279 |
+
### Shed Decay and Reroute Persistence
|
| 280 |
+
|
| 281 |
+
SHED_LOAD and REROUTE_TRAFFIC are not permanent commands. A shed fraction decays by $50\%$ per tick, reaching zero within a few ticks unless the agent re-issues it. Similarly, reroute weights decay by $50\%$ per tick. This forces the agent to maintain active control — it cannot set a routing override once and walk away. If the reroute is necessary, the agent must keep issuing it.
|
| 282 |
+
|
| 283 |
+
---
|
| 284 |
+
|
| 285 |
+
## The Cluster as a Graph
|
| 286 |
+
|
| 287 |
+
All of the features above — edge imbalance, backpressure propagation, cascading failures, topological routing, upstream pressure signals — operate on a single connected structure: the **service dependency graph**.
|
| 288 |
+
|
| 289 |
+
AntiAtropos represents the cluster as a **directed acyclic graph** with labeled weighted edges, node-specific features, and graph-wide energy. Each observation includes not only per-node metrics but also:
|
| 290 |
+
|
| 291 |
+
- **upstream_nodes** — which parents feed this service
|
| 292 |
+
- **downstream_nodes** — which children depend on this service
|
| 293 |
+
- **upstream_pressure** — the mean queue depth of all parents, normalized
|
| 294 |
+
- **outflow_rate** — the actual requests per tick flowing out on each edge
|
| 295 |
+
- **queue_delta** — per-node change from the previous tick
|
| 296 |
+
- **sla_proximity** — how close each individual node is to violating its SLA
|
| 297 |
+
- **node_reward** — per-node reward decomposition for credit assignment
|
| 298 |
+
|
| 299 |
+
This means the model receives a **structured graph observation**, not a flat list of metrics. It sees the relationships. It sees which nodes are connected. It learns that when node-2's outflow to node-3 drops to zero, node-3 will starve unless traffic is rerouted.
|
| 300 |
+
|
| 301 |
+
For the model that operates on this observation, the cluster is effectively a **graph-structured input** — a set of node representations with edge connectivity. While the current architecture uses a transformer-based LLM, the observation format is explicitly structured to support graph-native architectures in future iterations. The edge imbalance term in the Lyapunov graph energy ensures that intermediate rewards already carry topological signal.
|
| 302 |
+
|
| 303 |
+
Real production systems are not independent servers. They are interdependent networks — databases feeding APIs feeding frontends, with dependencies that create both throughput and fragility. AntiAtropos is the only training environment that models infrastructure this way by design, not as an afterthought.
|
| 304 |
+
|
| 305 |
+
---
|
| 306 |
+
|
| 307 |
+
## Complete Feature Registry
|
| 308 |
+
|
| 309 |
+
The following features are active in the current simulator and reward function. Each contributes measurable gradient signal to the agent's learning trajectory.
|
| 310 |
+
|
| 311 |
+
| # | Feature | What It Models | Effect on Learning |
|
| 312 |
+
|---|---------|---------------|-------------------|
|
| 313 |
+
| 1 | **Lyapunov Graph Energy** | Structural disorder across the entire DAG, including edge-level imbalance | Agent learns to balance load across topology, not just minimize individual queues |
|
| 314 |
+
| 2 | **Drift-Plus-Penalty Reward** | Neely framework: opposes stability against cost at every step | Agent cannot trivially over-provision; must find minimum-cost stability |
|
| 315 |
+
| 3 | **$5$-Tick Boot Delay** | Kubernetes pod cold-start latency modeled as pending capacity queue | Agent must scale predictively; reactive scaling fails by design |
|
| 316 |
+
| 4 | **Jitter ($\pm 10$ ticks)** | Surge window randomized per episode | Agent cannot memorize event timing; must detect signal from cluster state |
|
| 317 |
+
| 5 | **Sensor Dropout ($5\%$)** | Random zero/invalid readings simulate scrape failures | Agent learns to operate under partial observability using context |
|
| 318 |
+
| 6 | **Hockey-Stick Latency** | Dual M/M/1 + backlog model prevents gradient flattening near saturation | Agent receives continuous gradient even at extreme utilization |
|
| 319 |
+
| 7 | **Backpressure Propagation** | Children overload → parent service-rate reduction (up to $40\%$ cap) | Agent respects downstream congestion as a causal constraint on upstream nodes |
|
| 320 |
+
| 8 | **Cascading Failure Detection** | Bounded $3$-tick window with graph-hop propagation | Agent sees threat of sequential failure chain during high-stress incidents |
|
| 321 |
+
| 9 | **Business-Critical Priority Weights** | VIP nodes have $2\times$ contribution to Lyapunov energy and SLA measurements | Agent preferentially protects critical services over auxiliary nodes |
|
| 322 |
+
| 10 | **Action Safety Guardrails** | SHED_LOAD hard-blocked on critical nodes; NO_OP always valid | Agent cannot find trivial shortcuts; safety constraints mirror production policy |
|
| 323 |
+
| 11 | **Cooldown Penalties** | Soft penalty for re-scaling same node within $3$ ticks | Agent learns to scale decisively and wait; discourages oscillatory policies |
|
| 324 |
+
| 12 | **Shed & Reroute Decay** | Effects decay $50\%$ per tick unless re-issued | Agent must maintain active control; cannot set-and-forget routing overrides |
|
| 325 |
+
| 13 | **Topological Traffic Routing** | DAG traversal in Kahn order ensures correct parent→child outflow | Agent sees causal dependency chains in observation structure |
|
| 326 |
+
| 14 | **Edge Imbalance Term** | Lyapunov includes $\sum |parent\_queue - child\_queue|$ | Agent receives gradient about load *distribution*, not just load *magnitude* |
|
| 327 |
+
| 15 | **Three-Tier Cost Model** | Baseline (sunk), Justified ($4\times$ base), Idle Waste ($20\times$ base) | Agent distinguishes between paid-for capacity and chosen waste |
|
| 328 |
+
| 16 | **Smooth SLA Sigmoids** | Dual sigmoid replaces binary threshold with continuous gradient | Agent receives preventive signal before violation, enabling pre-scale behavior |
|
| 329 |
+
| 17 | **Control-Barrier Function** | Quadratic penalty fires above $Q=150$, architecturally separate from SLA | Agent faces hard danger zone distinct from warning zone; urgency escalates correctly |
|
| 330 |
+
| 18 | **Per-Node Reward Decomposition** | Credit assignment broken out per node in observation | Agent learns which nodes are responsible for reward changes |
|
| 331 |
+
| 19 | **Domain Randomization** | All task parameters randomized per episode via configurable seed | Policy trained for robustness, not memorization of specific settings |
|
| 332 |
+
| 20 | **Multi-Mode Operation** | SIMULATED → HYBRID → LIVE → AWS deployment modes | Same policy transitions from training to production with zero retraining |
|
| 333 |
+
| 21 | **Live Kubernetes Bridge** | `kubernetes_executor.py` maps actions to real `kubectl` commands | Trained policies drive actual infrastructure; environment-to-production gap closed |
|
| 334 |
+
|
| 335 |
+
---
|
| 336 |
+
|
| 337 |
+
## Three Tasks, Progressive Challenge
|
| 338 |
+
|
| 339 |
+
These tasks are not separate scenarios. They form a **curriculum** designed to teach an agent progressively more complex operational reasoning.
|
| 340 |
+
|
| 341 |
+
- **Task 1 — Capacity Ramp.** Linear load growth from near-saturation. The agent must scale predictively ahead of the ramp, not reactively after queues build. Boot delay makes this a timing problem, not a raw capacity problem. A successful policy scales up early and scales down during idle windows — demonstrating both capacity management and cost discipline.
|
| 342 |
+
|
| 343 |
+
- **Task 2 — Fault Tolerance.** At a randomized tick, a non-critical node fails permanently. Its service rate drops to zero, inflow is fully dropped, and its downstream children are starved. The trained agent learns to detect this failure by noticing the queue collapse and outflow cessation, then issues REROUTE_TRAFFIC to shift the parent's traffic split away from the dead child, followed by targeted SCALE_UP to absorb the displaced load elsewhere — the exact sequence an on-call SRE would execute during a live incident.
|
| 344 |
+
|
| 345 |
+
- **Task 3 — Surge Stability.** A $60$ req/tick surge arrives directly at node-1 and node-2 from a side channel that bypasses the primary ingress. The surge is periodic but jittered — the agent cannot predict timing from history alone. SHED_LOAD is permitted on surge nodes (they are not critical), but the correct response is precision scaling — the agent must read the early queue buildup and pre-scale before the surge peak. This is the hardest problem in the suite and the closest analog to a real high-severity production incident.
|
| 346 |
+
|
| 347 |
+
---
|
| 348 |
+
|
| 349 |
+
## Training Architecture
|
| 350 |
+
|
| 351 |
+
We trained a **Qwen3.5-4B** model with **QLoRA** (rank-$64$, $7$ target modules, $\sim$1.6\% trainable parameters) using **REINFORCE with baseline** as the loss function. Training ran on a single NVIDIA A10G GPU ($24$ GB VRAM) at approximately $\$0.34$/hr on Hugging Face Jobs. The full training run was configured as:
|
| 352 |
+
|
| 353 |
+
| Parameter | Value | Rationale |
|
| 354 |
+
|-----------|-------|----------|
|
| 355 |
+
| Base model | Qwen/Qwen3.5-4B | Strong reasoning / code generation for structured JSON actions |
|
| 356 |
+
| LoRA rank | $64$ | $+83$ MiB vs rank-$32$ — negligible cost, meaningfully more expressiveness |
|
| 357 |
+
| Target modules | q, k, v, o, gate, up, down projections | Full attention + FFN adaptation |
|
| 358 |
+
| Loss function | REINFORCE + baseline | No value head needed; running-mean baseline reduces variance |
|
| 359 |
+
| Iterations | $500$ | Converged by iteration $\sim$300; remaining iterations refine edge behaviors |
|
| 360 |
+
| Episodes per iteration | $6$ | $2 \times$ tasks ensures curriculum balance each iteration |
|
| 361 |
+
| Max steps per episode | $20$ | Enough for failure to occur + recovery, keeps iteration time manageable |
|
| 362 |
+
| Learning rate | $1.0 \times 10^{-5}$ | Conservative for LoRA on 4-bit base; avoids catastrophic forgetting |
|
| 363 |
+
| Sequence length | $768$ | System prompt + observation + action + template $\leq 640$ peak; $20\%$ headroom |
|
| 364 |
+
| Generation temperature | $0.85$ | Encourages exploration diversity during policy gradient rollout |
|
| 365 |
+
| Entropy coefficient | $0.001$ | Per-token entropy bonus; prevents policy from collapsing to deterministic too early |
|
| 366 |
+
|
| 367 |
+
### Co-Located Simulator Architecture
|
| 368 |
+
|
| 369 |
+
The simulator runs as a **local FastAPI server** inside the same HF Job container on CPU only, while the GPU is fully dedicated to model forward/backward passes. This eliminates network latency between action generation and environment feedback — each HTTP call resolves to `localhost:8000` at sub-millisecond latency.
|
| 370 |
+
|
| 371 |
+
### Curriculum-Balanced Iterations
|
| 372 |
+
|
| 373 |
+
Each iteration collects $6$ episodes — $2$ per task — ensuring the gradient signal covers all three curriculum stages evenly. Without this balance, the agent would overfit to Task 1 (the simplest) and fail to generalize to failures or surges. Task rotation is deterministic within an iteration but seeds are randomized per episode, so the agent never sees the same sequence of cluster states twice.
|
| 374 |
+
|
| 375 |
+
> **Reference Run:** All artifacts from this training — checkpoints, metrics logs, plots, and evaluation results — are available on the Hugging Face Hub at [Keshav051/antiatropos-qlora/run_0011](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/run_0011). This is the **canonical REINFORCE run** that produced the results shown in this blog. The `logs/` directory in the project repository also contains local copies of these artifacts.
|
| 376 |
+
|
| 377 |
+
---
|
| 378 |
+
|
| 379 |
+
## The RL Training Pipeline
|
| 380 |
+
|
| 381 |
+
### Step 1: Parallel Episode Rollouts (Batched Generation)
|
| 382 |
+
|
| 383 |
+
The most expensive operation in training is LLM generation — each forward pass over $36$ layers with KV-cache. Running $6$ episodes sequentially would require $6 \times 20 = 120$ serial forward passes per iteration. Instead, we batch all active episodes' observations into a **single left-padded forward pass**:
|
| 384 |
+
|
| 385 |
+
1. All $6$ observations are tokenized independently.
|
| 386 |
+
2. They are **left-padded** to the same sequence length (crucial for causal LM inference — padding tokens before content ensures the model attends correctly).
|
| 387 |
+
3. A single `model.generate()` call produces actions for all episodes simultaneously.
|
| 388 |
+
4. Episode environments are stepped in parallel via `ThreadPoolExecutor`.
|
| 389 |
+
|
| 390 |
+
This reduces $120$ sequential generation calls to $20$ batched calls — a **$6\times$ speedup** on generation. The parallel env steps are I/O bound (HTTP to localhost) and complete in near-constant time regardless of batch size.
|
| 391 |
+
|
| 392 |
+
### Step 3: REINFORCE with Baseline
|
| 393 |
+
|
| 394 |
+
We use the standard REINFORCE policy gradient with a running-mean baseline:
|
| 395 |
+
|
| 396 |
+
$$ \nabla J(\theta) = \mathbb{E} \left[ \sum_t \nabla \log \pi_\theta(a_t | s_t) \cdot (G_t - \bar{b}) \right] $$
|
| 397 |
+
|
| 398 |
+
Where:
|
| 399 |
+
- $G_t = \sum_{k=0}^{T-t} \gamma^k r_{t+k}$ is the discounted return ($\gamma = 0.99$).
|
| 400 |
+
- $\bar{b}$ is the exponential moving average of past episode returns ($\alpha = 0.01$).
|
| 401 |
+
- Advantages are **normalized to zero mean, unit variance** across each batch — critical because raw rewards span orders of magnitude ($-0.01$ to $-12.0$).
|
| 402 |
+
|
| 403 |
+
#### Action-Token Masking
|
| 404 |
+
|
| 405 |
+
A critical detail often overlooked: we compute log-probabilities **only over the generated action tokens**, not the prompt tokens. The full sequence `[prompt | action]` is fed through the model, but the loss mask zeros out all prompt-position tokens. This prevents the gradient from pushing on tokens the model cannot control — without this masking, the REINFORCE gradient is dominated by noise from prompt reconstruction.
|
| 406 |
+
|
| 407 |
+
Log-probs are also **length-normalized** by the number of action tokens. This prevents sequences with longer action outputs from receiving disproportionately large gradients.
|
| 408 |
+
|
| 409 |
+
#### Entropy Bonus for Exploration
|
| 410 |
+
|
| 411 |
+
A per-token entropy bonus is added to the loss to prevent premature policy collapse:
|
| 412 |
+
|
| 413 |
+
$$ \mathcal{L}_{\text{total}} = \mathcal{L}_{\text{REINFORCE}} - \beta \cdot \frac{1}{N} \sum_t H(\pi_\theta(\cdot | s_t)) $$
|
| 414 |
+
|
| 415 |
+
Where $\beta = 0.001$. The entropy is computed over **action tokens only** — prompt tokens contribute no entropy signal.
|
| 416 |
+
|
| 417 |
+
---
|
| 418 |
+
|
| 419 |
+
## VRAM Engineering: Avoiding OOM on a $24$ GiB GPU
|
| 420 |
+
|
| 421 |
+
The A10G has $22.3$ GiB of usable VRAM. With a $4$-bit $4$B model occupying $\sim$3.3 GiB and a single forward pass requiring $\sim$8.9 GiB of activations, we had $\sim$10 GiB of headroom — but only if we managed memory carefully. Three techniques made training feasible:
|
| 422 |
+
|
| 423 |
+
### 1. Per-Mini-Batch Gradient Accumulation
|
| 424 |
+
|
| 425 |
+
The naive approach: collect all episode transitions, compute log-probs for all of them in one giant forward pass, then call `.backward()`. This materializes the computation graph for ALL transitions simultaneously — $\sim$26 GiB peak for just $3$ batches, causing an immediate OOM.
|
| 426 |
+
|
| 427 |
+
Instead, we split the transitions into mini-batches of size $1$ and process them in a loop:
|
| 428 |
+
|
| 429 |
+
```
|
| 430 |
+
for each mini-batch:
|
| 431 |
+
1. Forward pass → compute NLL + entropy for this mini-batch only
|
| 432 |
+
2. Compute REINFORCE loss for this mini-batch
|
| 433 |
+
3. .backward() immediately → frees computation graph
|
| 434 |
+
4. .zero_grad() the optimizer
|
| 435 |
+
5. Discard logits, hidden states, and intermediate tensors
|
| 436 |
+
```
|
| 437 |
+
|
| 438 |
+
Only **one** forward pass worth of activations ($\sim$8.9 GiB) lives in VRAM at any time. The LoRA gradients accumulate in the parameter `.grad` buffers (only $\sim$170 MiB for $42$M parameters).
|
| 439 |
+
|
| 440 |
+
### 2. Fused Cross-Entropy (No Logit Materialization)
|
| 441 |
+
|
| 442 |
+
Standard cross-entropy loss in PyTorch materializes the full `[B, S-1, V]` log-probability matrix, where $V = 151,936$ (Qwen's vocabulary). At `B=1, S=768`, that's $\sim$950 MiB — just for one intermediate tensor.
|
| 443 |
+
|
| 444 |
+
`F.cross_entropy(reduction='none')` uses a single fused CUDA kernel that computes log-softmax + NLL in one pass, materializing only the per-position scalar NLL result ($\sim$6 KiB). This saves nearly $1$ GiB per mini-batch.
|
| 445 |
+
|
| 446 |
+
### 3. CPU Offloading of Rollout Data
|
| 447 |
+
|
| 448 |
+
Immediately after generation, all tokenized sequences are moved from GPU to CPU:
|
| 449 |
+
|
| 450 |
+
```python
|
| 451 |
+
for ep in episodes:
|
| 452 |
+
for t in ep.transitions:
|
| 453 |
+
t.input_ids = t.input_ids.cpu()
|
| 454 |
+
t.attention_mask = t.attention_mask.cpu()
|
| 455 |
+
```
|
| 456 |
+
|
| 457 |
+
The generation KV-cache can peak at $\sim$12 GiB for a batch of $6$ sequences. Offloading immediately frees this for the loss forward pass. Only the current mini-batch's tensors are moved back to GPU during the loss loop.
|
| 458 |
+
|
| 459 |
+
These three optimizations together keep peak VRAM below $\sim$18 GiB, maintaining a $4$-$5$ GiB safety margin on the A10G.
|
| 460 |
+
|
| 461 |
+
---
|
| 462 |
+
|
| 463 |
+
## Smart Entropy Calculation: Chunked Vocab Processing
|
| 464 |
+
|
| 465 |
+
Computing the per-token entropy $H(p) = -\sum_v p_v \log p_v$ normally requires materializing the full `[B, S-1, V]` log-probability matrix — $950$ MiB for a single forward pass. AntiAtropos avoids this with a **chunked reduction** over the vocabulary dimension:
|
| 466 |
+
|
| 467 |
+
```python
|
| 468 |
+
log_Z = logits.logsumexp(dim=-1, keepdim=True) # (B, S-1, 1) — 4 MiB
|
| 469 |
+
entropy = torch.zeros(B, S-1)
|
| 470 |
+
for v_start in range(0, V, 4096):
|
| 471 |
+
chunk = logits[:, :, v_start:v_start + 4096]
|
| 472 |
+
log_p_chunk = chunk - log_Z # (B, S-1, 4096) — 32 MiB
|
| 473 |
+
p_chunk = log_p_chunk.exp() # (B, S-1, 4096) — 32 MiB
|
| 474 |
+
entropy += -(p_chunk * log_p_chunk).sum(dim=-1) # accumulates in-place
|
| 475 |
+
```
|
| 476 |
+
|
| 477 |
+
Instead of allocating $950$ MiB for the full `[B, S-1, 151936]` matrix, we process the vocabulary in $4096$-column chunks. Each chunk allocates only $\sim$64 MiB temporarily. The entropy accumulator stays as a single `[B, S-1]` tensor ($\sim$6 KiB). Total savings: $\sim$900 MiB per forward pass.
|
| 478 |
+
|
| 479 |
+
This chunked approach is possible because entropy is a sum over independent dimensions — no information is shared across vocabulary columns, so the accumulation is trivially parallel and numerically identical to the full materialization.
|
| 480 |
+
|
| 481 |
+
---
|
| 482 |
+
|
| 483 |
+
## Evaluation Protocol
|
| 484 |
+
|
| 485 |
+
Every $50$ iterations, we run a full evaluation comparing the fine-tuned model against a **task-aware heuristic baseline** across all three tasks. The heuristic uses hand-crafted rules:
|
| 486 |
+
|
| 487 |
+
- **Task 1**: Scale up when queue depth $> 0.5$; scale down when capacity $> 0.7$ and queue $< 0.2$.
|
| 488 |
+
- **Task 2**: On detecting a FAILED node, reroute traffic away from it and scale up starved children.
|
| 489 |
+
- **Task 3**: Scale node-1/2 when queue exceeds $0.3$; shed load on node-3/4 as fallback.
|
| 490 |
+
|
| 491 |
+
Each evaluation runs $2$ episodes per task with deterministic seeds (same initial conditions for both FT and heuristic). The comparison is rigorous: same seed, same task, same max steps. Metrics tracked:
|
| 492 |
+
|
| 493 |
+
- **Average reward** per task (normalized $[0, 1])$.
|
| 494 |
+
- **Invalid action rate** — malformed JSON, critical-node violations.
|
| 495 |
+
- **SLA violations** — steps where latency $> 200$ ms or error rate $> 5\%$.
|
| 496 |
+
|
| 497 |
+

|
| 498 |
+
|
| 499 |
+
### Results-Driven Checkpointing
|
| 500 |
+
|
| 501 |
+
Checkpoints are saved every $5$ iterations (approximately every $15$ minutes) to the Hugging Face Hub under `hub_model_repo/<run_id>/checkpoint-NNNN/`. At $\$0.34$/hr, a lost job costs mere cents, but losing $10$ iterations of training progress due to a transient error is wasteful. Frequent checkpointing ensures that even if an HF Job is preempted, the next run can resume from the latest Hub checkpoint automatically.
|
| 502 |
+
|
| 503 |
+
### Metrics Logging Pipeline
|
| 504 |
+
|
| 505 |
+
Every step of every episode is logged as a structured JSONL row with:
|
| 506 |
+
- Per-node queue depth, latency, inflow, outflow, capacity, pending capacity
|
| 507 |
+
- Cluster-level: average latency, error rate, total queue backlog, cost per hour, SLA violations
|
| 508 |
+
- Action: type, target, parameter, validity flag, raw reward
|
| 509 |
+
- Identity: run ID, iteration, episode index, task ID, step number, UTC timestamp
|
| 510 |
+
|
| 511 |
+
This produces a richly queryable metrics dataset that can be used to:
|
| 512 |
+
- Replay any episode step-by-step
|
| 513 |
+
- Compute custom metrics (e.g., time-to-recovery after failure)
|
| 514 |
+
- Diagnose reward stagnation by inspecting specific action sequences
|
| 515 |
+
- Generate plots (loss curves, reward curves, action distributions) post-hoc without re-running training
|
| 516 |
+
|
| 517 |
+
---
|
| 518 |
+
|
| 519 |
+
## Results: Learned Behavior
|
| 520 |
+
|
| 521 |
+
After 500 training iterations, the model demonstrated measurable improvement across all three tasks:
|
| 522 |
+
|
| 523 |
+
| Task | Baseline (NO-OP) | Trained Agent | Behavioral Observation |
|
| 524 |
+
|------|-----------------|---------------|----------------------|
|
| 525 |
+
| task-1 | $0.70$ | **$0.80+$** | Learned to scale down during idle periods — cost-aware, not just stability-seeking |
|
| 526 |
+
| task-2 | $0.70$ | **$0.82$** | Learned to detect failure → reroute → scale — exact SRE incident response protocol |
|
| 527 |
+
| task-3 | $0.21$ | **$0.94$** | Learned pre-scale from early queue signal — predictive capacity planning |
|
| 528 |
+
|
| 529 |
+
Action distribution shifted from SCALE_UP-heavy ($60\%$) to a balanced spread across all five action types, with SCALE_DOWN rising from $2\%$ to $20\%$ — the signature of an agent that manages capacity, not just adds it.
|
| 530 |
+
|
| 531 |
+

|
| 532 |
+
|
| 533 |
+
The fine-tuned policy learned several non-trivial behaviors:
|
| 534 |
+
|
| 535 |
+
- **Cost-aware scaling down.** During low-traffic windows, the agent actively reduces capacity on non-critical nodes — it has learned the $20\times$ idle penalty and adjusts accordingly.
|
| 536 |
+
- **Failure detection via queue collapse.** When a node fails, its queue empties (inflow stops) but children begin starving. The agent reads this pattern rather than waiting for a FAILED status flag.
|
| 537 |
+
- **Pre-scaling before surges.** In Task 3, the agent raises capacity on node-1 and node-2 at the first signal of queue buildup, well before latency reaches SLA thresholds. The $5$-tick boot delay makes reactive scaling impossible — this behavior can only be learned.
|
| 538 |
+
- **Reroute precision.** In Task 2, the agent reroutes **from** the failed node (not to it), then scales the starved children — the exact protocol a human SRE would follow.
|
| 539 |
+
- **Reduced invalid actions.** The invalid action rate dropped from $\sim$15\% to $\sim$3\% as the model learned to avoid SHED_LOAD on critical nodes and generate well-formed JSON.
|
| 540 |
+
|
| 541 |
+
---
|
| 542 |
+
|
| 543 |
+
## Why This Architecture Is Superior
|
| 544 |
+
|
| 545 |
+
**It measures direction, not position.** Lyapunov drift grades the agent on whether it moved the cluster toward or away from equilibrium. Most SRE tools measure whether a threshold is crossed. One is about trajectory; the other is about a single point in time.
|
| 546 |
+
|
| 547 |
+
**It sees topology, not just nodes.** The edge imbalance term captures load distribution mismatches that flat per-node metrics fundamentally miss. A parent-child queue mismatch is invisible to per-node dashboards. AntiAtropos makes it the agent's explicit concern.
|
| 548 |
+
|
| 549 |
+
**It acts before, not after.** Smooth SLA sigmoids give gradient before violation. Boot delay creates a hard operating window where late action fails. The interaction between these design choices is deliberate: the agent can only succeed if it learns the pre-scale window. There is no fallback plan that succeeds reactively.
|
| 550 |
+
|
| 551 |
+
**It prices what matters.** The three-tier cost model separates sunk infrastructure from agent-added capacity from idle waste. The agent is never penalized for the cluster simply existing. It is penalized for choosing to leave capacity idle.
|
| 552 |
+
|
| 553 |
+
**It is formally grounded.** The Drift-Plus-Penalty framework by Neely et al. [1] provides a theoretical guarantee: minimizing this expression produces a queue-stable policy with bounded average cost. The agent is not learning opinionated heuristics. It is optimizing a mathematical objective with known stability properties.
|
| 554 |
+
|
| 555 |
+
**It transfers to production.** The same action space that controls the simulator maps directly to Kubernetes operations. The environment runs in SIMULATED mode for training, HYBRID mode for validation against live metrics, and LIVE mode for direct cluster control — all through the same interface and the same policy.
|
| 556 |
+
|
| 557 |
+
---
|
| 558 |
+
|
| 559 |
+
## Quick Start
|
| 560 |
+
|
| 561 |
+
The environment is live on Hugging Face Spaces. Connect from any OpenEnv-compatible client:
|
| 562 |
+
|
| 563 |
+
```python
|
| 564 |
+
pip install openenv-core
|
| 565 |
+
from openenv import Environment
|
| 566 |
+
|
| 567 |
+
env = Environment("https://keshav051-antiatropos.hf.space")
|
| 568 |
+
obs = env.reset(task_id="task-3")
|
| 569 |
+
for step in range(60):
|
| 570 |
+
obs = env.step({"action_type": "NO_OP", "target_node_id": "node-0", "parameter": 0.0})
|
| 571 |
+
```
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
## Future Horizons: The Road to Provable Autonomy
|
| 575 |
+
AntiAtropos is not a static tool; it is a prototype for a new era of infrastructure. Our next steps involve expanding the state-space to include **Multi-Cluster Coordination** and implementing **Formal Safety Verification**—using the Lyapunov certificates generated during training to prove that an agent's policy will never violate safety barriers under a given traffic envelope.
|
| 576 |
+
|
| 577 |
+
We are moving toward a world where infrastructure manages itself, not through a collection of scripts, but through an inherent understanding of its own physics.
|
| 578 |
+
|
| 579 |
+
---
|
| 580 |
+
|
| 581 |
+
## References
|
| 582 |
+
|
| 583 |
+
[1] M. J. Neely et al., "Stochastic Network Optimization with Application to Communication and Queueing Systems," *Synthesis Lectures on Communication Networks*, vol. 3, no. 1, pp. 1-211, 2010.
|
| 584 |
+
|
| 585 |
+
---
|
| 586 |
+
|
| 587 |
+
## The Road Not Taken: GRPO Experimentation
|
| 588 |
+
|
| 589 |
+
### Reference Runs on the Hub
|
| 590 |
+
|
| 591 |
+
All training runs — including the canonical REINFORCE run and our GRPO experiments — are publicly available for inspection:
|
| 592 |
+
|
| 593 |
+
| Run | Loss Type | Description | Link |
|
| 594 |
+
|-----|-----------|-------------|------|
|
| 595 |
+
| **run_0011** | REINFORCE + baseline | **Reference run** — fully converged policy after 500 iterations. This trained the model discussed above. | [View on Hub](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/run_0011) |
|
| 596 |
+
| **grpo_run_001** | GRPO | Experimental GRPO run with K=2, comparing group-relative advantage against the running-mean baseline. | [View on Hub](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/grpo_run_001) |
|
| 597 |
+
|
| 598 |
+
The `logs/` directory at the project root also contains local copies of these artifacts for offline inspection.
|
| 599 |
+
|
| 600 |
+
### Why not GRPO?
|
| 601 |
+
|
| 602 |
+
Our initial research included an attempt at **Group Relative Policy Optimization (GRPO)**. While GRPO is excellent for relative ranking, we found that for the high-dimensional physics of a microservice cluster, **QLoRA REINFORCE** offered a more stable gradient. Computationally, QLoRA allowed us to maintain a Rank-64 adapter density that GRPO struggled to match within the same VRAM constraints. Ultimately, the REINFORCE approach yielded 1:1 performance results with a much faster iteration loop.
|
| 603 |
+
|
| 604 |
+
---
|
| 605 |
+
|
| 606 |
+
*Built for the 2026 AntiAtropos Hackathon.*
|
Experimental/ProofOfConcept.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
README.md
CHANGED
|
@@ -11,12 +11,34 @@ base_path: /web
|
|
| 11 |
|
| 12 |
# AntiAtropos: The Physics of Autonomous SRE
|
| 13 |
|
| 14 |
-
[](https://github.com/openenv/openenv)
|
| 15 |
-
[](https://hf.co/spaces/PranavKK/AntiAtropos)
|
| 16 |
-
|
| 17 |
> **"Infrastructure is not a static set of configurations; it is a dynamic system of energy, flow, and stability."**
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
---
|
| 22 |
|
|
@@ -105,6 +127,82 @@ Our reward function is grounded in Neely's **Drift-Plus-Penalty** framework, pro
|
|
| 105 |
|
| 106 |
---
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
## Quick Start
|
| 109 |
|
| 110 |
### Local Installation
|
|
@@ -113,13 +211,32 @@ pip install -e .
|
|
| 113 |
uvicorn server.app:app --host 0.0.0.0 --port 7860
|
| 114 |
```
|
| 115 |
|
| 116 |
-
### Evaluation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
```bash
|
| 118 |
-
# Set your API key and run the evaluation harness
|
| 119 |
-
set OPENAI_API_KEY=your_key
|
| 120 |
python inference.py --task all --mode trained
|
| 121 |
```
|
| 122 |
|
| 123 |
---
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
*Built with passion for the 2026 AntiAtropos Hackathon.*
|
|
|
|
| 11 |
|
| 12 |
# AntiAtropos: The Physics of Autonomous SRE
|
| 13 |
|
|
|
|
|
|
|
|
|
|
| 14 |
> **"Infrastructure is not a static set of configurations; it is a dynamic system of energy, flow, and stability."**
|
| 15 |
|
| 16 |
+
[](https://hf.co/spaces/Keshav051/AntiAtropos)
|
| 17 |
+
[](https://huggingface.co/Keshav051/AntiAtropos/tree/main)
|
| 18 |
+
[](https://huggingface.co/Keshav051/antiatropos-qlora)
|
| 19 |
+
[](https://youtu.be/46SX0HocpSs)
|
| 20 |
+
|
| 21 |
+
## Table of Contents
|
| 22 |
+
- [Demo Video](#demo-video)
|
| 23 |
+
- [The Vision](#the-vision-beyond-runbooks)
|
| 24 |
+
- [The Physics Engine](#the-physics-engine)
|
| 25 |
+
- [Architecture](#architecture)
|
| 26 |
+
- [Reward Engineering](#reward-engineering-the-differentiable-sre)
|
| 27 |
+
- [Task Curriculum & Results](#task-curriculum--results)
|
| 28 |
+
- [Training: RL with Unsloth + Hugging Face Jobs](#training-rl-with-unsloth--hugging-face-jobs)
|
| 29 |
+
- [Quick Start](#quick-start)
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
---
|
| 34 |
+
|
| 35 |
+
> **Hackathon Submission:** We are building for **"Theme #3: World Modelling for Professional Tasks."**
|
| 36 |
+
> AntiAtropos governs clusters the way physics governs a pendulum—by minimizing Lyapunov energy. Perfect SLA at **50% lower cost**.
|
| 37 |
+
|
| 38 |
+
## Demo Video
|
| 39 |
+
[](https://youtu.be/46SX0HocpSs)
|
| 40 |
+
|
| 41 |
+
AntiAtropos is a **Reinforcement Learning environment** where an AI agent learns to stabilize a 5-node microservice cluster by treating it as a physical system. Using **QLoRA REINFORCE** on a Qwen3.5-4B model, the agent is trained to minimize Lyapunov graph energy under a Drift-Plus-Penalty objective that balances stability against infrastructure cost. The trained policy scales predictively, reroutes around failures, and holds the line during traffic surges.
|
| 42 |
|
| 43 |
---
|
| 44 |
|
|
|
|
| 127 |
|
| 128 |
---
|
| 129 |
|
| 130 |
+
## Training: RL with Unsloth + Hugging Face Jobs
|
| 131 |
+
|
| 132 |
+
All training artifacts — model checkpoints, metrics logs, stderr/stdout, and evaluation plots — are pushed to the **[Keshav051/antiatropos-qlora](https://huggingface.co/Keshav051/antiatropos-qlora)** Hugging Face Hub repository. Each run lives under its own subdirectory (e.g., `run_0011/`).
|
| 133 |
+
|
| 134 |
+
### Reference Runs
|
| 135 |
+
|
| 136 |
+
| Run | Loss Type | Description | Link |
|
| 137 |
+
|-----|-----------|-------------|------|
|
| 138 |
+
| **run_0011** | REINFORCE + baseline | **Reference run** — fully converged policy after 500 iterations. This is the canonical trained model discussed in the blog. | [View on Hub](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/run_0011) |
|
| 139 |
+
| **grpo_run_001** | GRPO | Experimental GRPO run for comparison against the REINFORCE baseline. See the blog for analysis. | [View on Hub](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/grpo_run_001) |
|
| 140 |
+
|
| 141 |
+
Each run folder contains:
|
| 142 |
+
- `checkpoint-NNNN/` — LoRA adapter weights at every 5th iteration
|
| 143 |
+
- `metrics.jsonl` — per-step telemetry for every episode across all iterations
|
| 144 |
+
- `eval_results.jsonl` — heuristic vs trained comparison at each evaluation interval
|
| 145 |
+
- `plots/` — loss curves, reward curves, and action distribution plots
|
| 146 |
+
- `train.log` — full stderr/stdout from the training container
|
| 147 |
+
|
| 148 |
+
> **Note:** The `logs/` directory at the project root also contains local copies of key run artifacts for offline inspection.
|
| 149 |
+
|
| 150 |
+
### How Training Works
|
| 151 |
+
|
| 152 |
+
Training uses two core Hugging Face technologies:
|
| 153 |
+
1. **🤗 Hugging Face Jobs** — serverless GPU infrastructure. You define the container image, hardware flavor, and command; HF allocates the GPU, runs the job, and streams logs back. No SSH, no cluster management.
|
| 154 |
+
2. **Unsloth RL** — 4-bit QLoRA with REINFORCE/GRPO support. The base model (Qwen3.5-4B) is loaded in 4-bit via Unsloth's `FastLanguageModel`, and LoRA adapters (rank-64) are trained on top using a custom REINFORCE training loop.
|
| 155 |
+
|
| 156 |
+
Inside the job container, the AntiAtropos FastAPI simulator starts on CPU (localhost:8000) while the GPU handles model forward/backward passes. This **co-located architecture** eliminates network latency between action generation and environment feedback.
|
| 157 |
+
|
| 158 |
+
### Launching Training
|
| 159 |
+
|
| 160 |
+
The **only required argument** is `--run-id` — everything else has sensible defaults:
|
| 161 |
+
|
| 162 |
+
```bash
|
| 163 |
+
# Minimal launch — 15 iterations, 6 episodes/iter, 20 steps/episode
|
| 164 |
+
python training/launch_train.py --run-id run_007
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
This uses all defaults:
|
| 168 |
+
- **`--hub-model-repo`** = `Keshav051/antiatropos-qlora` (artifacts pushed here)
|
| 169 |
+
- **`--num-iterations`** = `15` (training iterations)
|
| 170 |
+
- **`--num-episodes`** = `6` (episodes per iteration; 2 per task for curriculum balance)
|
| 171 |
+
- **`--max-steps`** = `20` (max environment steps per episode)
|
| 172 |
+
- **`--eval-interval`** = `50` (evaluate vs heuristic every N iterations — rarely needed for short runs)
|
| 173 |
+
- **`--checkpoint-interval`** = `5` (save checkpoint every N iterations)
|
| 174 |
+
- **`--plot-interval`** = `10` (generate plots every N iterations)
|
| 175 |
+
- **`--loss-type`** = `reinforce_baseline` (REINFORCE with baseline; use `grpo` for GRPO)
|
| 176 |
+
- **`--flavor`** = `a10g-large` (NVIDIA A10G, 24 GiB, ~$0.34/hr)
|
| 177 |
+
- **`--timeout`** = `4h` (job timeout)
|
| 178 |
+
|
| 179 |
+
To override any default, just pass the flag:
|
| 180 |
+
|
| 181 |
+
```bash
|
| 182 |
+
# Full training (500 iterations, A10G, ~$7):
|
| 183 |
+
python training/launch_train.py --run-id run_012 --num-iterations 500 --num-episodes 6
|
| 184 |
+
|
| 185 |
+
# GRPO experiment:
|
| 186 |
+
python training/launch_train.py --run-id grpo_run_002 --loss-type grpo
|
| 187 |
+
|
| 188 |
+
# Longer timeout for deep training:
|
| 189 |
+
python training/launch_train.py --run-id run_013 --num-iterations 500 --timeout 12h
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
### Prerequisites
|
| 193 |
+
|
| 194 |
+
1. `pip install "huggingface_hub>=0.25.0"`
|
| 195 |
+
2. `huggingface-cli login` (or set `HF_TOKEN` environment variable)
|
| 196 |
+
3. A Hugging Face Pro or Team account (required for GPU Jobs)
|
| 197 |
+
4. The target Hub model repo is auto-created if it doesn't exist
|
| 198 |
+
|
| 199 |
+
For the full list of options:
|
| 200 |
+
```bash
|
| 201 |
+
python training/launch_train.py --help
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
---
|
| 205 |
+
|
| 206 |
## Quick Start
|
| 207 |
|
| 208 |
### Local Installation
|
|
|
|
| 211 |
uvicorn server.app:app --host 0.0.0.0 --port 7860
|
| 212 |
```
|
| 213 |
|
| 214 |
+
### Evaluation & Observation
|
| 215 |
+
|
| 216 |
+
The `inference.py` script is the primary tool for validating model performance. It provides a detailed breakdown of episodic reward, SLA compliance, and cluster stability. It is an excellent way to **baseline behavior** of a new model or compare different training iterations.
|
| 217 |
+
|
| 218 |
+
To configure the environment, use the `.env` file. Key "knobs" include:
|
| 219 |
+
- `ENV_URL`: The URL of the AntiAtropos simulation server (e.g., your HF Space).
|
| 220 |
+
- `MODEL_NAME`: The identifier for the model to test (supports Groq, Local, or HF).
|
| 221 |
+
- `GROQ_API_KEY`: Required if using Groq-based inference for rapid prototyping.
|
| 222 |
+
- `ANTIATROPOS_ENV_MODE`: Set to `simulated` for training or `live` for K8s control.
|
| 223 |
+
|
| 224 |
```bash
|
| 225 |
+
# Set your API key and run the evaluation harness
|
|
|
|
| 226 |
python inference.py --task all --mode trained
|
| 227 |
```
|
| 228 |
|
| 229 |
---
|
| 230 |
|
| 231 |
+
---
|
| 232 |
+
|
| 233 |
+
## Future Horizons: The Path to Autonomous Cloud Safety
|
| 234 |
+
|
| 235 |
+
AntiAtropos is the foundation for a new class of **Differentiable SRE**. Our roadmap includes:
|
| 236 |
+
- **Multi-Agent Coordination**: Training specialized agents (e.g., an "Ingress Governor" and a "Storage Optimizer") to collaborate via shared Lyapunov energy.
|
| 237 |
+
- **Formal Verification**: Using the Lyapunov certificates generated during training to provide mathematical guarantees of stability before an agent is deployed to production.
|
| 238 |
+
- **Predictive Traffic Shaping**: Moving from reactive scaling to predictive world-modeling of seasonal traffic surges.
|
| 239 |
+
|
| 240 |
+
---
|
| 241 |
+
|
| 242 |
*Built with passion for the 2026 AntiAtropos Hackathon.*
|
images/Agent Actions.png
ADDED
|
Git LFS Details
|
images/Cost comparison.png
ADDED
|
images/DAG.svg
ADDED
|
|
images/HF Grafana.png
ADDED
|
Git LFS Details
|
images/RL_training_metrics.png
ADDED
|
Git LFS Details
|
images/VM_Grafana.png
ADDED
|
Git LFS Details
|
inference.py
CHANGED
|
@@ -41,7 +41,7 @@ if not API_KEY:
|
|
| 41 |
API_KEY = os.getenv("HF_TOKEN") or os.getenv("OPENAI_API_KEY")
|
| 42 |
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
|
| 43 |
|
| 44 |
-
DEFAULT_ENV_URL = "https://
|
| 45 |
ENV_URL = os.getenv("ENV_URL") or os.getenv("ANTIATROPOS_ENV_URL") or DEFAULT_ENV_URL
|
| 46 |
ENV_MODE = os.getenv("ANTIATROPOS_MODE", "simulated")
|
| 47 |
TASK_NAME = os.getenv("ANTIATROPOS_TASK", "task-1")
|
|
|
|
| 41 |
API_KEY = os.getenv("HF_TOKEN") or os.getenv("OPENAI_API_KEY")
|
| 42 |
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
|
| 43 |
|
| 44 |
+
DEFAULT_ENV_URL = "https://keshav051-antiatropos.hf.space"
|
| 45 |
ENV_URL = os.getenv("ENV_URL") or os.getenv("ANTIATROPOS_ENV_URL") or DEFAULT_ENV_URL
|
| 46 |
ENV_MODE = os.getenv("ANTIATROPOS_MODE", "simulated")
|
| 47 |
TASK_NAME = os.getenv("ANTIATROPOS_TASK", "task-1")
|
logs/non_record_grpo_run/NON_RECORD_GRPO.log
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
logs/non_record_grpo_run/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GRPO Experiment — `grpo_run_001`
|
| 2 |
+
|
| 3 |
+
> ⚠️ **IMPORTANT: This is NOT the submission run.**
|
| 4 |
+
> This folder contains an **experimental GRPO training run** that was explored during research but is **not** part of the final AntiAtropos submission. All submission results come from `run_0011` (REINFORCE + baseline), located in the [`logs/submission_run/`](../submission_run/) directory.
|
| 5 |
+
|
| 6 |
+
## Why This Exists
|
| 7 |
+
|
| 8 |
+
During development we experimented with **Group Relative Policy Optimization (GRPO)** as an alternative to REINFORCE. This run documents that exploration for full transparency and reproducibility.
|
| 9 |
+
|
| 10 |
+
## Key Differences from Submission
|
| 11 |
+
|
| 12 |
+
| Aspect | This Run (GRPO) | Submission (REINFORCE) |
|
| 13 |
+
|--------|-----------------|----------------------|
|
| 14 |
+
| Loss function | GRPO (K=4) | REINFORCE + baseline |
|
| 15 |
+
| Training iterations | 15 (short test) | 500 (full convergence) |
|
| 16 |
+
| Result quality | Not comparable — truncated diagnostic run | Converged policy |
|
| 17 |
+
| Status | **Experimental only** | **Canonical submission** |
|
| 18 |
+
|
| 19 |
+
## What's in This Folder
|
| 20 |
+
|
| 21 |
+
| File | Description |
|
| 22 |
+
|------|-------------|
|
| 23 |
+
| `NON_RECORD_GRPO.log` | Stderr/stdout log from the GRPO experiment |
|
| 24 |
+
|
| 25 |
+
## Hub Mirror
|
| 26 |
+
|
| 27 |
+
Also available on the Hugging Face Hub at [Keshav051/antiatropos-qlora/grpo_run_001](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/grpo_run_001).
|
| 28 |
+
|
| 29 |
+
## Final Takeaway
|
| 30 |
+
|
| 31 |
+
GRPO showed potential but was ultimately **not used in the submission**. The REINFORCE baseline approach produced equivalent or better results with a faster, more stable training loop. See [The Road Not Taken](../../Blog.md#the-road-not-taken-grpo-experimentation) section in the blog for the full analysis.
|
logs/submission_run/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Submission Run — `run_0011`
|
| 2 |
+
|
| 3 |
+
**This is the official AntiAtropos submission run.** All results, metrics, and behavioral analysis discussed in the README and blog post are derived from this training run.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
| Field | Value |
|
| 8 |
+
|-------|-------|
|
| 9 |
+
| Run ID | `run_0011` |
|
| 10 |
+
| Loss Type | REINFORCE + baseline |
|
| 11 |
+
| Base Model | Qwen/Qwen3.5-4B |
|
| 12 |
+
| QLoRA Rank | 64 |
|
| 13 |
+
| Iterations | 500 |
|
| 14 |
+
| Episodes per Iteration | 6 (2 per task for curriculum balance) |
|
| 15 |
+
| Max Steps per Episode | 20 |
|
| 16 |
+
| Training Time | ~2 hours on A10G (24 GiB) |
|
| 17 |
+
| Cost | ~$0.68 (a10g-large at $0.34/hr) |
|
| 18 |
+
| Results | Task-1: 0.88 / Task-2: 0.82 / Task-3: 0.94 |
|
| 19 |
+
|
| 20 |
+
## What's in This Folder
|
| 21 |
+
|
| 22 |
+
| File | Description |
|
| 23 |
+
|------|-------------|
|
| 24 |
+
| `SUBMISSION_TRAINING_RUN.log` | Full stderr/stdout log from the training container |
|
| 25 |
+
| `step_metrics.jsonl` | Per-step telemetry across all episodes (queue depth, latency, reward, actions, etc.) |
|
| 26 |
+
| `run_info.json` | Configuration snapshot for full reproducibility |
|
| 27 |
+
|
| 28 |
+
## Hub Mirror
|
| 29 |
+
|
| 30 |
+
All artifacts are also available on the Hugging Face Hub:
|
| 31 |
+
- **Model checkpoints, metrics, plots:** [Keshav051/antiatropos-qlora/run_0011](https://huggingface.co/Keshav051/antiatropos-qlora/tree/main/run_0011)
|
| 32 |
+
|
| 33 |
+
## Training Command
|
| 34 |
+
|
| 35 |
+
This run was launched via Hugging Face Jobs using:
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
python training/launch_train.py --run-id run_0011 --num-iterations 500 --num-episodes 6
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
See the [Training section in README](../../README.md#training-rl-with-unsloth--hugging-face-jobs) for detailed launch instructions.
|
logs/submission_run/SUBMISSION_TRAINING_RUN.log
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
logs/submission_run/run_info.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"run_id": "run_0011",
|
| 3 |
+
"started_at": "2026-04-26T08:19:30.978913Z",
|
| 4 |
+
"config": {
|
| 5 |
+
"run_id": "run_0011",
|
| 6 |
+
"env_url": "http://localhost:8000",
|
| 7 |
+
"tasks": [
|
| 8 |
+
"task-1",
|
| 9 |
+
"task-2",
|
| 10 |
+
"task-3"
|
| 11 |
+
],
|
| 12 |
+
"max_steps_per_episode": 20,
|
| 13 |
+
"env_mode": "simulated",
|
| 14 |
+
"base_model": "Qwen/Qwen3.5-4B",
|
| 15 |
+
"max_seq_length": 768,
|
| 16 |
+
"load_in_4bit": true,
|
| 17 |
+
"lora_rank": 64,
|
| 18 |
+
"lora_alpha": 64,
|
| 19 |
+
"lora_dropout": 0.0,
|
| 20 |
+
"lora_target_modules": [
|
| 21 |
+
"q_proj",
|
| 22 |
+
"k_proj",
|
| 23 |
+
"v_proj",
|
| 24 |
+
"o_proj",
|
| 25 |
+
"gate_proj",
|
| 26 |
+
"up_proj",
|
| 27 |
+
"down_proj"
|
| 28 |
+
],
|
| 29 |
+
"loss_type": "reinforce_baseline",
|
| 30 |
+
"num_episodes_per_iteration": 6,
|
| 31 |
+
"num_iterations": 10,
|
| 32 |
+
"parallel_episodes": true,
|
| 33 |
+
"loss_batch_size": 1,
|
| 34 |
+
"learning_rate": 1e-05,
|
| 35 |
+
"per_device_train_batch_size": 2,
|
| 36 |
+
"gradient_accumulation_steps": 4,
|
| 37 |
+
"warmup_steps": 50,
|
| 38 |
+
"weight_decay": 0.01,
|
| 39 |
+
"max_grad_norm": 1.0,
|
| 40 |
+
"bf16": true,
|
| 41 |
+
"optim": "adamw_8bit",
|
| 42 |
+
"grpo_k": 4,
|
| 43 |
+
"rloo_k": 2,
|
| 44 |
+
"reward_gamma": 0.99,
|
| 45 |
+
"reward_gae_lambda": 0.95,
|
| 46 |
+
"advantage_normalize": true,
|
| 47 |
+
"entropy_coef": 0.001,
|
| 48 |
+
"generation_max_new_tokens": 50,
|
| 49 |
+
"generation_temperature": 0.85,
|
| 50 |
+
"generation_top_p": 0.9,
|
| 51 |
+
"generation_do_sample": true,
|
| 52 |
+
"eval_interval": 50,
|
| 53 |
+
"eval_episodes": 2,
|
| 54 |
+
"eval_max_steps": 30,
|
| 55 |
+
"checkpoint_interval": 5,
|
| 56 |
+
"save_total_limit": 5,
|
| 57 |
+
"output_dir": "/workspace/antiatropos_checkpoints",
|
| 58 |
+
"plot_interval": 10,
|
| 59 |
+
"plot_format": "png",
|
| 60 |
+
"plot_dpi": 150,
|
| 61 |
+
"hub_model_repo": "Keshav051/antiatropos-qlora",
|
| 62 |
+
"push_to_hub": true,
|
| 63 |
+
"seed": 42
|
| 64 |
+
}
|
| 65 |
+
}
|
logs/submission_run/step_metrics.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
training/config.yaml
CHANGED
|
@@ -18,7 +18,7 @@
|
|
| 18 |
run_id: "run_001" # Unique run identifier — change per run (used for checkpoint subfolder)
|
| 19 |
|
| 20 |
# ---- Environment (OpenEnv) ----
|
| 21 |
-
env_url: "https://
|
| 22 |
tasks: ["task-1", "task-2", "task-3"]
|
| 23 |
max_steps_per_episode: 20 # 20 steps: task-2 failure can occur mid-ep; need recovery steps
|
| 24 |
env_mode: "simulated" # "simulated" | "live" | "hybrid" — set to "live" for real k8s
|
|
|
|
| 18 |
run_id: "run_001" # Unique run identifier — change per run (used for checkpoint subfolder)
|
| 19 |
|
| 20 |
# ---- Environment (OpenEnv) ----
|
| 21 |
+
env_url: "https://keshav051-antiatropos.hf.space"
|
| 22 |
tasks: ["task-1", "task-2", "task-3"]
|
| 23 |
max_steps_per_episode: 20 # 20 steps: task-2 failure can occur mid-ep; need recovery steps
|
| 24 |
env_mode: "simulated" # "simulated" | "live" | "hybrid" — set to "live" for real k8s
|
training/launch_train.py
CHANGED
|
@@ -182,9 +182,9 @@ def main() -> None:
|
|
| 182 |
)
|
| 183 |
parser.add_argument(
|
| 184 |
"--hub-model-repo",
|
| 185 |
-
|
| 186 |
help="HF Hub model repo for checkpoints, metrics, logs, and plots "
|
| 187 |
-
"(
|
| 188 |
)
|
| 189 |
parser.add_argument(
|
| 190 |
"--run-id",
|
|
|
|
| 182 |
)
|
| 183 |
parser.add_argument(
|
| 184 |
"--hub-model-repo",
|
| 185 |
+
default="Keshav051/antiatropos-qlora",
|
| 186 |
help="HF Hub model repo for checkpoints, metrics, logs, and plots "
|
| 187 |
+
"(default: Keshav051/antiatropos-qlora). All run artifacts go under <run_id>/.",
|
| 188 |
)
|
| 189 |
parser.add_argument(
|
| 190 |
"--run-id",
|