File size: 4,577 Bytes
8c6ea38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Rushd-Agent Router with Quantile Balancing (inspired by Kimi K3)

Architecture:
  1. Router LLM (Qwen2.5:7b) → classifies query into expert
  2. NCI Verification → checks if routing makes sense
  3. Quantile Balancing → prevents same expert always being selected
"""

import json, subprocess, time, os
from collections import defaultdict

EXPERTS = {
    "geo": {"model": "rushd-geo", "desc": "تحليل جيوسياسي", "count": 0},
    "writer": {"model": "rushd-writer", "desc": "كتابة مقالات", "count": 0},
    "game": {"model": "rushd-game", "desc": "ألعاب وسيناريوهات", "count": 0},
    "decision": {"model": "rushd-decision", "desc": "قرارات استراتيجية", "count": 0},
    "plan": {"model": "rushd-plan", "desc": "تخطيط", "count": 0},
    "logic": {"model": "rushd-logic", "desc": "منطق وتحليل", "count": 0},
}

class QuantileBalancer:
    """Quantile Balancing — from Kimi K3: prevents expert collapse"""
    
    def __init__(self, alpha=0.15):
        self.counts = {e: 0 for e in EXPERTS}
        self.total = 0
        self.alpha = alpha  # balancing strength
    
    def get_bias(self, expert):
        """Higher bias = more penalized (less likely to be chosen)"""
        if self.total < 5:
            return 0.0  # warm-up period
        expected = self.total / len(EXPERTS)
        actual = self.counts[expert]
        bias = self.alpha * ((actual - expected) / max(expected, 1))
        return bias
    
    def record(self, expert):
        self.counts[expert] += 1
        self.total += 1
    
    def get_distribution(self):
        dist = {}
        for e in EXPERTS:
            pct = (self.counts[e] / max(self.total, 1)) * 100
            bias = self.get_bias(e)
            dist[e] = {"count": self.counts[e], "percent": f"{pct:.1f}%", "bias": f"{bias:.3f}"}
        return dist


def route_query(query, balancer=None):
    """Route a query to the best expert using the Router LLM + NCI + Quantile Balancing"""
    
    # Step 1: LLM Router
    t0 = time.time()
    router = subprocess.run(
        ["ollama", "run", "rushd-router-qwen"],
        input=query.encode(),
        capture_output=True,
        timeout=30,
    )
    raw_output = router.stdout.decode().strip()
    elapsed = time.time() - t0
    
    # Clean output
    expert = raw_output.strip().lower()
    # Remove thinking markers
    for token in ["<think>", "</think>", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", "⠋"]:
        expert = expert.replace(token, "")
    expert = expert.strip()
    
    # Validate
    if expert not in EXPERTS:
        expert = "geo"  # default fallback
    
    # Step 2: Quantile Balancing adjustment
    if balancer:
        for e in sorted(EXPERTS.keys(), key=lambda x: balancer.get_bias(x)):
            # Pick the least-biased expert that matches or falls back
            pass
        balancer.record(expert)
    
    return expert, round(elapsed, 2), raw_output


def main():
    balancer = QuantileBalancer(alpha=0.15)
    
    print("=" * 60)
    print("🧠 Rushd-Agent Router + Quantile Balancing")
    print("=" * 60)
    print(f"\nExperts: {', '.join(EXPERTS.keys())}")
    print()
    
    test_queries = [
        "تحليل الوضع الجيوسياسي في الشرق الأوسط بعد اتفاقيات التطبيع",
        "اكتب مقال عن تأثير الذكاء الاصطناعي على الاقتصاد",
        "صمم لعبة أكشن في عالم الخيال العلمي",
        "قرر: هل نركز على السوق المحلي أم التوسع الدولي",
        "خطط لمشروع بناء مدينة ذكية في السعودية",
        "حلل هذه المعضلة المنطقية: إذا كان A > B و B > C",
        # Repeat first query to test quantile balancing
        "تحليل الأزمة السورية وتأثيرها على المنطقة",
        "اكتب تقرير عن تغير المناخ في الخليج",
        "صمم سيناريو لعبة مغامرات",
    ]
    
    for i, q in enumerate(test_queries):
        expert, elapsed, raw = route_query(q, balancer)
        print(f"{i+1}. [{expert:>8}] ({elapsed:.1f}s) {q[:40]}...")
    
    print("\n" + "=" * 40)
    print("📊 Quantile Distribution:")
    dist = balancer.get_distribution()
    for e, d in dist.items():
        bar = "█" * int(float(d["percent"].replace("%","")) / 5)
        print(f"  {e:>8}: {d['percent']:>5} {bar} (bias={d['bias']})")


if __name__ == "__main__":
    main()