Dela via


Azure OpenAI Assistants-klientbibliotek för JavaScript – version 1.0.0-beta.5

Azure OpenAI Assistants-klientbiblioteket för JavaScript är en anpassning av OpenAI:s REST-API:er som tillhandahåller ett idiomatiskt gränssnitt och omfattande integrering med resten av Azure SDK-ekosystemet. Den kan ansluta till Azure OpenAI-resurser eller till slutpunkten för icke-Azure OpenAI-slutsatsdragning, vilket gör den till ett bra val för även icke-Azure OpenAI-utveckling.

Nyckellänkar:

Komma igång

Miljöer som stöds för närvarande

Förutsättningar

Om du vill använda en Azure OpenAI-resurs måste du ha en Azure-prenumeration och Azure OpenAI-åtkomst. På så sätt kan du skapa en Azure OpenAI-resurs och hämta både en anslutnings-URL och API-nycklar. Mer information finns i Snabbstart: Kom igång med att generera text med Azure OpenAI-tjänsten.

Om du vill använda JS-klientbiblioteket för Azure OpenAI-assistenter för att ansluta till icke-Azure OpenAI behöver du en API-nyckel från ett utvecklarkonto på https://platform.openai.com/.

Installera @azure/openai-assistants-paketet

Installera Azure OpenAI Assistants-klientbiblioteket för JavaScript med npm:

npm install @azure/openai-assistants

Skapa och autentisera en AssistantsClient

Om du vill konfigurera en klient för användning med Azure OpenAI anger du en giltig slutpunkts-URI till en Azure OpenAI-resurs tillsammans med motsvarande nyckelautentiseringsuppgifter, tokenautentiseringsuppgifter eller Azure-identitetsautentiseringsuppgifter som har behörighet att använda Azure OpenAI-resursen. Om du i stället vill konfigurera klienten för att ansluta till OpenAI:s tjänst anger du en API-nyckel från OpenAI:s utvecklarportal.

Använda en API-nyckel från Azure

Använd Azure-portalen för att bläddra till din OpenAI-resurs och hämta en API-nyckel, eller använd Azure CLI-kodfragmentet nedan:

Observera: Ibland kallas API-nyckeln för en "prenumerationsnyckel" eller "prenumerations-API-nyckel".

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

Viktiga begrepp

Se OpenAI:s dokumentation om hur assistenter fungerar för en översikt över de begrepp och relationer som används med assistenter. Den här översikten följer noga OpenAI:s översiktsexempel för att demonstrera grunderna i att skapa, köra och använda assistenter och trådar.

Kom igång genom att skapa en AssistantsClient:

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

Med en klient kan en assistent sedan skapas. En assistent är ett specialbyggt gränssnitt för OpenAI-modeller som kan anropa verktyg samtidigt som instruktioner på hög nivå tillåts under hela assistent.

Koden för att skapa en 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" }]
});

En konversationssession mellan en assistent och en användare kallas för en tråd. Trådar lagrar meddelanden och hanterar automatiskt trunkering för att passa in innehåll i en modells kontext.

Så här skapar du en tråd:

const assistantThread = await assistantsClient.createThread();

Meddelandet representerar ett meddelande som skapats av en assistent eller en användare. Meddelanden kan innehålla text, bilder och andra filer. Meddelanden lagras som en lista i tråden. När en tråd har skapats kan meddelanden skapas på den:

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

En körning representerar ett anrop av en assistent i en tråd. Assistenten använder konfigurationen och trådens meddelanden för att utföra uppgifter genom att anropa modeller och verktyg. Som en del av en Körning lägger assistenten till Meddelanden i tråden. En körning kan sedan startas som utvärderar tråden mot en assistent:

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

När körningen har startat bör den avsökas tills den når en terminalstatus:

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

Förutsatt att körningen har slutförts återspeglar en lista över meddelanden från tråden som kördes nu ny information som lagts till av assistent:

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

Exempel på utdata från den här sekvensen:

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?

Arbeta med filer för hämtning

Filer kan laddas upp och sedan refereras av assistenter eller meddelanden. Använd först det generaliserade uppladdnings-API:et med syftet "assistenter" för att göra ett fil-ID tillgängligt:

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

När fil-ID:t har laddats upp kan det skickas till en assistent när det skapas. Observera att fil-ID:t endast används om ett lämpligt verktyg som kodtolkare eller hämtning är aktiverat.

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

Med en fil-ID-association och ett verktyg som stöds aktiverat kan assistent sedan använda associerade data när trådar körs.

Använda funktionsverktyg och parallella funktionsanrop

Enligt beskrivningen i OpenAI:s dokumentation för assistent verktyg kan verktyg som refererar till anropardefinierade funktioner som funktioner tillhandahållas till en assistent så att den dynamiskt kan lösa och skilja sig åt under en körning.

Här beskrivs en enkel assistent som "vet hur man", via funktioner som tillhandahålls av anroparen:

  1. Hämta användarens favoritstad
  2. Få ett smeknamn för en viss stad
  3. Hämta aktuellt väder, om du vill med en temperaturenhet, i en stad

Det gör du genom att börja med att definiera vilka funktioner som ska användas – de faktiska implementeringarna här är bara representativa 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"]
    }
  }
};

När funktionerna har definierats i lämpliga verktyg kan du nu skapa en assistent som har dessa verktyg aktiverade:

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

Om assistent anropar verktyg måste den anropande koden matcha ToolCall instanser till matchande ToolOutputSubmission instanser. För enkelhetens skull extraheras ett grundläggande exempel här:

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

För att hantera användarindata som "hur är vädret just nu i min favoritstad?", bör avsökning av svaret för slutförande kompletteras med en RunStatus sökning efter RequiresAction eller, i det här fallet, förekomsten av RequiredAction egenskapen på körningen. Sedan ska samlingen av ToolOutputSubmissions skickas till körningen SubmitRunToolOutputs via -metoden så att körningen kan fortsätta:

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

Observera att när du använder modeller som stöds kan assistent begära att flera funktioner anropas parallellt. Äldre modeller kan bara anropa en funktion i taget.

När alla nödvändiga funktionsanrop har lösts fortsätter körningen normalt och de slutförda meddelandena i tråden innehåller modellutdata som kompletteras av det tillhandahållna funktionsverktygets utdata.

Felsökning

Loggning

Aktivering av loggning kan hjälpa dig att hitta användbar information om fel. Om du vill se en logg över HTTP-begäranden och svar anger du AZURE_LOG_LEVEL miljövariabeln till info. Loggning kan också aktiveras vid körning genom att anropa setLogLevel i @azure/logger:

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

setLogLevel("info");

Mer detaljerade anvisningar om hur du aktiverar loggar finns i dokumentationen om @azure-/loggningspaket.