Megosztás a következőn keresztül:


Natív kód hozzáadása beépülő modulként

Az AI-ügynökök natívan nem támogatott képességeinek biztosításának legegyszerűbb módja a natív kód beépülő modulba való burkolása. Ez lehetővé teszi, hogy alkalmazásfejlesztőként meglévő készségeit kihasználva bővítse az AI-ügynökök képességeit.

A színfalak mögött a Szemantic Kernel ezután az Ön által megadott leírásokat használja a tükröződés mellett az AI-ügynök beépülő moduljának szemantikai leírására. Ez lehetővé teszi, hogy az AI-ügynök megértse a beépülő modul képességeit és a vele való interakciót.

Az LLM megfelelő információval való ellátása

Beépülő modul létrehozásakor meg kell adnia az AI-ügynöknek a megfelelő információkat a beépülő modul képességeinek és funkcióinak megértéséhez. Ide tartoznak az alábbiak:

  • A beépülő modul neve
  • A függvények nevei
  • A függvények leírása
  • A függvények paraméterei
  • A paraméterek sémája

A Szemantic Kernel értéke, hogy automatikusan generálja a legtöbb információt magából a kódból. Fejlesztőként ez csak azt jelenti, hogy meg kell adnia a függvények és paraméterek szemantikai leírását, hogy az AI-ügynök megértse őket. Ha azonban megfelelően megjegyzést fűz a kódhoz, és széljegyzetet fűz hozzá, valószínűleg már rendelkezik ezekkel az információkkal.

Az alábbiakban bemutatjuk az AI-ügynök natív kóddal való biztosításának két különböző módját, valamint azt, hogy hogyan adhatjuk meg ezeket a szemantikai információkat.

Beépülő modul definiálása osztály használatával

Natív beépülő modul létrehozásának legegyszerűbb módja az, ha egy osztályt használ, majd hozzáadja az KernelFunction attribútummal kiegészített metódusokat. Azt is javasoljuk, hogy a Description széljegyzetet használva biztosítsa az AI-ügynök számára a függvény megértéséhez szükséges információkat.

public class LightsPlugin
{
   private readonly List<LightModel> _lights;

   public LightsPlugin(LoggerFactory loggerFactory, List<LightModel> lights)
   {
      _lights = lights;
   }

   [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("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(LightModel changeState)
   {
      // Find the light to change
      var light = _lights.FirstOrDefault(l => l.Id == changeState.Id);

      // If the light does not exist, return null
      if (light == null)
      {
         return null;
      }

      // Update the light state
      light.IsOn = changeState.IsOn;
      light.Brightness = changeState.Brightness;
      light.Color = changeState.Color;

      return light;
   }
}
from typing import List, Optional, Annotated

class LightsPlugin:
    def __init__(self, lights: List[LightModel]):
        self._lights = lights

    @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="change_state",
        description="Changes the state of the light",
    )
    async def change_state(
        self,
        change_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"] == change_state["id"]:
                light["is_on"] = change_state.get("is_on", light["is_on"])
                light["brightness"] = change_state.get("brightness", light["brightness"])
                light["hex"] = change_state.get("hex", light["hex"])
                return light
        return None

Tipp.

Mivel az LLM-ek túlnyomórészt Python-kódon vannak betanítve, a függvénynevekhez és paraméterekhez ajánlott snake_case használni (még akkor is, ha C#-ot vagy Java-t használ). Ez segít az AI-ügynöknek jobban megérteni a függvényt és annak paramétereit.

Ha a függvény bemeneti változóként összetett objektummal rendelkezik, a Szemantikus Kernel is létrehoz egy sémát az objektumhoz, és átadja az AI-ügynöknek. A függvényekhez hasonlóan széljegyzeteket kell megadnia Description az AI számára nem nyilvánvaló tulajdonságokhoz. Az alábbiakban az osztály és az LightState enumerálás definíciója Brightness látható.

using System.Text.Json.Serialization;

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 enum? Brightness { get; set; }

   [JsonPropertyName("color")]
   [Description("The color of the light with a hex code (ensure you include the # symbol)")]
   public string? Color { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Brightness
{
   Low,
   Medium,
   High
}
from typing import TypedDict, Optional

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

Feljegyzés

Bár ez egy "szórakoztató" példa, jó munkát végez, amely megmutatja, mennyire összetett egy beépülő modul paraméterei. Ebben az esetben egy összetett objektumunk négy különböző tulajdonsággal rendelkezik: egész szám, sztring, logikai és enumerálás. A szemantikus kernel értéke az, hogy automatikusan létrehozhatja az objektum sémáját, és átadhatja az AI-ügynöknek, és a megfelelő objektumba továbbítja az AI-ügynök által létrehozott paramétereket.

Miután végzett a beépülő modulosztály szerkesztésével, hozzáadhatja a kernelhez a metódusok vagy AddFromObject metódusok AddFromType<> használatával.

Tipp.

Függvény létrehozásakor mindig tegye fel magának a kérdést: "Hogyan adhatok további segítséget az AI-nek a függvény használatához?" Ez magában foglalhatja adott bemeneti típusok használatát (lehetőség szerint sztringek elkerülését), leírások és példák megadását.

Beépülő modul hozzáadása a AddFromObject módszerrel

A AddFromObject módszer lehetővé teszi, hogy a beépülő modulosztály egy példányát közvetlenül a beépülő modul gyűjteményéhez adja hozzá, ha közvetlenül szabályozni szeretné a beépülő modul létrehozásának módját.

Az osztály konstruktorának LightsPlugin például szüksége van a fények listájára. Ebben az esetben létrehozhatja a beépülő modulosztály egy példányát, és hozzáadhatja a beépülő modulgyűjteményhez.

List<LightModel> lights = new()
   {
      new LightModel { Id = 1, Name = "Table Lamp", IsOn = false, Brightness = Brightness.Medium, Color = "#FFFFFF" },
      new LightModel { Id = 2, Name = "Porch light", IsOn = false, Brightness = Brightness.High, Color = "#FF0000" },
      new LightModel { Id = 3, Name = "Chandelier", IsOn = true, Brightness = Brightness.Low, Color = "#FFFF00" }
   };

kernel.Plugins.AddFromObject(new LightsPlugin(lights));

Beépülő modul hozzáadása a AddFromType<> módszerrel

A metódus használatakor a AddFromType<> kernel automatikusan függőséginjektálással hozza létre a beépülő modulosztály egy példányát, és hozzáadja a beépülő modulgyűjteményhez.

Ez akkor hasznos, ha a konstruktorhoz szolgáltatások vagy egyéb függőségek injektálása szükséges a beépülő modulba. Előfordulhat például, hogy az osztályban LightsPlugin a fények listája helyett egy naplózót és egy fényszolgáltatást kell beszúrni.

public class LightsPlugin
{
   private readonly Logger _logger;
   private readonly LightService _lightService;

   public LightsPlugin(LoggerFactory loggerFactory, LightService lightService)
   {
      _logger = loggerFactory.CreateLogger<LightsPlugin>();
      _lightService = lightService;
   }

   [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()
   {
      _logger.LogInformation("Getting lights");
      return lightService.GetLights();
   }

   [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(LightModel changeState)
   {
      _logger.LogInformation("Changing light state");
      return lightService.ChangeState(changeState);
   }
}

A Függőséginjektálással a kernel létrehozása előtt hozzáadhatja a szükséges szolgáltatásokat és beépülő modulokat a kernelszerkesztőhöz.

var builder = Kernel.CreateBuilder();

// Add dependencies for the plugin
builder.Services.AddLogging(loggingBuilder => loggingBuilder.AddConsole().SetMinimumLevel(LogLevel.Trace));
builder.Services.AddSingleton<LightService>();

// Add the plugin to the kernel
builder.Plugins.AddFromType<LightsPlugin>("Lights");

// Build the kernel
Kernel kernel = builder.Build();

Beépülő modul definiálása függvénygyűjtemény használatával

Kevésbé gyakori, de még mindig hasznos, ha függvények gyűjteményével definiál egy beépülő modult. Ez különösen akkor hasznos, ha futásidőben dinamikusan kell létrehoznia egy beépülő modult egy függvénykészletből.

A folyamat használatához a függvény-előállítót kell használnia az egyes függvények létrehozásához, mielőtt hozzáadja őket a beépülő modulhoz.

kernel.Plugins.AddFromFunctions("time_plugin",
[
    KernelFunctionFactory.CreateFromMethod(
        method: () => DateTime.Now,
        functionName: "get_time",
        description: "Get the current time"
    ),
    KernelFunctionFactory.CreateFromMethod(
        method: (DateTime start, DateTime end) => (end - start).TotalSeconds,
        functionName: "diff_time",
        description: "Get the difference between two times in seconds"
    )
]);

További stratégiák natív kód hozzáadásához függőséginjektálással

Ha függőséginjektálással dolgozik, további stratégiákkal hozhat létre és adhat hozzá beépülő modulokat a kernelhez. Az alábbiakban néhány példát mutatunk be arra, hogyan adhat hozzá beépülő modult a Függőséginjektálás használatával.

Beépülő modulgyűjtemény injektálása

Tipp.

Javasoljuk, hogy a beépülő modulgyűjteményt átmeneti szolgáltatássá tegye, hogy minden használat után megsemmisítse, mivel a beépülő modulgyűjtemény nem használható. Létrehozása egy új beépülő modul gyűjtemény minden használatra olcsó, így nem lehet a teljesítmény aggodalomra ad okot.

var builder = Host.CreateApplicationBuilder(args);

// Create native plugin collection
builder.Services.AddTransient((serviceProvider)=>{
   KernelPluginCollection pluginCollection = [];
   pluginCollection.AddFromType<LightsPlugin>("Lights");

   return pluginCollection;
});

// Create the kernel service
builder.Services.AddTransient<Kernel>((serviceProvider)=> {
   KernelPluginCollection pluginCollection = serviceProvider.GetRequiredService<KernelPluginCollection>();

   return new Kernel(serviceProvider, pluginCollection);
});

Tipp.

Ahogy a kernelről szóló cikkben említettük, a kernel rendkívül egyszerű, ezért nem jelent teljesítményproblémát, ha minden egyes használatra létrehozunk egy új kernelt átmenetiként.

Beépülő modulok létrehozása önállóan

A beépülő modulok nem módosíthatók, ezért általában biztonságosan hozhatók létre önállóként. Ezt úgy teheti meg, hogy a beépülő modul-előállítót használja, és hozzáadja az eredményként kapott beépülő modult a szolgáltatásgyűjteményhez.

var builder = Host.CreateApplicationBuilder(args);

// Create singletons of your plugin
builder.Services.AddKeyedSingleton("LightPlugin", (serviceProvider, key) => {
    return KernelPluginFactory.CreateFromType<LightsPlugin>();
});

// Create a kernel service with singleton plugin
builder.Services.AddTransient((serviceProvider)=> {
    KernelPluginCollection pluginCollection = [
      serviceProvider.GetRequiredKeyedService<KernelPlugin>("LightPlugin")
    ];

    return new Kernel(serviceProvider, pluginCollection);
});

Beépülő modul hozzáadása a add_plugin módszerrel

A add_plugin metódus lehetővé teszi egy beépülő modulpéldány hozzáadását a kernelhez. Az alábbi példa bemutatja, hogyan hozhatja létre és adhatja hozzá az LightsPlugin osztályt a kernelhez.

# Create the kernel
kernel = Kernel()

# Create dependencies for the plugin
lights = [
    {"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"},
]

# Create the plugin
lights_plugin = LightsPlugin(lights)

# Add the plugin to the kernel
kernel.add_plugin(lights_plugin)

Következő lépések

Most, hogy már tudja, hogyan hozhat létre beépülő modult, megtanulhatja, hogyan használhatja őket az AI-ügynökével. A beépülő modulokhoz hozzáadott függvények típusától függően különböző mintákat kell követnie. A lekérési függvényekkel kapcsolatban tekintse meg a lekérési függvények használatát ismertető cikket. Feladatautomatizálási függvények esetén tekintse meg a feladatautomatizálási függvények használatát ismertető cikket.