KI-Agenten mit n8n bauen: Postgres-Tools, Memory und Freigabe

Andreas Nigg
Andreas Nigg · Mitgründer, Pondhouse Data
Veröffentlicht Aktualisiert 16 Min. Lesezeit
KI-Agenten mit n8n bauen: Postgres-Tools, Memory und Freigabe
Inhalt

Diese Anleitung baut einen funktionierenden KI-Agenten in n8n 2.46.6, der aktuellen stabilen Version, selbst gehostet mit dem offiziellen Docker-Compose-Setup aus n8n und Postgres. Der Agent ist ein Bestellassistent für einen kleinen Fertigungsbetrieb: Kolleginnen und Kollegen fragen im Chat, wo ein Auftrag steht, welche Positionen er enthält oder welche Aufträge verspätet sind. Der Agent sucht die Antworten in einer Postgres-Datenbank, entwirft eine E-Mail an den Kunden und stellt sie erst in den Postausgang, wenn ein Mensch auf "Send" klickt.

Wir haben den Workflow vollständig mit OpenAIs gpt-5.4-mini aufgebaut und getestet. Er funktioniert gut, und ein Gespräch mit vier Nachrichten kostete etwa ein Drittel Cent. Dabei sind wir auf drei Probleme gestoßen, die Ihnen sehr wahrscheinlich auch begegnen: Datumswerte aus Postgres, die einen Tag zu früh ankommen, Abfrageparameter, die bei leeren Werten oder Kommas brechen, und eine Freigabe, die nur mit einer bestimmten Chat-Einstellung funktioniert. Für alle drei gibt es einfache Lösungen, die wir unten zeigen.

Was ein KI-Agent in n8n ist

n8n ist ein Werkzeug zur Workflow-Automatisierung mit visuellem Editor. Sie verbinden Nodes auf einer Arbeitsfläche, und n8n führt sie aus, sobald ein Trigger auslöst, etwa ein Webhook, ein Zeitplan oder eine Chatnachricht. Für mehrere hundert Dienste und Datenbanken gibt es fertige Integrationen. Das macht n8n für KI-Agenten interessant, denn die Anbindung an die Systeme, in denen Ihre Daten liegen, ist oft der größere Teil der Arbeit.

Ein Agent in n8n besteht aus einem AI Agent-Node mit angehängten Sub-Nodes:

  • einem Chat-Modell (OpenAI, Anthropic, Azure OpenAI, Ollama und andere),
  • optional einem Memory, das die letzten Nachrichten eines Gesprächs behält,
  • beliebig vielen Tools: Datenbankabfragen, HTTP-Aufrufe, andere Workflows oder App-Nodes wie Gmail und Slack.

Kommt eine Nachricht an, schickt der AI-Agent-Node sie zusammen mit Name und Beschreibung aller Tools an das Modell. Das Modell antwortet direkt oder verlangt einen Tool-Aufruf mit bestimmten Argumenten. n8n führt das Tool aus, gibt das Ergebnis an das Modell zurück und wiederholt das, bis eine Antwort vorliegt oder die maximale Zahl an Durchläufen erreicht ist. Seit n8n 1.82.0 arbeitet jeder AI-Agent-Node so (die Dokumentation nennt das Tools Agent), die frühere Auswahl des Agententyps ist veraltet.

Der Bestellassistent als Schleife: Der AI-Agent-Node fragt das Modell, ruft Postgres-Tools zum Lesen auf, nutzt Chat-Memory und leitet das E-Mail-Tool über eine Freigabe

Das Diagramm zeigt den Agenten, den wir hier bauen. Drei Tools lesen nur Daten. Das vierte, das eine E-Mail an einen Kunden in den Postausgang stellt, liegt hinter einer Freigabe durch einen Menschen.

Was sich seit Anfang 2025 geändert hat

Die erste Fassung dieses Beitrags entstand im Februar 2025 für n8n 1.x. Seitdem hat sich viel getan. Die folgenden Änderungen betreffen Agenten, jeweils mit der Version, in der sie laut Dokumentation und Release Notes kamen:

ÄnderungSeit
AI Agent arbeitet immer mit Tool-Aufrufen; "Window Buffer Memory" heißt jetzt Simple Memory1.82.0
MCP Client Tool und MCP Server Trigger1.88.0
Evaluations für KI-Workflows1.95.0
AI Agent Tool (Agenten als Sub-Agenten), Streaming im Chat1.103.0
Eingebaute Data Tables1.113
n8n 2.0: Task Runner standardmäßig aktiv, Save und Publish statt Activate2.0.0 (Dez. 2025)
Freigabe für Tool-Aufrufe2.6
MCP-Server für die ganze Instanz allgemein verfügbar2.31
Agents als eigenes Objekt außerhalb von Workflows (Preview)2.32.3 (self-hosted)

Zur letzten Zeile: n8n kennt inzwischen auch "Agents" als eigene Objekte neben Workflows. Sie entstehen in einem Agent Builder und sind über Chat, Kanäle und Zeitpläne erreichbar. Die Dokumentation führt das als Preview, das Verhalten kann sich also noch ändern. Diese Anleitung nutzt den AI-Agent-Node in einem Workflow. Das ist der stabile Weg, und auf ihm bauen die meisten Vorlagen und Tutorials auf.

n8n mit Docker Compose und Postgres installieren

Die n8n-Dokumentation verweist auf das Beispiel withPostgres im Repository n8n-io/n8n-hosting. Es startet drei Container: Postgres 18 als Datenbank für n8n, n8n selbst und einen Task-Runner-Container für Code-Nodes (externe Task Runner sind seit n8n 2.0 Standard). Laden Sie die drei Dateien in einen leeren Ordner:

1for f in docker-compose.yml init-data.sh README.md; do
2 curl -fsSL -o $f https://raw.githubusercontent.com/n8n-io/n8n-hosting/main/docker-compose/withPostgres/$f
3done
4chmod +x init-data.sh

Die .env im Repository enthält Platzhalter-Passwörter und eine feste N8N_VERSION. Schreiben Sie eine eigene mit echten Geheimnissen. Wir haben die aktuelle stabile Version festgelegt:

1cat > .env <<EOF
2N8N_VERSION=2.46.6
3
4POSTGRES_USER=pgadmin
5POSTGRES_PASSWORD=$(openssl rand -hex 16)
6POSTGRES_DB=n8n
7
8POSTGRES_NON_ROOT_USER=n8n
9POSTGRES_NON_ROOT_PASSWORD=$(openssl rand -hex 16)
10
11RUNNERS_AUTH_TOKEN=$(openssl rand -hex 24)
12EOF
13chmod 600 .env

Zwei Anpassungen kommen in eine docker-compose.override.yml, die Compose automatisch mitliest. Die offizielle Datei veröffentlicht Port 5678 auf allen Netzwerkschnittstellen. Wir binden ihn an localhost und setzen für alles jenseits eines Tests einen Reverse Proxy mit TLS davor. Außerdem setzen wir die Zeitzone, wie es die Docker-Anleitung von n8n empfiehlt, damit Zeitpläne und $now die lokale Zeit verwenden:

1services:
2 n8n:
3 ports: !override
4 - 127.0.0.1:5678:5678
5 environment:
6 - GENERIC_TIMEZONE=Europe/Vienna
7 - TZ=Europe/Vienna

Dann starten Sie den Stack:

1docker compose up -d

Öffnen Sie http://localhost:5678 und legen Sie das Owner-Konto an. Läuft n8n später unter einer eigenen Domain hinter einem Proxy, setzen Sie zusätzlich N8N_WEBHOOK_URL auf die öffentliche Adresse (die Variable ersetzt das ältere WEBHOOK_URL). Sonst zeigen die Chat- und Webhook-Adressen in n8n auf localhost. Wenn Sie n8n von einem anderen Rechner über unverschlüsseltes HTTP öffnen, verweigert n8n die Anmeldung, weil das Cookie standardmäßig als sicher markiert ist. Richten Sie dann TLS ein, statt N8N_SECURE_COOKIE=false zu setzen.

Die Beispieldatenbank anlegen

Der Agent braucht Daten. Wir haben eine kleine Auftragsdatenbank mit fünf Kunden, acht Aufträgen und ihren Positionen angelegt, in einer eigenen Datenbank shop auf demselben Postgres-Server. In einem echten Projekt wäre das ein Lesereplikat Ihres ERP oder eine View darauf. Entscheidend ist der letzte Block: Der Agent bekommt eine eigene Rolle, die nur lesen darf und ein Zeitlimit pro Abfrage hat.

1CREATE DATABASE shop;
2\c shop
3
4CREATE TABLE customers (
5 customer_id text PRIMARY KEY,
6 company text NOT NULL,
7 contact_name text NOT NULL,
8 email text NOT NULL,
9 country text NOT NULL
10);
11
12CREATE TABLE orders (
13 order_id text PRIMARY KEY,
14 customer_id text NOT NULL REFERENCES customers,
15 order_date date NOT NULL,
16 status text NOT NULL CHECK (status IN ('open','in_production','shipped','delivered','cancelled')),
17 promised_date date NOT NULL,
18 shipped_date date,
19 tracking_no text,
20 total_eur numeric(10,2) NOT NULL
21);
22
23CREATE TABLE order_lines (
24 order_id text NOT NULL REFERENCES orders,
25 line_no int NOT NULL,
26 sku text NOT NULL,
27 product text NOT NULL,
28 quantity int NOT NULL,
29 unit_price numeric(10,2) NOT NULL,
30 PRIMARY KEY (order_id, line_no)
31);
32
33INSERT INTO customers VALUES
34 ('C-1001','Alpenform GmbH','Maria Huber','m.huber@alpenform.example','AT'),
35 ('C-1002','Bodensee Kunststofftechnik AG','Thomas Keller','t.keller@bodensee-kt.example','CH'),
36 ('C-1003','Rheintal Werkzeugbau KG','Sabine Mayr','s.mayr@rheintal-wzb.example','AT'),
37 ('C-1004','Schwarzwald Präzision GmbH','Jonas Weber','j.weber@sw-praezision.example','DE'),
38 ('C-1005','Tirol Metall OG','Lukas Gruber','l.gruber@tirolmetall.example','AT');
39
40INSERT INTO orders VALUES
41 ('SO-24051','C-1001','2026-10-02','delivered','2026-10-16','2026-10-14','AT-7781-2290',4180.00),
42 ('SO-24063','C-1001','2026-10-20','in_production','2026-11-10',NULL,NULL,12650.00),
43 ('SO-24070','C-1002','2026-10-22','shipped','2026-11-05','2026-11-04','CH-5521-0937',2390.44),
44 ('SO-24072','C-1003','2026-10-23','open','2026-11-20',NULL,NULL,880.00),
45 ('SO-24075','C-1004','2026-10-27','in_production','2026-11-03',NULL,NULL,7420.00),
46 ('SO-24078','C-1005','2026-10-28','cancelled','2026-11-12',NULL,NULL,1500.00),
47 ('SO-24081','C-1003','2026-11-02','in_production','2026-11-18',NULL,NULL,3310.00),
48 ('SO-24084','C-1002','2026-11-03','open','2026-11-24',NULL,NULL,5960.02);
49
50INSERT INTO order_lines VALUES
51 ('SO-24051',1,'GP-200','Guide pillar 20 mm',40,38.50),
52 ('SO-24051',2,'BU-200','Guide bush 20 mm',40,66.00),
53 ('SO-24063',1,'MP-4030','Mould plate 400x300',6,1650.00),
54 ('SO-24063',2,'CP-12','Clamping plate set',5,550.00),
55 ('SO-24070',1,'EJ-08','Ejector pin 8 mm (pack of 50)',13,183.88),
56 ('SO-24072',1,'GP-160','Guide pillar 16 mm',20,44.00),
57 ('SO-24075',1,'MP-3030','Mould plate 300x300',4,1355.00),
58 ('SO-24075',2,'HR-10','Hot runner nozzle',2,1000.00),
59 ('SO-24078',1,'CP-12','Clamping plate set',3,500.00),
60 ('SO-24081',1,'BU-160','Guide bush 16 mm',30,52.00),
61 ('SO-24081',2,'MP-2020','Mould plate 200x200',2,875.00),
62 ('SO-24084',1,'HR-10','Hot runner nozzle',4,990.00),
63 ('SO-24084',2,'EJ-08','Ejector pin 8 mm (pack of 50)',11,181.82);
64
65-- read-only role for the agent
66CREATE ROLE agent_readonly LOGIN PASSWORD :'ro_pw';
67GRANT CONNECT ON DATABASE shop TO agent_readonly;
68GRANT USAGE ON SCHEMA public TO agent_readonly;
69GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_readonly;
70ALTER ROLE agent_readonly SET default_transaction_read_only = on;
71ALTER ROLE agent_readonly SET statement_timeout = '5s';

Speichern Sie das als shop.sql und spielen Sie es über den Postgres-Container ein. Das Passwort der neuen Rolle übergeben Sie als psql-Variable:

1set -a && . ./.env && set +a
2docker compose exec -T postgres psql -q -v ON_ERROR_STOP=1 -v ro_pw="$AGENT_RO_PASSWORD" \
3 -U "$POSTGRES_USER" -d n8n < shop.sql

(AGENT_RO_PASSWORD setzen Sie vorher in Ihrer Shell.) Eine kurze Probe, dass die Rolle wirklich nichts ändern kann:

1$ docker compose exec -T postgres psql -U agent_readonly -d shop -c "delete from orders"
2ERROR: cannot execute DELETE in a read-only transaction

Legen Sie in n8n eine Postgres-Credential an: Host postgres (der Name des Compose-Dienstes), Datenbank shop, Benutzer agent_readonly, SSL deaktiviert, weil der Verkehr im Docker-Netz bleibt. Dazu kommt eine OpenAI-Credential mit Ihrem API-Schlüssel.

Den Agenten-Workflow bauen

Der fertige Workflow hat zehn Nodes. Der Chat-Trigger startet ihn, der AI-Agent-Node in der Mitte erledigt die Arbeit, und ein Chat-Node am Ende schickt die Antwort zurück. Unter dem Agenten hängen Modell, Memory und vier Tools.

Der fertige Bestellassistent im Editor von n8n 2.46: Chat-Trigger, AI Agent, Send answer und darunter OpenAI-Modell, Simple Memory, drei Postgres-Tools sowie der Freigabeschritt Approve email mit dem Tool queue_customer_email

1. Chat-Trigger

Legen Sie einen neuen Workflow an und fügen Sie den Node Chat Trigger hinzu (auf der Arbeitsfläche heißt er "When chat message received"). Stellen Sie in den Optionen den Response Mode auf Using Response Nodes. Das ist für die Freigabe später wichtig, denn die Freigabe im Chat funktioniert nur in diesem Modus (siehe Fallstricke). In diesem Modus gibt der Trigger die Antwort des Agenten nicht mehr selbst zurück. Deshalb kommt in Schritt 7 ein Chat-Node ans Ende.

Damit Kolleginnen und Kollegen den Assistenten ohne Editor nutzen können, aktivieren Sie Make Chat Publicly Available, wählen Hosted Chat und setzen Authentication auf n8n User Auth. Dann können nur Personen mit n8n-Login chatten, und n8n fragt beim ersten Aufruf nach ihrer Zustimmung.

2. AI-Agent-Node und Systemnachricht

Hängen Sie einen AI Agent-Node an den Trigger. Die Quelle für den Prompt bleibt auf Connected Chat Trigger Node, der Agent bekommt also {{ $json.chatInput }}. Unter Options ergänzen Sie eine System Message und setzen Max Iterations auf 8.

Einstellungen des AI-Agent-Nodes: Prompt aus dem verbundenen Chat-Trigger, Systemnachricht mit den Regeln des Bestellassistenten und Max Iterations auf 8

In der Systemnachricht legen Sie die Regeln fest. Unsere, so wie sie im Export des Workflows steht:

1You are the order assistant of a manufacturer of mould components. Colleagues from sales and customer service ask you about customer orders.
2
3Rules:
4- Answer only from tool results. If a tool returns no rows, say so. Never invent order numbers, dates, prices or tracking numbers.
5- Today is {{ $now.toFormat('yyyy-MM-dd') }}. An order is late if promised_date is before today and its status is open or in_production.
6- If the user asks for an email to a customer, look up the order first, then show a short, friendly draft in the chat. Write it in English unless the user asks for another language. Sign it with "Customer Service".
7- Call queue_customer_email only after the user has confirmed the draft. Use the contact email from the order lookup.
8- Keep answers short. Use the order number in every answer.

Zwei Details sind Absicht. Das Modell kennt das heutige Datum nicht, deshalb schreibt der Ausdruck {{ $now.toFormat('yyyy-MM-dd') }} es in den Prompt. Ohne ihn hat die Frage "Welche Aufträge sind verspätet?" keine verlässliche Antwort. Und die Definition von "verspätet" steht ausdrücklich da, sonst entscheidet das Modell selbst, ob ein versendeter Auftrag mitzählt. Für deutschsprachige Teams schreiben Sie die Regeln einfach auf Deutsch und lassen die E-Mails auf Deutsch formulieren.

3. Chat-Modell

Klicken Sie unter dem Agenten auf Chat Model und wählen Sie OpenAI Chat Model mit Ihrer Credential. Wir haben gpt-5.4-mini verwendet. Es hat in unserem Test jedes Mal das richtige Tool gewählt und ist günstig (Preise weiter unten). Jedes Modell mit zuverlässigem Tool Calling funktioniert, n8n hat auch Nodes für Anthropic, Azure OpenAI, Google, Mistral und Ollama.

4. Memory

Ohne Memory ist jede Chatnachricht ein neues Gespräch. "Which items are in that order?" scheitert dann, weil der Agent nicht mehr weiß, welcher Auftrag gemeint ist. Hängen Sie unter Memory ein Simple Memory an. Es übernimmt die Session-ID automatisch vom Chat-Trigger. Wir haben die Context Window Length vom Standardwert 5 auf 10 Interaktionen erhöht.

Simple Memory liegt im Arbeitsspeicher des n8n-Prozesses. Nach einem Neustart ist es leer, und die Dokumentation rät im Queue Mode davon ab. Für den produktiven Einsatz nehmen Sie Postgres Chat Memory, einen Postgres-Server haben Sie in diesem Setup ohnehin.

5. Postgres-Tools mit festen Abfragen

Hier entscheidet sich, wie gut und wie sicher der Agent arbeitet. Sie könnten dem Modell ein Tool geben, das beliebiges SQL ausführt. Das machen wir nicht: Das Modell bräuchte das komplette Schema im Prompt, es kann falsche Joins schreiben, die trotzdem plausible Zahlen liefern, und Sie können nicht prüfen, was es ausführen wird. Stattdessen ist jedes Tool eine feste, parametrisierte Abfrage, und das Modell füllt nur die Werte aus.

Klicken Sie unter dem Agenten auf Tool, wählen Sie Postgres und die Operation Execute Query. Stellen Sie Tool Description auf Set Manually und beschreiben Sie in einfachen Worten, was das Tool liefert und wann es passt. Das Modell sieht nur den Namen des Tools, diese Beschreibung und die Beschreibungen der Parameter. Schreiben Sie diese Texte also für das Modell.

Postgres-Tool find_orders: manuelle Tool-Beschreibung, Execute Query mit parametrisierter SQL-Abfrage und Abfrageparameter aus $fromAI

Die Abfrage nutzt $1 als Platzhalter. Unter Options > Query Parameters kommt der Wert über $fromAI() vom Modell. Die Funktion sagt n8n "das füllt das Modell aus" und bekommt einen Schlüssel, eine Beschreibung und einen Typ. So sehen die Parameter unseres ersten Lese-Tools im Export aus:

1{
2 "name": "find_orders",
3 "type": "n8n-nodes-base.postgresTool",
4 "typeVersion": 2.6,
5 "parameters": {
6 "descriptionType": "manual",
7 "toolDescription": "Find customer orders by order number (e.g. SO-24063) or by part of the company name (e.g. Alpenform). Returns order number, company, contact name and email, order date, status (open, in_production, shipped, delivered, cancelled), promised date, shipped date, tracking number and total in EUR. Newest orders first, at most 20.",
8 "operation": "executeQuery",
9 "query": "SELECT o.order_id, c.company, c.contact_name, c.email,\n o.order_date::text AS order_date,\n o.status,\n o.promised_date::text AS promised_date,\n o.shipped_date::text AS shipped_date,\n o.tracking_no, o.total_eur\nFROM orders o\nJOIN customers c USING (customer_id)\nWHERE o.order_id ILIKE $1 OR c.company ILIKE '%' || $1 || '%'\nORDER BY o.order_date DESC\nLIMIT 20;",
10 "options": {
11 "queryReplacement": "={{ [ $fromAI('search_term', 'An order number such as SO-24063, or part of a company name such as Alpenform', 'string') ] }}"
12 }
13 }
14}

get_order_lines funktioniert genauso, mit WHERE order_id = $1 und dem Parameter order_id. list_open_orders hat gar keinen Parameter: Es liefert alle Aufträge mit Status open oder in_production, sortiert nach zugesagtem Liefertermin. Dieses Tool haben wir nach dem ersten Test ergänzt, weil find_orders die Frage "Welche Aufträge sind verspätet?" nicht beantworten kann. Für diese Frage gibt es keinen Suchbegriff. Ein Agent kann nur, was seine Tools erlauben, und eine Frage, die zu keinem Tool passt, bekommt eine vage Antwort oder einen falschen Tool-Aufruf.

Beachten Sie die Umwandlung ::text bei den Datumsspalten und die eckigen Klammern um $fromAI(). Beides behebt echte Probleme, die Fallstricke weiter unten erklären sie.

6. Freigabe, bevor die E-Mail rausgeht

Daten lesen ist harmlos, eine E-Mail an einen Kunden schicken nicht. Seit n8n 2.6 können Sie vor jedes Tool einen Freigabeschritt setzen: Klicken Sie auf das + an der Verbindung zwischen Agent und Tool, oder öffnen Sie den Tool-Anschluss und wählen Sie im Abschnitt Human review einen Kanal. n8n unterstützt für die Freigabe Chat, Slack, Teams, Gmail, Outlook, Telegram, Discord, WhatsApp und Google Chat. Wir haben Chat gewählt, damit die Freigabe im selben Chatfenster erscheint.

Das freizugebende Tool hängt unter dem Freigabeschritt. Unser queue_customer_email ist ein Data table-Node, der eine Zeile in die Tabelle email_outbox mit den Spalten to, subject, body und order_id schreibt, jede gefüllt über $fromAI(). Im Betrieb ersetzen Sie ihn durch den Outlook- oder Gmail-Node oder behalten den Postausgang und lassen einen zweiten Workflow daraus versenden.

Approve email (Chat, Send and wait): Freigabenachricht aus $tool.parameters, zwei Schaltflächen Send und Don't send

Die Nachricht des Freigabeschritts kann $tool.name und $tool.parameters verwenden. Bauen Sie die Nachricht selbst auf, statt $tool.parameters als Ganzes auszugeben (mehr dazu unten):

1{
2 "name": "Approve email",
3 "type": "@n8n/n8n-nodes-langchain.chatHitlTool",
4 "typeVersion": 1.3,
5 "parameters": {
6 "message": "=Queue this email for {{ $tool.parameters.order_id }}?\n\nTo: {{ $tool.parameters.to }}\nSubject: {{ $tool.parameters.subject }}\n\n{{ $tool.parameters.body }}",
7 "approvalOptions": {
8 "values": {
9 "approvalType": "double",
10 "approveLabel": "Send",
11 "disapproveLabel": "Don't send"
12 }
13 }
14 }
15}

Mit Approval Type auf Approve and Disapprove (double) bekommt die prüfende Person zwei Schaltflächen. Klickt sie auf "Don't send", läuft das Tool nicht, und der Agent erfährt, dass der Aufruf abgelehnt wurde.

7. Die Antwort zurückschicken

Weil der Trigger mit Response Nodes arbeitet, hängen Sie hinter den Agenten einen Chat-Node mit der Operation Send Message und der Nachricht {{ $json.output }}. Speichern Sie den Workflow und klicken Sie auf Publish. Seit n8n 2.0 ersetzen Save und Publish den früheren Schalter Active.

Den Agenten im Chat testen

Klicken Sie unten im Editor auf Open chat und stellen Sie eine Frage. Das Logs-Panel neben dem Chat zeigt jeden Schritt: welches Tool der Agent mit welchen Argumenten aufgerufen hat und was zurückkam. Der Screenshot zeigt die zweite Nachricht eines Gesprächs. "Which items are in that order?" klappt nur, weil Simple Memory die erste Antwort behalten hat. Der Agent ging deshalb direkt zu get_order_lines mit SO-24075.

Test im n8n-Editor: Der Agent beantwortet die Folgefrage aus dem Memory, die Logs zeigen Simple Memory, OpenAI Chat Model und den Aufruf von get_order_lines

Das ganze Szenario im Hosted Chat, also auf der Seite, die Kolleginnen und Kollegen nutzen würden: Statusfrage, E-Mail-Entwurf, "please send it", Freigabe, erledigt.

Hosted Chat des Bestellassistenten: Status des Auftrags von Schwarzwald Präzision, ein E-Mail-Entwurf, die Freigabenachricht mit der Schaltfläche Send und die Bestätigung

Nach dem Klick auf "Send" stand die Zeile in der Tabelle email_outbox, mit der Empfängeradresse aus der Datenbank, dem Betreff und dem Text aus der Freigabe. Wir haben auch die Fälle getestet, die scheitern sollten:

NachrichtWas der Agent getan hat
"Where is order SO-99999?"find_orders aufgerufen, keine Zeilen, Auftrag existiert laut Antwort nicht
"Cancel order SO-24072 for me."Geantwortet, dass er mit den vorhandenen Tools nicht stornieren kann
"Which orders are late?"list_open_orders aufgerufen, SO-24075 genannt (zugesagt für 2026-11-03)
"Write an email ... with the tracking number, and send it."Zuerst einen Entwurf gezeigt und um Bestätigung gebeten
"Yes, send it.", danach "Don't send" geklicktTool lief nicht, der Agent meldete, dass der Versand abgelehnt wurde

Eine Beobachtung: Nach dem freigegebenen Aufruf schrieb der Agent "email sent", obwohl das Tool die Mail nur in den Postausgang stellt. Die Tool-Beschreibung sagt "a separate workflow sends it". Wenn die Formulierung für Ihre Nutzer wichtig ist, legen Sie in der Systemnachricht fest, wie der Agent das Ergebnis beschreiben soll.

Die Tools über MCP wiederverwenden

Dieselben Postgres-Tools können auch andere KI-Clients bedienen. Legen Sie einen zweiten Workflow mit einem MCP Server Trigger an, hängen Sie die drei Lese-Tools daran (per Kopieren und Einfügen aus dem Agenten-Workflow), stellen Sie die Authentifizierung auf Bearer und veröffentlichen Sie den Workflow. Jeder MCP-Client, etwa Claude Desktop, Cursor oder ein eigener Agent, kann sich dann über Streamable HTTP mit https://<your-n8n>/mcp/<path> verbinden.

Was der Client sieht, erzeugt n8n aus Ihren Tool-Beschreibungen und den $fromAI()-Definitionen. So beginnt die Antwort auf tools/list, die wir von n8n bekommen haben:

1{
2 "name": "find_orders",
3 "description": "Find customer orders by order number (e.g. SO-24063) or by part of the company name (e.g. Alpenform). Returns order number, company, contact name and email, order date, status (open, in_production, shipped, delivered, cancelled), promised date, shipped date, tracking number and total in EUR. Newest orders first, at most 20.",
4 "inputSchema": {
5 "$schema": "https://json-schema.org/draft/2020-12/schema",
6 "type": "object",
7 "properties": {
8 "search_term": {
9 "type": "string",
10 "description": "An order number such as SO-24063, or part of a company name such as Alpenform"
11 }
12 },
13 "required": ["search_term"],
14 "additionalProperties": true
15 }
16}

Damit können Sie auch Ihre Tool-Beschreibungen prüfen, denn genau mit diesem JSON arbeitet das Modell. Die Gegenrichtung gibt es ebenfalls: Der Node MCP Client Tool verbindet einen n8n-Agenten mit einem externen MCP-Server. Wenn Sie einen MCP-Server lieber in Python schreiben, finden Sie das in unserer Anleitung MCP-Server mit FastMCP erstellen.

Fallstricke und wie Sie sie lösen

Datumswerte kommen einen Tag zu früh

Unsere erste Testantwort nannte für Auftrag SO-24075 den 2026-11-02 als zugesagten Termin. In der Datenbank steht 2026-11-03. Der Postgres-Node macht aus einem DATE ein JavaScript-Datum um Mitternacht Ortszeit, und mit TZ=Europe/Vienna steht im Tool-Ergebnis 2026-11-02T23:00:00.000Z. Das Modell liest den Tag aus dem Text und liegt einen Tag daneben. Die Lösung ist eine Umwandlung in der Abfrage (promised_date::text AS promised_date), dann liefert das Tool 2026-11-03. Das betrifft alle, die eine Zeitzone östlich von UTC setzen, wie es die Docker-Anleitung empfiehlt.

Abfrageparameter brechen bei leeren Werten und Kommas

Das Feld Query Parameters ist eine kommagetrennte Liste. Als das Modell find_orders mit leerem Suchbegriff aufrief, scheiterte das Tool mit there is no parameter $1. Ein Firmenname mit Komma würde in zwei Parameter zerfallen. Packen Sie den Wert in ein Array, wie es die Dokumentation des Postgres-Nodes für Ausdrücke zeigt: {{ [ $fromAI('search_term', ...) ] }}.

Tool-Fehler sehen aus wie erfolgreiche Läufe

Dieser fehlgeschlagene Tool-Aufruf hat die Ausführung nicht scheitern lassen. n8n gab den Fehlertext als Tool-Ergebnis an das Modell, das Modell antwortete "I couldn't retrieve the order list", und in der Liste der Ausführungen stand ein grünes "Success". Wer nur fehlgeschlagene Ausführungen überwacht, sieht kaputte Tools nicht. Prüfen Sie beim Bauen die Tool-Ergebnisse in den Logs und überwachen Sie im Betrieb, was die Tools zurückgeben, nicht nur den Status der Ausführung. Bei Agenten im Code zeichnet ein Observability-Werkzeug wie Langfuse genau deshalb jeden Tool-Aufruf auf.

Die Freigabe im Chat braucht "Using Response Nodes"

Mit dem Standardmodus "When Last Node Finishes" bricht der Freigabeschritt im Chat ab mit "Response Mode" in the chat trigger node must be set to "Using Response Nodes". Stellen Sie den Trigger auf diesen Modus um und ergänzen Sie einen Chat-Node, der {{ $json.output }} sendet, wie in Schritt 7. Freigaben über Slack, Teams oder E-Mail hängen nicht vom Chat-Trigger ab.

$tool.parameters erscheint als [object Object]

Die Standardnachricht der Freigabe enthält {{ $tool.parameters }}. Im Chat wurde daraus [object Object], und die prüfende Person sah nicht, was sie freigeben sollte. Greifen Sie einzeln auf die Felder zu, wie in unserer Nachricht oben. Wer freigibt, sollte die ganze E-Mail sehen.

Simple Memory ist nichts für den Betrieb

Simple Memory verliert bei einem Neustart von n8n alle Gespräche und funktioniert nicht im Queue Mode. Für alles, worauf sich Menschen verlassen, nehmen Sie Postgres Chat Memory oder Redis Chat Memory. Halten Sie das Fenster klein, denn das Memory geht in jeden Aufruf des Modells ein.

Die Kosten wachsen mit Memory und Tool-Ergebnissen

Jeder Modellaufruf enthält die Systemnachricht, alle Tool-Beschreibungen, das Memory und die Tool-Ergebnisse. In unserem Test brauchte die erste Nachricht eines Gesprächs rund 520 Input-Token pro Modellaufruf. Spätere Nachrichten mit Memory und der Freigabe kamen auf bis zu 1.600. Das Gespräch mit vier Nachrichten oben verbrauchte 3.558 Input- und 184 Output-Token. Zu den Listenpreisen von OpenAI für gpt-5.4-mini (0,75 USD pro Million Input-Token, 4,50 USD pro Million Output-Token) sind das etwa 0,0035 USD. Große Tool-Ergebnisse, etwa eine Abfrage mit Hunderten Zeilen, ändern das schnell. Behalten Sie deshalb die LIMIT-Klauseln in Ihren Abfragen. In der n8n Cloud zählt außerdem jede Chatnachricht als eine Ausführung Ihres Plans.

Fehlerbehandlung

Setzen Sie Max Iterations auf einen Wert, der zu Ihren Tools passt (wir haben 8 verwendet). Das stoppt ein Modell, das Tools im Kreis aufruft. Der OpenAI-Node wiederholt fehlgeschlagene Anfragen standardmäßig zweimal und hat eine Option für ein Timeout. Für Fehler außerhalb des Agenten hinterlegen Sie in den Workflow-Einstellungen einen Error Workflow, damit jemand benachrichtigt wird.

Mit Evaluations testen

Bei einem Agenten, auf den sich Ihr Team verlässt, wollen Sie wissen, ob eine Änderung am Prompt oder ein neues Modell Antworten kaputt macht, die vorher gestimmt haben. Die Evaluations in n8n schicken einen Datensatz mit Testfragen (aus einer Data Table oder Google Sheets) durch den Workflow und bewerten die Ergebnisse, zum Beispiel die Korrektheit durch ein Modell oder ob das erwartete Tool aufgerufen wurde. Laut Dokumentation muss eine selbst gehostete Community-Instanz dafür registriert sein (kostenlos, unter Settings > Usage and plan). Evaluations mit Metriken sind selbst gehostet ein Enterprise-Feature, registrierte Community-Instanzen dürfen sie für einen Workflow nutzen. Die Fragen aus der Tabelle oben wären ein guter erster Datensatz.

Wann n8n passt und wann Code besser ist

n8n passt gut, wenn der Agent vor allem Systeme verbindet: etwas in einer Datenbank nachschlagen, eine API aufrufen, in ein Ticketsystem schreiben, einen Menschen um Freigabe bitten. Der Bestellassistent besteht aus zehn Nodes, die Freigabe ist eingebaut, und auch wer nicht programmiert, kann in den Logs nachvollziehen, was der Agent tut.

Code ist die bessere Wahl, wenn der Agent selbst zum Produkt wird: komplexer Zustand, viele zusammenarbeitende Agenten, eigenes Retrieval, Unit-Tests und ein Code-Review für jede Änderung. Unsere Einschätzung:

Situationn8nCode (z. B. Agent Framework, SDK)
Agent verbindet bestehende Systeme und braucht FreigabenPasst gutMehr Aufwand
Prototyp, der in wenigen Tagen einer Abteilung gezeigt wirdPasst gutLangsamer
Fachbereich soll den Ablauf nachvollziehen und anpassenPasst gutSchwierig
Komplexe Multi-Agenten-Logik oder eigener ZustandWird unübersichtlichPasst gut
Versionierung, Tests und Code-Review für jede ÄnderungEingeschränktPasst gut
Agent als Teil Ihres eigenen Produkts ausliefernLizenzgrenzenPasst gut

Die letzte Zeile bezieht sich auf die Sustainable Use License von n8n: Sie erlaubt die Nutzung für eigene interne Geschäftszwecke, schränkt aber die Weitergabe von n8n und das kommerzielle Anbieten an Dritte ein. Prüfen Sie außerdem, welche Funktionen Sie brauchen. Projekte, SSO und Git-basierte Umgebungen sind zum Beispiel nicht Teil der kostenlosen Community Edition.

Wächst der Agent über n8n hinaus, nehmen Sie die Gestaltung der Tools direkt mit: feste Abfragen, eine Rolle nur zum Lesen, Freigabe für alles, was schreibt. Unser Beitrag zu KI-Workflows mit Microsoft Agent Framework zeigt die Variante im Code, und Zuverlässige KI-Agenten bauen beschreibt die Grundsätze dahinter. Wie Sie einem n8n-Agenten eine bessere Chatoberfläche geben, lesen Sie in n8n mit Open WebUI verbinden.

Zusammenfassung

Ein Agent in n8n 2.46 besteht aus einem Chat-Trigger, einem AI-Agent-Node mit Modell, Memory und Tools und einem Response-Node. Nachdenken müssen Sie vor allem über die Tools: feste Abfragen mit klaren Beschreibungen, eine Datenbankrolle nur zum Lesen und eine Freigabe vor allem, was schreibt oder versendet. Wandeln Sie Datumswerte in Text um, packen Sie Abfrageparameter in ein Array und stellen Sie den Chat-Trigger auf Response Nodes um, dann verhält sich der Workflow wie erwartet.

Wenn Sie einen solchen Agenten an Ihr ERP oder Ihre Auftragsdaten anbinden und in Ihrer eigenen Infrastruktur betreiben wollen, baut und prüft unser Team für KI-Beratung solche Systeme gemeinsam mit Ihnen.

Sie möchten zuverlässige KI-Agenten-Systeme bauen?

Wir haben auf Basis aktueller Forschung einen ausführlichen Leitfaden (auf Englisch) erstellt, wie Sie robuste KI-Agenten-Systeme bauen, die auch im produktiven Betrieb funktionieren. Der Leitfaden behandelt:

  • die 14 systematischen Fehlerquellen in Multi-Agenten-Systemen
  • belegte Best Practices für das Design von Agenten
  • strukturierte Kommunikationsprotokolle und Prüfmechanismen
Andreas Nigg

Über den Autor

Andreas Nigg

Mitgründer, Pondhouse Data

Andreas entwickelt seit mehr als acht Jahren Datenanwendungen. Bei Pondhouse Data konzipiert und baut er KI-Systeme und integriert sie in komplexe, gewachsene Unternehmensumgebungen.