Udostępnij za pośrednictwem


Biblioteka klienta asystentów usługi Azure OpenAI dla języka JavaScript — wersja 1.0.0-beta.5

Biblioteka klienta Asystentów OpenAI platformy Azure dla języka JavaScript jest adaptacją interfejsów API REST platformy OpenAI, które udostępnia interfejs idiotyczny i bogatą integrację z resztą ekosystemu zestawu Azure SDK. Może ona łączyć się z zasobami usługi Azure OpenAI lub z punktem końcowym wnioskowania innego niż Azure OpenAI, dzięki czemu jest to doskonały wybór nawet w przypadku programowania w usłudze Azure OpenAI.

Kluczowe linki:

Wprowadzenie

Obecnie obsługiwane środowiska

Wymagania wstępne

Jeśli chcesz użyć zasobu usługi Azure OpenAI, musisz mieć subskrypcję platformy Azure i dostęp do usługi Azure OpenAI. Pozwoli to utworzyć zasób usługi Azure OpenAI i uzyskać zarówno adres URL połączenia, jak i klucze interfejsu API. Aby uzyskać więcej informacji, zobacz Szybki start: rozpoczynanie generowania tekstu przy użyciu usługi Azure OpenAI Service.

Jeśli chcesz użyć biblioteki klienta JS Asystentów usługi Azure OpenAI w celu nawiązania połączenia z usługą Azure OpenAI, musisz mieć klucz interfejsu API z konta dewelopera pod adresem https://platform.openai.com/.

Instalowanie pakietu @azure/openai-assistants

Zainstaluj bibliotekę klienta Asystentów OpenAI platformy Azure dla języka JavaScript przy użyciu polecenia npm:

npm install @azure/openai-assistants

Tworzenie i uwierzytelnianie AssistantsClient

Aby skonfigurować klienta do użycia z usługą Azure OpenAI, podaj prawidłowy identyfikator URI punktu końcowego dla zasobu usługi Azure OpenAI wraz z odpowiednimi poświadczeniami klucza, poświadczeniami tokenu lub poświadczeniami tożsamości platformy Azure, które są autoryzowane do korzystania z zasobu usługi Azure OpenAI. Aby zamiast tego skonfigurować klienta w celu nawiązania połączenia z usługą OpenAI, podaj klucz interfejsu API z portalu deweloperskiego openAI.

Używanie klucza interfejsu API z platformy Azure

Użyj witryny Azure Portal , aby przejść do zasobu OpenAI i pobrać klucz interfejsu API lub użyć poniższego fragmentu wiersza polecenia platformy Azure :

Uwaga: Czasami klucz interfejsu API jest nazywany "kluczem subskrypcji" lub "kluczem interfejsu API subskrypcji".

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

Kluczowe pojęcia

Aby zapoznać się z omówieniem pojęć i relacji używanych z asystentami, zobacz dokumentację platformy OpenAI "how assistants work". Ten przegląd jest ściśle zgodny z przykładem przeglądu interfejsu OpenAI , aby zademonstrować podstawy tworzenia, uruchamiania i używania asystentów i wątków.

Aby rozpocząć pracę, utwórz element AssistantsClient:

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

Po utworzeniu klienta można utworzyć asystent. Asystent to specjalnie utworzony interfejs do modeli OpenAI, który może wywoływać narzędzia, umożliwiając jednocześnie wykonywanie instrukcji wysokiego poziomu przez cały okres istnienia asystent.

Kod do utworzenia asystent:

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" }]
});

Sesja konwersacji między Asystentem a użytkownikiem jest nazywana wątkiem. Wątki przechowują komunikaty i automatycznie obsługują obcinanie w celu dopasowania zawartości do kontekstu modelu.

Aby utworzyć wątek:

const assistantThread = await assistantsClient.createThread();

Komunikat reprezentuje komunikat utworzony przez Asystenta lub użytkownika. Komunikaty mogą zawierać tekst, obrazy i inne pliki. Komunikaty są przechowywane jako lista w wątku. Po utworzeniu wątku można utworzyć na nim komunikaty:

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

Uruchomienie reprezentuje wywołanie asystenta w wątku. Asystent używa konfiguracji i komunikatów wątku do wykonywania zadań przez wywołanie modeli i narzędzi. W ramach przebiegu Asystent dołącza komunikaty do wątku. Następnie można uruchomić przebieg, który ocenia wątek względem asystent:

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

Po rozpoczęciu przebiegu powinno być sondowane do momentu osiągnięcia stanu terminalu:

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

Zakładając, że przebieg został pomyślnie ukończony, wyświetlenie listy komunikatów z uruchomionego wątku będzie teraz odzwierciedlać nowe informacje dodane przez asystent:

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);
    }
  }
}

Przykładowe dane wyjściowe z tej sekwencji:

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?

Praca z plikami na potrzeby pobierania

Pliki mogą być przekazywane, a następnie przywoływane przez asystentów lub wiadomości. Najpierw użyj uogólnionego interfejsu API przekazywania z celem "asystentów", aby udostępnić identyfikator pliku:

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 });

Po przekazaniu identyfikator pliku można podać do asystent po utworzeniu. Należy pamiętać, że identyfikatory plików będą używane tylko wtedy, gdy jest włączone odpowiednie narzędzie, takie jak interpreter kodu lub pobieranie.

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 ]
});

Po włączeniu skojarzenia identyfikatora pliku i obsługiwanego narzędzia asystent będzie mógł korzystać z skojarzonych danych podczas uruchamiania wątków.

Używanie narzędzi funkcji i wywoływania funkcji równoległych

Jak opisano w dokumentacji interfejsu OpenAI dla narzędzi asystent, narzędzia odwołujące się do funkcji zdefiniowanych przez obiekt wywołujący jako funkcje mogą być udostępniane asystent, aby umożliwić dynamiczne rozpoznawanie i uściślanie podczas przebiegu.

W tym miejscu przedstawiono prosty asystent, który "wie, jak", za pośrednictwem funkcji dostarczanych przez obiekt wywołujący:

  1. Pobieranie ulubionego miasta użytkownika
  2. Uzyskiwanie pseudonimu dla danego miasta
  3. Pobierz bieżącą pogodę, opcjonalnie z jednostką temperatury, w mieście

W tym celu zacznij od zdefiniowania funkcji do użycia — rzeczywiste implementacje są jedynie reprezentatywnymi wycinkami.

// 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"]
    }
  }
};

Dzięki funkcjom zdefiniowanym w odpowiednich narzędziach można teraz utworzyć asystent z włączonymi tymi narzędziami:

  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]
});

Jeśli asystent wywołuje narzędzia, kod wywołujący będzie musiał rozpoznać ToolCall wystąpienia w pasujących ToolOutputSubmission wystąpieniach. Dla wygody podstawowy przykład jest wyodrębniany tutaj:

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;
};

Aby obsłużyć dane wejściowe użytkownika, takie jak "jaka jest pogoda jak teraz w moim ulubionym mieście?", sondowanie odpowiedzi na ukończenie powinno zostać uzupełnione RunStatus przez sprawdzenie RequiresAction lub, w tym przypadku, obecność RequiredAction właściwości w biegu. Następnie kolekcja ToolOutputSubmissions powinna zostać przesłana do przebiegu za pomocą SubmitRunToolOutputs metody , aby przebieg mógł kontynuować:

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")

Należy pamiętać, że w przypadku korzystania z obsługiwanych modeli asystent może zażądać równoległego wywołania kilku funkcji. Starsze modele mogą wywoływać tylko jedną funkcję naraz.

Po rozwiązaniu wszystkich wymaganych wywołań funkcji przebieg będzie kontynuowany normalnie, a ukończone komunikaty w wątku będą zawierać dane wyjściowe modelu uzupełnione przez dostarczone dane wyjściowe narzędzia funkcji.

Rozwiązywanie problemów

Rejestrowanie

Włączenie rejestrowania może pomóc odkryć przydatne informacje o błędach. Aby wyświetlić dziennik żądań HTTP i odpowiedzi, ustaw zmienną AZURE_LOG_LEVEL środowiskową na info. Możesz też włączyć rejestrowanie w czasie wykonywania, wywołując polecenie w elemecie setLogLevel@azure/logger:

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

setLogLevel("info");

Aby uzyskać bardziej szczegółowe instrukcje dotyczące włączania dzienników, zapoznaj się z dokumentami dotyczącymi pakietu @azure/rejestratora.