One of the most powerful features of Microsoft Semantic Kernel is the ability to combine native functions (traditional C# code) with semantic functions (AI-powered prompts) into a single, cohesive plugin. This hybrid approach allows developers to leverage the best of both worlds: the precision and performance of native code for deterministic operations, and the flexibility of large language models for complex reasoning tasks.
In this article, we'll build a Smart Document Assistant that demonstrates hybrid plugins through a practical scenario. Our assistant will:
- Extract text statistics using native functions (word count, reading time, keyword frequency)
- Generate AI-powered summaries and sentiment analysis using semantic functions
Understanding Hybrid Plugins
A hybrid plugin is simply a collection of KernelFunction objects that can originate from different sources:
| Function TypeCreated UsingBest For |
| Native | CreateFunctionFromMethod() | Deterministic operations, calculations, I/O |
| Semantic | CreateFunctionFromPrompt() | Text generation, analysis, reasoning |
Since both types implement KernelFunction, they can be combined into a single list and imported as one plugin using ImportPluginFromFunctions().
The Scenario: Smart Document Assistant
Imagine you're building an application that helps users quickly understand documents. Users can:
- Get precise statistics about the document (native)
- Extract key topics and themes (semantic)
- Generate executive summaries (semantic)
- Analyze document sentiment (semantic)
Implementation
Step 1: Define Native Functions
First, let's create our native functions for text analysis:
public class TextAnalysisFunctions
{
public static string AnalyzeTextStatistics(string text)
{
if (string.IsNullOrWhiteSpace(text))
return "No text provided for analysis.";
var words = text.Split([' ', '\n', '\r', '\t'],
StringSplitOptions.RemoveEmptyEntries);
var sentences = text.Split(['.', '!', '?'],
StringSplitOptions.RemoveEmptyEntries);
var paragraphs = text.Split(["\n\n", "\r\n\r\n"],
StringSplitOptions.RemoveEmptyEntries);
int wordCount = words.Length;
int sentenceCount = sentences.Length;
int paragraphCount = paragraphs.Length;
double avgWordsPerSentence = sentenceCount > 0
? Math.Round((double)wordCount / sentenceCount, 1)
: 0;
int estimatedReadingTimeMinutes = (int)Math.Ceiling(wordCount / 200.0);
return $"""
Word Count: {wordCount}
Sentence Count: {sentenceCount}
Paragraph Count: {paragraphCount}
Average Words per Sentence: {avgWordsPerSentence}
Estimated Reading Time: {estimatedReadingTimeMinutes} minute(s)
""";
}
public static string ExtractTopKeywords(string text, int topN = 5)
{
if (string.IsNullOrWhiteSpace(text))
return "No text provided.";
var stopWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"the", "a", "an", "is", "are", "was", "were", "be", "been",
"being", "have", "has", "had", "do", "does", "did", "will",
"would", "could", "should", "may", "might", "must", "shall",
"can", "need", "dare", "ought", "used", "to", "of", "in",
"for", "on", "with", "at", "by", "from", "as", "into", "through",
"during", "before", "after", "above", "below", "between", "under",
"again", "further", "then", "once", "here", "there", "when",
"where", "why", "how", "all", "each", "few", "more", "most",
"other", "some", "such", "no", "nor", "not", "only", "own",
"same", "so", "than", "too", "very", "just", "and", "but",
"if", "or", "because", "until", "while", "this", "that", "these",
"those", "it", "its", "they", "them", "their", "we", "us", "our",
"you", "your", "he", "him", "his", "she", "her", "i", "my", "me"
};
var words = text.ToLower()
.Split([' ', '\n', '\r', '\t', '.', ',', '!', '?', ';', ':', '"', '\'', '(', ')', '[', ']'],
StringSplitOptions.RemoveEmptyEntries)
.Where(w => w.Length > 2 && !stopWords.Contains(w))
.GroupBy(w => w)
.OrderByDescending(g => g.Count())
.Take(topN)
.Select(g => $"{g.Key} ({g.Count()})");
return $"Top {topN} Keywords: {string.Join(", ", words)}";
}
}
Step 2: Define Semantic Function Prompts
Next, create prompts for AI-powered analysis:
var summarizationPrompt = """
You are an expert document summarizer. Your task is to create a concise
executive summary of the provided document.
Guidelines:
- Keep the summary to 3-5 sentences maximum
- Focus on the main points and key takeaways
- Use clear, professional language
- Do not add information not present in the original text
[DOCUMENT START]
{{$document}}
[DOCUMENT END]
Executive Summary:
""";
var sentimentAnalysisPrompt = """
Analyze the sentiment and tone of the following document. Provide:
1. Overall sentiment (Positive/Negative/Neutral)
2. Confidence level (High/Medium/Low)
3. Key emotional indicators found in the text
4. Recommended audience approach based on tone
[DOCUMENT START]
{{$document}}
[DOCUMENT END]
Sentiment Analysis:
""";
var keyTopicsPrompt = """
Extract the main topics and themes from the following document.
Format your response as:
- Main Topic: [primary subject]
- Secondary Topics: [list 2-3 related themes]
- Domain: [business/technical/academic/general]
[DOCUMENT START]
{{$document}}
[DOCUMENT END]
Topics Analysis:
""";
Step 3: Create and Register the Hybrid Plugin
Now, combine everything into a hybrid plugin:
// Create native functions
var statisticsFunction = kernel.CreateFunctionFromMethod(
typeof(TextAnalysisFunctions).GetMethod(nameof(TextAnalysisFunctions.AnalyzeTextStatistics))!,
target: null,
functionName: "AnalyzeStatistics",
description: "Analyzes text and returns word count, sentence count, and reading time."
);
var keywordsFunction = kernel.CreateFunctionFromMethod(
typeof(TextAnalysisFunctions).GetMethod(nameof(TextAnalysisFunctions.ExtractTopKeywords))!,
target: null,
functionName: "ExtractKeywords",
description: "Extracts the most frequently used keywords from the document."
);
// Create semantic functions
var summarizeFunction = kernel.CreateFunctionFromPrompt(
summarizationPrompt,
functionName: "Summarize",
description: "Generates an executive summary of the document using AI."
);
var sentimentFunction = kernel.CreateFunctionFromPrompt(
sentimentAnalysisPrompt,
functionName: "AnalyzeSentiment",
description: "Analyzes the sentiment and tone of the document using AI."
);
var topicsFunction = kernel.CreateFunctionFromPrompt(
keyTopicsPrompt,
functionName: "ExtractTopics",
description: "Identifies main topics and themes in the document using AI."
);
// Combine into hybrid plugin
List<KernelFunction> hybridFunctions =
[
statisticsFunction, // Native
keywordsFunction, // Native
summarizeFunction, // Semantic
sentimentFunction, // Semantic
topicsFunction // Semantic
];
kernel.ImportPluginFromFunctions(
"document_assistant",
"Smart Document Assistant with text analysis and AI-powered insights.",
hybridFunctions
);
Step 4: Using the Hybrid Plugin
Here's how to use the hybrid plugin in practice:
var sampleDocument = """
Artificial Intelligence is transforming the software development landscape
at an unprecedented pace. Companies worldwide are adopting AI-powered tools
to enhance productivity, reduce errors, and accelerate delivery timelines.
The integration of large language models into development workflows has
enabled developers to write code faster, generate documentation automatically,
and identify potential bugs before they reach production. This shift represents
a fundamental change in how software is created and maintained.
However, challenges remain. Security concerns, model accuracy, and the need
for human oversight continue to be important considerations. Organizations
must balance the benefits of AI assistance with appropriate governance and
quality controls.
""";
var arguments = new KernelArguments
{
["document"] = sampleDocument,
["text"] = sampleDocument,
["topN"] = 5
};
// Use native function for statistics
var stats = await kernel.InvokeAsync("document_assistant", "AnalyzeStatistics", arguments);
Console.WriteLine($"📊 Document Statistics:\n{stats}\n");
// Use native function for keywords
var keywords = await kernel.InvokeAsync("document_assistant", "ExtractKeywords", arguments);
Console.WriteLine($"🔑 {keywords}\n");
// Use semantic function for summary
var summary = await kernel.InvokeAsync("document_assistant", "Summarize", arguments);
Console.WriteLine($"📝 AI Summary:\n{summary}\n");
// Use semantic function for sentiment
var sentiment = await kernel.InvokeAsync("document_assistant", "AnalyzeSentiment", arguments);
Console.WriteLine($"💭 Sentiment Analysis:\n{sentiment}\n");
Expected Output
📊 Document Statistics:
Word Count: 156
Sentence Count: 8
Paragraph Count: 3
Average Words per Sentence: 19.5
Estimated Reading Time: 1 minute(s)
🔑 Top 5 Keywords: software (2), development (2), code (2), ai-powered (2), productivity (1)
📝 AI Summary:
AI is rapidly transforming software development, with companies adopting AI-powered
tools to boost productivity and accelerate delivery. Large language models are enabling
faster coding, automatic documentation, and early bug detection. Despite these advances,
organizations must address security concerns and maintain human oversight while balancing
AI benefits with proper governance.
💭 Sentiment Analysis:
1. Overall Sentiment: Positive with cautious undertones
2. Confidence Level: High
3. Key Emotional Indicators: optimism ("transforming", "enhance"), caution ("challenges remain", "concerns")
4. Recommended Audience Approach: Present as opportunity with acknowledged risks
Advanced Usage: Orchestrating Hybrid Functions
You can also orchestrate hybrid functions within a single prompt:
var orchestrationPrompt = """
You are a document analysis assistant. Generate a comprehensive report.
Document Statistics (from analysis):
{{AnalyzeStatistics $document}}
Keywords Found:
{{ExtractKeywords $document}}
Based on the above metrics and the document content, provide your assessment
of the document's readability and target audience.
""";
var report = await kernel.InvokePromptAsync(orchestrationPrompt, arguments);
Benefits of Hybrid Plugins
| Benefit - Description |
| Performance | Native functions execute instantly for deterministic operations |
| Cost Efficiency | Reduce API calls by handling simple tasks locally |
| Reliability | Native functions provide consistent, testable results |
| Flexibility | AI handles complex reasoning that would be difficult to code |
| Maintainability | Single plugin interface regardless of function type |
Best Practices
- Use native functions for:
- Mathematical calculations
- Data transformations
- File I/O operations
- Validation logic
- Performance-critical operations
- Use semantic functions for:
- Text summarization
- Sentiment analysis
- Content generation
- Complex reasoning
- Pattern recognition in unstructured data
- Design considerations:
- Keep function responsibilities clear and focused
- Use descriptive names and descriptions for discoverability
- Consider caching semantic function results for repeated queries
- Implement proper error handling in native functions