Wat zijn filters?

Filters verbeteren de beveiliging door controle en zichtbaarheid te bieden over hoe en wanneer functies worden uitgevoerd. Dit is nodig om verantwoorde AI-principes in te voeren in uw werk, zodat u zich ervan overtuigd voelt dat uw oplossing gereed is voor ondernemingen.

Filters worden bijvoorbeeld gebruikt om machtigingen te valideren voordat een goedkeuringsstroom begint. Het filter wordt uitgevoerd om de machtigingen te controleren van de persoon die een goedkeuring wil indienen. Dit betekent dat alleen een selecte groep personen het proces kan starten.

Hier vindt u een goed voorbeeld van filters in onze gedetailleerde Semantische Kernel-blogpost over Filters.   Semantische Kernel-filters

Er zijn drie typen filters:

  • Functie-aanroepfilter - dit filter wordt telkens uitgevoerd wanneer een functie KernelFunction aanroept. Het maakt het mogelijk om:

    • Toegang tot informatie over de functie die wordt uitgevoerd en de bijbehorende argumenten
    • Afhandeling van uitzonderingen tijdens het uitvoeren van de functie
    • Overschrijven van het functieresultaat, vóór (bijvoorbeeld voor cachingscenario's) of na uitvoering (bijvoorbeeld voor verantwoorde AI-scenario's)
    • Opnieuw proberen van de functie in geval van storing (bijvoorbeeld overschakelen naar een alternatief AI-model)
  • Prompt Render-filter : dit filter wordt geactiveerd voordat de promptweergavebewerking wordt geactiveerd, waardoor:

    • De prompt weergeven en wijzigen die naar de AI wordt verzonden (bijvoorbeeld voor RAG- of PII-redaction)
    • Voorkomen dat prompts naar de AI worden verzonden door het functieresultaat te overschrijven (bijvoorbeeld voor Semantische caching)
  • Functie-aanroepfilter - dit filter wordt telkens uitgevoerd wanneer een functie KernelFunction wordt aangeroepen. Het maakt het mogelijk om:

    • Toegang tot informatie over de functie die wordt uitgevoerd en de bijbehorende argumenten
    • Afhandeling van uitzonderingen tijdens het uitvoeren van de functie
    • Overschrijven van het functieresultaat, vóór (bijvoorbeeld voor cachingscenario's) of na uitvoering (bijvoorbeeld voor verantwoorde AI-scenario's)
    • Opnieuw proberen van de functie in geval van storing (bijvoorbeeld overschakelen naar een alternatief AI-model)
  • Prompt Render-filter : dit filter wordt geactiveerd voordat de promptweergavebewerking wordt geactiveerd, waardoor:

    • De prompt weergeven en wijzigen die naar de AI wordt verzonden
    • Voorkomen dat prompts naar de AI worden verzonden door het functieresultaat te overschrijven (bijvoorbeeld voor Semantische caching)
  • Functie-aanroepfilter: dit filter wordt telkens uitgevoerd wanneer een functie KernelFunction aanroept. Het maakt het mogelijk om:

    • Toegang tot informatie over de functie die wordt uitgevoerd en de bijbehorende argumenten
    • Afhandeling van uitzonderingen tijdens het uitvoeren van de functie
    • Overschrijven van het functieresultaat, vóór (bijvoorbeeld voor cachingscenario's) of na uitvoering (bijvoorbeeld voor verantwoorde AI-scenario's)
    • Opnieuw proberen van de functie in geval van een fout
  • Prompt Render-filter : dit filter wordt geactiveerd voordat de promptweergavebewerking wordt geactiveerd, waardoor:

    • De prompt weergeven en wijzigen die naar de AI wordt verzonden
    • Voorkomen dat prompts naar de AI worden verzonden door het functieresultaat te overschrijven
  • Automatische functieaanroepfilter - vergelijkbaar met het functieaanroepfilter, dit filter werkt binnen het bereik van automatic function calling, biedt aanvullende context, inclusief chatgeschiedenis, een lijst met alle functies die moeten worden uitgevoerd en iteratiemeteritems. Ook kan het proces voor automatisch aanroepen van functies worden beëindigd (bijvoorbeeld als een gewenst resultaat wordt verkregen van de tweede van drie geplande functies).

Elk filter bevat een context object met alle relevante informatie over de uitvoering van de functie of het weergeven van prompts. Daarnaast heeft elk filter een next gedelegeerde/callback om het volgende filter in de pijplijn uit te voeren of de functie zelf, waarbij controle is over de uitvoering van de functie (bijvoorbeeld in gevallen van schadelijke prompts of argumenten). Meerdere filters van hetzelfde type kunnen worden geregistreerd, elk met een eigen verantwoordelijkheid.

In een filter is het essentieel om de next delegate aan te roepen om door te gaan naar het volgende geregistreerde filter of de oorspronkelijke bewerking (of het uitvoeren van de functie of het weergeven van de prompt). Zonder aanroepen nextwordt de bewerking niet uitgevoerd.

Als u een filter wilt gebruiken, definieert u het eerst en voegt u het vervolgens toe aan het Kernel object via afhankelijkheidsinjectie of de juiste Kernel eigenschap. Wanneer u afhankelijkheidsinjectie gebruikt, wordt de volgorde van filters niet gegarandeerd, dus met meerdere filters kan de uitvoeringsvolgorde onvoorspelbaar zijn.

Als u een filter wilt gebruiken, kunt u een functie definiëren met de vereiste parameters en deze registreren voor het Kernel object met behulp van de add_filter methode (een FilterTypes waarde of het equivalent van de tekenreeks doorgeven) of de @kernel.filter decorator gebruiken om het filter in één stap te definiëren en te registreren.

Functie aanroepfilter

Dit filter wordt telkens geactiveerd wanneer een Semantic Kernel functie wordt aangeroepen, ongeacht of het een functie is die is gemaakt op basis van een prompt of een methode.

/// <summary>
/// Example of function invocation filter to perform logging before and after function invocation.
/// </summary>
public sealed class LoggingFilter(ILogger logger) : IFunctionInvocationFilter
{
    public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
    {
        logger.LogInformation("FunctionInvoking - {PluginName}.{FunctionName}", context.Function.PluginName, context.Function.Name);

        await next(context);

        logger.LogInformation("FunctionInvoked - {PluginName}.{FunctionName}", context.Function.PluginName, context.Function.Name);
    }
}

Filter toevoegen met behulp van afhankelijkheidsinjectie:

IKernelBuilder builder = Kernel.CreateBuilder();

builder.Services.AddSingleton<IFunctionInvocationFilter, LoggingFilter>();

Kernel kernel = builder.Build();

Filter toevoegen met behulp van Kernel eigenschap:

kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger));

Codevoorbeelden


import logging
from typing import Awaitable, Callable
from semantic_kernel.filters import FilterTypes, FunctionInvocationContext

logger = logging.getLogger(__name__)

async def logger_filter(context: FunctionInvocationContext, next: Callable[[FunctionInvocationContext], Awaitable[None]]) -> None:
    logger.info(f"FunctionInvoking - {context.function.plugin_name}.{context.function.name}")

    await next(context)

    logger.info(f"FunctionInvoked - {context.function.plugin_name}.{context.function.name}")

# Add filter to the kernel
kernel.add_filter(FilterTypes.FUNCTION_INVOCATION, logger_filter)

U kunt de @kernel.filter decorator ook gebruiken om een filter rechtstreeks te registreren:


@kernel.filter(FilterTypes.FUNCTION_INVOCATION)
async def logger_filter(context: FunctionInvocationContext, next: Callable[[FunctionInvocationContext], Awaitable[None]]) -> None:
    logger.info(f"FunctionInvoking - {context.function.plugin_name}.{context.function.name}")

    await next(context)

    logger.info(f"FunctionInvoked - {context.function.plugin_name}.{context.function.name}")

Codevoorbeelden

Binnenkort meer informatie.

Prompt-weergavefilter

Dit filter wordt alleen aangeroepen tijdens een promptweergavebewerking, bijvoorbeeld wanneer een functie die is gemaakt op basis van een prompt wordt aangeroepen. Deze wordt niet geactiveerd voor Semantic Kernel functies die zijn gemaakt op basis van methoden.

/// <summary>
/// Example of prompt render filter which overrides rendered prompt before sending it to AI.
/// </summary>
public class SafePromptFilter : IPromptRenderFilter
{
    public async Task OnPromptRenderAsync(PromptRenderContext context, Func<PromptRenderContext, Task> next)
    {
        // Example: get function information
        var functionName = context.Function.Name;

        await next(context);

        // Example: override rendered prompt before sending it to AI
        context.RenderedPrompt = "Safe prompt";
    }
}

Filter toevoegen met behulp van afhankelijkheidsinjectie:

IKernelBuilder builder = Kernel.CreateBuilder();

builder.Services.AddSingleton<IPromptRenderFilter, SafePromptFilter>();

Kernel kernel = builder.Build();

Filter toevoegen met behulp van Kernel eigenschap:

kernel.PromptRenderFilters.Add(new SafePromptFilter());

Codevoorbeelden

from typing import Awaitable, Callable
from semantic_kernel.filters import FilterTypes, PromptRenderContext

async def safe_prompt_filter(
    context: PromptRenderContext,
    next: Callable[[PromptRenderContext], Awaitable[None]],
) -> None:
    # Example: get function information
    function_name = context.function.name

    await next(context)

    # Example: override the rendered prompt before sending it to the AI
    context.rendered_prompt = f"Safe prompt: {context.rendered_prompt or ''}"

# Register the filter on the kernel
kernel.add_filter(FilterTypes.PROMPT_RENDERING, safe_prompt_filter)

U kunt de @kernel.filter decorator ook gebruiken om een filter rechtstreeks te registreren:

@kernel.filter(FilterTypes.PROMPT_RENDERING)
async def prompt_rendering_filter(context: PromptRenderContext, next):
    await next(context)
    context.rendered_prompt = f"You pretend to be Mosscap, but you are Papssom who is the opposite of Moscapp in every way {context.rendered_prompt or ''}"

Codevoorbeelden

Binnenkort meer informatie.

Filter voor automatisch aanroepen van functie

Dit filter wordt alleen aangeroepen tijdens een proces voor automatisch aanroepen van functies. Deze wordt niet geactiveerd wanneer een functie buiten dit proces wordt aangeroepen.

/// <summary>
/// Example of auto function invocation filter which terminates function calling process as soon as we have the desired result.
/// </summary>
public sealed class EarlyTerminationFilter : IAutoFunctionInvocationFilter
{
    public async Task OnAutoFunctionInvocationAsync(AutoFunctionInvocationContext context, Func<AutoFunctionInvocationContext, Task> next)
    {
        // Call the function first.
        await next(context);

        // Get a function result from context.
        var result = context.Result.GetValue<string>();

        // If the result meets the condition, terminate the process.
        // Otherwise, the function calling process will continue.
        if (result == "desired result")
        {
            context.Terminate = true;
        }
    }
}

Filter toevoegen met behulp van afhankelijkheidsinjectie:

IKernelBuilder builder = Kernel.CreateBuilder();

builder.Services.AddSingleton<IAutoFunctionInvocationFilter, EarlyTerminationFilter>();

Kernel kernel = builder.Build();

Filter toevoegen met behulp van Kernel eigenschap:

kernel.AutoFunctionInvocationFilters.Add(new EarlyTerminationFilter());

Codevoorbeelden


from semantic_kernel.filters import FilterTypes, AutoFunctionInvocationContext

@kernel.filter(FilterTypes.AUTO_FUNCTION_INVOCATION)
async def auto_function_invocation_filter(context: AutoFunctionInvocationContext, next):
    await next(context)
    if context.function_result == "desired result":
        context.terminate = True

Net als bij de andere filtertypen kunt u het filter ook registreren met:kernel.add_filter

kernel.add_filter(FilterTypes.AUTO_FUNCTION_INVOCATION, auto_function_invocation_filter)

Codevoorbeelden

Binnenkort meer informatie.

Streaming en niet streaming-aanroep

Functies in Semantic Kernel kunnen op twee manieren worden aangeroepen: streaming en niet-streaming. In de streamingmodus retourneert een functie meestal IAsyncEnumerable<T>, terwijl in de niet-streamingmodus FunctionResult wordt geretourneerd. Dit onderscheid is van invloed op de manier waarop de resultaten in het filter kunnen worden overschreven: in de streamingmodus moet de nieuwe functieresultaatwaarde van het type IAsyncEnumerable<T>zijn, terwijl het in de niet-streamingmodus gewoon van het type Tkan zijn. Om te bepalen welk resultaattype moet worden geretourneerd, is de context.IsStreaming vlag beschikbaar in het filtercontextmodel.

/// <summary>Filter that can be used for both streaming and non-streaming invocation modes at the same time.</summary>
public sealed class DualModeFilter : IFunctionInvocationFilter
{
    public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
    {
        // Call next filter in pipeline or actual function.
        await next(context);

        // Check which function invocation mode is used.
        if (context.IsStreaming)
        {
            // Return IAsyncEnumerable<string> result in case of streaming mode.
            var enumerable = context.Result.GetValue<IAsyncEnumerable<string>>();
            context.Result = new FunctionResult(context.Result, OverrideStreamingDataAsync(enumerable!));
        }
        else
        {
            // Return just a string result in case of non-streaming mode.
            var data = context.Result.GetValue<string>();
            context.Result = new FunctionResult(context.Result, OverrideNonStreamingData(data!));
        }
    }

    private async IAsyncEnumerable<string> OverrideStreamingDataAsync(IAsyncEnumerable<string> data)
    {
        await foreach (var item in data)
        {
            yield return $"{item} - updated from filter";
        }
    }

    private string OverrideNonStreamingData(string data)
    {
        return $"{data} - updated from filter";
    }
}

Filters gebruiken met IChatCompletionService

In gevallen waarin IChatCompletionService filters rechtstreeks worden gebruikt in plaats van Kernel, worden filters alleen aangeroepen wanneer een Kernel object wordt doorgegeven als een parameter aan de chat-voltooiingsservicemethoden, omdat filters worden gekoppeld aan het Kernel exemplaar.

Kernel kernel = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion("gpt-4", "api-key")
    .Build();

kernel.FunctionInvocationFilters.Add(new MyFilter());

IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

// Passing a Kernel here is required to trigger filters.
ChatMessageContent result = await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings, kernel);

Functies in Semantic Kernel kunnen op twee manieren worden aangeroepen: streaming en niet-streaming. In de streamingmodus retourneert een functie meestal een AsyncGenerator[T] object waarbij T een soort streaming-inhoudstype is, terwijl deze in de niet-streamingmodus wordt geretourneerd FunctionResult. Dit onderscheid is van invloed op de manier waarop resultaten in het filter kunnen worden overschreven: in de streamingmodus moet de waarde van het nieuwe functieresultaat ook van het type AsyncGenerator[T]zijn. Om te bepalen welk resultaattype moet worden geretourneerd, is de context.is_streaming vlag beschikbaar in alle filtercontextmodellen.

Als u dus een eenvoudig logboekregistratiefilter wilt maken voor een aanroep van een streamingfunctie, gebruikt u zoiets:

@kernel.filter(FilterTypes.FUNCTION_INVOCATION)
async def streaming_exception_handling(
    context: FunctionInvocationContext,
    next: Callable[[FunctionInvocationContext], Awaitable[None]],
):
    await next(context)
    if not context.is_streaming:
        return

    async def override_stream(stream):
        try:
            async for partial in stream:
                yield partial
        except Exception as e:
            yield [
                StreamingChatMessageContent(role=AuthorRole.ASSISTANT, content=f"Exception caught: {e}", choice_index=0)
            ]

    stream = context.result.value
    context.result = FunctionResult(function=context.result.function, value=override_stream(stream))

Codevoorbeelden

Binnenkort meer informatie.

Bestellen

Wanneer u afhankelijkheidsinjectie gebruikt, wordt de volgorde van filters niet gegarandeerd. Als de volgorde van filters belangrijk is, is het raadzaam om filters rechtstreeks aan het Kernel object toe te voegen met behulp van de juiste eigenschappen. Met deze methode kunnen filters tijdens runtime worden toegevoegd, verwijderd of opnieuw worden gerangschikt.

Filters worden uitgevoerd in de volgorde waarin ze worden toegevoegd aan het kernelobject- of via add_filter of de @kernel.filter decorator. Omdat de uitvoeringsvolgorde van invloed kan zijn op het gedrag, is het belangrijk dat u de filtervolgorde zorgvuldig beheert.

Bekijk het volgende voorbeeld:

def func():
    print('function')


@kernel.filter(FilterTypes.FUNCTION_INVOCATION)
async def filter1(context: FunctionInvocationContext, next):
    print('before filter 1')
    await next(context)
    print('after filter 1')

@kernel.filter(FilterTypes.FUNCTION_INVOCATION)
async def filter2(context: FunctionInvocationContext, next):
    print('before filter 2')
    await next(context)
    print('after filter 2')

Bij het uitvoeren van de functie is de uitvoer:

before filter 1
before filter 2
function
after filter 2
after filter 1

Meer voorbeelden