JavaScript 用 Azure OpenAI Assistants クライアント ライブラリ - バージョン 1.0.0-beta.5

JavaScript 用 Azure OpenAI Assistants クライアント ライブラリは、慣用インターフェイスと Azure SDK エコシステムの残りの部分との豊富な統合を提供する OpenAI の REST API の適応です。 Azure OpenAI リソースまたは Azure 以外の OpenAI 推論エンドポイントに接続できるため、Azure OpenAI 以外の開発にも適しています。

主要リンク:

はじめに

現在サポートされている環境

前提条件

Azure OpenAI リソースを使用する場合は、 Azure サブスクリプションAzure OpenAI アクセス権が必要です。 これにより、Azure OpenAI リソースを作成し、接続 URL と API キーの両方を取得できます。 詳細については、「 クイック スタート: Azure OpenAI Service を使用してテキストの生成を開始する」を参照してください。

Azure OpenAI Assistants JS クライアント ライブラリを使用して Azure OpenAI 以外に接続する場合は、 の開発者アカウント https://platform.openai.com/から API キーが必要です。

@azure/openai-assistants パッケージのインストール

を使用して、JavaScript 用の Azure OpenAI Assistants クライアント ライブラリを npmインストールします。

npm install @azure/openai-assistants

AssistantsClient を作成して認証する

Azure OpenAI で使用するクライアントを構成するには、Azure OpenAI リソースへの有効なエンドポイント URI と、Azure OpenAI リソースの使用が許可されている対応するキー資格情報、トークン資格情報、または Azure ID 資格情報を指定します。 代わりに、OpenAI のサービスに接続するようにクライアントを構成するには、OpenAI の開発者ポータルから API キーを指定します。

Azure からの API キーの使用

Azure Portal を使用して OpenAI リソースを参照し、API キーを取得するか、次の Azure CLI スニペットを使用します。

メモ: API キーは、"サブスクリプション キー" または "サブスクリプション API キー" と呼ばれる場合があります。

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

主要な概念

アシスタントで使用される概念と関係の概要については、 OpenAI の 「アシスタントのしくみ」 のドキュメントを参照してください。 この概要は 、OpenAI の概要の例 に密接に従って、アシスタントとスレッドの作成、実行、および使用の基本を示します。

開始するには、 を作成します AssistantsClient

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

クライアントを使用すると、アシスタントを作成できます。 アシスタントは、アシスタントの有効期間を通じて高レベルの命令を可能にしながらツールを呼び出すことができる OpenAI モデルへの専用インターフェイスです。

アシスタントを作成するコード:

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

アシスタントとユーザーの間の会話セッションは、スレッドと呼ばれます。 スレッドはメッセージを格納し、コンテンツをモデルのコンテキストに収まるように切り捨てを自動的に処理します。

スレッドを作成するには:

const assistantThread = await assistantsClient.createThread();

メッセージは、アシスタントまたはユーザーによって作成されたメッセージを表します。 メッセージには、テキスト、画像、その他のファイルを含めることができます。 メッセージはスレッドにリストとして格納されます。 スレッドを作成すると、その上にメッセージを作成できます。

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

Run は、スレッドでのアシスタントの呼び出しを表します。 アシスタントは、構成とスレッドのメッセージを使用して、モデルとツールを呼び出してタスクを実行します。 実行の一部として、アシスタントはスレッドにメッセージを追加します。 その後、アシスタントに対してスレッドを評価する実行を開始できます。

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

実行が開始されると、ターミナルの状態に達するまでポーリングされます。

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

実行が正常に完了したと仮定すると、実行されたスレッドからのメッセージを一覧表示すると、アシスタントによって追加された新しい情報が反映されるようになります。

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

このシーケンスからの出力例:

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?

取得用のファイルの操作

ファイルをアップロードし、アシスタントまたはメッセージで参照できます。 まず、一般化されたアップロード API を "assistants" の目的で使用して、ファイル ID を使用できるようにします。

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

アップロード後、作成時にファイル ID をアシスタントに指定できます。 ファイル ID は、コード インタープリターや取得などの適切なツールが有効になっている場合にのみ使用されることに注意してください。

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

ファイル ID の関連付けとサポートされているツールが有効になっていると、アシスタントはスレッドの実行時に関連付けられたデータを使用できるようになります。

関数ツールと並列関数呼び出しの使用

アシスタント ツールに関する OpenAI のドキュメントで説明されているように、呼び出し元が定義した機能を関数として参照するツールをアシスタントに提供して、実行中に動的に解決およびあいまいさを解消できます。

ここでは、呼び出し元から提供される関数を介して "方法を知っている" 簡単なアシスタントについて説明します。

  1. ユーザーのお気に入りの都市を取得する
  2. 特定の都市のニックネームを取得する
  3. 都市で現在の天気 (必要に応じて温度ユニットを使用) を取得する

これを行うには、まず、使用する関数を定義します。ここでの実際の実装は、単なる代表的なスタブです。

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

適切なツールで関数を定義すると、これらのツールを有効にしたアシスタントを作成できるようになりました。

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

アシスタントがツールを呼び出す場合、呼び出し元のコードはインスタンスを一致するToolOutputSubmissionインスタンスに解決ToolCallする必要があります。 便宜上、ここでは基本的な例を抽出します。

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

"お気に入りの都市で今のような天気ですか?"などのユーザー入力を処理するには、完了の応答をポーリングするには、 または のチェックRequiresActionで補完RunStatusする必要があります。この場合は、実行時に プロパティがRequiredAction存在します。 次に、 の ToolOutputSubmissions コレクションを メソッドを使用して SubmitRunToolOutputs 実行に送信して、実行を続行できるようにする必要があります。

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

サポートされているモデルを使用する場合、アシスタントは複数の関数を並列で呼び出すように要求する場合があることに注意してください。 古いモデルでは、一度に 1 つの関数のみを呼び出す場合があります。

必要なすべての関数呼び出しが解決されると、実行は正常に続行され、スレッド上の完了したメッセージには、指定された関数ツールの出力によって補完されたモデル出力が含まれます。

トラブルシューティング

ログ記録

ログの記録を有効にすると、エラーに関する有用な情報を明らかにするのに役立つ場合があります。 HTTP 要求と応答のログを表示するには、環境変数 AZURE_LOG_LEVELinfo に設定します。 または、@azure/loggersetLogLevel を呼び出して、実行時にログ記録を有効にすることもできます。

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

setLogLevel("info");

ログを有効にする方法の詳細については、@azure/logger パッケージに関するドキュメントを参照してください。