Initial: Mesh-Skills, Configs, Scripts
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
task_agent_matcher.py
|
||||
|
||||
Ebene 3 des Skills: läuft bei Agent A (Master) zur Laufzeit, sobald ein
|
||||
neuer Task ansteht. Entscheidet, welcher Agent bei welchem Standard-Modell
|
||||
am besten zum Task-Typ passt - basierend auf:
|
||||
|
||||
- der von daily_model_check.py gebauten Prioritätsliste
|
||||
(state/model_priority.json)
|
||||
- aktiven Kill-Switches (state/kill_switches.json), z.B. wegen
|
||||
Budget-Überschreitung -> Agent wird übersprungen bzw. auf
|
||||
"postpone_to_next_day" gesetzt
|
||||
- den Capability-Tags pro Agent (config/agents.yaml)
|
||||
|
||||
Jeder Agent bekommt exakt dieses Konzept (Config + Prioritätsliste)
|
||||
mitgeliefert, damit alle konsistent nach derselben Logik laufen, egal
|
||||
wer die Entscheidung am Ende ausführt.
|
||||
|
||||
Aufruf (Beispiel):
|
||||
python3 task_agent_matcher.py --task-tag javascript
|
||||
python3 task_agent_matcher.py --task-tag homebrew_code
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
CONFIG_PATH = Path(__file__).parent.parent / "config" / "agents.yaml"
|
||||
STATE_DIR = Path(__file__).parent.parent / "state"
|
||||
PRIORITY_PATH = STATE_DIR / "model_priority.json"
|
||||
KILL_SWITCH_PATH = STATE_DIR / "kill_switches.json"
|
||||
|
||||
|
||||
def load_yaml(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def load_json(path, default=None):
|
||||
if not path.exists():
|
||||
return default if default is not None else {}
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def find_matches(task_tag, config, priority, kill_switches):
|
||||
"""Gibt eine nach Preis sortierte Liste von (agent, model, price) für
|
||||
Agents zurück, deren Capability-Tags zum Task-Typ passen und die
|
||||
NICHT gerade per Kill-Switch auf 'postpone_to_next_day' stehen.
|
||||
"""
|
||||
candidates = []
|
||||
postponed = []
|
||||
|
||||
for agent_name, agent_cfg in config["agents"].items():
|
||||
if task_tag not in agent_cfg.get("capability_tags", []):
|
||||
continue
|
||||
|
||||
if agent_name in kill_switches.get("kill_switches", {}):
|
||||
ks = kill_switches["kill_switches"][agent_name]
|
||||
postponed.append({
|
||||
"agent": agent_name,
|
||||
"reason": ks["reason"],
|
||||
"action": ks["action"],
|
||||
"fallback": ks.get("basic_questions_fallback"),
|
||||
})
|
||||
continue
|
||||
|
||||
ladder = priority.get("agents", {}).get(agent_name, [])
|
||||
for step in ladder:
|
||||
candidates.append({
|
||||
"agent": agent_name,
|
||||
"model": step["model"],
|
||||
"cost_factor": step.get("cost_factor"),
|
||||
"price_per_1k_tokens_eur": step.get("price_per_1k_tokens_eur"),
|
||||
})
|
||||
|
||||
# Günstigste zuerst; Steps ohne Preisangabe (z.B. Free-Router) ganz vorne
|
||||
candidates.sort(key=lambda c: (c["price_per_1k_tokens_eur"] is not None, c.get("price_per_1k_tokens_eur") or 0))
|
||||
|
||||
return candidates, postponed
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Task-zu-Agent-Matching (Ebene 3)")
|
||||
parser.add_argument("--task-tag", required=True, help="Capability-Tag des Tasks, z.B. javascript, homebrew_code")
|
||||
args = parser.parse_args()
|
||||
|
||||
config = load_yaml(CONFIG_PATH)
|
||||
priority = load_json(PRIORITY_PATH)
|
||||
kill_switches = load_json(KILL_SWITCH_PATH)
|
||||
|
||||
if not priority:
|
||||
print("FEHLER: state/model_priority.json fehlt - bitte zuerst daily_model_check.py laufen lassen")
|
||||
return
|
||||
|
||||
candidates, postponed = find_matches(args.task_tag, config, priority, kill_switches)
|
||||
|
||||
if not candidates:
|
||||
local_fb = config.get("local_fallback", {})
|
||||
if args.task_tag in local_fb.get("allowed_for_task_tags", []):
|
||||
print(f"Kein Cloud-Kandidat für '{args.task_tag}' verfügbar (evtl. alle betroffenen Agents "
|
||||
f"per Kill-Switch verschoben) - lokaler Fallback erlaubt:")
|
||||
print(f" -> {local_fb['model']} (kostenfrei, VPS-Ollama)")
|
||||
else:
|
||||
print(f"Kein Kandidat für '{args.task_tag}' verfügbar und lokaler Fallback für diesen "
|
||||
f"Task-Typ NICHT erlaubt (siehe config: local_fallback.allowed_for_task_tags). "
|
||||
f"Task sollte pausiert werden.")
|
||||
for p in postponed:
|
||||
print(f" (verschoben: {p['agent']} - {p['reason']}, Fallback: {p['fallback']})")
|
||||
return
|
||||
|
||||
best = candidates[0]
|
||||
print(f"Beste Wahl für Task-Tag '{args.task_tag}':")
|
||||
print(f" Agent: {best['agent']}")
|
||||
print(f" Modell: {best['model']} (Kostenfaktor {best['cost_factor']}x)")
|
||||
|
||||
if len(candidates) > 1:
|
||||
print("\nWeitere passende Kandidaten (Eskalationsreserve):")
|
||||
for c in candidates[1:]:
|
||||
print(f" - {c['agent']} / {c['model']} (Faktor {c['cost_factor']}x)")
|
||||
|
||||
if postponed:
|
||||
print("\nHinweis, folgende Agents sind aktuell wegen Budget verschoben:")
|
||||
for p in postponed:
|
||||
print(f" - {p['agent']}: {p['reason']} -> {p['action']} (Grundfragen: {p['fallback']})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user