How to Create and Register Custom Function Plugins in AntSK: A Complete Guide
Developers create custom function plugins in AntSK by marking .NET methods with a DescriptionAttribute containing the token "AntSK", enabling the FunctionService to automatically index them for registration into the Semantic Kernel as native functions or prompt-based plugins.
AntSK is an open-source .NET knowledge management system that leverages Microsoft Semantic Kernel to extend LLM capabilities with custom business logic. To integrate your own code into AntSK's AI workflows, you must understand how the framework discovers, indexes, and registers methods as callable functions that LLMs can invoke during prompt execution.
How AntSK Discovers Custom Functions
AntSK uses reflection-based scanning to locate candidate methods across all loaded assemblies. This discovery process is centralized in FunctionService.cs.
The "AntSK" Token Requirement
In src/AntSK.Domain/Domain/Service/FunctionService.cs (lines 36-88), the SearchMarkedMethods method scans every loaded assembly—including the main AntSK assembly and any additional dependencies—for methods decorated with System.ComponentModel.DescriptionAttribute. Only methods whose description text contains the literal string "AntSK" are indexed for plugin registration.
Key Generation and Metadata Caching
For each discovered method, the service generates a sanitized unique key using the assembly, type, and method names:
var key = $"{method.DeclaringType.Assembly.GetName().Name}_{method.DeclaringType.Name}_{method.Name}";
key = Regex.Replace(key, "[^a-zA-Z0-9_]", "_");
The method metadata is then stored in two internal concurrent dictionaries:
_methodCachemaps the key to theMethodInfoobject_methodInfosstores tuples containing the description, return type description, and parameter information
This indexing occurs at startup when FunctionService is instantiated via dependency injection in src/AntSK/Program.cs (lines 71-72).
Registering Functions with the Semantic Kernel
Once indexed, functions must be imported into a Kernel instance before they can be invoked by LLM prompts.
The Registration Entry Point
The KernelService.ImportFunctionsByApp method in src/AntSK.Domain/Domain/Service/KernelService.cs (lines 59-74) orchestrates the loading process. It first checks if a plugin named "AntSKFunctions" already exists in the kernel to prevent duplicate registration, then proceeds to import both API-based and native .NET functions.
Native Function Import Process
Inside ImportNativeFunction (lines 77-96), the service reads the comma-separated NativeFunctionList property from the Apps entity. For each function key present in this list, the service retrieves the cached MethodInfo and creates a KernelFunction using Kernel.CreateFunctionFromMethod. These functions are then grouped under the "AntSKFunctions" plugin name via _kernel.ImportPluginFromFunctions.
Creating a Native Function Plugin (Code-First Approach)
To expose your business logic as a callable function, follow these implementation steps.
Step 1: Define the Method with Proper Attributes
Create a static method in any accessible class, ensuring the DescriptionAttribute contains "AntSK":
using System.ComponentModel;
namespace MyAntSKPlugins;
public class UserInfoPlugin
{
[Description("AntSK: GetCurrentUserInfo - Retrieves user details by ID")]
public static string GetCurrentUserInfo(
[Description("The unique identifier of the user")] string userId)
{
// Your business logic here
return $"{{\"userId\":\"{userId}\",\"name\":\"John Doe\"}}";
}
}
Step 2: Register the Assembly for Discovery
Ensure FunctionService scans your assembly by adding it to the constructor call in Program.cs:
builder.Services.AddSingleton(sp =>
new FunctionService(sp,
[typeof(MyAntSKPlugins.UserInfoPlugin).Assembly]));
Step 3: Configure the App to Use the Function
Add the generated function key to your app's NativeFunctionList property. The key follows the pattern AssemblyName_TypeName_MethodName:
// Example configuration for an Apps entity
app.NativeFunctionList = "MyAntSKPlugins_UserInfoPlugin_GetCurrentUserInfo";
Step 4: Invoke the Function
When KernelService.GetKernelByApp(app) builds the kernel, your function becomes callable:
var kernel = kernelService.GetKernelByApp(app);
var result = await kernel.InvokeAsync(
kernel.Plugins.GetFunction("AntSKFunctions", "GetCurrentUserInfo"),
new() { ["userId"] = "12345" });
Console.WriteLine(result.GetValue<string>());
Creating a Prompt-Based Plugin (No-Code Approach)
For scenarios requiring only prompt templates without compiled code, AntSK supports directory-based prompt plugins.
Directory Structure and Files
Create a folder under the plugins directory (e.g., plugins/HelloPlugin/) containing:
skprompt.txt:
Say hello to the user.
User: {{$input}}
Assistant: Hello, {{$input}}! Welcome to AntSK.
config.json:
{
"name": "HelloPlugin",
"description": "Simple greeting plugin for AntSK",
"execution_settings": {
"default": {
"temperature": 0.7
}
}
}
Automatic Registration
The KernelService.RegisterPluginsWithKernel method (line 308) automatically imports all directories from RepoFiles.SamplePluginsPath() using kernel.ImportPluginFromPromptDirectory. Your prompt functions become available under the plugin name specified in config.json (e.g., "HelloPlugin").
Summary
- Discovery requires the "AntSK" token: Methods must use
[Description("AntSK: ...")]to be indexed byFunctionService. - Keys are auto-generated: The format is
AssemblyName_TypeName_MethodName, sanitized to replace non-alphanumeric characters with underscores. - Registration is app-specific: Functions are imported into the kernel only if their keys are listed in the
Apps.NativeFunctionListproperty. - Two plugin types supported: Native functions (.NET methods) and prompt-based plugins (directory with
skprompt.txtandconfig.json). - Plugin name is "AntSKFunctions": All native custom functions are grouped under this plugin name in the Semantic Kernel.
Frequently Asked Questions
What token must be included in the DescriptionAttribute for AntSK to discover my method?
The description text must contain the literal string "AntSK" (case-sensitive). For example: [Description("AntSK: Calculate sales tax")]. Without this token, FunctionService.SearchMarkedMethods will ignore the method during assembly scanning.
Can I register functions from external assemblies or NuGet packages?
Yes. Pass the external assembly to the FunctionService constructor in Program.cs using typeof(YourType).Assembly. As long as the assembly is loaded and methods contain the "AntSK" description token, they will be indexed and available for registration in any app's NativeFunctionList.
How do I prevent duplicate plugin registration when reusing Kernel instances?
The ImportFunctionsByApp method in KernelService explicitly checks _kernel.Plugins.Any(p => p.Name == "AntSKFunctions") and returns early if the plugin already exists. This guard clause prevents duplicate registration errors when the same kernel instance is reused across multiple operations.
What is the difference between native functions and prompt-based plugins in AntSK?
Native functions are compiled .NET methods that execute business logic directly via Kernel.CreateFunctionFromMethod, offering full access to the application's services and data. Prompt-based plugins are declarative templates stored in skprompt.txt files that rely entirely on the LLM for execution, requiring no compiled code but offering less programmatic control.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →