This release is a pre-release and may not be stable for production use.
Microsoft Agent Framework – Purview Integration (Python)
agent-framework-purview adds Microsoft Purview (Microsoft Graph dataSecurityAndGovernance) policy evaluation to the Microsoft Agent Framework. It lets you enforce data security / governance policies on both the prompt (user input + conversation history) and the model response before they proceed further in your workflow.
Status: Preview
Key Features
- Middleware-based policy enforcement (agent-level and chat-client level)
- Blocks or allows content at both ingress (prompt) and egress (response)
- Works with any
ChatAgent/ agent orchestration using the standard Agent Framework middleware pipeline - Supports both synchronous
TokenCredentialandAsyncTokenCredentialfromazure-identity - Simple, typed configuration via
PurviewSettings/PurviewAppLocation - Two middleware types:
PurviewPolicyMiddleware(Agent pipeline)PurviewChatPolicyMiddleware(Chat client middleware list)
When to Use
Add Purview when you need to:
- Prevent sensitive or disallowed content from being sent to an LLM
- Prevent model output containing disallowed data from leaving the system
- Apply centrally managed policies without rewriting agent logic
Quick Start
import asyncio
from agent_framework import ChatAgent, ChatMessage, Role
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.microsoft import PurviewPolicyMiddleware, PurviewSettings
from azure.identity import InteractiveBrowserCredential
async def main():
chat_client = AzureOpenAIChatClient() # uses environment for endpoint + deployment
purview_middleware = PurviewPolicyMiddleware(
credential=InteractiveBrowserCredential(),
settings=PurviewSettings(app_name="My Sample App")
)
agent = ChatAgent(
chat_client=chat_client,
instructions="You are a helpful assistant.",
middleware=[purview_middleware]
)
response = await agent.run(ChatMessage(role=Role.USER, text="Summarize zero trust in one sentence."))
print(response)
asyncio.run(main())
If a policy violation is detected on the prompt, the middleware terminates the run and substitutes a system message: "Prompt blocked by policy". If on the response, the result becomes "Response blocked by policy".
Authentication
PurviewClient uses the azure-identity library for token acquisition. You can use any TokenCredential or AsyncTokenCredential implementation.
The APIs require the following Graph Permissions:
- ProtectionScopes.Compute.All : (userProtectionScopeContainer)[https://learn.microsoft.com/en-us/graph/api/userprotectionscopecontainer-compute]
- Content.Process.All : (processContent)[https://learn.microsoft.com/en-us/graph/api/userdatasecurityandgovernance-processcontent]
- ContentActivity.Write : (contentActivity)[https://learn.microsoft.com/en-us/graph/api/activitiescontainer-post-contentactivities]
Scopes
PurviewSettings.get_scopes() derives the Graph scope list (currently https://graph.microsoft.com/.default style).
Tenant Enablement for Purview
- The tenant requires an e5 license and consumptive billing setup.
- There need to be (Data Loss Prevention)[https://learn.microsoft.com/en-us/purview/dlp-create-deploy-policy] or (Data Collection Policies)[https://learn.microsoft.com/en-us/purview/collection-policies-policy-reference] that apply to the user to call Process Content API else it calls Content Activities API for auditing the message.
Configuration
PurviewSettings
PurviewSettings(
app_name="My App", # Display / logical name
tenant_id=None, # Optional – used mainly for auth context
purview_app_location=None, # Optional PurviewAppLocation for scoping
graph_base_uri="https://graph.microsoft.com/v1.0/",
process_inline=False, # Reserved for future inline processing optimizations
blocked_prompt_message="Prompt blocked by policy", # Custom message for blocked prompts
blocked_response_message="Response blocked by policy" # Custom message for blocked responses
)
To scope evaluation by location (application, URL, or domain):
from agent_framework.microsoft import (
PurviewAppLocation,
PurviewLocationType,
PurviewSettings,
)
settings = PurviewSettings(
app_name="Contoso Support",
purview_app_location=PurviewAppLocation(
location_type=PurviewLocationType.APPLICATION,
location_value="<app-client-id>"
)
)
Customizing Blocked Messages
By default, when Purview blocks a prompt or response, the middleware returns a generic system message. You can customize these messages by providing your own text in the PurviewSettings:
from agent_framework.microsoft import PurviewSettings
settings = PurviewSettings(
app_name="My App",
blocked_prompt_message="Your request contains content that violates our policies. Please rephrase and try again.",
blocked_response_message="The response was blocked due to policy restrictions. Please contact support if you need assistance."
)
This is useful for:
- Providing more user-friendly error messages
- Including support contact information
- Localizing messages for different languages
- Adding branding or specific guidance for your application
Selecting Agent vs Chat Middleware
Use the agent middleware when you already have / want the full agent pipeline:
from agent_framework import ChatAgent
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.microsoft import PurviewPolicyMiddleware, PurviewSettings
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
client = AzureOpenAIChatClient()
agent = ChatAgent(
chat_client=client,
instructions="You are helpful.",
middleware=[PurviewPolicyMiddleware(credential, PurviewSettings(app_name="My App"))]
)
Use the chat middleware when you attach directly to a chat client (e.g. minimal agent shell or custom orchestration):
import os
from agent_framework import ChatAgent
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.microsoft import PurviewChatPolicyMiddleware, PurviewSettings
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
chat_client = AzureOpenAIChatClient(
deployment_name=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
credential=credential,
middleware=[
PurviewChatPolicyMiddleware(credential, PurviewSettings(app_name="My App (Chat)"))
],
)
agent = ChatAgent(chat_client=chat_client, instructions="You are helpful.")
The policy logic is identical; the difference is only the hook point in the pipeline.
Middleware Lifecycle
- Before agent execution (
prompt phase): allcontext.messagesare evaluated. - If blocked:
context.resultis replaced with a system message andcontext.terminate = True. - After successful agent execution (
response phase): the produced messages are evaluated. - If blocked: result messages are replaced with a blocking notice.
When a user identifier is discovered (e.g. in ChatMessage.additional_properties['user_id']) during the prompt phase it is reused for the response phase so both evaluations map consistently to the same user.
You can customize the blocking messages using the blocked_prompt_message and blocked_response_message fields in PurviewSettings. For more advanced scenarios, you can wrap the middleware or post-process context.result in later middleware.
Exceptions
| Exception | Scenario |
|---|---|
PurviewAuthenticationError |
Token acquisition / validation issues |
PurviewRateLimitError |
429 responses from service |
PurviewRequestError |
4xx client errors (bad input, unauthorized, forbidden) |
PurviewServiceError |
5xx or unexpected service errors |
Catch broadly if you want unified fallback:
from agent_framework.microsoft import (
PurviewAuthenticationError, PurviewRateLimitError,
PurviewRequestError, PurviewServiceError
)
try:
...
except (PurviewAuthenticationError, PurviewRateLimitError, PurviewRequestError, PurviewServiceError) as ex:
# Log / degrade gracefully
print(f"Purview enforcement skipped: {ex}")
Notes
- Provide a
user_idper request (e.g. inChatMessage(..., additional_properties={"user_id": "<guid>"})) when possible for per-user policy scoping; otherwise supply a default via settings or environment. - Blocking messages can be customized via
blocked_prompt_messageandblocked_response_messageinPurviewSettings. By default, they are "Prompt blocked by policy" and "Response blocked by policy" respectively. - Streaming responses: post-response policy evaluation presently applies only to non-streaming chat responses.
- Errors during policy checks are logged and do not fail the run; they degrade gracefully.
Release files for agent-framework-purview 1.0.0b251016
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_framework_purview-1.0.0b251016.tar.gz | 29.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_framework_purview-1.0.0b251016-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.9 kB
Release files / agent_framework_purview-1.0.0b251016.tar.gz
| Download URL | agent_framework_purview-1.0.0b251016.tar.gz |
|---|---|
| Size | 29.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6cb7ac962e42a5c75d9fd8dcca020fb8dd6321c020028e834d01adc6be2ee053
|
|
BLAKE2b-256 checksum How to use checksums |
4917a37aaaa69281d4453edffc3bdc2b38afc62ed2dadc8b94a339d53beb37f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.23
|
Release files / agent_framework_purview-1.0.0b251016-py3-none-any.whl
| Download URL | agent_framework_purview-1.0.0b251016-py3-none-any.whl |
|---|---|
| Size | 20.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fa5be4ce7738e60792668d65c67e2a8e915017248cff1a40bf5ec5d2c6860635
|
|
BLAKE2b-256 checksum How to use checksums |
e667f8ed2d7d87c228d4d6ac0f6fd6e4cf458d82df80306cde088a37ff02cd49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.23
|