Sdílet prostřednictvím


Přidání nativního kódu jako modulu plug-in

Nejjednodušší způsob, jak poskytnout agenta AI s funkcemi, které nejsou nativně podporovány, je zabalit nativní kód do modulu plug-in. Díky tomu můžete využít své stávající dovednosti jako vývojář aplikací k rozšíření možností agentů AI.

Na pozadí pak sémantické jádro použije popisy, které poskytnete spolu s reflexí, k sémantickému popisu modulu plug-in agentovi AI. To umožňuje agentu umělé inteligence porozumět možnostem modulu plug-in a jak s ním pracovat.

Poskytnutí správných informací pro LLM

Při vytváření modulu plug-in musíte agentovi AI poskytnout správné informace, abyste porozuměli možnostem modulu plug-in a jeho funkcím. Sem patří:

  • Název modulu plug-in
  • Názvy funkcí
  • Popisy funkcí
  • Parametry funkcí
  • Schéma parametrů

Hodnota sémantického jádra spočívá v tom, že může automaticky generovat většinu těchto informací ze samotného kódu. Jako vývojář to znamená, že musíte zadat sémantické popisy funkcí a parametrů, aby je agent umělé inteligence mohl pochopit. Pokud kód správně okomentujete a přidáte k němu poznámky, pravděpodobně už tyto informace máte po ruce.

Níže si projdeme dva různé způsoby poskytování nativního kódu agenta AI a způsobu poskytování těchto sémantických informací.

Definování modulu plug-in pomocí třídy

Nejjednodušší způsob, jak vytvořit nativní modul plug-in, je začít s třídou a pak přidat metody anotované atributem KernelFunction . Doporučuje se také použít poznámku Description k tomu, aby agent umělé inteligence poskytl potřebné informace k pochopení funkce.

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

Tip

Vzhledem k tomu, že LLM jsou převážně vytrénované v kódu Pythonu, doporučujeme použít snake_case pro názvy a parametry funkcí (i když používáte C# nebo Javu). To pomůže agentu AI lépe porozumět funkci a jeho parametrům.

Pokud má vaše funkce komplexní objekt jako vstupní proměnnou, sémantické jádro také vygeneruje schéma pro tento objekt a předá ho agentu AI. Podobně jako u funkcí byste měli zadat Description poznámky pro vlastnosti, které nejsou pro AI zřejmé. Níže je definice třídy LightState a výčtu Brightness .

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]

Poznámka:

I když se jedná o "zábavný" příklad, dělá to dobrou úlohu, která ukazuje, jak složité parametry modulu plug-in může být. V tomto jediném případě máme komplexní objekt se čtyřmi různými typy vlastností: celé číslo, řetězec, logická hodnota a výčt. Hodnota sémantického jádra je, že může automaticky generovat schéma pro tento objekt a předat ho agentovi AI a zařadí parametry vygenerované agentem AI do správného objektu.

Jakmile dokončíte vytváření předmětu modulu plug-in, můžete ho přidat do jádra pomocí AddFromType<> metod nebo AddFromObject metod.

Tip

Při vytváření funkce se vždy zeptejte, jak můžu umělé inteligenci poskytnout další pomoc s používáním této funkce? To může zahrnovat použití konkrétních typů zadávání (vyhýbejte se řetězcům, pokud je to možné), zadání popisů a příkladů.

Přidání modulu plug-in pomocí AddFromObject metody

Tato AddFromObject metoda umožňuje přidat instanci třídy plug-in přímo do kolekce modulů plug-in v případě, že chcete přímo řídit, jak je modul plug-in vytvořen.

Například konstruktor LightsPlugin třídy vyžaduje seznam světel. V tomto případě můžete vytvořit instanci třídy modulu plug-in a přidat ji do kolekce modulů plug-in.

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

Přidání modulu plug-in pomocí AddFromType<> metody

Při použití AddFromType<> metody jádro automaticky použije injektáž závislostí k vytvoření instance třídy modulu plug-in a jeho přidání do kolekce modulů plug-in.

To je užitečné, pokud konstruktor vyžaduje, aby se do modulu plug-in vložily služby nebo jiné závislosti. Například naše LightsPlugin třída může vyžadovat protokolovač a světlou službu, aby se do ní vložily místo seznamu světel.

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

Pomocí injektáže závislostí můžete před sestavením jádra přidat požadované služby a moduly plug-in do tvůrce jádra.

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

Definování modulu plug-in pomocí kolekce funkcí

Méně časté, ale stále užitečné je definování modulu plug-in pomocí kolekce funkcí. To je zvlášť užitečné, pokud potřebujete dynamicky vytvořit modul plug-in ze sady funkcí za běhu.

Použití tohoto procesu vyžaduje, abyste před přidáním do modulu plug-in pomocí objektu pro vytváření funkcí vytvořili jednotlivé funkce.

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

Další strategie pro přidání nativního kódu pomocí injektáže závislostí

Pokud pracujete s injektáží závislostí, můžete do jádra vytvořit a přidat moduly plug-in. Níže je několik příkladů, jak přidat modul plug-in pomocí injektáže závislostí.

Vložení kolekce modulů plug-in

Tip

Doporučujeme, aby kolekce modulů plug-in byla přechodná služba, aby se po každém použití odstranila, protože kolekce modulů plug-in je proměnlivá. Vytvoření nové kolekce modulů plug-in pro každé použití je levné, takže by nemělo být problém s výkonem.

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

Tip

Jak je uvedeno v článku o jádru, jádro je extrémně jednoduché, takže vytvoření nového jádra pro každé použití jako přechodné není problém s výkonem.

Generování modulů plug-in jako singletonů

Moduly plug-in nejsou proměnlivé, takže jejich obvykle bezpečné je vytvářet jako singletony. Můžete to provést pomocí továrny modulu plug-in a přidáním výsledného modulu plug-in do kolekce služeb.

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

Přidání modulu plug-in pomocí add_plugin metody

Metoda add_plugin umožňuje přidat instanci modulu plug-in do jádra. Níže je příklad, jak můžete sestavit LightsPlugin třídu a přidat ji do jádra.

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

Další kroky

Teď, když víte, jak vytvořit modul plug-in, se teď můžete naučit, jak je používat s vaším agentem AI. V závislosti na typu funkcí, které jste přidali do modulů plug-in, byste měli postupovat podle různých vzorů. Informace o funkcích načítání najdete v článku o funkcích načítání . Informace o funkcích automatizace úloh najdete v článku o funkcích automatizace úloh.