Sdílet prostřednictvím


Co je modul plug-in?

Moduly plug-in jsou klíčovou součástí sémantického jádra. Pokud jste už v Microsoftu 365 používali moduly plug-in z rozšíření ChatGPT nebo Copilot, už je znáte. Moduly plug-in umožňují zapouzdření stávajících rozhraní API do kolekce, kterou může používat AI. Díky tomu můžete umělé inteligenci provádět akce, které by jinak nemohla provést.

Sémantické jádro na pozadí využívá volání funkcí, což je nativní funkce většiny nejnovějších LLM, která umožňuje LLM provádět plánování a volat vaše rozhraní API. Při volání funkcí můžou LLM požadovat (tj. volat) konkrétní funkci. Sémantické jádro pak zařadí požadavek do příslušné funkce v základu kódu a vrátí výsledky zpět do LLM, aby LLM mohl vygenerovat konečnou odpověď.

Sémantický modul plug-in jádra

Ne všechny sady AI SDK mají podobný koncept modulů plug-in (většina má jenom funkce nebo nástroje). V podnikových scénářích jsou ale moduly plug-in cenné, protože zapouzdřují sadu funkcí, která odráží, jak už podnikoví vývojáři vyvíjejí služby a rozhraní API. Moduly plug-in také pěkně hrají s injektáží závislostí. V konstruktoru modulu plug-in můžete vkládat služby, které jsou nezbytné k provedení práce modulu plug-in (např. připojení k databázi, klienti HTTP atd.). To je obtížné provést s jinými sadami SDK, které nemají moduly plug-in.

Anatomie modulu plug-in

Na vysoké úrovni je modul plug-in skupinou funkcí , které je možné zpřístupnit aplikacím a službám AI. Funkce v rámci modulů plug-in pak můžou být orchestrovány aplikací AI, aby bylo možné provádět požadavky uživatelů. V rámci sémantického jádra můžete tyto funkce vyvolat automaticky pomocí volání funkce.

Poznámka:

V jiných platformách se funkce často označují jako "nástroje" nebo "akce". V sémantickém jádru používáme termín "funkce", protože jsou obvykle definovány jako nativní funkce v základu kódu.

Pouze poskytování funkcí, ale nestačí na to, aby modul plug-in. Pokud chcete zapnout automatickou orchestraci pomocí volání funkcí, moduly plug-in také musí poskytovat podrobnosti, které sémanticky popisují, jak se chovají. Vše od vstupu, výstupu a vedlejších efektů funkce je potřeba popsat způsobem, kterému AI rozumí, jinak AI funkci nebude správně volat.

Ukázkový modul plug-in na pravé straně má například WriterPlugin funkce s sémantických popisů, které popisují, co jednotlivé funkce dělá. LLM pak může pomocí těchto popisů zvolit ty nejlepší funkce, které budou volat, aby se splnila žádost uživatele.

Na obrázku vpravo by LLM pravděpodobně volal ShortPoem funkce a StoryGen funkce, které by uspokojily uživatele, díky zadaným sémantickým popisům.

Sémantický popis v modulu plug-in WriterPlugin

Import různých typů modulů plug-in

Existují dva hlavní způsoby importu modulů plug-in do sémantického jádra: pomocí nativního kódu nebo pomocí specifikace OpenAPI. Dříve vám umožní vytvářet moduly plug-in ve stávajícím základu kódu, které můžou využívat závislosti a služby, které už máte. Ten vám umožní importovat moduly plug-in ze specifikace OpenAPI, které se dají sdílet napříč různými programovacími jazyky a platformami.

Níže uvádíme jednoduchý příklad importu a použití nativního modulu plug-in. Další informace o importu těchto různých typů modulů plug-in najdete v následujících článcích:

Tip

Při zahájení práce doporučujeme používat moduly plug-in nativního kódu. S tím, jak vaše aplikace zralá a pracujete napříč multiplatformovými týmy, můžete zvážit použití specifikací OpenAPI ke sdílení modulů plug-in napříč různými programovacími jazyky a platformami.

Různé typy funkcí modulu plug-in

V rámci modulu plug-in budete mít obvykle dva různé typy funkcí, ty, které načítají data pro načítání rozšířené generace (RAG) a ty, které automatizují úlohy. Zatímco každý typ je funkčně stejný, obvykle se používají odlišně v aplikacích, které používají sémantické jádro.

Například u funkcí načítání můžete chtít použít strategie ke zlepšení výkonu (např. ukládání do mezipaměti a použití levnějších přechodných modelů pro sumarizaci). Zatímco u funkcí automatizace úkolů budete pravděpodobně chtít implementovat procesy schvalování mezi lidmi, abyste zajistili správné dokončení úkolů.

Další informace o různýchtypech

Začínáme s moduly plug-in

Použití modulů plug-in v rámci sémantického jádra je vždy třístupňový proces:

  1. Definování modulu plug-in
  2. Přidání modulu plug-in do jádra
  3. A pak buď vyvolat funkce modulu plug-in v příkazovém řádku s voláním funkce

Níže poskytneme základní příklad použití modulu plug-in v rámci sémantického jádra. Podrobnější informace o vytváření a používání modulů plug-in najdete na výše uvedených odkazech.

1) Definování modulu plug-in

Nejjednodušší způsob, jak vytvořit modul plug-in, je definováním třídy a přidáním poznámek k jeho metodám atributu KernelFunction . Toto je sémantické jádro vědět, že jde o funkci, kterou může volat AI nebo na kterou odkazuje výzva.

Moduly plug-in můžete také importovat ze specifikace OpenAPI.

Níže vytvoříme modul plug-in, který může načíst stav světel a změnit jeho stav.

Tip

Vzhledem k tomu, že většina LLM je vytrénovaná pomocí Pythonu pro volání funkcí, doporučuje se pro názvy funkcí a názvy vlastností používat písmena hada, a to i v případě, že používáte sadu C# nebo Java SDK.

using System.ComponentModel;
using Microsoft.SemanticKernel;

public class LightsPlugin
{
   // Mock data for the lights
   private readonly List<LightModel> lights = new()
   {
      new LightModel { Id = 1, Name = "Table Lamp", IsOn = false, Brightness = 100, Hex = "FF0000" },
      new LightModel { Id = 2, Name = "Porch light", IsOn = false, Brightness = 50, Hex = "00FF00" },
      new LightModel { Id = 3, Name = "Chandelier", IsOn = true, Brightness = 75, Hex = "0000FF" }
   };

   [KernelFunction("get_lights")]
   [Description("Gets a list of lights and their current state")]
   [return: Description("An array of lights")]
   public async Task<List<LightModel>> GetLightsAsync()
   {
      return lights
   }

   [KernelFunction("get_state")]
   [Description("Gets the state of a particular light")]
   [return: Description("The state of the light")]
   public async Task<LightModel?> GetStateAsync([Description("The ID of the light")] int id)
   {
      // Get the state of the light with the specified ID
      return lights.FirstOrDefault(light => light.Id == id);
   }

   [KernelFunction("change_state")]
   [Description("Changes the state of the light")]
   [return: Description("The updated state of the light; will return null if the light does not exist")]
   public async Task<LightModel?> ChangeStateAsync(int id, LightModel LightModel)
   {
      var light = lights.FirstOrDefault(light => light.Id == id);

      if (light == null)
      {
         return null;
      }

      // Update the light with the new state
      light.IsOn = LightModel.IsOn;
      light.Brightness = LightModel.Brightness;
      light.Hex = LightModel.Hex;

      return light;
   }
}

public class LightModel
{
   [JsonPropertyName("id")]
   public int Id { get; set; }

   [JsonPropertyName("name")]
   public string Name { get; set; }

   [JsonPropertyName("is_on")]
   public bool? IsOn { get; set; }

   [JsonPropertyName("brightness")]
   public byte? Brightness { get; set; }

   [JsonPropertyName("hex")]
   public string? Hex { get; set; }
}
from typing import List, Optional, TypedDict, Annotated

class LightModel(TypedDict):
   id: int
   name: str
   is_on: Optional[bool]
   brightness: Optional[int]
   hex: Optional[str]

class LightsPlugin:
   lights: List[LightModel] = [
      {"id": 1, "name": "Table Lamp", "is_on": False, "brightness": 100, "hex": "FF0000"},
      {"id": 2, "name": "Porch light", "is_on": False, "brightness": 50, "hex": "00FF00"},
      {"id": 3, "name": "Chandelier", "is_on": True, "brightness": 75, "hex": "0000FF"},
   ]

   @kernel_function(
      name="get_lights",
      description="Gets a list of lights and their current state",
   )
   async def get_lights(self) -> Annotated[List[LightModel], "An array of lights"]:
      """Gets a list of lights and their current state."""
      return self.lights

   @kernel_function(
      name="get_state",
      description="Gets the state of a particular light",
   )
   async def get_state(
      self,
      id: Annotated[int, "The ID of the light"]
   ) -> Annotated[Optional[LightModel], "The state of the light"]:
      """Gets the state of a particular light."""
      for light in self.lights:
         if light["id"] == id:
               return light
      return None

   @kernel_function(
      name="change_state",
      description="Changes the state of the light",
   )
   async def change_state(
      self,
      id: Annotated[int, "The ID of the light"],
      new_state: LightModel
   ) -> Annotated[Optional[LightModel], "The updated state of the light; will return null if the light does not exist"]:
      """Changes the state of the light."""
      for light in self.lights:
         if light["id"] == id:
               light["is_on"] = new_state.get("is_on", light["is_on"])
               light["brightness"] = new_state.get("brightness", light["brightness"])
               light["hex"] = new_state.get("hex", light["hex"])
               return light
      return None

Všimněte si, že poskytujeme popis funkce, návratové hodnoty a parametrů. To je důležité pro AI, abyste pochopili, co funkce dělá a jak ji používat.

Tip

Nebojte se poskytnout podrobné popisy vašich funkcí, pokud má AI potíže s jejich voláním. Několik snímků příkladů, doporučení pro použití (a ne použití) funkce a pokyny k tomu, kde získat požadované parametry, můžou být užitečné.

2) Přidání modulu plug-in do jádra

Jakmile modul plug-in definujete, můžete ho přidat do jádra tak, že vytvoříte novou instanci modulu plug-in a přidáte ho do kolekce modulů plug-in jádra.

Tento příklad ukazuje nejjednodušší způsob přidání třídy jako modulu plug-in s metodou AddFromType . Další informace o dalších způsobech přidávání modulů plug-in najdete v článku o přidávání nativních modulů plug-in .

var builder = new KernelBuilder();
builder.Plugins.AddFromType<LightsPlugin>("Lights")
Kernel kernel = builder.Build();
kernel = Kernel()
kernel.add_plugin(
   LightsPlugin(),
   plugin_name="Lights",
)

3) Vyvolání funkcí modulu plug-in

Nakonec můžete mít AI vyvolat funkce modulu plug-in pomocí volání funkce. Níže je příklad, který ukazuje, jak pomocí umělé inteligence volat get_lights funkci z Lights modulu plug-in před voláním change_state funkce, která zapne světlo.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

// Create a kernel with Azure OpenAI chat completion
var builder = Kernel.CreateBuilder().AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);

// Build the kernel
Kernel kernel = builder.Build();
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

// Add a plugin (the LightsPlugin class is defined below)
kernel.Plugins.AddFromType<LightsPlugin>("Lights");

// Enable planning
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() 
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

// Create a history store the conversation
var history = new ChatHistory();
history.AddUserMessage("Please turn on the lamp");

// Get the response from the AI
var result = await chatCompletionService.GetChatMessageContentAsync(
   history,
   executionSettings: openAIPromptExecutionSettings,
   kernel: kernel);

// Print the results
Console.WriteLine("Assistant > " + result);

// Add the message from the agent to the chat history
history.AddAssistantMessage(result);
import asyncio

from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.connectors.ai.function_call_behavior import FunctionCallBehavior
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments

from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
    AzureChatPromptExecutionSettings,
)

async def main():
   # Initialize the kernel
   kernel = Kernel()

   # Add Azure OpenAI chat completion
   kernel.add_service(AzureChatCompletion(
      deployment_name="your_models_deployment_name",
      api_key="your_api_key",
      base_url="your_base_url",
   ))

   # Add a plugin (the LightsPlugin class is defined below)
   kernel.add_plugin(
      LightsPlugin(),
      plugin_name="Lights",
   )

   chat_completion : AzureChatCompletion = kernel.get_service(type=ChatCompletionClientBase)

   # Enable planning
   execution_settings = AzureChatPromptExecutionSettings(tool_choice="auto")
   execution_settings.function_call_behavior = FunctionCallBehavior.EnableFunctions(auto_invoke=True, filters={})

   # Create a history of the conversation
   history = ChatHistory()
   history.add_message("Please turn on the lamp")

   # Get the response from the AI
   result = (await chat_completion.get_chat_message_contents(
      chat_history=history,
      settings=execution_settings,
      kernel=kernel,
      arguments=KernelArguments(),
   ))[0]

   # Print the results
   print("Assistant > " + str(result))

   # Add the message from the agent to the chat history
   history.add_message(result)

# Run the main function
if __name__ == "__main__":
    asyncio.run(main())

Ve výše uvedeném kódu byste měli získat odpověď, která vypadá takto:

Role Zpráva
🔵Uživatel Zapněte lampu.
🔴Asistent (volání funkce) Lights.get_lights()
🟢Nástroj [{ "id": 1, "name": "Table Lamp", "isOn": false, "brightness": 100, "hex": "FF0000" }, { "id": 2, "name": "Porch light", "isOn": false, "brightness": 50, "hex": "00FF00" }, { "id": 3, "name": "Chandelier", "isOn": true, "brightness": 75, "hex": "0000FF" }]
🔴Asistent (volání funkce) Lights.change_state(1; { "isOn": true })
🟢Nástroj { "id": 1, "name": "Table Lamp", "isOn": true, "brightness": 100, "hex": "FF0000" }
🔴Asistent Lampa je teď zapnutá.

Tip

I když můžete vyvolat funkci modulu plug-in přímo, nedoporučuje se to, protože AI by měla být ta, která funkce se mají volat. Pokud potřebujete explicitní kontrolu nad tím, které funkce se volají, zvažte použití standardních metod v základu kódu místo modulů plug-in.