{"id":55185,"date":"2026-05-01T15:46:05","date_gmt":"2026-05-01T14:46:05","guid":{"rendered":"https:\/\/edoardoguzzi.com\/"},"modified":"2026-05-01T15:46:49","modified_gmt":"2026-05-01T14:46:49","slug":"agentenfahigkeiten-von-anthropic-inside-n8n-wie-es-wirklich-gemacht-wird","status":"publish","type":"post","link":"https:\/\/edoardoguzzi.com\/de\/agentenfahigkeiten-von-anthropic-inside-n8n-wie-es-wirklich-gemacht-wird\/","title":{"rendered":"Anthropic Agent Skills in n8n: wie es wirklich gemacht wird"},"content":{"rendered":"<p>Es passiert an einem Donnerstag. Der Agent reagiert schon seit einer Woche schlecht, und als Sie endlich die Systemabfrage \u00f6ffnen, verstehen Sie, warum: achttausend Token, alle in einem Block. Immer im Kontext. Selbst wenn der Benutzer nur darum bittet, eine E-Mail zu \u00fcbersetzen.<\/p>\n\n\n\n<p>Das nennt man Aufbl\u00e4hung der Eingabeaufforderung. Und es ist der schnellste Weg, einen KI-Agenten gleichzeitig dumm und teuer zu machen.<\/p>\n\n\n\n<p>In den letzten Monaten habe ich mehrmals an ein und demselben internen Assistenten f\u00fcr WebWakeUp gearbeitet und bin jedes Mal in der gleichen Sackgasse gelandet: ein einzelner Agent, der zu viele verschiedene Dinge tun muss (Meta-Beschreibungen im Stil von Kunde X schreiben, PDFs auf Vorlagen generieren, Nachforschungen \u00fcber Mitbewerber anstellen, Support-Tickets beantworten), und eine Systemaufforderung, die mit jeder neuen hinzugef\u00fcgten F\u00e4higkeit w\u00e4chst. Das Modell beginnt, die Regeln zu verwirren, die Antworten fallen flach, die Kosten steigen. Klassisch.<\/p>\n\n\n\n<p>Dann \u00fcberflog ich die Anthropic-Dokumentation auf der <em>Agent-F\u00e4higkeiten<\/em>, Ich habe die ben\u00f6tigten Teile behalten und versucht, das Muster selbst gehostet in n8n einzubringen. Es funktioniert. Es funktioniert gut. Aber mit ein paar Unterschieden, die ich nicht \u00fcberall geschrieben finde, also reden wir hier dar\u00fcber.<\/p>\n\n\n\n<p>Was folgt, ist eine technische Lekt\u00fcre, in einer Version, die noch in Arbeit ist. Ich bin noch am Testen, betrachten Sie es also als eine offene Baustelle, nicht als fertiges Handbuch. Wenn Sie Dinge sehen, die nicht stimmen, schreiben Sie mir: Das St\u00fcck ist daf\u00fcr gedacht.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Das Problem, in drei Zeilen<\/h2>\n\n\n\n<p>Ein KI-Agent hat Kosten pro Schicht, die proportional zur Gr\u00f6\u00dfe seiner Systemansage sind. Wenn Sie alle Anweisungen f\u00fcr alle m\u00f6glichen Aufgaben in die Systemeingabeaufforderung aufnehmen, zahlen Sie diesen Preis auch dann, wenn der Benutzer nach der Zeit fragt. Wenn Sie sie hingegen weglassen, wei\u00df der Agent nicht, wie er etwas Bestimmtes tun soll. Dieses Problem ist so alt wie die ersten ausgekl\u00fcgelten Eingabeaufforderungen, und hier hat Anthropic zum ersten Mal seine Hand ins Spiel gebracht, und zwar ganz geschickt.<\/p>\n\n\n\n<p>Die Antwort, die sie konstruiert haben, hei\u00dft <em>schrittweise Offenlegung<\/em>, was mit \u201cschichtweises Laden des Kontexts\u201d \u00fcbersetzt werden kann. Der Agent sieht bei allen Schichten nur ein sehr kleines Poster und l\u00e4dt den Rest nur, wenn er wirklich gebraucht wird.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Drei Ebenen, die den Unterschied ausmachen<\/h2>\n\n\n\n<p>Das Anthropic-Muster unterteilt die Kontextladung in drei Ebenen. Sie getrennt zu halten, ist der Schl\u00fcssel zur ganzen Geschichte.<\/p>\n\n\n\n<p><strong>Stufe eins, immer geladen.<\/strong> Nur Name und Beschreibung jeder F\u00e4higkeit, jeweils eine Zeile. Drei\u00dfig oder f\u00fcnfzig Token pro F\u00e4higkeit. Kostet wenig, befindet sich in der System-Eingabeaufforderung und dient dem LLM zur Erkennung (um zu sehen, ob die Anfrage des Benutzers mit einer der verf\u00fcgbaren F\u00e4higkeiten \u00fcbereinstimmt).<\/p>\n\n\n\n<p><strong>Stufe zwei, auf Spiel geladen.<\/strong> Wenn der LLM eine relevante F\u00e4higkeit erkennt, ruft er ein Tool auf, das die vollst\u00e4ndigen Anweisungen f\u00fcr diese F\u00e4higkeit liefert. F\u00fcnfhundert, tausend, zweitausend Token: das h\u00e4ngt von der Fertigkeit ab. Sie werden nur f\u00fcr diesen Zug (oder nachfolgende Z\u00fcge, solange sie ben\u00f6tigt werden) in den Kontext eingef\u00fcgt, sie werden nicht bei jeder Anfrage neu geladen.<\/p>\n\n\n\n<p><strong>Stufe drei, geladen auf Anfrage der F\u00e4higkeit selbst.<\/strong> Eine F\u00e4higkeit kann in ihren Anweisungen sagen: \u201cWenn die Situation X ist, lesen Sie auch die Beispieldatei\u201d. Der Agent ruft ein zweites Tool auf, l\u00e4dt die zus\u00e4tzlichen Referenzen herunter und beh\u00e4lt sie nur so lange im Kontext, wie er sie braucht. Lange Beispiele, Grenzf\u00e4lle, Referenztabellen, leben hier.<\/p>\n\n\n\n<p>Das Ergebnis ist, dass die Eingabeaufforderung des Systems lebenslang klein bleibt, selbst wenn f\u00fcnfzig oder hundert Fertigkeiten im Katalog sind. Sie zahlen nur f\u00fcr die zus\u00e4tzlichen Token, wenn der Benutzer eine Anfrage stellt, die sie rechtfertigt. Kein Wasser, keine leeren Kosten.<\/p>\n\n\n\n<p>Anthropic verwendet es offensichtlich in seinen Produkten (Claude Code, seine Skills, das Agent SDK). Die Frage ist: Kann dasselbe Muster in n8n repliziert werden, ohne etwas zu erfinden? Die Antwort ist ja, und fast alles wird mit nativen Nodes gemacht.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Replizieren innerhalb von n8n, mit nativen Knotenpunkten<\/h2>\n\n\n\n<p>Das Spielzeug besteht aus vier Teilen: AI Agent (Version 3.1, ab 1.82.0 ist es Tools Agent fixed und das ist gut so), Call n8n Workflow Tool (Version 2.2), Execute Workflow Trigger (Version 1.1), Postgres (Version 2.6). Und ein kleiner Code-Knoten, um das Manifest zu erstellen. Anhalten.<\/p>\n\n\n\n<p>Die Idee ist einfach. Meine \u201cSkill-Dateien\u201d leben nicht als <code>.md<\/code> auf dem Dateisystem (wie bei Anthropic), sondern als Zeilen einer Postgres-Tabelle. Eigentlich sind es zwei Tabellen: eine f\u00fcr die F\u00e4higkeiten, eine f\u00fcr ihre zus\u00e4tzlichen Referenzen. Der MAIN_AGENT erstellt zu Beginn jeder Sitzung eine <code>SELECT<\/code> aller aktiven F\u00e4higkeiten und erstellen das Manifest an Ort und Stelle. Die Sub-Workflows, die als Werkzeuge offengelegt werden, dienen dem Modell dazu, bei Bedarf zu fragen: \u201cGib mir F\u00e4higkeit X\u201d oder \u201cGib mir Referenz Y von F\u00e4higkeit X\u201d.<\/p>\n\n\n\n<p>Kein benutzerdefinierter Code, keine externen Plugins, keine zus\u00e4tzlichen APIs, die Sie pflegen m\u00fcssen. Nur Knoten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Postgres ja. Datentabellen nein (und ich sage warum)<\/h2>\n\n\n\n<p>Hier spiele ich mit einer Meinung und halte mir die T\u00fcren offen: Ich lese es so, Sie sagen es mir.<\/p>\n\n\n\n<p>n8n hat die <em>Daten-Tabellen<\/em> durch mehrere Versionen integriert. Native, \u00fcber die Schnittstelle verwaltete Tabellen mit einem exponierten REST-API-Endpunkt (<code>\/Datentabellen<\/code>), um auch von au\u00dferhalb damit zu interagieren, nativer CSV-Import\/Export, eingeschr\u00e4nkter Zugriff pro Projekt. F\u00fcr das Prototyping eines solchen Systems sind sie perfekt: Sie k\u00f6nnen einen Workflow in einer halben Stunde einrichten, Zeilen werden per Mausklick bearbeitet, und wenn Sie ein externes Panel ben\u00f6tigen, ist die API da.<\/p>\n\n\n\n<p>Aber um ein Skill-System darauf zu setzen, von dem wir behaupten, dass es Monate, Jahre h\u00e4lt und w\u00e4chst, gibt es dokumentierte Einschr\u00e4nkungen, die abgewogen werden m\u00fcssen. Nichts Dramatisches, keine \u201cgeschlossene Box\u201d, aber pr\u00e4zise Grenzen, die die Wahl ver\u00e4ndern.<\/p>\n\n\n\n<p><strong>S\u00e4ulen-Typen<\/strong>: Boolesch, Datum, Zahl, String. Kein JSON, kein VECTOR, kein BLOB. Also keine Einbettung in dieselbe Tabelle, keine willk\u00fcrlich strukturierte Nutzlast.<\/p>\n\n\n\n<p><strong>Filter<\/strong>Gleich, Nicht gleich, Gr\u00f6\u00dfer als und Kleiner als (mit oder ohne Gleichheit), Ist leer, Ist nicht leer. Kein LIKE, kein Volltext, keine \u00c4hnlichkeit. F\u00fcr den Musterabgleich von F\u00e4higkeiten rufen Sie das Manifest ab und Amen, keine serverseitige Vorfilterung.<\/p>\n\n\n\n<p><strong>Betrieb<\/strong>Einf\u00fcgen, Aktualisieren, Upsert, L\u00f6schen, Get auf Zeilen. Erstellen, L\u00f6schen, Auflisten, Aktualisieren von Tabellen. Kein rohes SQL.<\/p>\n\n\n\n<p><strong>Aufbewahrungskappe<\/strong>50MB standardm\u00e4\u00dfig f\u00fcr alle Datentabellen einer Instanz. Bei selbst gehosteten Instanzen, die \u00fcber die Umgebungsvariable <code>N8N_DATA_TABLES_MAX_SIZE_BYTES<\/code>. n8n warnt bei 80%, blockiert Einf\u00fcgen und Aktualisieren bei 100%. F\u00fcr einen textuellen Skill-Katalog mag das f\u00fcr eine lange Zeit ausreichen, aber es ist eine Obergrenze, die Sie im Auge behalten sollten.<\/p>\n\n\n\n<p><strong>Kein Zugriff vom Knoten Code<\/strong>: wortw\u00f6rtliches Zitat aus den Docs, <em>\u201cDirekter programmatischer Zugriff auf Datentabellen von einem Code-Knoten aus wird nicht unterst\u00fctzt\u201d.\u201d<\/em> Bei der oben beschriebenen Architektur, bei der ein Code-Knoten das Manifest verpackt, ist dies ein direktes Problem. Machbar (Knoten setzen nach einem Get-many anstelle von Code), aber die Form \u00e4ndert sich.<\/p>\n\n\n\n<p>Die Dokumentation schweigt sich \u00fcber drei Dinge aus, daher behaupte ich dies nicht: native Versionierung, Verhalten bei starker Konkurrenz und ob n8n-Backups den Inhalt von Datentabellen enthalten. Ich wei\u00df es nicht, ich werde nachsehen. Wenn Sie genaue Hinweise zu diesen drei Punkten haben, schreiben Sie mir.<\/p>\n\n\n\n<p>Zusammenfassend kann ich sagen, dass ich mich trotzdem f\u00fcr Postgres entscheide, und daf\u00fcr gibt es f\u00fcnf Gr\u00fcnde, die alle konkret sind.<\/p>\n\n\n\n<p><strong>Erste<\/strong>, Ich m\u00f6chte die 50MB Obergrenze aus dem Kopf haben. Eine Skill Registry, die durch Versionierung und Historie wachsen k\u00f6nnte, soll nicht von einem Schwellenwert abh\u00e4ngig sein.<\/p>\n\n\n\n<p><strong>Laut<\/strong>, Ich m\u00f6chte die serverseitige \u00c4hnlichkeitssuche an dem Tag, an dem die F\u00e4higkeiten hundert oder mehr sind. pgvector ist bereit, bei den Datentabellen ist kein VECTOR-Typ am Horizont zu sehen.<\/p>\n\n\n\n<p><strong>Dritte<\/strong>, Ich m\u00f6chte, dass der Code-Knoten frei ist, um Daten zu lesen und zu packen. Ich brauche ihn heute f\u00fcr die KI-Agent-Pipeline und morgen f\u00fcr die Export- und Backup-Pipelines.<\/p>\n\n\n\n<p><strong>Vierte<\/strong>, Ich m\u00f6chte rohes SQL f\u00fcr den Tag, ich brauche eine Aggregation, einen JOIN, einen History Trigger. Der History-Trigger in Postgres ist eine Funktion f\u00fcr zehn Zeilen, bei Datentabellen muss er von Hand im Workflow erstellt werden.<\/p>\n\n\n\n<p><strong>Quinto<\/strong>, Ich habe kein Problem mit einem zus\u00e4tzlichen Container. Meine n8n-Instanz befindet sich bereits in Docker Compose, f\u00fcgen Sie ein Postgres mit Image hinzu <code>pgvector\/pgvector:pg16<\/code> kostet nichts an betrieblicher Reibung.<\/p>\n\n\n\n<p>Wenn das Ziel eine kleine Spalte w\u00e4re, zehn oder zwanzig feste Fertigkeiten ohne Anspruch auf Wachstum, w\u00e4re Data Tables eine absolut vertretbare Wahl. In meinem Fall, mit einem Wachstumshorizont, den ich noch nicht kenne, den ich aber nicht blockieren m\u00f6chte, habe ich den zus\u00e4tzlichen Container genommen.<\/p>\n\n\n\n<p>Vielleicht liege ich falsch. Wenn jemand ein Skill-System f\u00fcr Datentabellen in echtem Ma\u00dfstab einf\u00fchren w\u00fcrde (hundert, zweihundert Eintr\u00e4ge, mit Historie und externer Verwaltung \u00fcber die API <code>\/Datentabellen<\/code>), w\u00fcrde ich sie gerne h\u00f6ren: Die Debatte ist er\u00f6ffnet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Das Postgres-Schema, mit pgvector vom ersten Tag an<\/h2>\n\n\n\n<p>Dedizierter Container, keine Wiederverwendung von n8n. Ich m\u00f6chte in der Lage sein, separate Backups zu erstellen, ich m\u00f6chte die operative Datenbank von n8n nicht mit meinen Anwendungsdaten beschmutzen, ich m\u00f6chte in der Lage sein, das ganze Spielzeug eines Tages woanders hin zu migrieren, ohne n8n auseinandernehmen zu m\u00fcssen. Auf diese Weise ist es sauberer.<\/p>\n\n\n\n<p>Ein wichtiger Punkt, den ich sp\u00e4ter best\u00e4tigte: das richtige Bild ist <code>pgvector\/pgvector:pg16<\/code>, nicht <code>postgres:16-alpine<\/code>. Es handelt sich um ein Standard-Postgres plus die vorinstallierte pgvector-Erweiterung. Ohne die aktivierte Erweiterung verh\u00e4lt es sich wie ein Vanilla Postgres, null Overhead. Aber an dem Tag, an dem Sie die \u00c4hnlichkeitssuche f\u00fcr den Sprung zu manifest-via-RAG aktivieren m\u00f6chten, sind Sie schon dabei: keine Image-Migration, keine <code>pg_dump<\/code> kalt, keine Zeit verschwendet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Dienstleistungen:\n  wwu-skills-db:\n    Bild: pgvector\/pgvector:pg16\n    container_name: wwu-skills-db\n    Neustart: sofern nicht gestoppt\n    Umgebung:\n      POSTGRES_DB: wwu_skills\n      POSTGRES_USER: wwu_skills_user\n      POSTGRES_PASSWORD: ${WWU_SKILLS_DB_PASSWORD}\n    volumes:\n      - wwu_skills_data:\/var\/lib\/postgresql\/data\n    Netzwerke:\n      - n8n_network\nVolumes:\n  wwu_skills_data:\nNetzwerke:\n  n8n_network:\n    extern: true<\/code><\/pre>\n\n\n\n<p>Kein offener Port auf dem Host. Nur n8n spricht mit diesem Postgres \u00fcber das interne Docker-Netzwerk. Das Passwort befindet sich in einer Umgebungsvariablen, nicht in der Datei.<\/p>\n\n\n\n<p>Das eigentliche Schema. Drei Tabellen: <code>F\u00e4higkeiten<\/code>, <code>skill_references<\/code>, <code>skills_history<\/code>. Und einen Ausl\u00f6ser, der bei jedem <code>UPDATE<\/code> archivieren Sie die vorherige Version, die Versionierung ist also kostenlos.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE SCHEME IF NOT EXISTS skills_mgmt;\n \n-- Tag 1: Aktivieren Sie die Erweiterung. Null Kosten, wenn nicht verwendet.\nCREATE EXTENSION IF NOT EXISTS vector;\n \nCREATE TABLE skills_mgmt.skills (\n    name VARCHAR(80) PRIMARY KEY,\n    description TEXT NOT NULL,\n    content TEXT NOT NULL,\n    active BOOLEAN NOT NULL DEFAULT TRUE,\n    version INTEGER NOT NULL DEFAULT 1,\n    -- Einbettungsspalte, vorerst l\u00f6schbar. Die Dimension entspricht Ihrer Einbettung\n    -- Modell: 1536 f\u00fcr text-embedding-3-small, 3072 f\u00fcr text-embedding-3-large.\n    beschreibung_einbettung vektor(1536),\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    CHECK (name ~ '^[a-z0-9-]+$')\n);\n \nCREATE TABLE skills_mgmt.skills_references (\n    id SERIAL PRIMARY KEY,\n    skill_name VARCHAR(80) NOT NULL\n                    REFERENCES skills_mgmt.skills(name) ON DELETE CASCADE,\n    reference_name VARCHAR(120) NOT NULL,\n    content TEXT NOT NULL,\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    UNIQUE (skill_name, reference_name),\n    CHECK (referenz_name ~ '^[a-z0-9_.-]+$')\n);\n \nCREATE TABLE skills_mgmt.skills_history (\n    id BIGSERIAL PRIMARY KEY,\n    name VARCHAR(80) NOT NULL,\n    beschreibung TEXT,\n    inhalt TEXT,\n    version INTEGER,\n    archived_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n \nCREATE OR REPLACE FUNCTION skills_mgmt.fn_skills_history()\nGIBT DEN TRIGGER ALS $$ ZUR\u00dcCK\nBEGIN\n    INSERT INTO skills_mgmt.skills_history(Name, Beschreibung, Inhalt, Version)\n    VALUES (OLD.name, OLD.description, OLD.content, OLD.version);\n    NEW.version := OLD.version + 1;\n    NEW.updated_at := NOW();\n    RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n \nCREATE TRIGGER trg_skills_history\nBEFORE UPDATE ON skills_mgmt.skills\nFOR EACH ROW EXECUTE FUNCTION skills_mgmt.fn_skills_history();\n \nCREATE INDEX idx_skills_active\n    ON skills_mgmt.skills(active) WHERE active = TRUE;<\/code><\/pre>\n\n\n\n<p>I <code>CHECK<\/code> auf Namen sind nicht unbedingt notwendig, da die Abfragen durch den n8n Postgres-Knoten parametrisiert werden und die Injektion bereits abgedeckt ist. Aber es werden weitere Klammern gesetzt: Wenn jemand die Arbeitsabl\u00e4ufe umgeht und von Hand einf\u00fcgt, l\u00e4sst zumindest die Datenbank keine fremden Bezeichner durch.<\/p>\n\n\n\n<p>Der HNSW-Index f\u00fcr die \u00c4hnlichkeitssuche wird erst erstellt, wenn Sie die Spalte tats\u00e4chlich ausgef\u00fcllt haben <code>beschreibung_einbettung<\/code>. Die Erstellung eines leeren Feldes f\u00fcr eine NULL-Spalte ist sinnlos und verbraucht Speicher.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Die drei Arbeitsabl\u00e4ufe<\/h2>\n\n\n\n<p>Drei separate Arbeitsabl\u00e4ufe. Ein Hauptarbeitsablauf und zwei kleine Arbeitsabl\u00e4ufe, die nur als Hilfsmittel dienen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Arbeitsablauf A: <code>tool_load_skill<\/code><\/h3>\n\n\n\n<p>Drei Knoten und das war's. <em>Workflow-Ausl\u00f6ser ausf\u00fchren<\/em> mit <code>inputSource: workflowInputs<\/code> und eine einzige Eingabe deklariert <code>{ name: \"skill_name\", type: \"string\" }<\/code>. <em>Postgres<\/em> im Modus <code>executeQuery<\/code> mit der unten stehenden Abfrage. Ein Knoten <em>Setzen Sie<\/em> final, die die Ausgabe normalisiert und entweder <code>{ skill_name, content }<\/code> oder <code>{ error }<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT Name, Beschreibung, Inhalt\nFROM skills_mgmt.skills\nWHERE name = $1 AND active = TRUE\nLIMIT 1;<\/code><\/pre>\n\n\n\n<p>Der Parameter <code>$1<\/code> ist gef\u00fcllt mit <code>options.queryReplacement<\/code> gleich <code>={ $json.skill_name }}<\/code>. Keine String-Verkettung in der Abfrage, keine Injektion m\u00f6glich, n8n bereinigt den Wert, bevor er ihn an den Treiber weitergibt. Dokumentiert, gepr\u00fcft, vergessen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Arbeitsablauf B: <code>tool_load_reference<\/code><\/h3>\n\n\n\n<p>Identisch mit dem vorherigen, zwei Eing\u00e4nge anstelle von einem: <code>skill_name<\/code> e <code>referenz_name<\/code>. Abfrage mit zwei Parametern, <code>queryReplacement<\/code> gleich <code>={{ $json.skill_name }},={{ $json.reference_name }}<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT Inhalt\nFROM skills_mgmt.skills_references\nWHERE skill_name = $1 AND reference_name = $2\nLIMIT 1;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Arbeitsablauf C: der <code>MAIN_AGENT<\/code><\/h3>\n\n\n\n<p>Der eigentliche Arbeitsablauf, derjenige, auf den der Client oder das interne System trifft. Vier Knoten in einer Reihe plus AI Agent-Unterknoten.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Chat-Ausl\u00f6ser<\/strong> (Version 1.4): Dies ist das Gateway. Ein Webhook ist auch in Ordnung, wenn Sie dies bevorzugen.<\/li>\n\n\n\n<li><strong>Postgres ausw\u00e4hlen<\/strong> auf dem Tisch <code>skills_mgmt.skills<\/code> mit <code>wobei: aktiv = wahr<\/code>, <code>outputColumns: Name, Beschreibung<\/code>, <code>returnAll: true<\/code>. Laden Sie das Poster hoch.<\/li>\n\n\n\n<li><strong>Code-Knoten<\/strong> um das Manifest in einen sauberen JSON-String zu verpacken.<\/li>\n\n\n\n<li><strong>KI-Agent<\/strong> (Version 3.1) mit der Systemnachricht, die das Manifest \u00fcber einen Ausdruck injiziert, und den beiden Tool-Workflows, die als Tools angeschlossen sind.<\/li>\n<\/ul>\n\n\n\n<p>Der Code-Knoten ist das einzige St\u00fcck Code im ganzen System. Nichts Esoterisches:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/**\n * Erstellen Sie einen kompakten JSON-Index f\u00fcr die Systemansage.\n * Halten Sie die Beschreibung kurz, jedes Zeichen kostet Token bei jeder Umdrehung.\n *\/\nconst rows = $input.all().map(i =&gt; i.json);\nconst index = rows.map(r =&gt; ({\n  name: r.name,\n  Beschreibung: r.Beschreibung\n}));\nreturn [{ json: { skills_index: JSON.stringify(index, null, 2) } }];<\/code><\/pre>\n\n\n\n<p>Die Systemnachricht des KI-Agenten. Ja, er akzeptiert n8n-Ausdr\u00fccke: Ich habe dies direkt am TypeScript-Typ des Knotens best\u00e4tigt, <code>systemMessage: string | Expression | PlaceholderValue<\/code>. Also:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Sie sind der KI-Agent der WWU.\n \nBEVOR Sie eine nicht-triviale Aufgabe ausf\u00fchren, scannen Sie den SKILLS_INDEX unten nach einer passenden Fertigkeit.\nWenn eine F\u00e4higkeit mit der Benutzeranfrage \u00fcbereinstimmt, M\u00dcSSEN Sie das Tool `load_skill` mit dessen\nmit dem genauen Namen aufrufen, BEVOR Sie eine Ausgabe erzeugen. Erfinden Sie niemals Namen f\u00fcr Fertigkeiten. Produzieren Sie niemals ein\nErgebnis, das eine Fertigkeit verwenden soll, ohne sie vorher zu laden.\n \nWenn eine geladene Fertigkeit Sie anweist, eine Referenzdatei zu lesen, rufen Sie `load_reference`\nnur dann auf, wenn die aktuelle Aufgabe diesen Detailgrad tats\u00e4chlich ben\u00f6tigt.\n \nSKILLS_INDEX:\n{{ $('Build Index').item.json.skills_index }}<\/code><\/pre>\n\n\n\n<p>Die beiden Workflow-Tools m\u00fcssen mit zwei besonders raffinierten Dingen konfiguriert werden. Erstens, die <em>Beschreibung<\/em> des Tools, denn der LLM liest es und entscheidet, ob es aufgerufen wird. Verbringen Sie f\u00fcnf Minuten damit, es richtig zu schreiben, das erspart Ihnen sp\u00e4ter stundenlange Fehlleitungen.<\/p>\n\n\n\n<p>Zweitens m\u00fcssen die Eingabeparameter des Sub-Workflows ausgef\u00fcllt werden, indem Sie auf die Schaltfl\u00e4che \u201cAI\u201d auf dem Feld klicken, so dass das Modell den Wert \u00fcber <code>$fromAI()<\/code>. Kodieren Sie sie niemals fest, setzen Sie niemals statische Ausdr\u00fccke darauf, sonst wird das Tool dumm und der LLM knallt dagegen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Der Fluss einer Anfrage, Ende-zu-Ende<\/h2>\n\n\n\n<p>Der Benutzer schreibt: \u201cSchreiben Sie mir die Meta-Beschreibung f\u00fcr die E-Mail-Marketing-Seite\u201d. Die Runde ist dies, erz\u00e4hlt in den beiden Zeitformen.<\/p>\n\n\n\n<p><strong>Zeit eins, die Vorbereitung des Kontextes.<\/strong> Der Chat-Trigger empf\u00e4ngt die Nachricht. Der Postgres-Knoten zieht die Zeilen mit den aktiven F\u00e4higkeiten heraus (Name und Beschreibung, kein Inhalt), der Code-Knoten packt sie in einen wenige KB gro\u00dfen JSON-String, und der KI-Agent startet mit der Systemnachricht, die bereits mit dem Manifest versehen ist. All dies ist bei jeder Anfrage gleich und kostet nur sehr wenig.<\/p>\n\n\n\n<p><strong>Zeit zwei, Aktivierung der F\u00e4higkeiten.<\/strong> Das Modell liest das Manifest, erkennt die <code>wwu-meta-description<\/code>, Anrufe <code>load_skill<\/code> unter Angabe des genauen Namens. Der Sub-Workflow f\u00fchrt die parametrisierte Abfrage aus und gibt den vollst\u00e4ndigen Inhalt der F\u00e4higkeit zur\u00fcck. Das Modell liest ihn und entscheidet, ob es die zus\u00e4tzlichen Referenzen ben\u00f6tigt (und wenn ja, ruft es <code>load_reference<\/code>), dann schreiben Sie die Meta-Beschreibung nach den Regeln, die Sie gerade geladen haben.<\/p>\n\n\n\n<p>Der Punkt, unten.<\/p>\n\n\n\n<p>Die System-Eingabeaufforderung bleibt die gleiche Gr\u00f6\u00dfe wie bei zehn oder zweihundert Fertigkeiten. Die Kosten pro Runde \u00e4ndern sich kaum. Die Kosten pro Eingabeaufforderung steigen nur im Verh\u00e4ltnis zu der Anzahl der tats\u00e4chlich geladenen Fertigkeiten, und das ist normalerweise eine. Vielleicht auch zwei. Niemals alle auf einmal.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scharfe Kanten, die ich gefangen habe (bis jetzt)<\/h2>\n\n\n\n<p>Der ehrliche Teil des Artikels. Ich bin am Testen, und es gibt Dinge, zu denen ich gerne tausend weitere Meinungen h\u00e4tte.<\/p>\n\n\n\n<p><strong>Unterknoten mit Ausdr\u00fccken und Erst-Eintr\u00e4gen.<\/strong> In den n8n-Dokumenten wird dies ausdr\u00fccklich erw\u00e4hnt: Unterknoten, die ein Array von Elementen erhalten, l\u00f6sen nur das erste auf. Wenn Sie aus irgendeinem Grund mehrere Elemente an den Werkzeug-Workflow \u00fcbergeben, sieht der LLM nur das erste. Das bedeutet: Behalten Sie die Einweg-Tools mit nur einer Eingabe und einer Ausgabe bei. Kein Stapel.<\/p>\n\n\n\n<p><strong>Die Erinnerung beschmutzt sich selbst.<\/strong> Wenn Sie einen langen Speicherpuffer anlegen, landen die Antworten der Hilfsmittel im Gespr\u00e4chsverlauf und werden bei jedem nachfolgenden Zug an die Vorlage zur\u00fcckgeschickt. Der Inhalt einer Fertigkeit, die vor einer halben Stunde geladen wurde, ist immer noch da. Das ist eine bekannte Auswirkung des Musters, kein Fehler, aber wenn Sie das nicht wissen, ist es f\u00fcr Sie ein gefundenes Fressen. Ich versuche folgende L\u00f6sungen: Verwenden Sie einen Kurzzeitspeicher oder einen Summenspeicher. Ich muss noch entscheiden, welche L\u00f6sung sich in der Produktion besser bew\u00e4hrt.<\/p>\n\n\n\n<p><strong>Die Qualit\u00e4t der Beschreibung ist mehr wert als die des Inhalts.<\/strong> Das Modell w\u00e4hlt die zu ladende F\u00e4higkeit anhand der Beschreibung aus, nicht anhand des Inhalts. Eine Fertigkeit, die gut geschrieben ist, aber eine vage Beschreibung enth\u00e4lt, wird ignoriert. Eine Fertigkeit, die mittelm\u00e4\u00dfig geschrieben ist, aber eine klare Beschreibung hat, wird immer aufgerufen. Verbringen Sie dort Zeit, nicht anderswo.<\/p>\n\n\n\n<p><strong>Gr\u00f6\u00dfenbeschr\u00e4nkungen f\u00fcr Systemnachrichten und Nutzdaten.<\/strong> n8n dokumentiert keine festen Grenzen. In der Praxis setzt Ihnen das zugrunde liegende Modell die Grenze. Bei zweihundert F\u00e4higkeiten mit je einer Zeile Beschreibung sind Sie bei sechzehn KB Manifest, was immer noch \u00fcberschaubar ist. Ab f\u00fcnfhundert sollten Sie auf das Muster <code>Suche_Kompetenzen<\/code> mit der Einbettung, weshalb pgvector von Anfang an eingebaut werden sollte, auch wenn Sie es nicht sofort verwenden.<\/p>\n\n\n\n<p><strong>Schnelles Nachladen von Fertigkeiten.<\/strong> Das Manifest wird zu Beginn der Sitzung geladen, nicht bei jeder Runde. Wenn Sie eine Fertigkeit bearbeiten, w\u00e4hrend ein Gespr\u00e4ch bereits ge\u00f6ffnet ist, sieht der LLM bis zum Ende der Sitzung weiterhin die alte Version. F\u00fcr meinen Anwendungsfall ist das in Ordnung, aber wenn Sie ein System entwerfen, in dem Operatoren im Laufe des Tages Fertigkeiten bearbeiten, sollten Sie dies bedenken.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was Sie konkret tun k\u00f6nnen, wenn Sie es ausprobieren m\u00f6chten<\/h2>\n\n\n\n<p>F\u00fcnf Schritte, der Reihe nach.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>F\u00fcgen Sie zu docker-compose neben n8n den Container hinzu <code>pgvector\/pgvector:pg16<\/code>, im selben Docker-Netzwerk, ohne Ports f\u00fcr den Host freizugeben.<\/li>\n\n\n\n<li>Postgres-Anmeldeinformationen in n8n erstellen gegen\u00fcber <code>wwu-skills-db<\/code>, Schema <code>F\u00e4higkeiten_mgmt<\/code>, und wendet das obige SQL-Schema an.<\/li>\n\n\n\n<li>Erstellen Sie den Sub-Workflow <code>tool_load_skill<\/code>drei Knoten, exakte Kopie. Das Gleiche gilt f\u00fcr <code>tool_load_reference<\/code>.<\/li>\n\n\n\n<li>Erstellen Sie den Haupt-Workflow: Chat-Trigger, Postgres select, Code-Knoten f\u00fcr das Manifest, AI Agent mit den beiden Workflow-Tools verbunden. Geben Sie in der Systemnachricht den Ausdruck ein, der Folgendes einf\u00fcgt <code>f\u00e4higkeiten_index<\/code>. Auf den Werkzeugparametern, die Schaltfl\u00e4che AI.<\/li>\n\n\n\n<li>Tragen Sie zwei oder drei Testf\u00e4higkeiten in die Tabelle ein, mit differenzierten Beschreibungen. Senden Sie dem Chatbot Anfragen, von denen jeweils nur eine aktiviert werden sollte. Sehen Sie in den Ausf\u00fchrungsprotokollen nach, ob die Tools in der richtigen Reihenfolge aufgerufen werden und ob die Inhalte vollst\u00e4ndig zur\u00fcckkommen.<\/li>\n<\/ol>\n\n\n\n<p>Schritt f\u00fcnf ist die H\u00e4lfte der eigentlichen Arbeit. Die ersten Ausf\u00fchrungen werden die Risse in der Eingabeaufforderung zeigen: falsches Routing, nicht aufgerufene Werkzeuge, geladene F\u00e4higkeiten, die nicht ben\u00f6tigt werden. Das ist normal. Sie iterieren \u00fcber Beschreibungen, iterieren \u00fcber die Systemmeldung, Datei.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wohin das f\u00fchrt<\/h2>\n\n\n\n<p>Wenn die F\u00e4higkeiten drei oder vier Ziffern \u00fcberschreiten, ist es naheliegend, das flache Manifest aus der Eingabeaufforderung zu entfernen und durch ein drittes Tool zu ersetzen: <code>search_skills(abfrage, top_k)<\/code>. Intern geht es folgenderma\u00dfen vor: Es nimmt die Anfrage des Benutzers entgegen, berechnet eine Einbettung \u00fcber den von Ihnen bevorzugten Anbieter (OpenAI, Voyage, Cohere, es gibt den Knoten Embeddings innerhalb von n8n), stellt eine Anfrage <code>vector_cosine_ops<\/code> auf der Fertigkeitstabelle, zieht die drei \u00e4hnlichsten Namen heraus und das war's. Das Modell sieht nur diese drei, l\u00e4dt einen und arbeitet.<\/p>\n\n\n\n<p>Die pgvector-Erweiterung ist wegen des Images bereits aktiviert, die <code>beschreibung_einbettung<\/code> Sie haben sie bereits gel\u00f6scht, Sie m\u00fcssen sie nur noch f\u00fcllen (ein einmaliger Workflow oder ein Trigger auf <code>INSERT<\/code> e <code>UPDATE<\/code> Aufruf des einbettenden Anbieters) und erstellen den HNSW-Index. Null Migration, null Ausfallzeit. Aus diesem Grund habe ich darauf bestanden, mit dem pgvector-Image und nicht mit dem Standard-Postgres zu beginnen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quellen<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.n8n.io\/integrations\/builtin\/cluster-nodes\/sub-nodes\/n8n-nodes-langchain.toolworkflow\/\" target=\"_blank\" rel=\"noopener\">n8n Docs, n8n Workflow Tool Knoten aufrufen<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.n8n.io\/integrations\/builtin\/cluster-nodes\/root-nodes\/n8n-nodes-langchain.agent\/\" target=\"_blank\" rel=\"noopener\">n8n Docs, AI Agent (Tools Agent)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.n8n.io\/integrations\/builtin\/app-nodes\/n8n-nodes-base.postgres\/\" target=\"_blank\" rel=\"noopener\">n8n Docs, Postgres Knoten<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hub.docker.com\/r\/pgvector\/pgvector\" target=\"_blank\" rel=\"noopener\">pgvector\/pgvector offizielles Image Docker Hub<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/pgvector\/pgvector\" target=\"_blank\" rel=\"noopener\">pgvector, GitHub Repo<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Schlie\u00dfen<\/h2>\n\n\n\n<p>Ich bin noch dabei, diese Architektur zu testen. Vielleicht schreibe ich in einem Monat, dass ich meine Meinung \u00fcber etwas ge\u00e4ndert habe, dass das von mir gew\u00e4hlte Speichermuster der Belastung nicht standh\u00e4lt, dass die Datentabellen in Ordnung waren und ich es umsonst kompliziert gemacht habe.<\/p>\n\n\n\n<p>Das ist die Lekt\u00fcre f\u00fcr den Moment. Wenn jemand einen anderen Ansatz ausprobiert hat, insbesondere im gro\u00dfen Ma\u00dfstab (einhundert, zweihundert F\u00e4higkeiten oder mehr), oder eine starke Meinung zu dem Ged\u00e4chtnisst\u00fcck hat, schreiben Sie mir bitte.<\/p>\n\n\n\n<p>Hier eine Zusammenfassung des technischen Teils auf Englisch <a href=\"https:\/\/gist.github.com\/mredodos\/b71e2ee9a4431bbbe09a0f2ed0539df5\" target=\"_blank\" rel=\"noopener\">https:\/\/gist.github.com\/mredodos\/b71e2ee9a4431bbbe09a0f2ed0539df5<\/a><\/p>\n\n\n\n<p><\/p>","protected":false},"excerpt":{"rendered":"<p>Succede un gioved\u00ec. L&#8217;agent risponde male da una settimana, e quando apri finalmente il system prompt capisci perch\u00e9: ottomila token, tutto in un blocco. Sempre in contesto. Anche quando l&#8217;utente chiede solo di tradurre una mail. Si chiama prompt bloat. Ed \u00e8 il modo pi\u00f9 rapido per rendere un AI Agent stupido e caro nello [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":55187,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[391,64,406,388,411],"tags":[386,410,385,390],"class_list":["post-55185","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automazioni-e-integrazioni","category-blog","category-intelligenza-artificiale","category-n8n","category-no-code-low-code","tag-automazioni","tag-intelligenza-artificiale","tag-n8n","tag-self-hosting"],"meta_box":{"articolo_correlato_di_approfondimento":"","data_pubblicazione_sui_social":"0","pubblicato_sui_social":"","immagine_generata":"true"},"_links":{"self":[{"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/posts\/55185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/comments?post=55185"}],"version-history":[{"count":2,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/posts\/55185\/revisions"}],"predecessor-version":[{"id":55188,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/posts\/55185\/revisions\/55188"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/media\/55187"}],"wp:attachment":[{"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/media?parent=55185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/categories?post=55185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/edoardoguzzi.com\/de\/wp-json\/wp\/v2\/tags?post=55185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}