- Create docs/ with 7 focused files: architecture, api, scoring, agent, deployment, freeswitch, development - Rewrite README.md as concise overview with links to docs/ - Remove AGENTS.md (content merged into docs/ and README)
2.0 KiB
2.0 KiB
Scoring Engine
Вычисляет score (0..100) для каждой ноды. Ноды со score = -100 исключаются из роутинга.
Lethal Conditions (score → -100)
Если хотя бы одно условие истинно — нода исключается: If at least one is true — node is excluded from routing.
| Condition | Threshold |
|---|---|
status != "ok" |
Any status other than ok |
| Stale | > stale_threshold_sec (default 20s) |
active_calls >= max_calls |
100% capacity used |
idle_cpu < idle_cpu_min |
< 5% |
call_failure_rate > call_failure_rate_lethal |
> 15% |
disabled == true |
Manual admin disable |
Weighted Score (0..100)
call_score = clamp(100 - (active_calls / max_calls * 100), 0, 100)
load_score = clamp(100 - (load_avg * 50), 0, 100)
idle_score = clamp(idle_cpu, 0, 100)
fail_score = clamp(100 - call_failure_rate, 0, 100)
score = call_score × 0.40 +
load_score × 0.30 +
idle_score × 0.20 +
fail_score × 0.10
Weight defaults (configurable in config.json): call 40%, load 30%, idle 20%, fail 10%.
Example
Node pbx-01: active_calls=42, max_calls=250, load_avg=0.85, idle_cpu=35, fail_rate=0.5
call_score = clamp(100 - (42/250)*100, 0, 100) = 83.2
load_score = clamp(100 - 0.85*50, 0, 100) = 57.5
idle_score = clamp(35, 0, 100) = 35.0
fail_score = clamp(100 - 0.5, 0, 100) = 99.5
score = 83.2×0.40 + 57.5×0.30 + 35.0×0.20 + 99.5×0.10 = 70.1
Caching
best_node_id, best_score, and fallback_active are recalculated on every metric update.
/api/route reads them under RLock — O(1) constant time.
Configuration
{
"stale_threshold_sec": 20,
"scoring": {
"idle_cpu_min": 5,
"call_failure_rate_lethal": 15.0,
"weights": {
"call_score": 0.40,
"load_score": 0.30,
"idle_score": 0.20,
"fail_score": 0.10
}
}
}
Weights must sum to 1.0. All thresholds configurable in config.json.