feat: standardize long-running tool workflows
This commit is contained in:
1 parent
0a5da6e5f1
commit
7978a4ee56
9 files changed
+277
-24
No files matched your search
+1
-1
@@ -714,7 +714,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: platform/openwebui/Dockerfile
|
dockerfile: platform/openwebui/Dockerfile
|
||||||
image: ${OPENWEBUI_IMAGE:-mike-ai/openwebui:main-01f4282-agent-loop-v8}
|
image: ${OPENWEBUI_IMAGE:-mike-ai/openwebui:main-01f4282-agent-loop-v9}
|
||||||
container_name: mike-ai-open-webui
|
container_name: mike-ai-open-webui
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Merge this block into ~/.hermes/config.yaml on any VPN-connected client.
|
||||||
|
# Hermes discovers the tools from each HTTP MCP server automatically at startup.
|
||||||
|
mcp_servers:
|
||||||
|
athena_operator:
|
||||||
|
url: "http://192.168.1.212:8202/mcp"
|
||||||
|
timeout: 3600
|
||||||
|
connect_timeout: 30
|
||||||
|
enabled: true
|
||||||
|
supports_parallel_tool_calls: false
|
||||||
|
web:
|
||||||
|
url: "http://192.168.1.212:8203/mcp"
|
||||||
|
timeout: 180
|
||||||
|
connect_timeout: 30
|
||||||
|
enabled: true
|
||||||
|
supports_parallel_tool_calls: true
|
||||||
|
athena_context:
|
||||||
|
url: "http://192.168.1.212:8201/mcp"
|
||||||
|
timeout: 120
|
||||||
|
connect_timeout: 30
|
||||||
|
enabled: true
|
||||||
|
supports_parallel_tool_calls: true
|
||||||
@@ -289,6 +289,56 @@ class AutoToolSelectorTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.assertIn("search_web/fetch_url", result["messages"][0]["content"])
|
self.assertIn("search_web/fetch_url", result["messages"][0]["content"])
|
||||||
self.assertEqual(result["reasoning_effort"], "medium")
|
self.assertEqual(result["reasoning_effort"], "medium")
|
||||||
|
|
||||||
|
async def test_unraid_media_write_gets_portable_operator_and_web(self):
|
||||||
|
result = await self._select(
|
||||||
|
"Ermittle online das neueste Video, lade es mit yt-dlp auf dem Unraid-Host "
|
||||||
|
"in einen temporären Ordner, konvertiere es mit ffmpeg und lege die fertige "
|
||||||
|
"Datei im Share Transfer ab."
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
result["tool_ids"],
|
||||||
|
[
|
||||||
|
"server:mcp:athena-operator-local",
|
||||||
|
"server:mcp:mua-readonly-local",
|
||||||
|
"server:mcp:mua",
|
||||||
|
"server:mcp:web-general-local",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.assertIn("direct MUA management or shell tool", result["messages"][0]["content"])
|
||||||
|
self.assertIn("Do not ask the user to enable another tool", result["messages"][0]["content"])
|
||||||
|
self.assertIn("start-status-result pattern", result["messages"][0]["content"])
|
||||||
|
self.assertTrue(result["metadata"]["mikeai_long_operator_task"])
|
||||||
|
|
||||||
|
async def test_generic_remote_host_file_operation_gets_operator(self):
|
||||||
|
result = await self._select(
|
||||||
|
"Führe auf dem Server ein vorhandenes Skript aus und speichere die neue Datei unter /data/export."
|
||||||
|
)
|
||||||
|
self.assertEqual(result["tool_ids"], ["server:mcp:athena-operator-local"])
|
||||||
|
self.assertTrue(result["metadata"]["mikeai_long_operator_task"])
|
||||||
|
|
||||||
|
async def test_readonly_diagnostics_do_not_get_long_operator_budget(self):
|
||||||
|
result = await self._select("Prüfe nur lesend den Zustand von Unraid.")
|
||||||
|
self.assertNotIn("mikeai_long_operator_task", result["metadata"])
|
||||||
|
|
||||||
|
async def test_narrow_safety_clause_does_not_cancel_authorized_unraid_write(self):
|
||||||
|
result = await self._select(
|
||||||
|
"Lade das Video auf dem Unraid-Host herunter, konvertiere es und lege es "
|
||||||
|
"im Transfer-Share ab. Installiere dabei kein Paket dauerhaft und ändere "
|
||||||
|
"keine Unraid-Systemkonfiguration."
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
result["tool_ids"],
|
||||||
|
[
|
||||||
|
"server:mcp:athena-operator-local",
|
||||||
|
"server:mcp:mua-readonly-local",
|
||||||
|
"server:mcp:mua",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_plain_download_advice_does_not_attach_operator(self):
|
||||||
|
result = await self._select("Erkläre mir, wie ein Browser einen Download technisch durchführt.")
|
||||||
|
self.assertNotIn("tool_ids", result)
|
||||||
|
|
||||||
async def test_voice_transcription_variants_select_unraid(self):
|
async def test_voice_transcription_variants_select_unraid(self):
|
||||||
result = await self._select(
|
result = await self._select(
|
||||||
"Welche Dacher Contäner laufen aktuell auf dem Anrate Server?"
|
"Welche Dacher Contäner laufen aktuell auf dem Anrate Server?"
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Client-unabhängiger Werkzeugstandard
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
Eine Aufgabe darf nicht nur deshalb scheitern, weil sie in Hermes oder Pi statt
|
||||||
|
in OpenWebUI gestellt wurde. OpenWebUI-Filter sind eine Komfort- und
|
||||||
|
Kontextoptimierung, aber niemals die eigentliche Berechtigungs- oder
|
||||||
|
Fähigkeitsschicht.
|
||||||
|
|
||||||
|
## Kernfähigkeiten für jeden vertrauenswürdigen VPN-Client
|
||||||
|
|
||||||
|
| Fähigkeit | MCP-Endpunkt | Zweck |
|
||||||
|
|---|---|---|
|
||||||
|
| Breiter Operator | `http://192.168.1.212:8202/mcp` | Terminal, Dateien, Docker, Git, Downloads, Konvertierung, APIs und SSH zu konfigurierten Systemen |
|
||||||
|
| Allgemeines Web | `http://192.168.1.212:8203/mcp` | Site-unabhängige Suche und Seitenabruf |
|
||||||
|
| Plattformwissen | `http://192.168.1.212:8201/mcp` | Aufbau, Ist-Zustand, Quellen und Änderungsablauf von Athena |
|
||||||
|
|
||||||
|
Diese drei Server bilden das tragfähige Minimum. Fach-MCPs wie Home Assistant,
|
||||||
|
MUA, ARR, Navidrome und GitHub ergänzen kurze strukturierte Operationen. Sie
|
||||||
|
sind der bevorzugte Weg, aber keine Voraussetzung: Fehlt eine Spezialfunktion,
|
||||||
|
bleiben allgemeines Web und Operator verfügbar.
|
||||||
|
|
||||||
|
## Client-Verhalten
|
||||||
|
|
||||||
|
- **OpenWebUI:** Native Websuche bleibt grundsätzlich verfügbar. Der Auto Tool
|
||||||
|
Selector hängt Fach-MCPs und den Operator anhand allgemeiner
|
||||||
|
Fähigkeitsklassen an. Allgemeine Hostarbeit wird anhand von Ausführungs- oder
|
||||||
|
Änderungsabsicht plus Host-, Datei-, Kommando- oder Dienstkontext erkannt;
|
||||||
|
nicht anhand einzelner Programme oder Websites.
|
||||||
|
- **Hermes:** Die drei Kernendpunkte werden in `~/.hermes/config.yaml`
|
||||||
|
eingetragen. Hermes verbindet sie beim Start, ruft MCP `tools/list` auf und
|
||||||
|
stellt die entdeckten Werkzeuge in jedem Gespräch bereit. Vorlage:
|
||||||
|
`config/hermes-mcp-core.yaml.example`.
|
||||||
|
- **Pi und weitere MCP-Clients:** Dieselben Streamable-HTTP-Endpunkte direkt
|
||||||
|
konfigurieren. Es ist kein OpenWebUI-Filter und kein zusätzlicher Proxy
|
||||||
|
erforderlich.
|
||||||
|
|
||||||
|
## Verbindliche Sicherheitsgrenze
|
||||||
|
|
||||||
|
Toolbeschreibungen und Systemprompts helfen dem Modell bei der Wahl, sind aber
|
||||||
|
keine Sicherheitsgrenze. Unverzichtbare Verbote, Ausgabelimits,
|
||||||
|
Schreibabläufe und Schutz vor dem Verlust der Remote-Erreichbarkeit werden im
|
||||||
|
MCP beziehungsweise im Athena-Operator-Dienst erzwungen. Dadurch gelten sie
|
||||||
|
identisch für OpenWebUI, Hermes, Pi und zukünftige Clients.
|
||||||
|
|
||||||
|
## Lange operative Aufgaben
|
||||||
|
|
||||||
|
Lange Arbeiten verwenden clientunabhängig das Muster
|
||||||
|
`Start -> kompakter Status -> Ergebnis/Verifikation`. Recherche wird vor der
|
||||||
|
Mutation abgeschlossen; Hilfsmittel werden gebündelt vorbereitet und lange
|
||||||
|
Kommandos asynchron gestartet, wenn ein synchroner Werkzeugaufruf in ein
|
||||||
|
Zeitlimit laufen könnte. OpenWebUI gewährt dieser allgemeinen Aufgabenklasse
|
||||||
|
ein höheres, aber weiterhin endliches Ausführungsbudget: 64 Aufrufe insgesamt
|
||||||
|
und 24 je Werkzeug. Normale Recherche bleibt bei 40 beziehungsweise 12. Das
|
||||||
|
ist keine Sonderregel für YouTube, ffmpeg, MUA oder einen bestimmten Server.
|
||||||
|
|
||||||
|
## Community-Bezug
|
||||||
|
|
||||||
|
Das folgt dem MCP-Modell: Ein Client verbindet einen vertrauenswürdigen
|
||||||
|
Streamable-HTTP-Server und entdeckt dessen Werkzeuge über `tools/list`.
|
||||||
|
Hermes registriert konfigurierte HTTP-MCPs beim Start als normale Werkzeuge;
|
||||||
|
OpenWebUI registriert Remote-MCPs global und kann sie pro Anfrage über
|
||||||
|
`tool_ids` aktivieren. Clientseitige Auswahl optimiert Kontext und Bedienung,
|
||||||
|
serverseitige Regeln bleiben maßgeblich.
|
||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
| Platform Context MCP | Athena-/MikeAI-Wissen, begrenzter Laufzeitsnapshot und kontrollierte Dokumentationspflege | eigener Container ohne Docker-Socket, Shell, Egress oder Secrets | Kern |
|
| Platform Context MCP | Athena-/MikeAI-Wissen, begrenzter Laufzeitsnapshot und kontrollierte Dokumentationspflege | eigener Container ohne Docker-Socket, Shell, Egress oder Secrets | Kern |
|
||||||
| Athena Operator MCP | Entwicklung und vollständiger Betrieb der KI-Plattform | strukturierte Operationen plus breites, begrenztes Terminal; Erreichbarkeitsänderungen blockiert | Kern |
|
| Athena Operator MCP | Entwicklung und vollständiger Betrieb der KI-Plattform | strukturierte Operationen plus breites, begrenztes Terminal; Erreichbarkeitsänderungen blockiert | Kern |
|
||||||
| Operator-Kontext | `docs/QWEN_OPERATOR_CONTEXT.md` plus `config/operator-system-prompt.txt` | versionierte Selbstbeschreibung und Sicherheitsregeln für Qwen | Kern |
|
| Operator-Kontext | `docs/QWEN_OPERATOR_CONTEXT.md` plus `config/operator-system-prompt.txt` | versionierte Selbstbeschreibung und Sicherheitsregeln für Qwen | Kern |
|
||||||
| Unraid/MUA | MUA r020+ auf dem HomeServer, direkter MCP-Endpunkt | read-only Automatik; begrenzte Datei-/Medieninventare; Verwaltung bei explizitem Änderungsauftrag; idempotente Batch-Updates | Kern |
|
| Unraid/MUA | MUA r022+ auf dem HomeServer, direkter MCP-Endpunkt | read-only Automatik; begrenzte Datei-/Medieninventare; Verwaltung bei explizitem Änderungsauftrag; idempotente Batch-Updates; asynchrone Jobs mit Start/Status/Aufräumen für lange Arbeiten | Kern |
|
||||||
| Whisper | ggml-org/whisper.cpp | Service im Router-Deploy | optional |
|
| Whisper | ggml-org/whisper.cpp | Service im Router-Deploy | optional |
|
||||||
| XTTS-v2 | Coqui, offizielles CUDA-12.1-Image per Digest | RTX-3060-Container, Stimme `Annmarie Nele`, CPML | Kern |
|
| XTTS-v2 | Coqui, offizielles CUDA-12.1-Image per Digest | RTX-3060-Container, Stimme `Annmarie Nele`, CPML | Kern |
|
||||||
| TTS-Gateway | `platform/docker/tts-gateway/` | interne Queue, stabile deutsche Satzblöcke, automatische Erkennung rein englischer Texte und Piper-Fallback | Kern |
|
| TTS-Gateway | `platform/docker/tts-gateway/` | interne Queue, stabile deutsche Satzblöcke, automatische Erkennung rein englischer Texte und Piper-Fallback | Kern |
|
||||||
|
|||||||
@@ -197,6 +197,21 @@ aktiviert er zusätzlich die native Websuche. Der Ablauf steht in
|
|||||||
`docs/UNRAID_MEDIA_AUDIT_WORKFLOW.md`. Der frühere GraphQL-basierte Unraid-MCP wurde
|
`docs/UNRAID_MEDIA_AUDIT_WORKFLOW.md`. Der frühere GraphQL-basierte Unraid-MCP wurde
|
||||||
entfernt und gehört weder zum Start noch zum Recovery.
|
entfernt und gehört weder zum Start noch zum Recovery.
|
||||||
|
|
||||||
|
Ab MUA r022 stehen für lange, explizit autorisierte Arbeiten zusätzlich
|
||||||
|
`unraid_system_job_start`, `unraid_system_job_status` und
|
||||||
|
`unraid_system_job_cleanup` bereit. Der Start kehrt sofort mit einer Job-ID
|
||||||
|
zurück; Statusabfragen liefern nur kompakte, redigierte Ausgaben. Damit blockiert
|
||||||
|
ein Download, Transcode oder vergleichbarer Auftrag weder Open WebUI noch Hermes
|
||||||
|
oder Pi bis zum Prozessende. Die drei Werkzeuge erben dieselbe ausdrücklich
|
||||||
|
erteilte Berechtigung wie die uneingeschränkte Shell.
|
||||||
|
|
||||||
|
Auto Tool Selector 4.5 kennzeichnet allgemeine lange Operator-Aufgaben
|
||||||
|
produktunabhängig. Der OpenWebUI-Agentenloop stellt dafür bis zu 64
|
||||||
|
Werkzeugausführungen insgesamt und 24 je Werkzeug bereit; normale Aufgaben
|
||||||
|
bleiben bei 40/12. Zusätzlich verlangt der Systemhinweis das generische
|
||||||
|
Start-Status-Ergebnis-Muster und reserviert den Abschluss für Verifikation,
|
||||||
|
Zielablage und Aufräumen.
|
||||||
|
|
||||||
Der GitHub-Container läuft produktiv. Token-Datei, interner
|
Der GitHub-Container läuft produktiv. Token-Datei, interner
|
||||||
Streamable-HTTP-Handshake, fehlende Host-Portfreigabe und exakt drei
|
Streamable-HTTP-Handshake, fehlende Host-Portfreigabe und exakt drei
|
||||||
read-only Werkzeuge wurden am 24. August 2026 verifiziert.
|
read-only Werkzeuge wurden am 24. August 2026 verifiziert.
|
||||||
@@ -204,14 +219,15 @@ Die Transportbrücke verwendet den OpenWebUI-kompatiblen `mcp-proxy` 0.12.0 im
|
|||||||
stateless Betrieb. Supergateway wurde nach reproduzierbaren HTTP-400-Fehlern
|
stateless Betrieb. Supergateway wurde nach reproduzierbaren HTTP-400-Fehlern
|
||||||
bei `notifications/initialized` aus diesem Pfad entfernt.
|
bei `notifications/initialized` aus diesem Pfad entfernt.
|
||||||
|
|
||||||
Die agentische OpenWebUI-Schleife führt höchstens 40 einzelne Werkzeuge aus,
|
Die agentische OpenWebUI-Schleife führt bei normalen Aufgaben höchstens 40
|
||||||
höchstens zwölf Aufrufe desselben Werkzeugnamens und höchstens zweimal exakt
|
einzelne Werkzeuge und höchstens zwölf Aufrufe desselben Werkzeugnamens aus.
|
||||||
dieselbe Signatur. Nach Ende des Budgets stehen zusätzliche interne Runden ausschließlich
|
Allgemeine lange Operator-Aufgaben erhalten 64 beziehungsweise 24. Exakt
|
||||||
für eine sichtbare werkzeugfreie Schlussantwort bereit. Das produktive
|
dieselbe Signatur bleibt stets auf zwei Wiederholungen begrenzt. Nach Ende des
|
||||||
OpenWebUI-Derivat trägt den Tag
|
Budgets stehen zusätzliche interne Runden ausschließlich für eine sichtbare
|
||||||
`mike-ai/openwebui:main-01f4282-agent-loop-v8`.
|
werkzeugfreie Schlussantwort bereit. Das produktive OpenWebUI-Derivat trägt
|
||||||
|
den Tag `mike-ai/openwebui:main-01f4282-agent-loop-v9`.
|
||||||
|
|
||||||
Das in V8 enthaltene Verhalten aus V7 begrenzt zusätzlich die
|
Das in V9 enthaltene Verhalten aus V8 begrenzt zusätzlich die
|
||||||
Werkzeugantworten in OpenWebUIs internen Fortsetzungsrunden auf 12.000 Zeichen
|
Werkzeugantworten in OpenWebUIs internen Fortsetzungsrunden auf 12.000 Zeichen
|
||||||
je Ergebnis und 64.000 Zeichen pro Antwortlauf. Damit greift die Begrenzung
|
je Ergebnis und 64.000 Zeichen pro Antwortlauf. Damit greift die Begrenzung
|
||||||
auch bei Ergebnissen, die erst nach dem ersten Request entstehen.
|
auch bei Ergebnissen, die erst nach dem ersten Request entstehen.
|
||||||
@@ -238,6 +254,22 @@ Angebote und sortierte Notebook, Zubehör sowie ein Ersatzteilgerät aus. Das
|
|||||||
belegt sowohl die technische Grenze als auch eine brauchbare Synthese; ein
|
belegt sowohl die technische Grenze als auch eine brauchbare Synthese; ein
|
||||||
eBay-spezifischer MCP war nicht erforderlich.
|
eBay-spezifischer MCP war nicht erforderlich.
|
||||||
|
|
||||||
|
Auto Tool Selector 4.5 erkennt zusätzlich allgemeine operative Arbeit über
|
||||||
|
Fähigkeitsklassen: Eine ausdrückliche Ausführungs- oder Änderungsabsicht in
|
||||||
|
Verbindung mit Host, Dateisystem, Kommando, Dienst, Pfad oder typischen
|
||||||
|
Kommandozeilenwerkzeugen stellt den Athena Operator bereit. Dadurch benötigen
|
||||||
|
neue Programme wie Download- oder Medienwerkzeuge keine eigene Selector-Regel.
|
||||||
|
Bei Unraid-Arbeit werden MUA für kompakte Bestandsaufnahme und der Operator für
|
||||||
|
die ausdrücklich verlangte allgemeine Schreibarbeit gemeinsam angeboten.
|
||||||
|
Lange Operator-Aufgaben werden dabei produktunabhängig markiert und folgen dem
|
||||||
|
Start-Status-Ergebnis-Muster, damit Recherche und Vorbereitung nicht das
|
||||||
|
Budget für Ausführung, Verifikation und Aufräumen verbrauchen.
|
||||||
|
|
||||||
|
Für andere Clients gilt `docs/CLIENT_TOOL_STANDARD.md`. Hermes lädt die drei
|
||||||
|
Kern-MCPs Web, Operator und Plattformwissen direkt per Streamable HTTP; die
|
||||||
|
Vorlage liegt unter `config/hermes-mcp-core.yaml.example`. Damit hängt die
|
||||||
|
Fähigkeit nicht vom OpenWebUI-Filter ab.
|
||||||
|
|
||||||
Der Platform Context MCP läuft ohne Docker-Socket, Shell, Egress oder Secrets.
|
Der Platform Context MCP läuft ohne Docker-Socket, Shell, Egress oder Secrets.
|
||||||
Ein root-eigener Minutentimer erzeugt nur einen begrenzten Laufzeitsnapshot.
|
Ein root-eigener Minutentimer erzeugt nur einen begrenzten Laufzeitsnapshot.
|
||||||
Der Schreibpfad ist auf `docs/*.md`, Vorschau, ausdrückliche Freigabe, atomare
|
Der Schreibpfad ist auf `docs/*.md`, Vorschau, ausdrückliche Freigabe, atomare
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ bereit und hängt anhand der Anfrage passende Fachkataloge an. Er verweigert
|
|||||||
keine Fähigkeit und erfordert keine Site-spezifischen Regeln. Alle zentralen
|
keine Fähigkeit und erfordert keine Site-spezifischen Regeln. Alle zentralen
|
||||||
MCPs sind über feste WireGuard-Ports auch für Hermes und Pi erreichbar.
|
MCPs sind über feste WireGuard-Ports auch für Hermes und Pi erreichbar.
|
||||||
|
|
||||||
|
Der verbindliche client-unabhängige Mindeststandard ist in
|
||||||
|
`docs/CLIENT_TOOL_STANDARD.md` festgelegt: allgemeines Web, Athena Operator und
|
||||||
|
Plattformwissen werden in jedem vertrauenswürdigen VPN-Client konfiguriert.
|
||||||
|
OpenWebUI darf diese Grundfähigkeiten zur Kontextoptimierung automatisch
|
||||||
|
auswählen; Hermes und Pi entdecken sie direkt über die MCP-Endpunkte. Die
|
||||||
|
Sicherheitsgrenze liegt immer serverseitig und hängt nicht von einem Filter ab.
|
||||||
|
|
||||||
Marketplace-Recherche bleibt ebenfalls allgemein: Der Selector erkennt
|
Marketplace-Recherche bleibt ebenfalls allgemein: Der Selector erkennt
|
||||||
Kauf-, Angebots-, Preis- und Versandsuchen unabhängig von einer einzelnen
|
Kauf-, Angebots-, Preis- und Versandsuchen unabhängig von einer einzelnen
|
||||||
Website. Qwen beginnt mit einer fokussierten Suche, nutzt höchstens zwei
|
Website. Qwen beginnt mit einer fokussierten Suche, nutzt höchstens zwei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
title: MikeAI Auto Tool Selector
|
title: MikeAI Auto Tool Selector
|
||||||
author: MikeAI
|
author: MikeAI
|
||||||
version: 4.3.0
|
version: 4.5.0
|
||||||
description: Keeps broad web access available and adds relevant portable MCP domains without acting as a gate.
|
description: Keeps broad web access available and adds relevant portable MCP domains without acting as a gate.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -109,6 +109,48 @@ class Filter:
|
|||||||
)
|
)
|
||||||
return search_intent and marketplace_context
|
return search_intent and marketplace_context
|
||||||
|
|
||||||
|
def _is_explicitly_readonly(self, raw_text: str) -> bool:
|
||||||
|
"""Recognise a global read-only request, not a narrow safety exception."""
|
||||||
|
text = self._normalise(raw_text)
|
||||||
|
return self._matches(
|
||||||
|
text,
|
||||||
|
(
|
||||||
|
r"\b(?:nur|ausschlie(?:ß|ss)lich|lediglich)\s+(?:lesen|pr[uü]fen|anzeigen|auflisten)\b",
|
||||||
|
r"\bread[- ]only\b",
|
||||||
|
r"\bohne\s+(?:downloads?|umbenennungen?|schreibzugriff|[aä]nderungen?)\b",
|
||||||
|
r"\bohne\s+downloads?\b.{0,80}\b(?:[aä]nderungen?|umbenennungen?)\b",
|
||||||
|
r"\b(?:nichts|keinerlei)\s+(?:ver[aä]ndern|[aä]ndern|schreiben)\b",
|
||||||
|
r"\b(?:[aä]ndere|ver[aä]ndere)\s+nichts\b",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _needs_general_operator(self, raw_text: str) -> bool:
|
||||||
|
"""Detect broad host work by capability, not product or website name."""
|
||||||
|
text = self._normalise(raw_text)
|
||||||
|
if self._is_explicitly_readonly(raw_text):
|
||||||
|
return False
|
||||||
|
action_intent = self._matches(
|
||||||
|
text,
|
||||||
|
(
|
||||||
|
r"\b(?:ausf[uü]hr|starte?|stoppe?|restart|installier|deploy|bau|erstell|"
|
||||||
|
r"schreib|speicher|leg|kopier|verschieb|umbenenn|l[oö]sch|entfern|"
|
||||||
|
r"download|herunterlad|konvertier|transkodier|komprimier|extrahier|"
|
||||||
|
r"bearbeit|[aä]nder|modifizier|aktualisier|reparier|fix)\w*\b",
|
||||||
|
r"\b(?:f[uü]hre|lass)\b.{0,80}\b(?:befehl|kommando|skript|command)\b",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
operational_context = self._matches(
|
||||||
|
text,
|
||||||
|
(
|
||||||
|
r"\b(?:host|server|unraid|unreid|unrate|anrate|nas|linux|debian|docker|"
|
||||||
|
r"container|terminal|shell|kommando|befehl|datei(?:system)?|ordner|"
|
||||||
|
r"verzeichnis|pfad|share|freigabe|repository|repo|mcp|api)\b",
|
||||||
|
r"\b(?:ffmpeg|ffprobe|yt-dlp|youtube-dl|curl|wget|git|docker\s+compose)\b",
|
||||||
|
r"(?:^|\s)/(?:tmp|data|mnt|opt|srv|var|etc|home)(?:/|\b)",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return action_intent and operational_context
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _add_system_rule(
|
def _add_system_rule(
|
||||||
body: dict,
|
body: dict,
|
||||||
@@ -129,7 +171,25 @@ class Filter:
|
|||||||
"the Athena Operator terminal when it can answer the unresolved question. Never "
|
"the Athena Operator terminal when it can answer the unresolved question. Never "
|
||||||
"duplicate an existing backend; integrate or relay it and report any remaining gap."
|
"duplicate an existing backend; integrate or relay it and report any remaining gap."
|
||||||
)
|
)
|
||||||
if "unraid" in selected and "unraid_admin" in selected:
|
if "unraid" in selected and "unraid_admin" in selected and "operator" in selected:
|
||||||
|
rule += (
|
||||||
|
" This request explicitly authorizes general work on Unraid. Use "
|
||||||
|
"read-only MUA first for compact evidence, then use the direct MUA "
|
||||||
|
"management or shell tool for commands and writes on Unraid. Use "
|
||||||
|
"the Athena Operator only for orchestration on Athena or as a "
|
||||||
|
"fallback when the target MCP cannot perform the required operation; "
|
||||||
|
"do not spend repeated calls inventing a second SSH path. Do not ask "
|
||||||
|
"the user to enable another tool: all three capabilities are attached. "
|
||||||
|
"Do not install persistent packages unless explicitly requested; "
|
||||||
|
"prefer an existing binary or task-local temporary copy. Avoid "
|
||||||
|
"overwrites unless authorized and verify the final artifact."
|
||||||
|
" For long-running work, finish discovery before mutation, then use "
|
||||||
|
"a generic start-status-result pattern: prepare prerequisites in as few "
|
||||||
|
"calls as possible, start the long command asynchronously when the tool "
|
||||||
|
"timeout might be exceeded, poll only compact progress, and reserve "
|
||||||
|
"enough calls for verification, final placement, and cleanup."
|
||||||
|
)
|
||||||
|
elif "unraid" in selected and "unraid_admin" in selected:
|
||||||
rule += (
|
rule += (
|
||||||
" This request explicitly asks for an Unraid state change. Use the "
|
" This request explicitly asks for an Unraid state change. Use the "
|
||||||
"read-only MUA tools to establish evidence, then the narrowest MUA "
|
"read-only MUA tools to establish evidence, then the narrowest MUA "
|
||||||
@@ -140,6 +200,19 @@ class Filter:
|
|||||||
"Do not ask the user to enable another tool: both required MUA "
|
"Do not ask the user to enable another tool: both required MUA "
|
||||||
"capabilities are already attached to this request."
|
"capabilities are already attached to this request."
|
||||||
)
|
)
|
||||||
|
elif "unraid" in selected and "operator" in selected:
|
||||||
|
rule += (
|
||||||
|
" This request requires general filesystem, command, download, "
|
||||||
|
"conversion or other host work on Unraid. Use read-only MUA first "
|
||||||
|
"for compact inventory and runtime evidence, then use the Athena "
|
||||||
|
"Operator terminal for the exact user-authorized command sequence. "
|
||||||
|
"Do not ask the user to enable another tool: both capabilities are "
|
||||||
|
"already attached. Do not install persistent packages unless the "
|
||||||
|
"user explicitly requested installation; prefer an existing binary "
|
||||||
|
"or a task-local temporary copy. Write only to the requested target, "
|
||||||
|
"avoid overwrites unless explicitly authorized, and verify the final "
|
||||||
|
"artifact independently before reporting success."
|
||||||
|
)
|
||||||
elif "unraid" in selected:
|
elif "unraid" in selected:
|
||||||
rule += (
|
rule += (
|
||||||
" For bounded file or media-library inventory on Unraid, prefer "
|
" For bounded file or media-library inventory on Unraid, prefer "
|
||||||
@@ -193,7 +266,8 @@ class Filter:
|
|||||||
|
|
||||||
selected: list[str] = []
|
selected: list[str] = []
|
||||||
|
|
||||||
operator = self._matches(
|
general_operator = self._needs_general_operator(raw_text)
|
||||||
|
operator = general_operator or self._matches(
|
||||||
text,
|
text,
|
||||||
(
|
(
|
||||||
r"\bathena[- ]operator\b",
|
r"\bathena[- ]operator\b",
|
||||||
@@ -267,18 +341,12 @@ class Filter:
|
|||||||
r"\b(?:installier|entfern|l[oö]sch|[aä]nder|modifizier)\w*\b.*\b(?:unraid|docker|cont[aä]iner)\b",
|
r"\b(?:installier|entfern|l[oö]sch|[aä]nder|modifizier)\w*\b.*\b(?:unraid|docker|cont[aä]iner)\b",
|
||||||
r"\b(?:unraid|docker|cont[aä]iner)\b.*\b(?:installier|entfern|l[oö]sch|[aä]nder|modifizier)\w*\b",
|
r"\b(?:unraid|docker|cont[aä]iner)\b.*\b(?:installier|entfern|l[oö]sch|[aä]nder|modifizier)\w*\b",
|
||||||
r"\bf[uü]hr\w*\b.*\bupdates?\b.*\b(?:durch|aus)\b",
|
r"\bf[uü]hr\w*\b.*\bupdates?\b.*\b(?:durch|aus)\b",
|
||||||
|
r"\b(?:lad(?:e|en)?|download|herunterlad|konvertier|transkodier|kopier|verschieb|speicher|schreib|leg|umbenenn|l[oö]sch)\w*\b.{0,160}\b(?:unraid|unreid|unrate|anrate|nas|share|freigabe)\b",
|
||||||
|
r"\b(?:unraid|unreid|unrate|anrate|nas|share|freigabe)\b.{0,160}\b(?:lad(?:e|en)?|download|herunterlad|konvertier|transkodier|kopier|verschieb|speicher|schreib|leg|umbenenn|l[oö]sch)\w*\b",
|
||||||
|
r"\b(?:kommando|befehl|shell[- ]befehl)\w*\b.{0,100}\b(?:auf|in)\b.{0,30}\b(?:unraid|unreid|unrate|anrate|nas)\b",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if unraid_write and self._matches(
|
if unraid_write and self._is_explicitly_readonly(raw_text):
|
||||||
text,
|
|
||||||
(
|
|
||||||
r"\b(?:nur|lediglich|ausschlie(?:ß|ss)lich)\b.{0,30}\b(?:pr[uü]f|anzeig|auflist|status|nachseh)\w*\b",
|
|
||||||
r"\b(?:keine|keinerlei|nichts?)\b.{0,40}\b(?:[aä]nder|update|aktualisier|durchf[uü]hr|installier|download|umbenenn)\w*\b",
|
|
||||||
r"\bohne\b.{0,50}\b(?:[aä]nder|update|aktualisier|durchf[uü]hr|installier|download|umbenenn)\w*\b",
|
|
||||||
r"\b(?:nicht|nichts?)\b.{0,20}\b(?:durchf[uü]hr|ausf[uü]hr|aktualisier|installier)\w*\b",
|
|
||||||
r"\b(?:[aä]nder|erstell|installier|l[oö]sch|entfern|download|umbenenn)\w*\b.{0,25}\b(?:nichts?|keine[nrms]?|keinerlei)\b",
|
|
||||||
),
|
|
||||||
):
|
|
||||||
unraid_write = False
|
unraid_write = False
|
||||||
github = self._matches(
|
github = self._matches(
|
||||||
text,
|
text,
|
||||||
@@ -365,6 +433,11 @@ class Filter:
|
|||||||
# Consumed by the versioned Open WebUI continuation patch.
|
# Consumed by the versioned Open WebUI continuation patch.
|
||||||
# This is a request-local execution policy, not a site rule.
|
# This is a request-local execution policy, not a site rule.
|
||||||
metadata["mikeai_marketplace_research"] = True
|
metadata["mikeai_marketplace_research"] = True
|
||||||
|
# Long operational tasks need more executions than bounded research.
|
||||||
|
# This is a capability-class policy, not a rule for any product, site,
|
||||||
|
# command or MCP. The patched Open WebUI loop consumes this marker.
|
||||||
|
if self._needs_general_operator(latest_text):
|
||||||
|
metadata["mikeai_long_operator_task"] = True
|
||||||
|
|
||||||
if not selected:
|
if not selected:
|
||||||
return body
|
return body
|
||||||
|
|||||||
@@ -31,8 +31,14 @@ replacement = """ tool_call_iterations = 0
|
|||||||
# Per-tool and exact-repeat limits below prevent one low-level MCP
|
# Per-tool and exact-repeat limits below prevent one low-level MCP
|
||||||
# operation from consuming the whole turn.
|
# operation from consuming the whole turn.
|
||||||
tool_call_executions = 0
|
tool_call_executions = 0
|
||||||
max_tool_call_executions = 40
|
long_operator_task = bool(
|
||||||
max_executions_per_tool = 12
|
(metadata or {}).get('mikeai_long_operator_task', False)
|
||||||
|
)
|
||||||
|
# Research stays tightly bounded. Explicit long-running host work
|
||||||
|
# receives a larger, still finite budget so discovery cannot consume
|
||||||
|
# the calls needed for execution, verification and cleanup.
|
||||||
|
max_tool_call_executions = 64 if long_operator_task else 40
|
||||||
|
max_executions_per_tool = 24 if long_operator_task else 12
|
||||||
# Inlet filters only see the first request, not later internal
|
# Inlet filters only see the first request, not later internal
|
||||||
# tool continuations. Bound the evidence injected into those
|
# tool continuations. Bound the evidence injected into those
|
||||||
# continuations so one broad inventory cannot consume context.
|
# continuations so one broad inventory cannot consume context.
|
||||||
|
|||||||
Reference in new issue
Block a user