Eklenti nedir?

Eklentiler Semantik Çekirdeğin önemli bir bileşenidir. Microsoft 365'te ChatGPT veya Copilot uzantılarından gelen eklentileri zaten kullandıysanız, bunları zaten biliyorsunuz demektir. Eklentilerle mevcut API'lerinizi yapay zeka tarafından kullanılabilecek bir koleksiyonda kapsülleyebilirsiniz. Bu, yapay zekanıza aksi takdirde yapamayacağı eylemleri gerçekleştirme olanağı sunmanızı sağlar.

Semantik Çekirdek arka planda, LLM'lere izin vermek, planlama yapmak ve API'lerinizi çağırmak için en son LLM'lerin çoğuna ait yerel bir özellik olan işlev çağrısını kullanır. İşlev çağrısıyla, LLM'ler belirli bir işlevi isteyebilir (örneğin, çağırabilir). Semantik Çekirdek daha sonra isteği kod tabanınızdaki uygun işleve sıralar ve LLM'nin son yanıtı oluşturabilmesi için sonuçları LLM'ye geri döndürür.

Semantik Çekirdek Eklentisi

Tüm yapay zeka SDK'larının eklentilere benzer bir kavramı yoktur (çoğu yalnızca işlevlere veya araçlara sahiptir). Ancak kurumsal senaryolarda eklentiler, kurumsal geliştiricilerin zaten hizmet ve API'leri nasıl geliştirdiğini yansıtan bir işlev kümesini kapsüllediğinden değerlidir. Eklentiler de bağımlılık ekleme ile düzgün bir şekilde yürütülmektedir. Eklentinin oluşturucusunun içinde, eklentinin çalışmasını gerçekleştirmek için gereken hizmetleri (örneğin, veritabanı bağlantıları, HTTP istemcileri vb.) ekleyebilirsiniz. Eklenti olmayan diğer SDK'larla bunu başarmak zordur.

Eklentinin anatomisi

Üst düzey bir eklenti, yapay zeka uygulamalarına ve hizmetlerine açık olabilecek bir işlev grubudur. Eklentilerdeki işlevler daha sonra kullanıcı isteklerini gerçekleştirmek için bir yapay zeka uygulaması tarafından düzenlenebilir. Anlam Çekirdeği'nin içinde, işlev çağrısı ile bu işlevleri otomatik olarak çağırabilirsiniz.

Not

Diğer platformlarda işlevler genellikle "araçlar" veya "eylemler" olarak adlandırılır. Anlam Çekirdeği'nde genellikle kod tabanınızda yerel işlevler olarak tanımlandığından "işlevler" terimini kullanırız.

Ancak yalnızca işlevleri sağlamak bir eklenti yapmak için yeterli değildir. İşlev çağrısıyla otomatik düzenlemeyi desteklemek için eklentilerin nasıl davrandıklarını gösteren ayrıntıları da sağlaması gerekir. İşlevin girişinden, çıkışından ve yan etkilerinden gelen her şeyin yapay zekanın anlayabileceği şekilde açıklanması gerekir, aksi takdirde yapay zeka işlevi doğru şekilde çağırmaz.

Örneğin, sağdaki örnek WriterPlugin eklenti, her işlevin ne yaptığını açıklayan anlamsal açıklamalara sahip işlevlere sahiptir. LlM daha sonra bu açıklamaları kullanarak kullanıcının isteklerini yerine getirmek için çağrılabilecek en iyi işlevleri seçebilir.

Sağdaki resimde LLM, sağlanan anlamsal açıklamalar sayesinde kullanıcıların sorularını karşılamak için ve StoryGen işlevlerini çağıracakShortPoem.

WriterPlugin eklentisi içinde anlamsal açıklama

Farklı eklenti türlerini içeri aktarma

Eklentileri Anlam Çekirdeği'ne aktarmanın iki birincil yolu vardır: yerel kod veya OpenAPI belirtimi kullanma. Önceki, mevcut kod tabanınızda zaten sahip olduğunuz bağımlılıklardan ve hizmetlerden yararlanabilen eklentiler yazmanıza olanak tanır. İkincisi, eklentileri farklı programlama dillerinde ve platformlarda paylaşılabilen bir OpenAPI belirtiminden içeri aktarmanıza olanak tanır.

Aşağıda yerel eklentiyi içeri aktarma ve kullanma ile ilgili basit bir örnek sağlıyoruz. Bu farklı eklenti türlerini içeri aktarma hakkında daha fazla bilgi edinmek için aşağıdaki makalelere bakın:

İpucu

Başlarken yerel kod eklentileri kullanmanızı öneririz. Uygulamanız büyüdükçe ve platformlar arası ekiplerde çalışırken eklentileri farklı programlama dillerinde ve platformlarda paylaşmak için OpenAPI belirtimlerini kullanmayı düşünebilirsiniz.

Farklı eklenti işlevleri türleri

Bir eklentide genellikle iki farklı işlev türüne sahip olursunuz: bunlar artırılmış nesil (RAG) için veri alan ve görevleri otomatik hale getiren işlevlerdir. Her tür işlevsel olarak aynı olsa da, bunlar genellikle Anlam Çekirdeği kullanan uygulamalarda farklı şekilde kullanılır.

Örneğin, alma işlevleriyle performansı geliştirmek için stratejiler kullanmak isteyebilirsiniz (örneğin, özetleme için önbelleğe alma ve daha ucuz ara modelleri kullanma). Görev otomasyonu işlevlerinde görevlerin doğru şekilde tamamlandığından emin olmak için döngüdeki insan onay işlemlerini uygulamak isteyebilirsiniz.

Farklı eklenti işlevleri türleri hakkında daha fazla bilgi edinmek için aşağıdaki makalelere bakın:

Eklentileri kullanmaya başlama

Anlam Çekirdeği içinde eklentileri kullanmak her zaman üç adımlı bir işlemdir:

  1. Eklentinizi tanımlama
  2. Eklentiyi çekirdeğinize ekleme
  3. Ardından, işlev çağrısı ile bir istemde eklentinin işlevlerini çağırın

Aşağıda Anlam Çekirdeği içinde eklentinin nasıl kullanılacağına yönelik üst düzey bir örnek sağlayacağız. Eklentileri oluşturma ve kullanma hakkında daha ayrıntılı bilgi için yukarıdaki bağlantılara bakın.

1) Eklentinizi tanımlayın

Eklenti oluşturmanın en kolay yolu bir sınıf tanımlamak ve öznitelik ile yöntemlerine KernelFunction açıklama eklemektir. Bu, Semantic Kernel'in bunun bir yapay zeka tarafından çağrılabilen veya istemde başvurulabilen bir işlev olduğunu bilmesini sağlayın.

Eklentileri openAPI belirtiminden de içeri aktarabilirsiniz.

Aşağıda, ışıkların durumunu alabilen ve durumunu değiştirebilen bir eklenti oluşturacağız.

İpucu

LLM'lerin çoğu işlev çağrısı için Python ile eğitildiğinden, C# veya Java SDK'sını kullanıyor olsanız bile işlev adları ve özellik adları için yılan büyük/küçük harf kullanılması önerilir.

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 TypedDict, Annotated

class LightModel(TypedDict):
   id: int
   name: str
   is_on: bool | None
   brightness: int | None
   hex: str | None

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
   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
   async def get_state(
      self,
      id: Annotated[int, "The ID of the light"]
   ) -> Annotated[LightModel | None], "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
   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
public class LightsPlugin {

  // Mock data for the lights
  private final Map<Integer, LightModel> lights = new HashMap<>();

  public LightsPlugin() {
    lights.put(1, new LightModel(1, "Table Lamp", false));
    lights.put(2, new LightModel(2, "Porch light", false));
    lights.put(3, new LightModel(3, "Chandelier", true));
  }

  @DefineKernelFunction(name = "get_lights", description = "Gets a list of lights and their current state")
  public List<LightModel> getLights() {
    System.out.println("Getting lights");
    return new ArrayList<>(lights.values());
  }

  @DefineKernelFunction(name = "change_state", description = "Changes the state of the light")
  public LightModel changeState(
      @KernelFunctionParameter(name = "id", description = "The ID of the light to change") int id,
      @KernelFunctionParameter(name = "isOn", description = "The new state of the light") boolean isOn) {
    System.out.println("Changing light " + id + " " + isOn);
    if (!lights.containsKey(id)) {
      throw new IllegalArgumentException("Light not found");
    }

    lights.get(id).setIsOn(isOn);

    return lights.get(id);
  }
}

İşlev, dönüş değeri ve parametreler için açıklamalar sağladığımıza dikkat edin. Bu, yapay zekanın işlevin ne yaptığını ve nasıl kullanılacağını anlaması açısından önemlidir.

İpucu

Yapay zeka onları çağırmada sorun yaşıyorsa işlevleriniz için ayrıntılı açıklamalar sağlamaktan çekinmeyin. Birkaç örnek, işlevin ne zaman kullanılacağına (ve kullanılmayacağına) ilişkin öneriler ve gerekli parametrelerin nereden alınacağı konusunda rehberlik yararlı olabilir.

2) Eklentiyi çekirdeğinize ekleyin

Eklentinizi tanımladıktan sonra eklentinin yeni bir örneğini oluşturup çekirdeğin eklenti koleksiyonuna ekleyerek eklentiyi çekirdeğinize ekleyebilirsiniz.

Bu örnek, yöntemiyle AddFromType bir sınıfı eklenti olarak eklemenin en kolay yolunu gösterir. Eklenti eklemenin diğer yolları hakkında bilgi edinmek için yerel eklenti ekleme makalesine bakın.

var builder = new KernelBuilder();
builder.Plugins.AddFromType<LightsPlugin>("Lights")
Kernel kernel = builder.Build();
kernel = Kernel()
kernel.add_plugin(
   LightsPlugin(),
   plugin_name="Lights",
)
// Import the LightsPlugin
KernelPlugin lightPlugin = KernelPluginFactory.createFromObject(new LightsPlugin(),
    "LightsPlugin");
// Create a kernel with Azure OpenAI chat completion and plugin
Kernel kernel = Kernel.builder()
    .withAIService(ChatCompletionService.class, chatCompletionService)
    .withPlugin(lightPlugin)
    .build();

3) Eklentinin işlevlerini çağırma

Son olarak, yapay zekanın işlev çağrısını kullanarak eklentinizin işlevlerini çağırmasını sağlayabilirsiniz. Aşağıda, bir ışığı açmak için işlevi çağırmadan önce eklentiden Lights işlevi çağırmak get_lights için yapay zekanın nasıl eş eksene change_state alındığını gösteren bir örnek verilmiştir.

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() 
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

// 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_choice_behavior import FunctionChoiceBehavior
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
   chat_completion = AzureChatCompletion(
      deployment_name="your_models_deployment_name",
      api_key="your_api_key",
      base_url="your_base_url",
   )
   kernel.add_service(chat_completion)

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

   # Enable planning
   execution_settings = AzureChatPromptExecutionSettings()
   execution_settings.function_call_behavior = FunctionChoiceBehavior.Auto()

   # 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_content(
      chat_history=history,
      settings=execution_settings,
      kernel=kernel,
   )

   # 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())
// Enable planning
InvocationContext invocationContext = new InvocationContext.Builder()
    .withReturnMode(InvocationReturnMode.LAST_MESSAGE_ONLY)
    .withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true))
    .build();

// Create a history to store the conversation
ChatHistory history = new ChatHistory();
history.addUserMessage("Turn on light 2");

List<ChatMessageContent<?>> results = chatCompletionService
    .getChatMessageContentsAsync(history, kernel, invocationContext)
    .block();

System.out.println("Assistant > " + results.get(0));

Yukarıdaki kodla aşağıdakine benzer bir yanıt almanız gerekir:

Role İleti
🔵Kullanıcı Lütfen lambayı açın
🔴Yardımcı (işlev çağrısı) Lights.get_lights()
🟢Araç [{ "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" }]
🔴Yardımcı (işlev çağrısı) Lights.change_state(1, { "isOn": true })
🟢Araç { "id": 1, "name": "Table Lamp", "isOn": true, "brightness": 100, "hex": "FF0000" }
🔴Asistan Lamba artık açık

İpucu

Bir eklenti işlevini doğrudan çağırabilirsiniz ancak bu önerilmez çünkü hangi işlevlerin çağrılacağı yapay zekanın karar vermesi gerekir. Hangi işlevlerin çağrıldığı üzerinde açık denetime ihtiyacınız varsa, eklentiler yerine kod tabanınızda standart yöntemleri kullanmayı göz önünde bulundurun.