Semantic Kernel Integration¶
This document explains how Microsoft Semantic Kernel is integrated into the GridLab.Gmss.Cim module to enable natural language querying of CIM (Common Information Model) documents.
What is Semantic Kernel?¶
Microsoft Semantic Kernel is an open-source SDK that enables developers to integrate Large Language Models (LLMs) with conventional programming languages like C#, Python, and Java. It acts as an orchestration layer that:
- Connects LLMs to Your Code: Allows AI models to call your application functions
- Plugin Architecture: Exposes application capabilities as "plugins" that AI can discover and invoke
- Multi-Model Support: Works with OpenAI, Azure OpenAI, Ollama, HuggingFace, and other AI services
- Agentic Behavior: Enables AI agents to reason, plan, and execute tasks using your code
- Memory & Context Management: Maintains conversation history and context across interactions
In essence, Semantic Kernel bridges the gap between natural language understanding (via LLMs) and structured code execution.
Semantic Kernel vs MCP Server¶
While both technologies enable AI-powered interactions with application data, they serve different purposes and use different architectures:
| Aspect | Semantic Kernel | MCP (Model Context Protocol) Server |
|---|---|---|
| Purpose | Orchestration SDK for integrating LLMs into applications | Standardized protocol for exposing application context to AI assistants |
| Architecture | Embedded SDK within your application | External server process that communicates via stdio/HTTP |
| Integration | Code-first: Define functions in C#/Python/Java | Protocol-first: Implement MCP server specification |
| LLM Control | You host/manage LLM connections (OpenAI, Ollama, etc.) | AI client (like Claude Desktop) manages the LLM |
| Execution | Functions execute in your application process | Tools execute in MCP server process, results sent to client |
| Use Case | Building AI-powered features within your app (chatbots, agents) | Exposing app capabilities to external AI assistants (Claude, VS Code Copilot) |
| Conversation | Full control over chat flow and history | AI client controls conversation flow |
| Deployment | Deployed as part of your application | Deployed as a separate service/binary |
When to Use Each:¶
Use Semantic Kernel when:
- You want to embed AI capabilities directly into your application
- You need full control over the LLM, prompts, and execution flow
- You're building custom chatbots, AI agents, or intelligent features
- You want to maintain conversation context within your app
Use MCP Server when:
- You want external AI assistants (Claude Desktop, VS Code Copilot) to access your app's data
- You're exposing read-only or utility functions to AI tools
- You want a standardized protocol for AI assistant integration
- You don't need to manage LLM infrastructure yourself
In GridLab.Gmss.Cim:
- Semantic Kernel powers the integrated chat interface for querying CIM documents via natural language
Architecture Overview¶
The Semantic Kernel integration in this module follows a clean architecture pattern:
graph TB
subgraph "User Interface Layer"
A[Chat UI<br/>Razor Page]
B[SignalR Hub<br/>CimChatHub]
end
subgraph "Service Layer"
C[CimChatService]
D[Semantic Kernel]
end
subgraph "Plugin Layer"
E[CimDocumentQueryPlugin]
end
subgraph "Application Layer"
F[CimDocumentQueryAppService]
end
subgraph "Domain Layer"
G[CimDocumentManager]
H[RDF Graph Store]
end
subgraph "AI Infrastructure"
I[Ollama<br/>Local LLM]
end
A -->|WebSocket| B
B -->|Chat Request| C
C -->|Kernel.InvokeAsync| D
D -->|Function Calling| E
E -->|Query Documents| F
F -->|Read Graph| G
G -->|LINQ| H
D <-->|HTTP API| I Implementation in GridLab.Gmss.Cim¶
The Semantic Kernel integration enables users to query CIM documents using natural language through a conversational AI interface. The implementation consists of:
- Kernel Factory - Creates and configures Semantic Kernel instances
- Chat Service - Manages conversation flow and LLM interactions
- Query Plugin - Exposes CIM document query capabilities to the AI
- SignalR Hub - Provides real-time communication with the UI
- Chat UI - User-facing interface for natural language queries
How It Works¶
1. User Asks a Question¶
User: "How many SynchronousMachines are in the document?"
2. Chat Service Sends to LLM¶
The CimChatService sends the user's message to the LLM (Ollama) with:
- System Prompt: Instructions on how to use available functions
- Chat History: Previous conversation context
- Function Definitions: Metadata about available CIM query functions
3. LLM Decides to Call a Function¶
The LLM reasons:
- "The user wants to count SynchronousMachine instances"
- "I should use
search_definitionswith searchTerm='SynchronousMachine'"
It generates a function call request:
{
"function": "search_definitions",
"arguments": {
"searchTerm": "SynchronousMachine",
"maxResults": 100
}
}
4. Semantic Kernel Executes the Plugin Function¶
Semantic Kernel intercepts the function call and invokes:
CimDocumentQueryPlugin.SearchDefinitionsAsync(
searchTerm: "SynchronousMachine",
maxResults: 100
)
5. Plugin Queries CIM Document¶
The plugin calls the application service:
var definitions = await _queryService.SearchDefinitionsAsync(
documentId,
"SynchronousMachine"
);
This executes a bultin query against the RDF graph store.
6. Results Return to LLM¶
The plugin returns formatted results:
Found 12 results for 'SynchronousMachine':
1. cim:SynchronousMachine (ID: _72cb1d28-c3ce-11f0-988b-f8edfcb6b192)
2. cim:SynchronousMachine (ID: _8f3a4b12-d4ef-22c1-899c-a9fedcb7c203)
...
7. LLM Generates Human-Friendly Response¶
The LLM processes the function result and responds:
I found **12 SynchronousMachines** in the document. Here's a summary:
1. **_72cb1d28-c3ce-11f0-988b-f8edfcb6b192** - SynchronousMachine
2. **_8f3a4b12-d4ef-22c1-899c-a9fedcb7c203** - SynchronousMachine
...
Would you like details about any specific machine?
8. User Sees the Answer¶
The response is streamed back through SignalR to the chat UI in real-time.
Key Components¶
1. CimSemanticKernelFactory¶
Location: host/GridLab.Gmss.Cim/SemanticKernel/CimSemanticKernelFactory.cs
Factory class for creating and configuring Semantic Kernel instances with Ollama LLM support.
Key Methods:
// Create kernel with Ollama LLM and CIM query plugin
public static (Kernel kernel, CimDocumentQueryPlugin plugin) CreateKernelWithPlugin(
ICimDocumentQueryAppService queryService,
string ollamaEndpoint,
string modelId)
// Get execution settings for automatic function calling
public static OllamaPromptExecutionSettings GetOllamaExecutionSettings()
Configuration:
- Connects to Ollama (local LLM runtime)
- Registers
CimDocumentQueryPluginas "CimDocument" - Enables automatic function calling (temperature=0.0 for deterministic behavior)
2. CimChatService¶
Location: host/GridLab.Gmss.Cim/SemanticKernel/Services/CimChatService.cs
Service that manages conversation flow between users and the LLM.
Responsibilities:
- Maintains chat history with system prompt
- Sends user messages to LLM via Semantic Kernel
- Handles automatic function invocation
- Returns AI-generated responses
System Prompt Highlights:
You are an expert assistant for querying CIM documents...
Available functions:
1. set_document_context - Set active document
2. get_definition - Get specific definition by ID
3. get_all_definitions - Get all definitions
4. search_definitions - Search by term
5. list_class_types - List all CIM classes
6. find_content_in_document - Search text patterns
ALWAYS use functions to query the RDF graph!
Key Methods:
// Process user message and return AI response
public async Task<string> ChatAsync(string userMessage)
// Set the active CIM document for queries
public Task SetDocumentContextAsync(Guid documentId)
// Clear conversation history
public void ClearHistory()
3. CimDocumentQueryPlugin¶
Location: host/GridLab.Gmss.Cim/SemanticKernel/Plugins/CimDocumentQueryPlugin.cs
Semantic Kernel plugin that exposes CIM document query capabilities as callable functions.
Available Functions:
| Function | Description | Parameters |
|---|---|---|
set_document_context | Set active document for queries | documentId (GUID) |
get_definition | Retrieve specific definition by instance ID | instanceId, documentId (optional) |
get_all_definitions | Get all definitions in document | documentId (optional), maxResults |
search_definitions | Search definitions by term | searchTerm, documentId (optional), maxResults |
list_class_types | List all CIM class types | documentId (optional) |
find_content_in_document | Search text patterns | pattern, documentId (optional), maxResults |
Example Function Definition:
[KernelFunction("search_definitions")]
[Description("Searches CIM definitions by term. Matches IDs, class names, property values.")]
public async Task<string> SearchDefinitionsAsync(
[Description("Search term (e.g., 'SynchronousMachine', 'Generator')")]
string searchTerm,
[Description("Optional document ID. Uses current context if not provided.")]
string? documentId = null,
[Description("Max results to return (default: 10)")]
int maxResults = 10,
CancellationToken cancellationToken = default)
How Functions Are Discovered:
[KernelFunction]attribute marks methods as callable by LLM[Description]attributes provide semantic information to the AI- Parameter descriptions help the LLM understand how to call functions
4. CimChatHub (SignalR)¶
Location: host/GridLab.Gmss.Cim/Hubs/CimChatHub.cs
SignalR hub for real-time chat communication between browser and server.
Client-to-Server Methods:
// Send user message to AI
public async Task SendMessage(string message, Guid? documentId = null)
// Clear conversation history
public async Task ClearHistory()
Server-to-Client Events:
"ReceiveMessage" // AI response text
"ReceiveStatus" // Processing status (e.g., "thinking")
"ReceiveError" // Error messages
"HistoryCleared" // Confirmation of history reset
5. Chat UI¶
Location: host/GridLab.Gmss.Cim/Pages/Cim/Chat/Index.cshtml
Razor page providing the chat interface:
- Document selector dropdown
- Message input field
- Chat history display with markdown rendering
- Real-time updates via SignalR
- Status indicators (thinking, error)
JavaScript Integration:
// Connect to SignalR hub
const connection = new signalR.HubConnectionBuilder()
.withUrl("/cim-chat-hub")
.build();
// Send message
connection.invoke("SendMessage", message, documentId);
// Receive response
connection.on("ReceiveMessage", (response) => {
// Display formatted message
});
Configuration¶
Application Settings¶
File: host/GridLab.Gmss.Cim/appsettings.json
{
"SemanticKernel": {
"OllamaEndpoint": "http://localhost:7869",
"ModelId": "qwen2.5:14b-instruct"
}
}
Configuration Options:
| Setting | Description | Default |
|---|---|---|
OllamaEndpoint | URL of Ollama server | http://localhost:7869 |
ModelId | LLM model to use | qwen2.5:14b-instruct |
Supported Models:
qwen2.5:14b-instruct(recommended for function calling)llama3.2:latestmistral:latestdeepseek-r1:14b- Any Ollama-compatible model with function calling support
Module Registration¶
File: host/GridLab.Gmss.Cim/CimWebHostModule.cs
private void ConfigureSemanticKernel(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
// Create shared kernel container with plugin
context.Services.AddSingleton(sp =>
{
var queryService = sp.GetRequiredService<ICimDocumentQueryAppService>();
var ollamaEndpoint = configuration["SemanticKernel:OllamaEndpoint"]
?? "http://localhost:7869";
var modelId = configuration["SemanticKernel:ModelId"]
?? "qwen2.5:14b-instruct";
return CimSemanticKernelFactory.CreateKernelContainer(
queryService, ollamaEndpoint, modelId);
});
// Register Kernel and Plugin as separate services
context.Services.AddSingleton(sp => sp.GetRequiredService<KernelContainer>().Kernel);
context.Services.AddSingleton(sp => sp.GetRequiredService<KernelContainer>().Plugin);
}
Dependency Injection:
Kernel- Singleton instance shared across requestsCimDocumentQueryPlugin- Singleton plugin instanceCimChatService- Scoped service per user session
Usage Example¶
Scenario: Query Equipment in a CIM Document¶
Step 1: User selects a document
Document: "Nordic44-HV_EQ.xml" (Selected from dropdown)
Step 2: User asks about equipment
User: "What types of equipment are in this document?"
Step 3: LLM calls list_class_types function
{
"function": "list_class_types",
"arguments": {}
}
Step 4: Plugin queries RDF graph
var definitions = await _queryService.GetDefinitionsAsync(documentId);
var classTypes = definitions
.GroupBy(d => d.ClassName)
.Select(g => new { ClassName = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);
Step 5: Function returns results
Found 15 class types in document:
1. cim:Terminal (120 instances)
2. cim:SynchronousMachine (12 instances)
3. cim:ACLineSegment (45 instances)
4. cim:PowerTransformer (8 instances)
...
Step 6: LLM generates friendly response
The document contains **15 different equipment types**:
**Most Common:**
- **Terminal** (120 instances) - Connection points
- **ACLineSegment** (45 instances) - Transmission lines
- **SynchronousMachine** (12 instances) - Generators
**Other Equipment:**
- PowerTransformer (8)
- Breaker (22)
- Load (18)
...
Would you like details about any specific equipment type?
Demo¶
The following demonstration showcases the natural language chat interface in action:

What's happening in the demo:
- User selects a CIM document from the dropdown
- User types natural language questions about the document
- AI assistant uses Semantic Kernel to:
- Understand the intent
- Call appropriate query functions
- Access the RDF graph store
- Format results in markdown
- Responses appear in real-time via SignalR
- Conversation context is maintained across multiple questions
Example Queries Shown:
- "How many generators are in this document?"
- "Show me the properties of the first generator"
- "What is the rated power of generator XYZ?"
- "List all substations"
Benefits of This Approach¶
1. Natural Language Interface¶
Users can query complex CIM data without knowing SPARQL or GraphQL syntax.
2. Contextual Conversations¶
The AI maintains conversation history, allowing follow-up questions:
User: "How many generators?"
AI: "I found 12 generators."
User: "Show me the first one" // AI remembers "generators" from context
3. Automatic Function Orchestration¶
The LLM decides which functions to call and in what order:
User: "Compare the rated power of all generators"
AI calls:
1. search_definitions(searchTerm="SynchronousMachine")
2. get_definition(instanceId=...) for each result
3. Analyzes and compares power ratings
4. Flexible Querying¶
Users can ask questions in many ways:
- "How many SynchronousMachines?"
- "List all generators"
- "Count the power generation equipment"
5. Developer-Friendly¶
Adding new query capabilities is simple:
[KernelFunction("get_voltage_levels")]
[Description("Get all voltage levels in the network")]
public async Task<string> GetVoltageLevelsAsync()
{
// Implementation
}
The LLM automatically discovers and uses new functions!
Future Enhancements¶
1. Streaming Responses¶
Implement token-by-token streaming for faster perceived response times.
2. Multi-Document Queries¶
Enable queries across multiple CIM documents simultaneously.
3. Advanced Analytics¶
Add plugins for:
- Power flow calculations
- Network topology analysis
- Compliance checking
4. Visualization Generation¶
Generate diagrams and charts based on natural language requests:
User: "Show me a network diagram of all substations"
AI: Generates SVG/PNG diagram
5. Memory Persistence¶
Store conversation history in database for: - Session recovery - Audit trails - Learning from user patterns
Troubleshooting¶
Issue: "No document context set"¶
Cause: User asked a question without selecting a document.
Solution: Either: - Select document from dropdown before chatting - Ask with explicit document ID: "Query document ABC123..."
Issue: "Ollama connection failed"¶
Cause: Ollama is not running or wrong endpoint.
Solution:
- Start Ollama:
ollama serve - Pull model:
ollama pull qwen2.5:14b-instruct - Verify endpoint in
appsettings.json
Issue: "Function calls not working"¶
Cause: Model doesn't support function calling or wrong settings.
Solution:
- Use a function-calling capable model (qwen2.5, llama3, mistral)
- Ensure
FunctionChoiceBehavior.Auto()is set - Check model supports OpenAI function calling format
Issue: "Responses are generic, not using functions"¶
Cause: System prompt might be ignored or model temperature too high.
Solution:
- Verify system prompt includes function instructions
- Set temperature to 0.0 for deterministic function calling
- Add explicit instruction in user message: "Use the available functions to answer this"
References¶
- Microsoft Semantic Kernel Documentation
- Semantic Kernel GitHub Repository
- Ollama Documentation
- Function Calling with Ollama
- SignalR Documentation
Summary¶
The Semantic Kernel integration in GridLab.Gmss.Cim demonstrates how modern AI orchestration can transform complex domain-specific queries into natural conversations. By combining:
- Semantic Kernel for AI orchestration
- Ollama for local LLM execution
- Custom Plugins for domain-specific operations
- SignalR for real-time communication
We've created an intuitive interface that makes CIM document querying accessible to non-technical users while maintaining the full power and precision of structured queries under the hood.