Vom Modell ausgewählte Tool-Aufrufe validieren

ShellBeginner
Jetzt üben

Einführung

Ein KI-Modell kann Antworten als Text formulieren. Eine Anwendung benötigt jedoch manchmal strukturierte Informationen, bevor sie sinnvoll arbeiten kann. Tool Calling ermöglicht es der Anwendung, eine Operation zu beschreiben, etwa die Suche nach einem Katalogartikel, und dem Modell, einen Tool-Namen mit Argumenten vorzuschlagen. Das Modell erhält dadurch keine Berechtigung, beliebigen Code auszuführen. Es erzeugt Daten, die Ihr Worker als nicht vertrauenswürdige Eingabe behandeln muss.

In diesem Lab erstellen Sie POST /catalog-help. Ein bei Cloudflare gehostetes Llama-Modell erhält eine kurze Frage wie „Ist SKU KB-101 auf Lager?“ und kann das schreibgeschützte Tool lookup_catalog_item vorschlagen. Ihr Worker akzeptiert genau ein bekanntes Tool, validiert ein exaktes Argumentobjekt { sku } und liest erst danach aus einem kleinen synthetischen Katalog. Unbekannte Tools, fehlende oder zusätzliche Felder, fehlerhafte SKUs und mehrere Aufrufe erreichen den Executor nie.

Sie verwenden traditionelles Function Calling, damit die Sicherheitsgrenze sichtbar bleibt: Die Inferenz macht einen Vorschlag, die Validierung entscheidet und der Anwendungscode führt ihn aus. Das zurückgegebene Ergebnis ist auf wenige öffentliche Fixture-Felder begrenzt. Diese Übung gewährt keinen Schreibzugriff, ruft keinen externen Dienst auf und erlaubt dem Modell nicht, ausführbaren Code auszuwählen.

Dies ist das fünfte Lab des Kurses. Wenn Sie direkt hier eingestiegen sind, bearbeiten Sie zuerst Connect LabEx to Your Cloudflare Account, damit Sie wissen, wie Sie das VM-Terminal verwenden, Wrangler autorisieren, Ihr Lernkonto bestätigen und dessen Account-ID konfigurieren.

Das ausgewählte Modell @cf/meta/llama-3.3-70b-instruct-fp8-fast unterstützt Function Calling und ist über die standardmäßige Workers-AI-Zuteilung verfügbar. Workers Free umfasst derzeit 10.000 Neurons pro Tag. Dieses Lab sendet lokal nur eine kurze Live-Anfrage und nach der Bereitstellung eine weitere. Solange die kostenlose Zuteilung ausreicht, benötigen Sie daher keinen Workers-Paid-Tarif. Auch die lokale Inferenz erreicht Cloudflare und verbraucht Kontingent. Brechen Sie ab, statt wiederholt zu versuchen, wenn das Modell oder die Zuteilung nicht verfügbar ist.

Das Setup installiert Node.js 22.22.0 und Wrangler 4.132.0 als lokale Projektabhängigkeit in /home/labex/project/tool-call-guard. Außerdem stellt es deterministische Modell-Fixtures und unabhängige Prüfungen bereit. Das Setup autorisiert Wrangler nicht, erstellt keine Worker-Quelle, ruft kein Modell auf, stellt nichts bereit und erstellt keine Cloud-Ressource.

VM autorisieren und den Tool-Call-Worker konfigurieren

In diesem Schritt autorisieren Sie diese neue VM und konfigurieren einen temporären Worker. Ihr Browser ist möglicherweise bereits im Cloudflare-Dashboard angemeldet. Wrangler innerhalb einer neuen VM benötigt jedoch eine eigene, eingeschränkte Autorisierung.

Wechseln Sie in das vorbereitete Projekt und prüfen Sie die festgelegte CLI-Version:

cd /home/labex/project/tool-call-guard
npx wrangler --version

Erwartet wird 4.132.0. Fordern Sie nur die Berechtigungen an, die für einen Worker mit KI-Anbindung erforderlich sind. Wrangler 4.132.0 prüft beim Löschen außerdem KV-Abhängigkeiten. Deshalb benötigt der Bereinigungspfad die KV-Berechtigung, obwohl dieses Lab keine KV-Daten erstellt.

npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write ai:write

Öffnen Sie den angezeigten Link, geben Sie den aktuellen Code ein, prüfen Sie Account und Berechtigungen und autorisieren Sie Ihr Lernkonto. Senden Sie Code, Passwort oder Token niemals an eine andere Person. Prüfen Sie anschließend die strukturierten Identitätsdaten:

npx wrangler whoami --json

Bestätigen Sie loggedIn: true. Erzeugen Sie danach einen eindeutigen Namen, damit die Bereinigung ausschließlich den Worker dieses Labs entfernen kann:

RUN="labex-c07-a05-$(openssl rand -hex 6)"
printf '%s\n' "$RUN"

Der folgende Here-Document-Befehl schreibt eine normale JSON-Konfiguration. Ersetzen Sie YOUR_ACCOUNT_ID durch die tatsächliche ID des vorgesehenen Lernkontos:

cat > wrangler.jsonc <<JSON
{
  "\$schema": "./node_modules/wrangler/config-schema.json",
  "name": "$RUN",
  "account_id": "YOUR_ACCOUNT_ID",
  "main": "src/index.js",
  "compatibility_date": "2026-09-16",
  "workers_dev": true,
  "preview_urls": false,
  "observability": { "enabled": true, "head_sampling_rate": 1 },
  "ai": { "binding": "AI", "remote": true }
}
JSON

Das Binding AI stellt dem Code über env.AI einen sicheren Zugriff bereit, ohne einen Model-API-Key in den Quellcode einzutragen. remote: true bedeutet, dass die lokale Entwicklung weiterhin das kontogebundene Modell aufruft, anstatt die Inferenz offline zu simulieren.

Die Tool-Grenze verstehen

In diesem Schritt verbinden Sie das Plattform-Binding mit der Grenze, die Ihre Anwendung durchsetzen muss.

Erzeugen Sie aus wrangler.jsonc die Umgebungs-Typen und prüfen Sie anschließend das erzeugte Interface:

npx wrangler types
grep -A4 'interface __BaseEnv_Env' worker-configuration.d.ts

Suchen Sie nach AI: Ai. Eine Tool-Beschreibung ist eine strukturierte Datenstruktur, die an das Modell gesendet wird: ein Name, ein Zweck in natürlicher Sprache und ein Schema für mögliche Argumente. Sie hilft dem Modell, einen Aufruf vorzuschlagen. Sie ist jedoch weder eine Autorisierung noch ausführbarer Code.

Dieses Lab erlaubt ein schreibgeschütztes Tool: lookup_catalog_item mit einem Argument wie { "sku": "KB-101" }. Nach der Inferenz verlangt die Anwendung genau einen vorgeschlagenen Aufruf mit dem exakt erlaubten Namen. Anschließend muss arguments ein Objekt sein, das ausschließlich sku enthält. Die Anwendung prüft das kurze öffentliche SKU-Format des Labs und übergibt den validierten Wert nur an die fest definierte schreibgeschützte Funktion der Anwendung.

Prüfen Sie die bereitgestellten Fixtures für abgelehnte Aufrufe:

grep -nE 'unknown tools|missing, extra|zero or multiple' test/worker.test.mjs

Bei den Fixtures handelt es sich bewusst um gefälschte Modellantworten. Sie zeigen die Sicherheitsgrenze, ohne Neurons zu verbrauchen oder darauf angewiesen zu sein, dass ein Live-Modell einen fehlerhaften Aufruf erzeugt.

Das validierte Katalog-Tool erstellen

In diesem Schritt beschreiben Sie dem Modell das Tool, validieren den Vorschlag des Modells und führen ausschließlich die schreibgeschützte Katalogfunktion der Anwendung aus.

Erstellen Sie den Einstiegspunkt des Workers:

cat > src/index.js <<'JS'
const MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast";
const TOOL_NAME = "lookup_catalog_item";
const MAX_QUESTION = 240;
const SKU_PATTERN = /^[A-Z]{2}-[0-9]{3}$/;
const CATALOG = [
  { sku: "KB-101", name: "Compact Keyboard", priceUsd: 49, inStock: true },
  { sku: "MS-205", name: "Wireless Mouse", priceUsd: 29, inStock: false }
];

const TOOLS = [{
  name: TOOL_NAME,
  description: "Read one public catalog item by the exact SKU stated in the user's question.",
  parameters: {
    type: "object",
    properties: { sku: { type: "string", description: "An exact catalog SKU such as KB-101" } },
    required: ["sku"]
  }
}];

function json(data, status = 200) { return Response.json(data, { status }); }

async function readQuestion(request) {
  if (!(request.headers.get("content-type") || "").toLowerCase().includes("application/json")) {
    return { error: json({ error: "json_required" }, 415) };
  }
  let body;
  try { body = await request.json(); } catch { return { error: json({ error: "invalid_json" }, 400) }; }
  const question = typeof body?.question === "string" ? body.question.trim() : "";
  if (!question) return { error: json({ error: "invalid_question" }, 400) };
  if (question.length > MAX_QUESTION) return { error: json({ error: "question_too_large" }, 413) };
  return { question };
}

export function validateToolSelection(toolCalls) {
  if (!Array.isArray(toolCalls) || toolCalls.length !== 1) throw new Error("exactly one tool call is required");
  const call = toolCalls[0];
  if (!call || call.name !== TOOL_NAME) throw new Error("unknown tool");
  const args = call.arguments;
  if (!args || typeof args !== "object" || Array.isArray(args)) throw new Error("arguments must be an object");
  if (Object.keys(args).length !== 1 || !Object.hasOwn(args, "sku")) throw new Error("unexpected arguments");
  if (typeof args.sku !== "string" || !SKU_PATTERN.test(args.sku)) throw new Error("invalid sku");
  return { name: TOOL_NAME, arguments: { sku: args.sku } };
}

export function executeCatalogTool(argumentsValue) {
  const item = CATALOG.find((candidate) => candidate.sku === argumentsValue.sku);
  return item ? { ...item, found: true } : { sku: argumentsValue.sku, found: false };
}

export async function handleCatalogHelp(request, env, execute = executeCatalogTool) {
  const parsed = await readQuestion(request);
  if (parsed.error) return parsed.error;
  const requestId = crypto.randomUUID();
  let inference;
  try {
    inference = await env.AI.run(MODEL, {
      messages: [
        { role: "system", content: "Use exactly one provided read-only tool. Copy only the exact SKU from the user. Do not answer from memory." },
        { role: "user", content: parsed.question }
      ],
      tools: TOOLS,
      max_tokens: 128,
      temperature: 0
    });
  } catch {
    console.error(JSON.stringify({ event: "tool_inference_failed", requestId, model: MODEL }));
    return json({ error: "model_unavailable", requestId }, 502);
  }
  let selected;
  try { selected = validateToolSelection(inference?.tool_calls); }
  catch {
    console.error(JSON.stringify({ event: "tool_call_rejected", requestId, model: MODEL }));
    return json({ error: "invalid_tool_call", requestId }, 502);
  }
  const result = execute(selected.arguments);
  console.log(JSON.stringify({ event: "tool_call_executed", requestId, model: MODEL, tool: selected.name, found: result.found }));
  return json({ model: MODEL, tool: selected.name, arguments: selected.arguments, result, requestId });
}

export default { async fetch(request, env) {
  const url = new URL(request.url);
  if (request.method === "GET" && url.pathname === "/health") return json({ status: "ok" });
  if (request.method === "POST" && url.pathname === "/catalog-help") return handleCatalogHelp(request, env);
  return json({ error: "not_found" }, 404);
} };
JS

Beachten Sie die Reihenfolge: env.AI.run() liefert Daten zurück, validateToolSelection() schränkt diese auf eine zulässige Form ein, und erst danach wird executeCatalogTool() ausgeführt. Das Modell liefert niemals JavaScript, wählt keine URL aus und erhält keinen Zugriff auf eine Schreiboperation. Die Logs erfassen Metadaten zum Ablauf, lassen aber die Frage des Benutzers und das Katalogergebnis aus.

Führen Sie die fünf deterministischen Tests aus. Bitten Sie Wrangler anschließend, das Projekt ohne Bereitstellung zu bündeln:

node --test test/worker.test.mjs
npx wrangler deploy --dry-run

Die Tests sollten fünf erfolgreiche Durchläufe melden. Im Dry Run sollte env.AI als AI-Binding aufgeführt werden. Zusammen belegen die Ergebnisse, dass Validierungscode und Worker-Konfiguration zusammenpassen, bevor ein Live-Modellaufruf Kontingent verbraucht.

Eine Live-Tool-Auswahl ausführen

In diesem Schritt führen Sie den Worker lokal aus, während sein AI-Binding eine echte Remote-Inferenz durchführt. Nur die Katalogabfrage läuft lokal; das Modell wird weiterhin auf Cloudflare ausgeführt.

Starten Sie Wrangler im Hintergrund und warten Sie auf die AI-unabhängige Health-Route. & erstellt einen Hintergrundprozess, $! enthält dessen Prozess-ID, und die begrenzte Schleife beendet das Warten, sobald /health erfolgreich ist:

npx wrangler dev --port 8787 > .labex/dev.log 2>&1 &
echo $! > .labex/dev.pid
for attempt in $(seq 1 30); do
  if curl --silent --fail http://127.0.0.1:8787/health; then break; fi
  sleep 1
done

Senden Sie eine kurze Frage mit einer exakten synthetischen SKU:

curl --silent --show-error http://127.0.0.1:8787/catalog-help \
  --header 'Content-Type: application/json' \
  --data '{"question":"Is SKU KB-101 in stock and what does it cost?"}'

Erwartet werden das exakte Llama-Modell, tool: "lookup_catalog_item", Argumente, die ausschließlich KB-101 enthalten, sowie die begrenzten Fixture-Daten für das Compact Keyboard. Die vom Modell erzeugte Formulierung wird nicht bewertet, weil die Anwendung den strukturierten Tool-Vorschlag und keinen frei formulierten Text verarbeitet.

Lehnen Sie eine leere Frage vor der Inferenz ab:

curl --silent --show-error --write-out '\nHTTP %{http_code}\n' http://127.0.0.1:8787/catalog-help \
  --header 'Content-Type: application/json' --data '{"question":""}'

Erwartet werden {"error":"invalid_question"} und HTTP 400. Damit ist belegt, dass die normale Anfragevalidierung vor der Modellnutzung erfolgt.

Bereitstellen und Tool-Nachweise prüfen

In diesem Schritt stellen Sie denselben Endpunkt bereit und verbinden sein Laufzeitverhalten mit sichtbaren Cloudflare-Nachweisen.

Beenden Sie ausschließlich den gespeicherten Entwicklungsprozess, warten Sie auf dessen Ende und stellen Sie anschließend den Worker bereit:

kill "$(cat .labex/dev.pid)"
wait "$(cat .labex/dev.pid)" 2>/dev/null || true
npx wrangler deploy

Speichern Sie die von Wrangler ausgegebene exakte URL und senden Sie eine öffentliche Frage:

WORKER_URL="https://YOUR_WORKER_URL"
curl --silent --show-error "$WORKER_URL/catalog-help" \
  --header 'Content-Type: application/json' \
  --data '{"question":"Is SKU KB-101 in stock and what does it cost?"}'

Prüfen Sie, dass das öffentliche Ergebnis das exakte Modell und das erlaubte Tool verwendet, nur das validierte SKU-Argument zurückgibt und dieselben begrenzten schreibgeschützten Fixture-Felder enthält.

Öffnen Sie Workers & Pages → Ihren labex-c07-a05-...-Worker → Bindings. Ein Binding ist die benannte Verbindung, über die ein Cloudflare-Dienst für Worker-Code verfügbar wird. Bestätigen Sie eine Verbindung zu Workers AI mit dem Namen AI. Dieser Name ist der Grund dafür, dass das Programm env.AI.run(...) aufrufen kann.

Workers-AI-Binding mit dem Namen AI

Öffnen Sie anschließend Observability. Diese Seite sammelt Aufrufdatensätze und Anwendungslogs. Das folgende Beispiel zeigt nach der öffentlichen Anfrage und den unabhängigen Prüfungen vier erfolgreiche Ereignisse und keine Fehler. Ihre Anzahl kann abweichen, weil jede Anfrage sowohl einen Aufrufdatensatz als auch ein Anwendungsereignis erzeugen kann und die Übertragung an das Dashboard verzögert sein kann.

Erfolgreiche Worker-Ereignisse in Observability

Der blaue Hinweis zum Free-Tarif auf dieser Seite bezieht sich auf das Workers-Logs-Ereigniskontingent, nicht auf die KI-Inferenz. Geben Sie im Suchfeld tool_call_executed ein und erweitern Sie eine passende Zeile. Das fokussierte Beispiel zeigt zwei erfolgreiche Treffer sowie die bewusst begrenzten Felder am Anfang des Ereignisses: lookup_catalog_item, das exakte Llama-Modell und eine Request-ID. Das vollständige Ereignis enthält außerdem event: "tool_call_executed" und found: true. Es protokolliert jedoch weder die Frage des Benutzers noch die unverarbeitete Modellantwort oder den zurückgegebenen Katalogeintrag.

Datenschutzbegrenztes Tool-Ausführungslog

Öffnen Sie zuletzt AI → Workers AI und lassen Sie den Tab Neurons ausgewählt. Ein Neuron ist die Einheit, mit der Cloudflare den Rechenaufwand von Workers AI misst. Das Beispielkonto verwendete an diesem Tag 342.34/10k Neurons; in der Llama-Zeile werden 341.57 angezeigt, während eine frühere Embedding-Übung separat erscheint. Diese Werte sind gemeinsame Kontobeispiele und keine zugesicherten Kosten für eine einzelne Anfrage. Suchen Sie in Ihrem Konto die exakte Llama-Zeile und bestätigen Sie, dass die heutige Gesamtsumme innerhalb der 10k-Workers-Free-Zuteilung bleibt.

Tägliche Workers-AI-Neuron-Nutzung

Die Dashboard-Seiten helfen Ihnen, Konfiguration, Datenverkehr und Nutzung mit dem Ergebnis der Befehlszeile zu verbinden. Wiederholen Sie die Inferenz nicht nur, um eine Diagrammaktualisierung zu erzwingen. Die JSON-Antwort und die unabhängige Prüfung bleiben maßgeblich, da Diagramme und Logs später eintreffen können.

Worker entfernen und abmelden

In diesem Schritt entfernen Sie den temporären öffentlichen Endpunkt und anschließend die Autorisierung dieser VM. Die Nutzung von Workers AI bleibt in der Kontohistorie erhalten. Durch das Löschen des Workers wird der Nutzungsdatensatz nicht gelöscht.

Löschen Sie genau den Worker, der in wrangler.jsonc angegeben ist:

npx wrangler delete

Bestätigen Sie nur, wenn Wrangler den eindeutigen Namen dieses Labs labex-c07-a05-... anzeigt. Warten Sie auf Successfully deleted. Führen Sie anschließend die unabhängige Prüfung auf Abwesenheit in der Cloud aus, solange die Autorisierung noch verfügbar ist:

python3 .labex/verify.py deleted

Melden Sie sich erst ab, wenn PASS: deleted ausgegeben wurde, und prüfen Sie danach den strukturierten Status:

npx wrangler logout
npx wrangler whoami --json

Erwartet wird loggedIn: false. Das Schließen eines Browser-Tabs oder das Löschen des lokalen Quellcodes würde nicht beweisen, dass der öffentliche Worker entfernt wurde.

Zusammenfassung

Sie haben die Modellauswahl von den Berechtigungen der Anwendung getrennt. Workers AI schlug eine strukturierte Katalogabfrage vor, Ihr Worker validierte den exakten Tool-Namen und das Argumentobjekt, und erst danach wurde fester schreibgeschützter Code ausgeführt. Deterministische Fixtures zeigten, dass unbekannte Tools, fehlerhafte Argumente und mehrere Aufrufe keine Aktion auslösen können. Die Live-Inferenz demonstrierte zusätzlich den tatsächlichen Austausch mit dem Modell. Außerdem prüften Sie datenschutzbegrenzte Nachweise und entfernten den temporären Worker sowie die Autorisierung der VM.