Freigeben über


Azure OpenAI Assistants-Clientbibliothek für JavaScript – Version 1.0.0-beta.5

Die Azure OpenAI Assistants-Clientbibliothek für JavaScript ist eine Anpassung der REST-APIs von OpenAI, die eine idiomatische Schnittstelle und eine umfassende Integration in das restliche Azure SDK-Ökosystem bietet. Es kann eine Verbindung mit Azure OpenAI-Ressourcen oder mit dem Nicht-Azure OpenAI-Rückschlussendpunkt herstellen, was es zu einer hervorragenden Wahl für die Nicht-Azure OpenAI-Entwicklung macht.

Wichtige Links:

Erste Schritte

Die derzeitig unterstützten Umgebungen

Voraussetzungen

Wenn Sie eine Azure OpenAI-Ressource verwenden möchten, benötigen Sie ein Azure-Abonnement und Azure OpenAI-Zugriff. Dadurch können Sie eine Azure OpenAI-Ressource erstellen und sowohl eine Verbindungs-URL als auch API-Schlüssel abrufen. Weitere Informationen finden Sie unter Schnellstart: Erste Schritte beim Generieren von Text mithilfe von Azure OpenAI Service.

Wenn Sie die JS-Clientbibliothek von Azure OpenAI Assistants verwenden möchten, um eine Verbindung mit Nicht-Azure OpenAI herzustellen, benötigen Sie einen API-Schlüssel aus einem Entwicklerkonto unter https://platform.openai.com/.

Installieren Sie das Paket @azure/openai-assistants.

Installieren Sie die Azure OpenAI Assistants-Clientbibliothek für JavaScript mit npm:

npm install @azure/openai-assistants

Erstellen und Authentifizieren eines AssistantsClient

Um einen Client für die Verwendung mit Azure OpenAI zu konfigurieren, geben Sie einen gültigen Endpunkt-URI für eine Azure OpenAI-Ressource zusammen mit den entsprechenden Schlüsselanmeldeinformationen, Tokenanmeldeinformationen oder Azure-Identitätsanmeldeinformationen an, die für die Verwendung der Azure OpenAI-Ressource autorisiert sind. Um stattdessen den Client für die Verbindung mit dem OpenAI-Dienst zu konfigurieren, geben Sie einen API-Schlüssel aus dem OpenAI-Entwicklerportal an.

Verwenden eines API-Schlüssels aus Azure

Verwenden Sie das Azure-Portal , um zu Ihrer OpenAI-Ressource zu navigieren und einen API-Schlüssel abzurufen, oder verwenden Sie den folgenden Azure CLI-Codeausschnitt :

Hinweis: Manchmal wird der API-Schlüssel als "Abonnementschlüssel" oder "Abonnement-API-Schlüssel" bezeichnet.

az cognitiveservices account keys list --resource-group <your-resource-group-name> --name <your-resource-name>

Wichtige Begriffe

Eine Übersicht über die Konzepte und Beziehungen, die mit Assistenten verwendet werden, finden Sie in der OpenAI-Dokumentation "Funktionsweise von Assistenten" . Diese Übersicht folgt genau dem Übersichtsbeispiel von OpenAI , um die Grundlagen des Erstellens, Ausführens und Verwendens von Assistenten und Threads zu veranschaulichen.

Erstellen Sie zunächst ein AssistantsClient:

const assistantsClient = new AssistantsClient("<endpoint>", new AzureKeyCredential("<azure_api_key>"));

Mit einem Client kann dann ein Assistent erstellt werden. Ein Assistent ist eine speziell für OpenAI-Modelle erstellte Schnittstelle, die Tools aufrufen kann und gleichzeitig allgemeine Anweisungen während der gesamten Lebensdauer des Assistent zulässt.

Der Code zum Erstellen eines Assistent:

const assistant = await assistantsClient.createAssistant({
  model: "gpt-4-1106-preview",
  name: "JS Math Tutor",
  instructions: "You are a personal math tutor. Write and run code to answer math questions.",
  tools: [{ type: "code_interpreter" }]
});

Eine Konversationssitzung zwischen einem Assistenten und einem Benutzer wird als Thread bezeichnet. Threads speichern Nachrichten und behandeln das Abschneiden automatisch, um Inhalte in den Kontext eines Modells einzufügen.

So erstellen Sie einen Thread:

const assistantThread = await assistantsClient.createThread();

Die Nachricht stellt eine Nachricht dar, die von einem Assistenten oder einem Benutzer erstellt wurde. Nachrichten können Text, Bilder und andere Dateien enthalten. Nachrichten werden als Liste im Thread gespeichert. Wenn ein Thread erstellt wurde, können Nachrichten darauf erstellt werden:

const question = "I need to solve the equation '3x + 11 = 14'. Can you help me?";
const messageResponse = await assistantsClient.createMessage(assistantThread.id, "user", question);

Eine Ausführung stellt einen Aufruf eines Assistenten für einen Thread dar. Der Assistent verwendet die Konfiguration und die Nachrichten des Threads, um Aufgaben auszuführen, indem Modelle und Tools aufgerufen werden. Im Rahmen einer Ausführung fügt der Assistent Meldungen an den Thread an. Anschließend kann eine Ausführung gestartet werden, die den Thread mit einem Assistent auswertet:

let runResponse = await assistantsClient.createRun(assistantThread.id, {
   assistantId: assistant.id,
   instructions: "Please address the user as Jane Doe. The user has a premium account." 
});

Sobald die Ausführung gestartet wurde, sollte sie abgefragt werden, bis sie ein Terminal erreicht status:

do {
  await new Promise((resolve) => setTimeout(resolve, 800));
  runResponse = await assistantsClient.getRun(assistantThread.id, runResponse.id);
} while (runResponse.status === "queued" || runResponse.status === "in_progress")

Wenn die Ausführung erfolgreich abgeschlossen wurde, werden nun neue Informationen angezeigt, die vom Assistent hinzugefügt wurden:

const runMessages = await assistantsClient.listMessages(assistantThread.id);
for (const runMessageDatum of runMessages.data) {
  for (const item of runMessageDatum.content) {
    if (item.type === "text") {
      console.log(item.text.value);
    } else if (item.type === "image_file") {
      console.log(item.imageFile.fileId);
    }
  }
}

Beispielausgabe aus dieser Sequenz:

2023-11-14 20:21:23 -  assistant: The solution to the equation \(3x + 11 = 14\) is \(x = 1\).
2023-11-14 20:21:18 -       user: I need to solve the equation `3x + 11 = 14`. Can you help me?

Arbeiten mit Dateien zum Abrufen

Dateien können hochgeladen und dann von Assistenten oder Nachrichten referenziert werden. Verwenden Sie zunächst die generalisierte Upload-API mit dem Zweck "Assistenten", um eine Datei-ID verfügbar zu machen:

const filename = "<path_to_text_file>";
await fs.writeFile(filename, "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457.", "utf8");
const uint8array = await fs.readFile(filename);
const uploadAssistantFile = await assistantsClient.uploadFile(uint8array, "assistants", { filename });

Nach dem Hochladen kann die Datei-ID bei der Erstellung einer Assistent angegeben werden. Beachten Sie, dass Datei-IDs nur verwendet werden, wenn ein geeignetes Tool wie CodeInterpreter oder Abruf aktiviert ist.

const fileAssistant = await assistantsClient.createAssistant({
  model: "gpt-4-1106-preview",
  name: "JS SDK Test Assistant - Retrieval",
  instructions: "You are a helpful assistant that can help fetch data from files you know about.",
  tools: [{ type: "retrieval" }],
  fileIds: [ uploadAssistantFile.id ]
});

Wenn eine Datei-ID-Zuordnung und ein unterstütztes Tool aktiviert sind, kann der Assistent die zugeordneten Daten beim Ausführen von Threads nutzen.

Verwenden von Funktionstools und parallelen Funktionsaufrufen

Wie in der OpenAI-Dokumentation für Assistent Tools beschrieben, können Tools, die auf vom Aufrufer definierte Funktionen als Funktionen verweisen, einer Assistent bereitgestellt werden, damit sie während einer Ausführung dynamisch aufgelöst und begriffsklärt werden kann.

Hier ist eine einfache Assistent beschrieben, die über vom Aufrufer bereitgestellte Funktionen "weiß, wie es geht":

  1. Abrufen der Lieblingsstadt des Benutzers
  2. Abrufen eines Spitznamens für eine bestimmte Stadt
  3. Abrufen des aktuellen Wetters(optional mit einer Temperatureinheit) in einer Stadt

Definieren Sie dazu zunächst die zu verwendenden Funktionen . Die tatsächlichen Implementierungen sind hier lediglich repräsentative Stubs.

// Example of a function that defines no parameters
const getFavoriteCity = () => "Atlanta, GA";
const getUserFavoriteCityTool = { 
  type: "function",
  function: {
    name: "getUserFavoriteCity",
    description: "Gets the user's favorite city.",
    parameters: {
      type: "object",
      properties: {}
    }
  }
}; 

// Example of a function with a single required parameter
const getCityNickname = (city) => { 
  switch (city) { 
    case "Atlanta, GA": 
      return "The ATL"; 
    case "Seattle, WA": 
      return "The Emerald City"; 
    case "Los Angeles, CA":
      return "LA"; 
    default: 
      return "Unknown"; 
  }
};

const getCityNicknameTool = { 
  type: "function",
  function: {
    name: "getCityNickname",
    description: "Gets the nickname for a city, e.g. 'LA' for 'Los Angeles, CA'.",
    parameters: { 
      type: "object",
      properties: { 
        city: {
          type: "string",
          description: "The city and state, e.g. San Francisco, CA"
        } 
      }
    }
  }
};

// Example of a function with one required and one optional, enum parameter
const getWeatherAtLocation = (location, temperatureUnit = "f") => {
  switch (location) { 
    case "Atlanta, GA": 
      return temperatureUnit === "f" ? "84f" : "26c"; 
    case "Seattle, WA": 
      return temperatureUnit === "f" ? "70f" : "21c"; 
    case "Los Angeles, CA":
      return temperatureUnit === "f" ? "90f" : "28c"; 
    default: 
      return "Unknown"; 
  }
};

const getWeatherAtLocationTool = { 
  type: "function",
  function: {
    name: "getWeatherAtLocation",
    description: "Gets the current weather at a provided location.",
    parameters: { 
      type: "object",
      properties: { 
        location: {
          type: "string",
          description: "The city and state, e.g. San Francisco, CA"
        },
        temperatureUnit: {
          type: "string",
          enum: ["f", "c"],
        }
      },
      required: ["location"]
    }
  }
};

Wenn die Funktionen in den entsprechenden Tools definiert sind, kann nun eine Assistent erstellt werden, für die diese Tools aktiviert sind:

  const weatherAssistant = await assistantsClient.createAssistant({
  // note: parallel function calling is only supported with newer models like gpt-4-1106-preview
  model: "gpt-4-1106-preview",
  name: "JS SDK Test Assistant - Weather",
  instructions: `You are a weather bot. Use the provided functions to help answer questions.
    Customize your responses to the user's preferences as much as possible and use friendly
    nicknames for cities whenever possible.
  `,
  tools: [getUserFavoriteCityTool, getCityNicknameTool, getWeatherAtLocationTool]
});

Wenn der Assistent Tools aufruft, muss der aufrufende Code Instanzen in übereinstimmende ToolOutputSubmission Instanzen auflösenToolCall. Der Einfachheit halber wird hier ein einfaches Beispiel extrahiert:

const getResolvedToolOutput = (toolCall) => {
  const toolOutput = { toolCallId: toolCall.id };

  if (toolCall["function"]) {
    const functionCall = toolCall["function"];
    const functionName = functionCall.name;
    const functionArgs = JSON.parse(functionCall["arguments"] ?? {});

    switch (functionName) {
      case "getUserFavoriteCity":
        toolOutput.output = getFavoriteCity();
        break;
      case "getCityNickname":
        toolOutput.output = getCityNickname(functionArgs["city"]);
        break;
      case "getWeatherAtLocation":
        toolOutput.output = getWeatherAtLocation(functionArgs.location, functionArgs.temperatureUnit);
        break;
      default:
        toolOutput.output = `Unknown function: ${functionName}`;
        break;
    }
  }
  return toolOutput;
};

Um Benutzereingaben wie "Wie ist das Wetter gerade in meiner Lieblingsstadt?" zu behandeln, sollte das Abfragen der Antwort auf Abschluss durch eine RunStatus Überprüfung für RequiresAction oder in diesem Fall das Vorhandensein der RequiredAction Eigenschaft auf der Ausführung ergänzt werden. Anschließend sollte die Auflistung von ToolOutputSubmissions über SubmitRunToolOutputs die -Methode an die Ausführung übermittelt werden, damit die Ausführung fortgesetzt werden kann:

const question = "What's the weather like right now in my favorite city?";
let runResponse = await assistantsClient.createThreadAndRun({ 
  assistantId: weatherAssistant.id, 
  thread: { messages: [{ role: "user", content: question }] },
  tools: [getUserFavoriteCityTool, getCityNicknameTool, getWeatherAtLocationTool]
});

do {
  await new Promise((resolve) => setTimeout(resolve, 500));
  runResponse = await assistantsClient.getRun(runResponse.threadId, runResponse.id);
  
  if (runResponse.status === "requires_action" && runResponse.requiredAction.type === "submit_tool_outputs") {
    const toolOutputs = [];

    for (const toolCall of runResponse.requiredAction.submitToolOutputs.toolCalls) {
      toolOutputs.push(getResolvedToolOutput(toolCall));
    }
    runResponse = await assistantsClient.submitToolOutputsToRun(runResponse.threadId, runResponse.id, toolOutputs);
  }
} while (runResponse.status === "queued" || runResponse.status === "in_progress")

Beachten Sie, dass der Assistent bei Verwendung unterstützter Modelle möglicherweise anfordern kann, dass mehrere Funktionen parallel aufgerufen werden. Bei älteren Modellen kann es sein, dass jeweils nur eine Funktion aufgerufen wird.

Sobald alle erforderlichen Funktionsaufrufe aufgelöst wurden, wird die Ausführung normal fortgesetzt, und die abgeschlossenen Meldungen im Thread enthalten eine Modellausgabe, die durch die bereitgestellten Funktionstoolausgaben ergänzt wird.

Problembehandlung

Protokollierung

Die Aktivierung der Protokollierung kann hilfreiche Informationen über Fehler aufdecken. Um ein Protokoll von HTTP-Anforderungen und -Antworten anzuzeigen, legen Sie die Umgebungsvariable AZURE_LOG_LEVEL auf info fest. Alternativ kann die Protokollierung zur Laufzeit aktiviert werden, indem Sie setLogLevel in @azure/logger aufrufen:

const { setLogLevel } = require("@azure/logger");

setLogLevel("info");

Ausführlichere Anweisungen zum Aktivieren von Protokollen finden Sie in der Paketdokumentation zu @azure/logger.