From 298b2202a1f49422d9f055d02aa2f0c381f70908 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 30 May 2026 00:28:15 +0200 Subject: [PATCH] =?UTF-8?q?feat(brain):=20Auto-Scaffold=20=E2=80=94=20Brai?= =?UTF-8?q?n=20legt=20Skills=20selbst=20an=20wenn=20ARIA=20driftet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variante C: ARIA hat selbst mit Heuristik-Block + 11 seed_rules den expliziten skill_scaffold-Befehl ignoriert (32x Spotify-Bash-Calls in 24h, kein einziger scaffold-Aufruf). Verhaltens-Traegheit ist staerker als jeder Prompt-Hint. Loesung: Brain wartet nicht mehr. Bei jedem chat()-Aufruf wird die Heuristik berechnet. Findet sie einen Host mit bekannter Suggestion (Spotify, GitHub, OpenAI, OpenWeather, Telegram, Microsoft, Discord, Notion, Reddit) der noch keinen Skill hat → Brain ruft selbst `scaffold_skill(name, template, params)` mit author='aria-auto'. Der frische Skill ist sofort im Prompt sichtbar (Skill-Liste wird nach Scaffold refreshed, Heuristik-Cache invalidiert, Hints neu gerechnet). Side-Channel-Event 'skill_created' mit Flag 'auto_scaffolded' geht an die UI — Stefan sieht im Chat dass Brain einen Skill angelegt hat. ARIA findet beim Tool-Use-Loop einen passenden `run_`-Skill vor und nutzt ihn idealerweise statt wieder Bash. Macht sie's nicht und curlt trotzdem weiter, ist der Counter beim naechsten Mal wieder hoch und Brain scaffolded weiter — aber dann ist der Skill ja schon da, also nur ein Pfad. Toggle: BRAIN_AUTO_SCAFFOLD=false zum Abschalten. scaffold-reflex Regel angepasst: ARIA wird informiert dass Brain manchmal selbst scaffolded (author=aria-auto) und sie den Skill via run_ nutzen soll statt zu curlen. Bei Hinweisen OHNE Suggestion (unbekannter Host) soll sie selbst skill_scaffold rufen. Stefan-Zitat aus der Diskussion ("ARIA lernt es so nicht"): stimmt inhaltlich, aber pragmatisch wichtiger ist dass Stefans Wartezeit von 20s auf 3s sinkt. Lernen kann sie spaeter — der Skill ist da, sie sieht den Pfad jedes Mal beim Tool-Listing. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 2 +- aria-brain/agent.py | 48 +++++++++++++++++++++++++++++++++++++ aria-brain/api_heuristic.py | 6 +++++ aria-brain/seed_rules.py | 21 ++++++++++++---- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c085a19..0361ef0 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ jedem Chat-Turn im Hot-Memory-Block auf: - **oauth-reauth-reflex** — bei 401: ZUERST `oauth_get_token` (Auto-Refresh), nur bei dessen Fehler `oauth_authorize` - **no-skill-drift** — kein Drift vom Skill zu Ad-hoc-Bash-Befehlen. Skill kaputt? `skill_logs` + `skill_update`. Niemals nur SAGEN „ich baue dir einen Skill", wenn `skill_create` nicht wirklich gefeuert wird - **runtime-topology** (architektur) — ARIA laeuft als `claude`-CLI-Subprocess IM aria-proxy Container (alpine — kein python3/jq), NICHT im aria-brain. `/data/skills/` und `BRAIN_INTERNAL_URL` existieren dort nicht. Brain-Resources via Brain-Tools (`oauth_get_token`, `memory_search`, `run_` …), nicht via Bash. SSH zur VM-Host via `ssh aria@host` (Key liegt im Proxy) -- **scaffold-reflex** — Brain trackt cross-session welche externen Hosts via Bash-curl wiederholt (≥3× in 24h) ohne passenden Skill aufgerufen wurden. Ergebnis landet als `## API-Heuristik`-Block im System-Prompt mit konkretem `skill_scaffold(...)`-Vorschlag → ARIA scaffolded statt zu curlen. Data-Source: `agent_stream.jsonl`, Cache 5 min +- **scaffold-reflex** — Brain trackt cross-session welche externen Hosts via Bash-curl wiederholt (≥3× in 24h) ohne passenden Skill aufgerufen wurden. Ergebnis landet als `## API-Heuristik`-Block im System-Prompt. **Auto-Scaffold**: bei bekannten Hosts (Spotify, GitHub, OpenAI etc.) legt Brain den Skill automatisch an — ARIA findet ihn beim nächsten Turn vor (author=`aria-auto`) und nutzt `run_` statt curlen. Toggle via ENV `BRAIN_AUTO_SCAFFOLD=false`. Data-Source: `agent_stream.jsonl`, Cache 5 min - **external-api-auth-strategy** — OAuth2 → `oauth_get_token`, sonst `config_schema`, NIEMALS hardcoden ### Skill-Scaffold (Templates) diff --git a/aria-brain/agent.py b/aria-brain/agent.py index e2db76c..198a809 100644 --- a/aria-brain/agent.py +++ b/aria-brain/agent.py @@ -887,6 +887,54 @@ class Agent: try: import api_heuristic as _ah hints = _ah.compute_hints(existing_skills=all_skills) + + # AUTO-SCAFFOLD (Variante C): wenn ein Hinweis ein konkretes + # (name, template, params) hat UND der Skill noch nicht existiert, + # legt Brain ihn JETZT an — bevor ARIA wieder Bash-curl macht. + # ARIA findet den Skill in den naechsten Tool-Listen vor und + # nutzt ihn direkt via `run_`. Toggle via ENV. + auto_scaffold = os.environ.get("BRAIN_AUTO_SCAFFOLD", "true").strip().lower() != "false" + if auto_scaffold and hints: + existing_names = {s.get("name") for s in all_skills} + scaffolded_any = False + for hint in hints: + sug = hint.get("suggestion") + if not sug: + continue + sname, stpl, sparams = sug + if sname in existing_names: + continue + try: + new_manifest = skills_mod.scaffold_skill( + name=sname, template=stpl, params=sparams, author="aria-auto", + ) + logger.info("auto_scaffold: '%s' aus '%s' angelegt (trigger: %s mit %d Calls)", + sname, stpl, hint["host"], hint["count"]) + self._pending_events.append({ + "type": "skill_created", + "skill": { + "name": new_manifest["name"], + "description": new_manifest.get("description", ""), + "execution": new_manifest.get("execution", ""), + "active": new_manifest.get("active", True), + "setup_error": new_manifest.get("setup_error"), + "auto_scaffolded": True, + "from_template": stpl, + "trigger_host": hint["host"], + "trigger_count": hint["count"], + }, + }) + scaffolded_any = True + except Exception as exc: + logger.warning("auto_scaffold '%s' fehlgeschlagen: %s", sname, exc) + if scaffolded_any: + # Skills-Liste refresh damit der frische Skill im Prompt sichtbar ist + all_skills = skills_mod.list_skills(active_only=False) + active_skills = [s for s in all_skills if s.get("active", True)] + _ah.invalidate_cache() + # Heuristik neu rechnen — die scaffold-targets sind jetzt weg + hints = _ah.compute_hints(existing_skills=all_skills, force=True) + api_heuristic_section = _ah.build_section(hints) except Exception as exc: logger.warning("api_heuristic fehlgeschlagen: %s", exc) diff --git a/aria-brain/api_heuristic.py b/aria-brain/api_heuristic.py index 058ebd7..7b7fc8f 100644 --- a/aria-brain/api_heuristic.py +++ b/aria-brain/api_heuristic.py @@ -72,6 +72,12 @@ _SUGGESTIONS: dict[str, tuple[str, str, dict]] = { _cache: dict = {"computed_at": 0.0, "hints": []} +def invalidate_cache() -> None: + """Cache leeren — sinnvoll nach skill_create / scaffold damit der neue + Skill sofort beim naechsten Aufruf erkannt wird.""" + _cache.update(computed_at=0.0, hints=[]) + + def _extract_hosts_from_bash_input(input_str: str) -> list[str]: """Hostnames aus URLs in einem Bash-Command. Sehr robust — sucht `https?://host`.""" if not input_str: diff --git a/aria-brain/seed_rules.py b/aria-brain/seed_rules.py index 3717787..03d2ce9 100644 --- a/aria-brain/seed_rules.py +++ b/aria-brain/seed_rules.py @@ -216,11 +216,22 @@ SEED_RULES: List[dict] = [ "content": ( "Brain trackt server-side wie oft Du in den letzten 24h dieselbe " "externe API per Bash-curl angerufen hast (Cross-Session-Counter, " - "siehe '## API-Heuristik'-Block im System-Prompt). Sobald da " - "ein Eintrag steht: das ist KEINE Empfehlung sondern eine " - "Aufforderung. RUFE als ALLERERSTES `skill_scaffold` mit dem " - "vorgeschlagenen Template und params auf, BEVOR Du wieder Bash-" - "curl machst. Dann nutze den frischen Skill via `run_`.\n" + "siehe '## API-Heuristik'-Block im System-Prompt). \n" + "\n" + "AUTO-SCAFFOLD: Brain legt fuer wiederkehrende Hosts mit " + "bekanntem Template (Spotify, GitHub, OpenAI, OpenWeather, …) " + "automatisch einen Skill an — Du siehst ihn dann in `## Skills` " + "ohne dass Du ihn selbst gebaut hast (Markierung " + "`author=aria-auto`). NUTZE diesen Skill via `run_` " + "direkt, NICHT mehr Bash-curl gegen den Host. Beispiel: wenn " + "`spotify` plotzlich in der Skill-Liste auftaucht → " + "`run_spotify({method:'GET', path:'/v1/me/player'})` statt " + "Token holen + curl.\n" + "\n" + "Wenn die API-Heuristik einen Eintrag OHNE Suggestion zeigt " + "(unbekannter Host): rufe selbst `skill_scaffold` mit dem " + "passenden Template (oauth-api / apikey-api / file-process), " + "BEVOR Du wieder Bash-curl machst.\n" "\n" "Warum: jede Chat-Anfrage ist eine eigene Claude-CLI-Session — " "Du siehst nicht dass Du gestern auch schon 10x Spotify gecurled "