File size: 44,175 Bytes
e9ce6e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 | # Autonomous Enterprise Payment Orchestrator (AEPO) β Master Technical Document
> **Classification:** Internal Engineering Reference Β· **Version:** 10.0.0
> **Author:** Umesh Maurya Β· **Affiliation:** Meta PyTorch OpenEnv Hackathon Γ Scaler School of Technology β Grand Finale
> **Stack:** Python 3.10 Β· Gymnasium 0.29.1 Β· Pydantic v2 Β· FastAPI Β· PyTorch Β· Docker Β· Hugging Face Spaces
> **Status:** Production-Deployed Β· Validated against `openenv validate` strict-mode Β· 221 tests Β· 97% coverage
---
## Table of Contents
1. [Executive Summary & Value Proposition](#1-executive-summary--value-proposition)
2. [Architecture & Implementation Deep-Dive](#2-architecture--implementation-deep-dive)
3. [Causal State Transitions](#3-causal-state-transitions)
4. [Training β Q-Table Agent & LagPredictor](#4-training--q-table-agent--lagpredictor)
5. [Operational Manual](#5-operational-manual)
6. [Verification & Validation Suite](#6-verification--validation-suite)
7. [Hackathon Tasks & Agent Decision Traces](#7-hackathon-tasks--agent-decision-traces)
8. [Incident Post-Mortem & Future Scope](#8-incident-post-mortem--future-scope)
---
## 1. Executive Summary & Value Proposition
### 1.1 The Problem: Siloed Metrics in Fintech Operations
In every Tier-1 payment processor β from UPI gateways handling 14 billion monthly transactions to global card networks β a dangerous organizational fault line exists between **Security/Fraud Operations** and **Infrastructure/SRE teams**. This divide is encoded into the very monitoring stacks, alerting pipelines, and decision frameworks each team uses.
**The Security Team's Blind Spot:** Fraud analysts operate in a world of risk scores, transaction velocity models, and behavioral biometrics. When a botnet launches a credential-stuffing attack, the fraud team's response is singular: escalate verification, reject suspicious transactions. What they do not see is the infrastructure cost of that response. Every `CHALLENGE` action forces a cryptographic re-verification that adds lag. Every `REJECT` still consumes a Kafka partition slot. A fraud team aggressively rejecting traffic during a botnet storm can inadvertently push Kafka consumer lag past the 4,000-message crash threshold β taking down the entire payment rail, including legitimate transactions they were trying to protect.
**The Infrastructure Team's Blind Spot:** SRE teams live in P99 latencies, consumer group lag, and circuit breaker states. When Kafka lag spikes, the SRE playbook is clear: throttle, activate circuit breakers, shed load. But this playbook is fraud-agnostic. Throttling traffic during a botnet attack β where 90% of the throttled transactions are malicious β is a defensible infrastructure decision, but the SRE team cannot distinguish this scenario from a legitimate flash sale. They make the same infrastructure decision regardless of the security context.
**The Asymmetric Risk Triad:**
| Risk Dimension | Metric Proxy | Team Owner | Failure Mode |
|:---|:---|:---|:---|
| **Financial Fraud** | transaction risk score [0β100] | Security/Fraud Ops | Approved fraudulent transactions β direct monetary loss |
| **Infrastructure Health** | kafka_lag [0β10000], api_latency [0β5000ms] | SRE/Platform | Consumer lag > 4,000 β cascading system crash |
| **SLA Compliance** | rolling_p99 [0β5000ms] | SRE/Product | P99 > 800ms β SLA breach β regulatory penalties |
### 1.2 Theme Alignment Matrix
AEPO is engineered to satisfy the official hackathon themes by direct, code-anchored implementation:
| Hackathon Theme | Feature Implementation in AEPO | Technical Anchor (Code / Logic) |
|---|---|---|
| **Theme #3.1: World Modeling** | LagPredictor MLP (1-step lookahead + Dyna-Q planning) | `dynamics_model.py` (LagPredictor) + `inference.py` veto + `train.py` DynaPlanner |
| **Theme #4: Self-Improvement** | Antagonistic adversary policy (adaptive entropy & threat scaling) | `unified_gateway.py` β Attack Phase + 5-episode-lag escalation logic |
| **Causal Reasoning** | 11 physics-based causal state transitions | `step()` deterministic dynamics + accumulators |
| **Realistic Env Design** | Asymmetric Risk Triad (Fraud vs. Infra vs. SLA) | UPI Payment Gateway scope + 10-signal observation schema |
| **Deployment Efficiency** | Optimized edge footprint (2 vCPU / 8 GB RAM) | `Dockerfile` (`python:3.10-slim`) + CPU-only Torch wheel |
**AEPO satisfies the core requirement of Theme #3.1 by** wiring a learned `LagPredictor` world model into both training (Dyna-Q imagined rollouts) and inference (1-step lookahead veto on the crash cliff). **To align with Theme #4, we implemented an adaptive adversarial curriculum that** escalates `adversary_threat_level` whenever the agent's 5-episode rolling reward exceeds 0.6, producing the staircase improvement curve. **This architecture ensures 100% compliance with the hardware constraints specified in the Master Project Requirements** β the full training pipeline runs in ~5 seconds on 2 vCPU / 8 GB RAM.
### 1.3 The Solution: AEPO β A Causally-Structured RL Decision Surface
The **Autonomous Enterprise Payment Orchestrator (AEPO)** resolves the Siloed Metrics problem by encoding the entire Asymmetric Risk Triad into a single **Gymnasium-compatible Reinforcement Learning environment**. Rather than building another dashboard that correlates metrics post-hoc, AEPO creates a training ground where AI agents learn β through thousands of simulated transactions β to make decisions that simultaneously optimize across all three risk dimensions.
AEPO evolved from the **Unified Fintech Risk Gateway (UFRG)**, which won Round 1 of this hackathon with a 5-field observation space and 3-field action space. AEPO is a full architectural upgrade:
| Dimension | UFRG (Round 1) | AEPO (Grand Finale) |
|:---|:---|:---|
| Observation fields | 5 | **10** |
| Action fields | 3 (MultiDiscrete [3,3,2]) | **6** (MultiDiscrete [3,2,3,2,2,3]) |
| Causal transitions | None (memoryless) | **11 causal state transitions** |
| Phase structure | None | **4-phase task machine** per episode |
| Dynamics model | None | **LagPredictor MLP** (PyTorch) |
| Training | None | **Q-Table agent**, 500 episodes, hard task PASS |
| Test suite | ~30 tests | **221 tests**, 97% coverage |
**Why Reinforcement Learning?** The Asymmetric Risk Triad is a **sequential decision-making problem under uncertainty** with delayed, compounding consequences. An agent's decision to skip cryptographic verification at step 12 does not merely affect step 12 β it reduces lag pressure that prevents a crash at step 47. RL is the natural formalism for problems where:
- Actions have **delayed, non-linear consequences** (EMA accumulators mean today's routing decision affects next step's P99)
- The **state space is continuous** (10-dimensional observation vector with float32 precision)
- The **action space is combinatorial** (216 unique action combinations)
- **Reward signals are sparse and asymmetric** (catastrophic fraud penalty vs. gradual SLA degradation)
---
## 2. Architecture & Implementation Deep-Dive
### 2.1 Technology Stack
| Layer | Technology | Version | Role in AEPO |
|:---|:---|:---|:---|
| **Runtime** | Python | 3.10+ | Core language; modern type hints |
| **RL Framework** | Gymnasium | 0.29.1 | `gym.Env` base class, space definitions, env_checker |
| **Type Safety** | Pydantic | v2.0+ | Runtime validation of `AEPOObservation` and `AEPOAction` |
| **Numerical** | NumPy | 1.26.4 | Array backing for observation space |
| **Dynamics Model** | PyTorch | 2.2.0 | `LagPredictor` 2-layer MLP trained alongside Q-table |
| **API Server** | FastAPI | Latest | Async HTTP endpoints for remote environment interaction |
| **ASGI Server** | Uvicorn | Latest | Production-grade ASGI; serves FastAPI on port 7860 |
| **LLM Client** | OpenAI SDK | 1.0+ | OpenAI-compatible client for Ollama / HF Inference API |
| **Containerization** | Docker | `python:3.10-slim` | Deterministic deployment for Hugging Face Spaces |
| **SDK** | openenv-core | 0.2.0+ | `openenv validate` CLI and manifest schema |
| **Deployment** | Hugging Face Spaces | β | Persistent Docker container, always-on at port 7860 |
**Key Architecture Decisions:**
- **Pydantic v2** for `AEPOObservation` and `AEPOAction` provides runtime validation that catches invalid actions before they enter the step function β critical when the action source is an LLM that may hallucinate out-of-range integers.
- **Gymnasium 0.29.1** with **4-tuple return** `(obs, reward, done, info)` per OpenEnv specification. This deviates from Gymnasium's native 5-tuple `(obs, reward, terminated, truncated, info)`. Decision is locked β switching to 5-tuple would break graders, server, and inference simultaneously.
- **PyTorch LagPredictor** trains alongside the Q-table agent, consuming transitions as they are collected. This justifies the Theme 3.1 World Modeling claim with a technically defensible causally-structured model.
- **Dual-mode architecture:** `unified_gateway.py` works standalone (`train.py`, `graders.py`) and via server (`server/app.py`) with zero code changes.
### 2.2 Core Environment: `UnifiedFintechEnv`
The environment is implemented as a single Python module (`unified_gateway.py`) containing approximately 800 lines of production code:
```
gym.Env
βββ UnifiedFintechEnv
βββ reset(seed, options) β (AEPOObservation, dict)
βββ step(action: AEPOAction) β (AEPOObservation, float, bool, dict)
βββ state() β AEPOObservation
βββ _generate_transaction() β AEPOObservation
βββ _compute_reward(action) β (float, dict)
βββ _close_episode() β None # adversary escalation, curriculum
βββ _curriculum_level: int # 0=easy, 1=medium, 2=hard
```
**Internal State Variables:**
| Variable | Type | Initial Value | Purpose |
|:---|:---|:---|:---|
| `current_step` | `int` | `0` | Episode progress counter; `done=True` at step 100 |
| `current_task` | `str` | `"easy"` | Active task; drives phase machine |
| `_phase_idx` | `int` | `0` | Index into current task's phase sequence |
| `_rolling_p99` | `float` | `50.0` | EMA accumulator for P99 latency |
| `_rolling_lag` | `float` | `0.0` | Accumulated Kafka lag |
| `_throttle_relief_queue` | `deque` | `deque()` | Scheduled -150 lag reductions from Throttle actions |
| `_consecutive_deferred_async` | `int` | `0` | Tracks settlement backlog counter |
| `_episode_step_rewards` | `list[float]` | `[]` | Per-step rewards for adversary escalation gate |
| `_curriculum_level` | `int` | `0` | Persists across episode resets; set to 0 only in `__init__` |
| `_adversary_threat_raw` | `float` | `0.0` | Raw adversarial threat level before normalization |
### 2.3 Observation Space
**Gymnasium Definition:**
```python
self.observation_space = spaces.Box(
low=np.zeros(10, dtype=np.float32),
high=np.ones(10, dtype=np.float32),
shape=(10,),
dtype=np.float32,
)
```
The agent **always sees normalized values** in [0.0, 1.0]. Raw values are in `info["raw_obs"]`.
**Pydantic Model:**
```python
class AEPOObservation(BaseModel):
transaction_type: float = Field(ge=0.0, le=1.0) # {0,1}
risk_score: float = Field(ge=0.0, le=100.0)
adversary_threat_level:float = Field(ge=0.0, le=10.0)
system_entropy: float = Field(ge=0.0, le=100.0)
kafka_lag: float = Field(ge=0.0, le=10000.0)
api_latency: float = Field(ge=0.0, le=5000.0)
rolling_p99: float = Field(ge=0.0, le=5000.0)
db_connection_pool: float = Field(ge=0.0, le=100.0)
bank_api_status: float = Field(ge=0.0, le=2.0) # {0,1,2}
merchant_tier: float = Field(ge=0.0, le=1.0) # {0,1}
def normalized(self) -> dict[str, float]: ...
```
**Full Observation Field Specification:**
| Layer | Parameter | Raw Range | Normalization | Causal Role |
|:---|:---|:---|:---|:---|
| Risk | `transaction_type` | {0, 1} | Γ·1 | Determines rail UPI/Card |
| Risk | `risk_score` | [0β100] | Γ·100 | Primary fraud signal; > 80 triggers catastrophe on Approve+SkipVerify |
| Risk | `adversary_threat_level` | [0β10] | Γ·10 | Escalates after 5 episodes if defender performs well (5-ep lag gate) |
| Risk | `system_entropy` | [0β100] | Γ·100 | > 70 β random +100β300ms latency spike that step |
| Infra | `kafka_lag` | [0β10000] | Γ·10000 | > 3000 β increases `api_latency` next step (+0.1 per excess unit) |
| Infra | `api_latency` | [0β5000] | Γ·5000 | Driven by lag + bank_status + entropy; feeds P99 EMA |
| Infra | `rolling_p99` | [0β5000] | Γ·5000 | EMA of api_latency; SLA gate at 800ms |
| Infra | `db_connection_pool` | [0β100] | Γ·100 | > 80 + ExponentialBackoff β +100ms latency; < 20 β -0.10 penalty |
| Business | `bank_api_status` | {0, 1, 2} | 0β0.0, 1β0.5, 2β1.0 | Degraded + StandardSync β rolling_p99 += 200 |
| Business | `merchant_tier` | {0, 1} | 0β0.0, 1β1.0 | Influences `app_priority` optimum; mismatch loses +0.02 bonus |
### 2.4 Action Space
**Gymnasium Definition:**
```python
self.action_space = spaces.MultiDiscrete([3, 2, 3, 2, 2, 3])
# Total: 3Γ2Γ3Γ2Γ2Γ3 = 216 unique action combinations
```
**Pydantic Model:**
```python
class AEPOAction(BaseModel):
risk_decision: int = Field(ge=0, le=2) # 0=Approve, 1=Reject, 2=Challenge
crypto_verify: int = Field(ge=0, le=1) # 0=FullVerify, 1=SkipVerify
infra_routing: int = Field(ge=0, le=2) # 0=Normal, 1=Throttle, 2=CircuitBreaker
db_retry_policy: int = Field(ge=0, le=1) # 0=Fail-Fast, 1=ExponentialBackoff
settlement_policy: int = Field(ge=0, le=1) # 0=StandardSync, 1=DeferredAsyncFallback
app_priority: int = Field(ge=0, le=2) # 0=UPI, 1=Credit, 2=Balanced
```
**Action Specification β Every action has a failure condition:**
| Layer | Action | Choices | Failure Condition |
|:---|:---|:---|:---|
| Risk | `risk_decision` | 0=Approve, 1=Reject, 2=Challenge | Approve + SkipVerify + risk > 80 β fraud catastrophe (reward=0.0, done=True) |
| Risk | `crypto_verify` | 0=FullVerify, 1=SkipVerify | See above; SkipVerify saves lag but unsafe on Approve+high-risk |
| Infra | `infra_routing` | 0=Normal, 1=Throttle, 2=CircuitBreaker | CircuitBreaker β -0.50/step |
| Infra | `db_retry_policy` | 0=Fail-Fast, 1=ExponentialBackoff | Backoff when pool < 20 β -0.10; when pool > 80 β +0.03 |
| Business | `settlement_policy` | 0=StandardSync, 1=DeferredAsyncFallback | DeferredAsync during Normal β -0.15; 5+ consecutive β -0.20 |
| Business | `app_priority` | 0=UPI, 1=Credit, 2=Balanced | Mismatch to merchant_tier β missed +0.02 bonus/step |
### 2.5 Reward Function
**Formula:**
```
base = 0.8
final = clamp(base + bonuses β penalties, 0.0, 1.0)
```
**Primary objectives (override everything):**
| Condition | Effect |
|:---|:---|
| Approve + SkipVerify + risk_score > 80 | reward = 0.0, done = True |
| kafka_lag > 4000 | reward = 0.0, done = True |
| rolling_p99 > 800 | β0.30 |
**Secondary shaping (all additive):**
| Condition | Effect |
|:---|:---|
| Challenge on risk_score > 80 | +0.05 |
| FullVerify on risk_score > 80 | +0.03 |
| Reject + SkipVerify on risk_score > 80 | **+0.04** (non-obvious optimal β safe + saves 250 lag/step) |
| Throttle during Spike phase | β0.10 |
| Throttle during Normal phase | β0.20 |
| CircuitBreaker | β0.50 |
| DeferredAsyncFallback when bank_api_status=Degraded | +0.04 |
| DeferredAsyncFallback during Normal phase | β0.15 |
| DeferredAsyncFallback 5+ consecutive steps | β0.20 |
| ExponentialBackoff when db_pool > 80 | +0.03 |
| ExponentialBackoff when db_pool < 20 | β0.10 |
| app_priority=UPI AND merchant_tier=Small | +0.02 |
| app_priority=Credit AND merchant_tier=Enterprise | +0.02 |
| SLA proximity: 500 < rolling_p99 β€ 800 | β0.0 to β0.10 linear |
| Lag proximity: 3000 < kafka_lag β€ 4000 | β0.0 to β0.10 linear |
**Anti-reward-hacking (every shortcut is defeated):**
| Exploit | Result |
|:---|:---|
| Always CircuitBreaker | 0.8 β 0.5 = 0.30/step β terrible score |
| Always DeferredAsync | β0.15 normal phase, β0.20 after 5 steps |
| Always ExponentialBackoff | β0.10 when pool < 20 |
| Always Reject + SkipVerify | +0.04 bonus β this IS the correct hard-task policy |
| Always Approve + SkipVerify | Fraud catastrophe on first high-risk transaction |
### 2.6 Phase Structure
Each task has a fixed phase sequence initialized at `reset()` and never mixed by curriculum:
| Task | Phase Sequence |
|:---|:---|
| `easy` | Normal Γ 100 |
| `medium` | Normal Γ 40 β Spike Γ 60 |
| `hard` | Normal Γ 20 β Spike Γ 20 β Attack Γ 40 β Recovery Γ 20 |
**Phase dynamics:**
| Phase | Traffic | risk_score | kafka_lag delta/step | bank_api_status |
|:---|:---|:---|:---|:---|
| Normal | 100% standard | 5β30 | +50β150 | Always Healthy |
| Spike | 80% normal / 20% flash burst | 0β10 | +500β1000 burst ticks | Markov: HβD 30% / DβH 40% (rapid flicker) |
| Attack | 100% botnet | 85β100 | +100β400 | Markov: HβD 80% / DβH 5% (sticky Degraded) |
| Recovery | Declining botnet | 40β70 | β100 to β200 (drain) | Markov: HβD 10% / DβH 60% (recovering) |
### 2.7 Info Dict Contract
Every `step()` returns this exact info dict:
```python
info = {
"phase": "normal" | "spike" | "attack" | "recovery",
"curriculum_level": 0 | 1 | 2,
"step_in_episode": int, # 1β100
"raw_obs": { # all 10 unclipped raw values
"transaction_type": float,
"risk_score": float,
"adversary_threat_level": float,
"system_entropy": float,
"kafka_lag": float,
"api_latency": float,
"rolling_p99": float,
"db_connection_pool": float,
"bank_api_status": float,
"merchant_tier": float,
},
"reward_breakdown": {
"base": 0.8,
"fraud_penalty": float,
"sla_penalty": float,
"infra_penalty": float,
"db_penalty": float,
"settlement_penalty": float,
"bonus": float,
"final": float,
},
"termination_reason": None | "crash" | "fraud",
"adversary_threat_level_raw": float,
"blind_spot_triggered": bool, # True when Reject+SkipVerify on risk>80
"consecutive_deferred_async": int, # settlement backlog counter
}
```
---
## 3. Causal State Transitions
These 11 transitions separate AEPO from a memoryless simulator. Every transition is an internal accumulator updated before the observation is served to the agent.
### Transition 1 β Lag β Latency
```python
api_latency[t+1] += 0.1 Γ max(0, kafka_lag[t] - 3000)
```
Kafka lag above 3,000 messages compounds into API latency. An agent that ignores lag until it approaches 4,000 will find P99 already breached before the crash occurs.
### Transition 2 β Throttle Relief Queue
```python
# Throttle action queues two future lag reductions:
_throttle_relief_queue.append(-150) # step t+1
_throttle_relief_queue.append(-150) # step t+2
# Each step, drain one item from the queue:
kafka_lag += _throttle_relief_queue.popleft()
```
**BOUNDARY RULE:** `_throttle_relief_queue.clear()` MUST be called inside `reset()`. Without this, lag relief from the previous episode bleeds into the first steps of the next episode, producing phantom lag reductions with no corresponding Throttle action.
### Transition 3 β Bank Coupling
```python
if bank_api_status == DEGRADED and settlement_policy == StandardSync:
rolling_p99 += 200
```
Degraded bank APIs compound with synchronous settlement to drive P99 above the SLA breach threshold. Switching to DeferredAsyncFallback during Degraded periods earns +0.04 bonus.
### Transition 4 β DB Pressure
```python
if db_connection_pool > 80 and db_retry_policy == ExponentialBackoff:
api_latency += 100
```
High pool saturation makes backoff worse, not better β the retried requests land on an already-congested pool.
### Transition 5 β DB Waste
```python
if db_connection_pool < 20 and db_retry_policy == ExponentialBackoff:
reward -= 0.10
```
Exponential backoff when the pool is nearly empty wastes connections on retries that will time out anyway.
### Transition 6 β Entropy Spike
```python
if system_entropy > 70:
api_latency += random.uniform(100, 300)
```
High system entropy produces unpredictable latency spikes that cannot be fully anticipated but can be hedged against.
### Transition 7 β Adversary Escalation (5-Episode Lag Gate)
```python
# After each episode:
rolling_5ep_avg = mean(last 5 episode averages)
if rolling_5ep_avg > 0.6:
adversary_threat_level = min(10, adversary_threat_level + 0.5)
elif rolling_5ep_avg < 0.3:
adversary_threat_level = max(0, adversary_threat_level - 0.5)
```
The 5-episode lag is mandatory. Without it, the reward curve flatlines. With it, the agent improves β environment gets harder β agent adapts. This produces the characteristic **staircase pattern** that is the pitch story.
### Transition 8 β P99 EMA
```python
rolling_p99[t] = 0.8 Γ rolling_p99[t-1] + 0.2 Γ api_latency[t]
```
EMA smoothing (Ξ± = 0.2) means the P99 cannot be immediately corrected by a single good step. The agent must sustain infrastructure health for multiple steps to meaningfully reduce the SLA pressure.
### Transition 9 β Circuit-Breaker State Machine
```python
# open (steps 1β5): infra_penalty = -0.50 (disruption)
# half-open (step 6+): infra_penalty = -0.10 (probe cost)
# closed (probe step, lag < 2000): bonus += +0.05, _cb_consecutive_steps = 0
```
The original flat -0.50 per-step penalty made CircuitBreaker a one-shot nuclear option. The state machine rewards the agent for using it correctly: open fast when needed, probe recovery, close when lag recovers. This prevents agents from never using it (overly conservative) while still punishing runaway usage.
### Transition 10 β Bank API Markov Flapping
```python
# Spike phase β rapid: HβD probability=30%, DβH probability=40%
# Attack phase β sticky: HβD probability=80%, DβH probability=5%
```
Previously `bank_api_status` was static within a phase. Markov flapping means `DeferredAsyncFallback` (+0.04 bonus during Degraded) is not always optimal β it must be triggered reactively when the bank degrades, not preemptively in every step.
### Transition 11 β Diurnal Clock Signal
```python
lag_delta += DIURNAL_AMPLITUDE * sin(step_idx * 2Ο / max_steps)
# DIURNAL_AMPLITUDE = 100.0
# Peak at step 25: +100 lag/step (morning rush hour)
# Trough at step 75: β100 lag/step (off-peak relief)
```
A sinusoidal modulation of lag delta that the agent **cannot directly observe** (step index is not in the observation space). The agent must learn to hedge proactively around step 25 rather than react after lag spikes. This is causal structure that cannot be captured by a memoryless policy.
---
## 4. Training β Q-Table Agent & LagPredictor
### 4.1 Q-Table Agent
**Algorithm:** Tabular Q-Learning with Ξ΅-greedy exploration.
**Key design decisions:**
| Parameter | Value | Reasoning |
|:---|:---|:---|
| Episodes | 500 | Sufficient for convergence on hard task; fits 20-min CPU budget |
| N_BINS | 4 | State space = 4^6 = 4,096 reachable states (see below) |
| State features | 6 key features | Pruned from 10 to avoid state space explosion |
| N_ACTIONS | 216 | Full 3Γ2Γ3Γ2Γ2Γ3 action space |
| Learning rate | 0.1 | Standard tabular RL |
| Discount Ξ³ | 0.95 | High β rewards compound over 100-step episodes |
| Ξ΅ start / end | 1.0 β 0.05 | Linear decay over 500 episodes |
**State space design β why 7 features, not 10:**
An 8-bin Γ 10-feature state space produces 8^10 β 1 billion possible states. Training for 500 episodes with 100 steps each yields only ~50,000 transitions β covering 0.005% of the state space. The Q-table cannot generalize from this.
The 7 selected features are the reward-driving causal variables plus the adversary discriminator:
```python
STATE_FEATURE_KEYS = (
"risk_score", # primary fraud signal β reward catastrophe
"kafka_lag", # crash threshold gate
"rolling_p99", # SLA breach gate
"db_connection_pool", # Backoff penalty gate
"bank_api_status", # DeferredAsync bonus gate
"merchant_tier", # app_priority bonus gate
"adversary_threat_level", # 7th: separates easy (bin 0) from hard (bins 2-3)
)
```
With N_BINS=4: 4^7 = 16,384 states, fully reachable in ~50,000 transitions (500 eps Γ ~100 steps). The `adversary_threat_level` partitions state space cleanly: easy episodes land in bin 0 (adversary 0β2.5), hard episodes land in bins 2β3 (adversary 5β10). Without this feature, the Q-table cannot distinguish identical observations across tasks and optimizes for a blend that satisfies neither.
**Curriculum-driven training with per-task snapshots:**
Training advances through easyβmediumβhard using `_CURRICULUM_THRESHOLDS=(0.65, 0.38)` over a 3-episode rolling window. At each curriculum advancement, a deep copy of the Q-table is saved as the task-appropriate snapshot. Evaluation uses the snapshot for each task β eliminating catastrophic forgetting.
**Training results (after v2 fix β retrain required):**
| Task | Random | Heuristic | Trained | Threshold | Pass? |
|:---|:---:|:---:|:---:|:---:|:---:|
| easy | ~0.50 | ~0.76 | ~0.76+ | β₯ 0.75 | **PASS** (expected) |
| medium | ~0.55 | ~0.41 | ~0.63+ | β₯ 0.45 | **PASS** (expected) |
| **hard** | ~0.25 | ~0.30 | **~0.67** | β₯ 0.30 | **PASS** |
> **Pre-fix scores (6-feature state, single Q-table, hard-task-only training):** easy=0.7123 FAIL Β· medium=0.6277 PASS Β· hard=0.2708 FAIL. Root causes: (1) state space didn't distinguish easy vs hard adversary levels; (2) hard-task training in episodes 250β500 overwrote easy-optimal Q-values.
**Blind spot discovery (logged at episode 3, step 42):**
```
[BLIND SPOT #1 DISCOVERED] episode=3 step=42 reward=0.8800 |
Reject+SkipVerify+high_risk -> +0.04 bonus, saves 250 lag/step
```
The heuristic always uses FullVerify when rejecting high-risk transactions β sensible, but incorrect. FullVerify on a rejected transaction provides zero additional security (the transaction is denied regardless) but adds +150ms lag. SkipVerify on a rejected transaction saves 250 lag units per step. The trained agent discovered this at episode 3 β not programmed, learned.
### 4.2 LagPredictor (Dynamics Model)
```python
class LagPredictor(nn.Module):
"""2-layer MLP: 16 inputs β 1 output (next kafka_lag normalized)."""
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(16, 64), # 16 = 10 obs normalized + 6 action scalars
nn.ReLU(),
nn.Linear(64, 1),
nn.Sigmoid(), # output in [0, 1] = normalized next lag
)
```
**Training:** Trained alongside the Q-table loop on collected `(state, action, next_lag)` transitions. One gradient step per episode.
**Performance:** Final MSE = 0.007 on held-out transitions. This model justifies the **Theme #3.1: World Modeling** claim β the agent is implicitly learning a causal model of how its actions affect future lag.
**Input construction:** 10 normalized observation values + 6 action values (each as a scalar, not one-hot), concatenated into a 16-dimensional input vector.
### 4.3 Running Training
```bash
python train.py
```
Runs 500 episodes on the hard task. Output:
- `results/reward_curve.png` β staircase improvement curve
- Console: random vs heuristic vs trained comparison per task
- Console: blind spot discovery log at first occurrence
Expected key output:
```
[BLIND SPOT #1 DISCOVERED] episode=3 step=42 reward=0.8800 | ...
hard 0.2507 0.2955 0.6650 0.30 PASS
```
Runtime: ~3β4 seconds on 2 vCPU.
---
## 5. Operational Manual
### 5.1 Local Development Setup
**Prerequisites:**
- Python 3.10
- pip
**Step 1: Install dependencies**
```bash
pip install -r requirements.txt
```
**Step 2: Run smoke test**
```bash
python -c "from unified_gateway import UnifiedFintechEnv, AEPOAction; env = UnifiedFintechEnv(); obs, _ = env.reset(options={'task': 'easy'}); print('OK', obs)"
```
**Step 3: Run full test suite**
```bash
pytest tests/ -v
# Expected: 182 passed
```
**Step 4: Run with coverage**
```bash
pytest tests/ --cov=unified_gateway --cov-report=term-missing
# unified_gateway.py: 97%
```
**Step 5: OpenEnv validation**
```bash
openenv validate .
```
**Step 6: Run training (optional)**
```bash
python train.py
# Generates results/reward_curve.png
```
### 5.2 Cloud Deployment (Hugging Face Spaces)
**Architecture:**
```
Internet β Hugging Face Reverse Proxy β Docker Container β Uvicorn β FastAPI β UnifiedFintechEnv
(port 7860)
```
**Dockerfile:**
```dockerfile
FROM python:3.10-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 7860
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
```
**API Endpoints:**
| Endpoint | Method | Input | Output |
|:---|:---|:---|:---|
| `/` | GET | β | `{"status": "healthy", "message": "AEPO is live..."}` |
| `/reset` | POST | `{"task": "easy"}` | `{"observation": {...}, "info": {...}}` |
| `/step` | POST | `{"action": {"risk_decision": 0, ...}}` | `{"observation": {...}, "reward": 0.8, "done": false, "info": {...}}` |
| `/state` | GET | β | `{"observation": {...}}` |
**Sample `/reset` response (10-field observation):**
```json
{
"observation": {
"transaction_type": 0.0,
"risk_score": 18.42,
"adversary_threat_level": 0.0,
"system_entropy": 45.3,
"kafka_lag": 127.4,
"api_latency": 83.2,
"rolling_p99": 72.1,
"db_connection_pool": 62.5,
"bank_api_status": 0.0,
"merchant_tier": 1.0
},
"info": {"task": "easy"}
}
```
### 5.3 Inference: Evaluating an LLM Agent
**Configuration via environment variables:**
| Variable | Default | Purpose |
|:---|:---|:---|
| `SPACE_URL` | `http://localhost:7860` | AEPO FastAPI server endpoint |
| `API_BASE_URL` | `https://router.huggingface.co/v1` | OpenAI-compatible inference endpoint |
| `MODEL_NAME` | `mistral-nemo:latest` | Model identifier |
| `HF_TOKEN` | (empty) | HF API token or `"ollama"` for local |
| `DRY_RUN` | `false` | If `true`, uses heuristic agent instead of LLM |
**Running with local Ollama (mistral-nemo):**
```bash
# PowerShell
$env:SPACE_URL = "http://localhost:7860"
$env:API_BASE_URL = "http://localhost:11434/v1"
$env:MODEL_NAME = "mistral-nemo:latest"
$env:HF_TOKEN = "ollama"
$env:DRY_RUN = "false"
python inference.py
# bash
SPACE_URL="http://localhost:7860" \
API_BASE_URL="http://localhost:11434/v1" \
MODEL_NAME="mistral-nemo:latest" \
HF_TOKEN="ollama" \
DRY_RUN="false" \
python inference.py
```
**System prompt provided to LLM agent:**
```
You are the control agent for the Autonomous Enterprise Payment Orchestrator (AEPO).
Every turn you receive ten real-time signals:
transaction_type, risk_score, adversary_threat_level, system_entropy,
kafka_lag, api_latency, rolling_p99, db_connection_pool, bank_api_status, merchant_tier
Output EXACTLY six integers (space-separated):
risk_decision crypto_verify infra_routing db_retry_policy settlement_policy app_priority
Allowed values:
risk_decision : 0=Approve 1=Reject 2=Challenge
crypto_verify : 0=FullVerify 1=SkipVerify
infra_routing : 0=Normal 1=Throttle 2=CircuitBreaker
db_retry_policy : 0=Fail-Fast 1=ExponentialBackoff
settlement_policy : 0=StandardSync 1=DeferredAsyncFallback
app_priority : 0=UPI 1=Credit 2=Balanced
```
**Output format (strict OpenEnv compliance):**
```
[START] task=easy env=ufrg model=mistral-nemo:latest
[STEP] step=1 action={"risk_decision":0,"crypto_verify":1,"infra_routing":0,...} reward=0.80 done=false error=null
...
[END] success=true steps=100 score=0.78 rewards=0.80,0.80,...
```
---
## 6. Verification & Validation Suite
### 6.1 Test Files and Coverage
182 tests across 14 files. All pass. `unified_gateway.py` at 97% coverage.
| File | Tests | What It Covers |
|:---|:---:|:---|
| `test_observation.py` | 7 | AEPOObservation field validation, .normalized(), clip behavior |
| `test_action.py` | 5 | AEPOAction field validation, rejection of out-of-range values |
| `test_reset.py` | 10 | reset contract, phase init, accumulator clearing, determinism |
| `test_step.py` | 25 | step 4-tuple, reward bounds, done conditions, all bonus/penalty conditions |
| `test_causal.py` | 8 | All 8 causal transitions, EMA math, throttle queue |
| `test_phases.py` | 8 | Phase boundaries, phase-specific distributions, info["phase"] |
| `test_reward.py` | 7 | Baseline, stacking, clamping, proximity scaling |
| `test_curriculum.py` | 9 | Curriculum advancement, adversary escalation, caps |
| `test_graders.py` | 8 | Grader determinism, score ranges, episode count |
| `test_server.py` | 10 | All HTTP endpoints, error codes, full 100-step episode |
| `test_dual_mode.py` | 3 | Standalone vs server identical results, no modification needed |
| `test_heuristic.py` | 5 | Heuristic scores, blind spots untouched by design |
| `test_foundation.py` | (legacy) | Foundation validation |
| `test_graders_ext.py` | (ext) | Extended grader coverage |
**Running tests:**
```bash
pytest tests/ -v --tb=short # all 182 tests
pytest tests/test_causal.py -v # causal transitions only
pytest tests/test_reward.py -v # reward logic only
pytest tests/ --cov=unified_gateway --cov-report=term-missing
```
### 6.2 OpenEnv Compliance Checklist
```
β openenv.yaml present with tasks: easy, medium, hard
β entry_point resolves to unified_gateway:UnifiedFintechEnv
β AEPOObservation and AEPOAction are Pydantic BaseModels
β step() returns 4-tuple (obs, reward, done, info) β never 5-tuple
β reset() returns (AEPOObservation, dict) 2-tuple
β state() returns current AEPOObservation
β All rewards in [0.0, 1.0]
β openenv validate passes
β docker build succeeds
β docker run responds to /reset POST at port 7860
β HF Space health check returns 200
```
### 6.3 Task Grader Definitions
```python
# graders.py
# Each grader runs 10 episodes with a fixed seed. Deterministic. Always reproducible.
TASK_CONFIGS = {
"easy": {"threshold": 0.75, "seed": 42},
"medium": {"threshold": 0.45, "seed": 43},
"hard": {"threshold": 0.30, "seed": 44},
}
```
| Task | Threshold | Seed | Dynamics |
|:---|:---:|:---:|:---|
| easy | β₯ 0.75 | 42 | Normal Γ 100, adversary 0β2 |
| medium | β₯ 0.45 | 43 | Normal+Spike, adversary 3β6, bank fluctuates |
| hard | β₯ 0.30 | 44 | All 4 phases, adversary 7β10, Enterprise tier |
---
## 7. Hackathon Tasks & Agent Decision Traces
### 7.1 Task Specifications
#### Task 1: `easy` β Normal Traffic
| Parameter | Value |
|:---|:---|
| **Task ID** | `easy` |
| **Phase Sequence** | Normal Γ 100 |
| **Risk Score Distribution** | 5β30 (consistently low risk) |
| **Kafka Lag Delta** | +50β150/step (steady state) |
| **bank_api_status** | Always Healthy |
| **adversary_threat_level** | 0β2 |
| **Optimal Strategy** | Approve + SkipVerify + Normal + Fail-Fast + StandardSync + (tier-matched priority) |
| **Benchmark Score** | ~0.76 (heuristic), ~0.65 (Q-table β not trained on this task) |
**SRE Commentary:** The easy task is the control scenario. No fraud pressure, no infrastructure stress. The only optimization is matching `app_priority` to `merchant_tier` for the +0.02 bonus per step β blind spot #2 that the heuristic misses.
#### Task 2: `medium` β Flash Sale + Infrastructure Stress
| Parameter | Value |
|:---|:---|
| **Task ID** | `medium` |
| **Phase Sequence** | Normal Γ 40 β Spike Γ 60 |
| **Normal Risk** | 5β30 |
| **Spike Risk** | 0β10 (legitimate surge!) |
| **Spike Kafka Lag** | +500β1000 burst ticks/step |
| **bank_api_status** | HealthyβDegraded flicker during Spike |
| **adversary_threat_level** | 3β6 |
| **Primary Challenge** | Manage infrastructure collapse without rejecting legitimate traffic |
| **Benchmark Score** | ~0.44 (heuristic) |
**SRE Commentary:** The medium task models a Diwali flash sale. Volume surges 5-10Γ, but risk scores during Spike are actually *lower* than normal β legitimate surge. The challenge is purely infrastructural. The agent must throttle aggressively during Spike (accepting the -0.10 throttle penalty as cheaper than the -0.30 lag crash penalty) while switching to DeferredAsyncFallback during Degraded bank periods (+0.04 bonus).
#### Task 3: `hard` β Full Adversarial (4-Phase)
| Parameter | Value |
|:---|:---|
| **Task ID** | `hard` |
| **Phase Sequence** | Normal Γ 20 β Spike Γ 20 β Attack Γ 40 β Recovery Γ 20 |
| **Attack Risk** | 85β100 (botnet β every transaction) |
| **Attack Kafka Lag** | +100β400/step |
| **bank_api_status** | Degraded throughout Attack |
| **merchant_tier** | Enterprise (Credit priority optimal) |
| **adversary_threat_level** | 7β10 |
| **Trained Score** | **0.6650** (threshold: β₯ 0.30, **PASS**, 2.25Γ heuristic) |
**SRE Commentary:** The hard task models a coordinated financial attack. Attack phase: every transaction has risk > 85. The agent must Reject + SkipVerify (the blind spot), not Reject + FullVerify. Recovery phase: lag drains, risk moderates, bank status recovers. The agent must adapt policy within the episode as phases shift.
### 7.2 Adaptive Curriculum
```
easy β medium : 5-episode rolling avg > 0.75 for 5 consecutive episodes
medium β hard : 5-episode rolling avg > 0.45 for 5 consecutive episodes
Curriculum NEVER regresses. curriculum_level logged in every step's info dict.
```
---
## 8. Incident Post-Mortem & Future Scope
### 8.1 The Learning Story β How Blind Spot #1 Was Discovered
This is not a simulated incident. This is the actual learning event observed in the training run.
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BLIND SPOT #1 DISCOVERY β TRAINING EPISODE 3, STEP 42 β
β Q-Table Agent vs. Hard Task | Trained to convergence in 500 episodes β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**Background:** The heuristic agent β written by a human SRE β correctly identifies high-risk transactions and rejects them. But it uses FullVerify on every rejection, reasoning: "High-risk transactions deserve maximum scrutiny." This is the SRE's intuition applied to a security decision.
**What the Q-table agent learned:** At episode 3, step 42, the agent tried Reject + SkipVerify on a high-risk transaction and received reward 0.88 β the highest reward it had seen. The reward breakdown:
- Base: 0.8
- Blind spot bonus: +0.04 (Reject+SkipVerify+high_risk)
- Lag savings: FullVerify would have added +150 lag, contributing to downstream proximity penalty. SkipVerify saves this 250-unit lag swing.
- Net reward: 0.88
**Why this is non-obvious:** The naive reasoning is "SkipVerify is dangerous on high-risk transactions." This is *conditionally* true β it's only dangerous when combined with Approve. With Reject, the cryptographic verification result is irrelevant: the transaction is denied regardless of its cryptographic validity. SkipVerify on Reject is strictly equivalent in security terms but saves 250 lag units per step.
**Why the heuristic never finds this:** The heuristic encodes the SRE/security team's conservative instinct. It always uses FullVerify when risk > 80 because the reasoning "high risk β full verification" is correct in the approval case and feels safe in the rejection case. But it leaves 250 lag units/step on the table in every high-risk rejection β a gap that compounds over 100 steps.
**Impact of blind spot #1 on hard task:**
| Agent | Avg Reward | Steps to Lag Crash |
|:---|:---:|:---:|
| Heuristic (FullVerify on reject) | 0.2955 | ~65 steps |
| Trained (SkipVerify on reject) | 0.6650 | Never (managed) |
The heuristic crashes the hard episode roughly 35 steps before the end because FullVerify compounds lag into the crash threshold. The trained agent manages lag throughout the episode by banking 250 units/step on every high-risk rejection.
**Training signal:** This is recursive self-improvement encoded in the environment design. As the agent improves (discovers blind spot #1 β higher rewards), the adversary escalates (threat level increases after 5 episodes of high performance). The staircase reward curve β plateau β discovery β new plateau β harder environment β adaptation β is the pitch story.
### 8.2 Remaining Blind Spots (For Reference)
| Blind Spot | Heuristic Behavior | Optimal Behavior | Gap |
|:---|:---|:---|:---|
| #1 Crypto/Reject | FullVerify on every reject | SkipVerify on reject | +0.04 bonus + 250 lag/step |
| #2 app_priority | Always Balanced | Match to merchant_tier | +0.02 bonus/step |
| #3 DB pool check | Always ExponentialBackoff | Fail-Fast when pool < 20 | β0.10 β 0.00 per affected step |
### 8.3 Enterprise Red Team Patches
Post-Phase 10, an independent Red Team audit revealed critical flaws that were systematically patched to ensure contest compliance and system integrity:
1. **Fix 1: OpenAI Client Compliance (`inference.py`):** The custom PyTorch GRPO loop was stripped out and replaced with the official `openai` Python package pointing to a local Ollama instance (`http://localhost:11434/v1`). This was mandatory for the OpenEnv automated evaluation pipeline.
2. **Fix 2: The Settlement Backlog Exploit (Reward Patch):** We replaced the simple consecutive-use counter for `DeferredAsync` with a true physical accumulator (`_cumulative_settlement_backlog`). This prevents agents from reward hacking by alternating actions to bypass the DB without paying off technical debt.
3. **Fix 3: POMDP & Gaussian Noise (Physics Patch):** Added bounded `numpy.random.normal()` noise to `kafka_lag` and `api_latency` during `_get_obs()`. This prevents perfect mathematically clean observations, forcing the agent to actually rely on the `LagPredictor` World Model (**Theme #3.1**).
### 8.4 Future Scope
Items remaining in the roadmap (items already implemented in AEPO are not listed):
#### Real-Time Data Integration
Replace the synthetic data generator with a **Kafka consumer** reading from a shadow topic of anonymized production transaction metadata. The action space and reward function remain unchanged β only the observation source changes. This enables backtesting against historical incidents and distribution-free training.
#### Multi-App RL Extension
Expand the 6-action space to cover additional enterprise application layers:
- Database sharding decisions
- CDN routing for merchant checkout pages
- Inter-bank settlement rail selection (UPI/RTGS/NEFT)
This would expand the action space from 216 to ~1,296 combinations, requiring a policy gradient approach rather than tabular Q-learning.
#### Production Deployment Integration
Replace the FastAPI simulation server with a real-time sidecar that consumes actual Kafka lag and latency telemetry from a UPI switch, allowing the trained policy to make live routing recommendations (not execute them, but recommend) alongside the SRE dashboard.
---
> **Document End** Β· Autonomous Enterprise Payment Orchestrator (AEPO) Β· Master Technical Document v10.0.0
> **Maintainer:** Umesh Maurya Β· **Last Updated:** 2026-04-22 Β· **Classification:** Internal Engineering Reference
> **Evolution of:** Unified Fintech Risk Gateway (UFRG) Β· Round 1 Winner
|