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
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 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
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, LightModel.Brightness.MEDIUM, "#FFFFFF"));
lights.put(2, new LightModel(2, "Porch light", false, LightModel.Brightness.HIGH, "#FF0000"));
lights.put(3, new LightModel(3, "Chandelier", true, LightModel.Brightness.LOW, "#FFFF00"));
}
@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 = "model",
description = "The new state of the model to set. Example model: " +
"{\"id\":99,\"name\":\"Head Lamp\",\"isOn\":false,\"brightness\":\"MEDIUM\",\"color\":\"#FFFFFF\"}",
type = LightModel.class) LightModel model
) {
System.out.println("Changing light " + model.getId() + " " + model.getIsOn());
if (!lights.containsKey(model.getId())) {
throw new IllegalArgumentException("Light not found");
}
lights.put(model.getId(), model);
return lights.get(model.getId());
}
}
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
class LightModel(TypedDict):
id: int
name: str
is_on: bool | None
brightness: int | None
hex: str | None
public class LightModel {
private int id;
private String name;
private Boolean isOn;
private Brightness brightness;
private String color;
public enum Brightness {
LOW,
MEDIUM,
HIGH
}
public LightModel(int id, String name, Boolean isOn, Brightness brightness, String color) {
this.id = id;
this.name = name;
this.isOn = isOn;
this.brightness = brightness;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getIsOn() {
return isOn;
}
public void setIsOn(Boolean isOn) {
this.isOn = isOn;
}
public Brightness getBrightness() {
return brightness;
}
public void setBrightness(Brightness brightness) {
this.brightness = brightness;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
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)
Přidání modulu plug-in pomocí createFromObject
metody
Tato createFromObject
metoda umožňuje vytvořit modul plug-in jádra z objektu pomocí anotovaných metod.
// Import the LightsPlugin
KernelPlugin lightPlugin = KernelPluginFactory.createFromObject(new LightsPlugin(),
"LightsPlugin");
Tento modul plug-in je pak možné přidat do jádra.
// Create a kernel with Azure OpenAI chat completion and plugin
Kernel kernel = Kernel.builder()
.withAIService(ChatCompletionService.class, chatCompletionService)
.withPlugin(lightPlugin)
.build();
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.