Microsoft Agent Framework Integration (AevumMAFMiddleware)¶
Three middleware classes integrate Aevum governance into the Microsoft Agent
Framework (MAF) pipeline. A convenience factory, AevumMAFMiddleware, returns
all three as a list for direct use with Agent(middleware=...).
What it captures¶
Based on packages/aevum-core/src/aevum/core/adapters/maf.py,
verified against agent-framework 1.6.0:
| Class | MAF base | Trigger | Cedar check | Sigchain record |
|---|---|---|---|---|
AevumAgentMiddleware |
AgentMiddleware |
Agent run start/end | No | agent.start, agent.end (if kernel) |
AevumFunctionMiddleware |
FunctionMiddleware |
Every tool/function call | Yes | tool.start, tool.end (if kernel); raises MiddlewareTermination on deny |
AevumChatMiddleware |
ChatMiddleware |
Every LLM API call | No | llm.call (if kernel) |
All process(context, call_next) methods are async.
MAF dispatches by isinstance — inheritance from the MAF base classes is
required (not duck typing). When agent-framework is not installed, the
module is importable but the base classes are replaced with no-op stubs.
Deny path (AevumFunctionMiddleware)¶
On Cedar deny:
1. context.result is set to {"error": "...", "aevum_denied": True, "tool_name": "<name>"}
2. MiddlewareTermination is raised
3. call_next is NOT called — the tool/function does not execute
Cedar fail-open: if Cedar evaluation raises an exception, a warning is logged
and call_next is called (execution continues).
Installation¶
Requires agent-framework>=1.0.0. Verified against agent-framework 1.6.0.
Note: agent-framework 1.6.0 installs approximately 159 transitive
dependencies, including the Azure SDK. Use a dedicated virtual environment
for MAF deployments to avoid version conflicts with other packages.
Minimal example¶
from aevum.core.adapters.maf import AevumMAFMiddleware
# kernel=None: Cedar evaluation only, no sigchain writes.
# Pass an aevum.core.Engine instance to enable full recording.
# from agent_framework import Agent
# from openai import AsyncAzureOpenAI
#
# client = AsyncAzureOpenAI(...)
# agent = Agent(
# client=client,
# name="assistant",
# middleware=AevumMAFMiddleware(kernel=None),
# )
# AevumMAFMiddleware returns:
# [AevumAgentMiddleware(kernel), AevumFunctionMiddleware(kernel), AevumChatMiddleware(kernel)]
With a kernel for full sigchain recording:
from aevum.core import Engine
from aevum.core.adapters.maf import AevumMAFMiddleware
kernel = Engine()
# from agent_framework import Agent
# agent = Agent(
# client=client,
# name="assistant",
# middleware=AevumMAFMiddleware(kernel=kernel),
# )
Using individual middleware classes¶
If you need only one middleware, import the class directly:
from aevum.core.adapters.maf import (
AevumAgentMiddleware,
AevumFunctionMiddleware,
AevumChatMiddleware,
)
# agent = Agent(
# client=client,
# middleware=[AevumFunctionMiddleware(kernel=kernel)],
# )
Middleware order¶
AevumMAFMiddleware returns middleware in this order:
1. AevumAgentMiddleware — outermost, wraps the full agent run
2. AevumFunctionMiddleware — gates tool calls with Cedar
3. AevumChatMiddleware — innermost, observes raw LLM calls
Cedar context (AevumFunctionMiddleware)¶
principal: AevumMAFAgent / maf-agent
action: tool_call
resource: ToolAction / <function.name>
context: {args_hash: sha256(str(context.arguments))}
Tool name is sourced from context.function.name; if absent, "unknown" is used.
Constructor reference¶
All three classes accept the same constructor:
| Argument | Type | Default | Description |
|---|---|---|---|
kernel |
Engine \| None |
None |
Aevum kernel; if None, sigchain writes are skipped |