Technik0 阅读

Von der Funktion zum Tool: Mit dem Vercel AI SDK die Tür zur Tool-Nutzung öffnen

Wenn du bereits daran gewöhnt bist, API-Routen in Next.js zu schreiben und Schnittstellen von Drittanbietern aufzurufen, bist du jetzt nur noch einen halben Schritt davon entfernt, einen LLM-Assistenten zu bauen, der selbstständig Schnittstellen aufrufen kann: Verpacke diese Funktionen als Tool und überlasse es dem Modell, selbst zu entscheiden, wann es sie verwendet.

In diesem Artikel sprechen wir zunächst nicht über große Konzepte wie MCP oder Agent-Frameworks, sondern machen nur eines:

Wir helfen dir, vom Schreiben einer normalen Funktion zum Schreiben eines Tools zu gelangen, das von einem LLM aufgerufen werden kann, und erklären dir dabei das zugrunde liegende Gedankenmodell.

Nach dem Lesen solltest du in der Lage sein:

1. Warum brauchen wir die Ebene der Tools?

Stell dir zuerst ein einfaches Szenario vor: Du möchtest einen Chatbot bauen, der das Wetter abfragt.

Der naivste Ansatz wäre:

  1. Der Benutzer gibt ein: „Bitte schau nach dem Wetter in Peking heute.“

  2. Du erhältst diesen natürlichen Ausdruck auf dem Server.

  3. Du schreibst selbst einen if/else-Block oder einen regulären Ausdruck, um den Stadtnamen zu extrahieren.

  4. Du rufst die Wetter-API auf und erhältst das Ergebnis.

  5. Du fügst das Ergebnis zurück in den Prompt und lässt das Modell eine Antwort formulieren, die du an den Benutzer sendest.

Dieser Ansatz funktioniert, hat aber offensichtliche Probleme:

Die Tool-Ebene erhebt genau diesen „if/else + Schnittstellenaufruf“ zu einer für das Modell sichtbaren Fähigkeit.

Oder anders ausgedrückt:

Diese Abstraktion ist es, die das Vercel AI SDK mit seinen Tools für dich erledigt.

2. Was ist ein Tool im Vercel AI SDK?

Du kannst dir ein Tool zunächst vorstellen als:

Eine Funktion mit einer Bedienungsanleitung – die Bedienungsanleitung ist für das Modell, der Funktionskörper für deinen eigenen Code.

Im Vercel AI SDK enthält ein Tool normalerweise drei Informationen:

  1. description: Ein Satz, der erklärt, wofür dieses Tool da ist.

  2. parameters: Beschreibt mit JSON Schema (oder Zod), welche Eingabeparameter benötigt werden.

  3. execute: Die tatsächlich auszuführende Funktion, z.B. ein HTTP-API-Aufruf oder eine Datenbankabfrage.

Es sieht ungefähr so aus (Pseudocode, um erst einmal ein Gefühl zu bekommen):

ts
import { tool } from 'ai';

const getWeather = tool({
  description: '查询指定城市的实时天气',
  parameters: {
    type: 'object',
    properties: {
      city: { type: 'string', description: '城市名称,比如 Beijing' },
    },
    required: ['city'],
  },
  execute: async ({ city }) => {
    // 这里就是你平时会写的业务代码
    const res = await fetch(`https://api.example.com/weather?city=${city}`);
    return await res.json();
  },
});

Wenn du das Modell später aufrufst, übergibst du dieses getWeather an das AI SDK:

Du musst nicht mehr selbst die Logik „Stadtnamen parsen“ oder „entscheiden, wann die Schnittstelle aufgerufen wird“ schreiben – du übergibst die Initiative an das Modell.

3. Ein minimal nutzbares Tool-Beispiel

Wir schreiben ein minimales, wirklich lauffähiges Beispiel: Abfrage der aktuellen Zeit.

3.1 Ein einfaches Tool definieren

Angenommen, du hast in deinem Next.js-Projekt bereits das Paket ai (Vercel AI SDK) installiert. Auf der Serverseite erstellen wir ein einfaches Tool:

ts
// app/api/chat/tools.ts
import { tool } from 'ai';

export const getCurrentTime = tool({
  description: '获取当前服务器时间(ISO 字符串)',
  parameters: {
    type: 'object',
    properties: {},
  },
  async execute() {
    return {
      now: new Date().toISOString(),
    };
  },
});

Dabei sind einige Punkte zu beachten:

3.2 Das Tool in der API-Route verwenden

Als Nächstes schreiben wir eine einfache Chat-API, die es dem Modell erlaubt, dieses Tool während des Gesprächs aufzurufen. Die genauen Aufrufdetails können je nach Version leicht variieren; hier betonen wir nur die Struktur und die Idee. Der Pseudocode sieht ungefähr so aus:

ts
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import { streamText } from 'ai'; // 假设使用 AI SDK 的某个调用方法
import { getCurrentTime } from './tools';
import { openai } from '@ai-sdk/openai';

export async function POST(req: NextRequest) {
  const { messages } = await req.json();

  const response = await streamText({
    model: openai('gpt-4o'), // 你选的模型
    messages,
    tools: {
      getCurrentTime,
    },
  });

  return response.toAIStreamResponse();
}

Hier sind die Kernpunkte:

3.3 Eine einfache Chat-Oberfläche

Auf der Frontend-Seite kannst du einen Hook wie useChat verwenden, um eine minimalistische Chat-UI zu erstellen. Das ist nicht der Schwerpunkt dieses Artikels, also gehen wir nicht näher darauf ein. Wichtig ist: Aus Frontend-Sicht gibt es keinen Unterschied zu einer normalen Chat-API – nur dass das Modell jetzt selbstständig die Zeit abfragen kann.

4. Der wesentliche Unterschied zwischen Tool und normaler Funktion

An diesem Punkt fragst du dich vielleicht: Kann ich nicht einfach new Date().toISOString() in der Route schreiben, um die Zeit abzurufen? Warum der Umweg über ein Tool?

Der entscheidende Unterschied liegt darin, wer die Entscheidung trifft:

Das bringt mehrere praktische Vorteile:

  1. Weniger if/else

    • Du musst keine natürliche Sprache parsen, Schlüsselwörter abgleichen oder Verzweigungen auswählen.

    • Das Modell entscheidet anhand des Kontexts selbst, ob es ein bestimmtes Tool verwenden soll.

  2. Unterstützung für mehrere Tools und automatische Orchestrierung

    • Definiere mehrere Werkzeuge (Zeit abfragen, Wetter abfragen, Termine abfragen). Das Modell kann in einer einzigen Konversation je nach Bedarf mehrere aufrufen.

    • Je mehr Werkzeuge du hast, desto weniger musst du eine Routing-Tabelle für Geschäftslogik pflegen – das Modell findet selbst seinen Weg.

  3. Modell-übergreifende Einheitlichkeit

    • Verschiedene Modellanbieter haben unterschiedliche Schnittstellen für Tools / Function Calls. Das AI SDK vereinheitlicht diese Details für dich.

    • Du musst nur einen Satz von Tool-Definitionen pflegen und kannst zwischen verschiedenen Modellen wechseln.

Du kannst das Tool also betrachten als:

Eine Funktion mit strukturierten Metadaten, die zwischen dem Modell und deinem Code eine Ebene einzieht, die diese Verbindung sowohl intelligent als auch kontrollierbar macht.

5. Ein benutzerfreundliches Tool gestalten: Parameter und Rückgabewerte

Ein Tool zu schreiben bedeutet nicht nur „es muss funktionieren“. Wenn Parameter und Rückgabewerte gut gestaltet sind, kann das Modell es stabiler und kontrollierter nutzen.

5.1 Parameter: Dem Modell helfen, die Sache klar zu beschreiben

Bei der Parametrierung empfehlen sich einige kleine Prinzipien:

Beispiel:

ts
const getWeather = tool({
  description: '查询指定城市在指定日期的天气',
  parameters: {
    type: 'object',
    properties: {
      city: {
        type: 'string',
        description: '城市名称,比如 Beijing、Shanghai',
      },
      date: {
        type: 'string',
        description: '日期,格式为 YYYY-MM-DD,比如 2025-05-17',
      },
      unit: {
        type: 'string',
        enum: ['celsius', 'fahrenheit'],
        description: '温度单位,默认 celsius',
      },
    },
    required: ['city', 'date'],
  },
  async execute({ city, date, unit = 'celsius' }) {
    // ...
  },
});

5.2 Rückgabewerte: Möglichst strukturiert, nicht ein Brocken Text

Obwohl du in execute direkt einen Satz wie „Peking heute 25 Grad, sonnig“ zurückgeben kannst, verlierst du damit viel Flexibilität:

Empfohlen wird, ein klares Objekt zurückzugeben:

ts
async execute({ city, date, unit = 'celsius' }) {
  const data = await fetchWeather(city, date, unit);
  return {
    city,
    date,
    unit,
    temperature: data.temp,
    condition: data.condition, // sunny, cloudy, etc.
  };
}

So kann das Modell die Antwort entweder selbst formulieren oder diese Felder in nachfolgenden Schritten weiterverwenden (z.B. ein weiteres Tool aufrufen).

6. Mehrere Tools auf einmal anschließen: Das Modell selbst wählen lassen

Bisher haben wir nur getCurrentTime verwendet. Jetzt fügen wir ein getWeather hinzu und geben beide gemeinsam an das Modell weiter.

ts
// app/api/chat/tools.ts
export const tools = {
  getCurrentTime,
  getWeather,
};

Beim Modellaufruf:

ts
const response = await streamText({
  model: openai('gpt-4o'),
  messages,
  tools,
});

Wenn der Benutzer nun sagt:

„Schau mir bitte das Wetter für morgen in Shanghai an. Wenn es sonnig ist, erinnere mich gleichzeitig an die aktuelle Uhrzeit.“

Das Modell hat die Möglichkeit, Folgendes zu tun (auf logischer Ebene):

  1. Zuerst getWeather aufrufen, um festzustellen, ob es sonnig ist.

  2. Dann getCurrentTime aufrufen, um die aktuelle Zeit zu erhalten.

  3. Eine endgültige Antwort generieren, die die Ergebnisse beider Aufrufe kombiniert.

Du musst für diese Logik kein Geschäftsablaufdiagramm schreiben. Du musst nur:

Das ist die wahre Stärke des Tool-Aufrufs: Du wechselst vom Schreiben von Abläufen zum Definieren von Fähigkeiten.

7. Abschluss dieses Artikels: Erst das Tool richtig kennenlernen, dann über Protokollebenen nachdenken

In diesem Artikel haben wir nur drei Dinge getan:

  1. Das Tool konzeptionell aufgeschlüsselt: Es ist eine Funktion mit einer Bedienungsanleitung, die für das Modell bestimmt ist.

  2. Ein minimales Tool-Beispiel geschrieben und gezeigt, wie man es in einer Next.js API-Route verwendet.

  3. Erklärt, wie man Parameter und Rückgabewerte gestaltet, damit das Modell dein Werkzeug besser nutzen kann.

Allein mit den Tools des Vercel AI SDKs kannst du bereits Folgendes erstellen:

Und in dieser Phase musst du dich überhaupt nicht um Protokollebenen wie MCP kümmern – sie lösen hauptsächlich das Problem der Wiederverwendbarkeit von Tools über Hosts und Projekte hinweg und sind eher geeignet, wenn die Anzahl der Tools und die Aufrufumgebung komplexer werden.

Im nächsten Artikel werden wir auf Basis von „nur Tools“ genau darüber sprechen: Wenn die Anzahl der Tools wächst und die Aufrufumgebung komplexer wird, auf welche Grenzen und Schwachstellen man mit bloßen Tools stößt und warum Protokolle wie MCP entstehen.

Teilen

Teilen

Diesen Artikel teilen.