Semantic Kernel + AxonFlow Integration
What Semantic Kernel Does Well
Semantic Kernel is Microsoft's SDK for building AI agents and copilots—used in production across Microsoft's own products. Its strengths are substantial:
Plugin Architecture: Modular, composable functions that can be orchestrated together. Native and semantic functions combine naturally.
Enterprise-Ready Design: Built for production from day one. Strongly typed, with proper error handling and observability hooks.
Multi-Language Support: Native SDKs for C#, Python, and Java. First-class support across Microsoft's ecosystem.
Planner Capabilities: Automatic orchestration of plugins to achieve goals. The AI decides which functions to call and in what order.
Memory Systems: Persistent memory with vector store integrations. Context preservation across sessions.
Azure Integration: Deep integration with Azure OpenAI, Azure AI Search, and other Azure services. Enterprise SSO and compliance built in.
What Semantic Kernel Doesn't Try to Solve
Semantic Kernel focuses on AI orchestration and plugin composition. These concerns are explicitly out of scope:
| Production Requirement | Semantic Kernel's Position |
|---|---|
| Policy enforcement before plugin execution | Not provided—plugins execute based on planner logic, not policies |
| PII detection in plugin inputs/outputs | Not addressed—data flows through plugins without filtering |
| SQL injection prevention | Not provided—must implement at plugin level |
| Per-user or per-plugin cost attribution | Not tracked—requires external monitoring |
| Audit trails for compliance | Not built in—must implement logging externally |
| Cross-plugin access control | Not addressed—any plugin can be invoked by the planner |
| Token budget enforcement | Not provided—planners can consume unlimited tokens |
This isn't a criticism—it's a design choice. Semantic Kernel handles orchestration. Governance is a separate concern.
Where Teams Hit Production Friction
Based on real enterprise deployments, here are the blockers that appear after the prototype works:
1. The Planner Gone Wild
A planner decides to use a database plugin, then a file plugin, then the database again. Each step is valid. 500 iterations later, the planner is still "planning".
Semantic Kernel executed the planner correctly. Nothing was watching how much it was planning.
2. The "What Plugins Ran?" Question
A user complains about an incorrect AI response. Support needs:
- Which plugins were invoked?
- In what order?
- What inputs did each receive?
- What was the planner's reasoning?
Semantic Kernel orchestrated the plugins. Without custom logging, the execution trace is gone.
3. The PII in Plugin Output
A summarization plugin processes customer feedback. The output includes customer names and email addresses. This output feeds into a reporting plugin.
Now PII is in your reports. Semantic Kernel has no mechanism to filter sensitive data between plugins.
4. The Security Review Block
Security review: BLOCKED
- No audit trail for plugin invocations
- PII can flow through plugin chains
- No policy enforcement before plugin execution
- Cost controls missing
- Access control for plugins not implemented
The copilot worked perfectly. It can't ship.
5. The Unauthorized Database Access
A planner decides a user's query requires a database plugin. The user shouldn't have database access. Semantic Kernel invokes the plugin—there's no permission check at the AI layer.
How AxonFlow Plugs In
AxonFlow doesn't replace Semantic Kernel. It sits underneath it—providing the governance layer that Semantic Kernel intentionally doesn't include:
┌─────────────────┐
│ Your App │
└────────┬────────┘
│
v
┌─────────────────┐
│ Semantic Kernel │ <-- Plugins, Planners, Memory
└────────┬────────┘
│
v
┌─────────────────────────────────┐
│ AxonFlow │
│ ┌───────────┐ ┌────────────┐ │
│ │ Policy │ │ Audit │ │
│ │ Enforce │ │ Trail │ │
│ └───────────┘ └────────────┘ │
│ ┌───────────┐ ┌────────────┐ │
│ │ PII │ │ Cost │ │
│ │ Detection│ │ Control │ │
│ └───────────┘ └────────────┘ │
└────────────────┬────────────────┘
│
v
┌─────────────────┐
│ LLM Provider │
└─────────────────┘
What this gives you:
- Every plugin invocation logged with input/output and user context
- PII detected and blocked before flowing between plugins
- SQL injection attempts blocked in data-processing plugins
- Cost tracked per plugin, per user, per planner execution
- Compliance auditors can query the full plugin execution chain
What stays the same:
- Your Semantic Kernel code doesn't change
- Plugin definitions work as before
- No new abstractions to learn
Integration Patterns
Pattern 1: Governed Kernel (Java)
Wrap Semantic Kernel with AxonFlow governance:
package com.example.semantickernel;
import com.getaxonflow.sdk.AxonFlow;
import com.getaxonflow.sdk.AxonFlowConfig;
import com.getaxonflow.sdk.PolicyApprovalResult;
import com.getaxonflow.sdk.TokenUsage;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class GovernedKernel {
private final AxonFlow axonflow;
private final Map<String, Function<String, String>> plugins = new HashMap<>();
private final String userToken;
public GovernedKernel(String userToken) {
this.userToken = userToken;
this.axonflow = AxonFlow.create(AxonFlowConfig.builder()
.agentUrl(System.getenv("AXONFLOW_AGENT_URL"))
.clientId("semantic-kernel-java")
.clientSecret(System.getenv("AXONFLOW_CLIENT_SECRET"))
.build());
}
public void registerPlugin(String name, Function<String, String> execute) {
plugins.put(name, execute);
}
public PluginResult invokePlugin(String pluginName, String input) throws Exception {
long startTime = System.currentTimeMillis();
Map<String, Object> context = new HashMap<>();
context.put("plugin", pluginName);
context.put("framework", "semantic-kernel");
// 1. Policy check
PolicyApprovalResult approval = axonflow.getPolicyApprovedContext(
userToken, input, context
);
if (!approval.isApproved()) {
return new PluginResult(false, null, approval.getBlockReason());
}
// 2. Execute plugin
Function<String, String> plugin = plugins.get(pluginName);
if (plugin == null) {
throw new IllegalArgumentException("Plugin not found: " + pluginName);
}
String result = plugin.apply(input);
long latencyMs = System.currentTimeMillis() - startTime;
// 3. Audit
axonflow.auditLLMCall(
approval.getContextId(),
result.substring(0, Math.min(200, result.length())),
"semantic-kernel",
pluginName,
TokenUsage.of(50, 100, 150),
latencyMs
);
return new PluginResult(true, result, null);
}
public record PluginResult(boolean success, String result, String blockReason) {}
}
// Usage
GovernedKernel kernel = new GovernedKernel("user-123");
kernel.registerPlugin("summarize", text -> "Summary: " + text.substring(0, 100) + "...");
kernel.registerPlugin("translate", text -> "Translated: " + text);
PluginResult result = kernel.invokePlugin("summarize", "Long document text...");
if (result.success()) {
System.out.println(result.result());
} else {
System.out.println("Blocked: " + result.blockReason());
}
Pattern 2: Governed Kernel (TypeScript)
import { AxonFlow } from "@axonflow/sdk";
interface PluginResult {
success: boolean;
result?: string;
blockReason?: string;
}
type PluginFunction = (input: string) => Promise<string>;
class GovernedKernel {
private axonflow: AxonFlow;
private plugins: Map<string, PluginFunction> = new Map();
private userToken: string;
constructor(userToken: string) {
this.userToken = userToken;
this.axonflow = new AxonFlow({
endpoint: process.env.AXONFLOW_AGENT_URL!,
tenant: "semantic-kernel-ts",
});
}
registerPlugin(name: string, execute: PluginFunction): void {
this.plugins.set(name, execute);
}
async invokePlugin(pluginName: string, input: string): Promise<PluginResult> {
const startTime = Date.now();
// 1. Policy check
const approval = await this.axonflow.getPolicyApprovedContext({
userToken: this.userToken,
query: input,
context: {
plugin: pluginName,
framework: "semantic-kernel",
},
});
if (!approval.approved) {
return { success: false, blockReason: approval.blockReason };
}
// 2. Execute plugin
const plugin = this.plugins.get(pluginName);
if (!plugin) {
throw new Error(`Plugin not found: ${pluginName}`);
}
const result = await plugin(input);
const latencyMs = Date.now() - startTime;
// 3. Audit
await this.axonflow.auditLLMCall({
contextId: approval.contextId,
responseSummary: result.slice(0, 200),
provider: "semantic-kernel",
model: pluginName,
tokenUsage: { promptTokens: 50, completionTokens: 100, totalTokens: 150 },
latencyMs,
});
return { success: true, result };
}
}
// Usage
const kernel = new GovernedKernel("user-123");
kernel.registerPlugin("summarize", async (text) =>
`Summary: ${text.substring(0, 100)}...`
);
const result = await kernel.invokePlugin("summarize", "Long document...");
Pattern 3: Multi-Step Plans with Governance
Execute multi-step plans with policy checks at each step:
interface PlanStep {
plugin: string;
input: string;
}
class GovernedPlanner {
private kernel: GovernedKernel;
constructor(kernel: GovernedKernel) {
this.kernel = kernel;
}
async executePlan(steps: PlanStep[]): Promise<PluginResult[]> {
const results: PluginResult[] = [];
for (const step of steps) {
const result = await this.kernel.invokePlugin(step.plugin, step.input);
results.push(result);
if (!result.success) {
console.log(`Plan halted at step ${steps.indexOf(step)}: ${result.blockReason}`);
break;
}
}
return results;
}
}
// Execute a governed plan
const planner = new GovernedPlanner(kernel);
const results = await planner.executePlan([
{ plugin: "search", input: "Find renewable energy data" },
{ plugin: "analyze", input: "Analyze the data" },
{ plugin: "summarize", input: "Create a summary" },
]);
Example Implementations
| Language | SDK | Example |
|---|---|---|
| Java | axonflow-sdk | semantic-kernel/java |
| TypeScript | @axonflow/sdk | semantic-kernel/typescript |