Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions python/semantic_kernel/agents/audit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.

"""Audit and compliance module for Semantic Kernel agents."""

from semantic_kernel.agents.audit.agent_audit_handler import AgentAuditHandler

__all__ = ["AgentAuditHandler"]

128 changes: 128 additions & 0 deletions python/semantic_kernel/agents/audit/agent_audit_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright (c) Microsoft. All rights reserved.

"""Agent Audit Handler for EU AI Act compliance."""

import logging
from typing import Any, Optional
Comment on lines +5 to +6

logger = logging.getLogger(__name__)


class AgentAuditHandler:
"""
Agent Audit Handler for recording and tracking AI agent actions for compliance purposes.

This handler provides EU AI Act compliance by maintaining an immutable audit trail
of all agent actions, enabling incident reporting and risk documentation.

Attributes:
service_url: The AgentAudit AI service endpoint for audit logging
api_key: Authentication credentials for the audit service
enabled: Whether audit logging is enabled
"""

def __init__(
self,
service_url: Optional[str] = None,
api_key: Optional[str] = None,
enabled: bool = True,
) -> None:
"""
Initialize the AgentAuditHandler.

Args:
service_url: The AgentAudit AI service endpoint URL
api_key: API key for authentication with the audit service
enabled: Whether to enable audit logging (default: True)
"""
self.service_url = service_url
self.api_key = api_key
self.enabled = enabled
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: api_key is stored as a plain str attribute, which means it can leak via repr(), __dict__, serialization, or accidental logging of this object. The rest of the SK Python codebase (e.g., openai_assistant_agent.py) uses Pydantic's SecretStr to wrap API keys. For a compliance-oriented module this is especially important—store as SecretStr and call .get_secret_value() only when transmitting to the service.

Suggested change
self.enabled = enabled
self.api_key: SecretStr | None = SecretStr(api_key) if api_key else None

self._validated = False

async def initialize(self) -> None:
"""Initialize and validate the audit handler connection."""
if not self.enabled:
logger.info("Agent audit logging is disabled")
return

if not self.service_url:
raise ValueError("service_url is required when audit logging is enabled")

if not self.api_key:
raise ValueError("api_key is required when audit logging is enabled")

logger.info(f"Initializing AgentAuditHandler with service: {self.service_url}")
self._validated = True
Comment on lines +55 to +56
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service_url is logged at INFO level. If a user supplies a URL with embedded credentials or tokens as query parameters (e.g., https://service.example.com?token=secret), those will be written to application logs. Consider logging only the scheme+host or omitting the URL entirely.

Suggested change
self._validated = True
logger.info("AgentAuditHandler initialized successfully")


async def log_agent_action(
self,
agent_name: str,
action_type: str,
action_details: dict[str, Any],
timestamp: Optional[str] = None,
) -> None:
Comment on lines +58 to +64
"""
Log an agent action to the audit trail.

Args:
agent_name: Name of the agent performing the action
action_type: Type of action being performed
action_details: Detailed information about the action
timestamp: ISO 8601 formatted timestamp (defaults to current time)

Raises:
RuntimeError: If audit logging is not initialized
"""
if not self.enabled:
return

if not self._validated:
raise RuntimeError("AgentAuditHandler not initialized. Call initialize() first.")

audit_record = {
"agent_name": agent_name,
"action_type": action_type,
"action_details": action_details,
"timestamp": timestamp,
}

logger.debug(f"Recording audit entry: {audit_record}")
Comment on lines +86 to +90

# TODO: Implement actual audit logging to AgentAudit AI service
# This will send the audit record to the service for blockchain anchoring
# and compliance report generation

async def generate_compliance_report(self) -> dict[str, Any]:
"""
Generate an EU AI Act compliance report based on audit logs.

Returns:
Dictionary containing compliance report data

Raises:
RuntimeError: If audit logging is not initialized
"""
if not self.enabled:
return {"compliance_enabled": False}

if not self._validated:
raise RuntimeError("AgentAuditHandler not initialized. Call initialize() first.")

# TODO: Implement compliance report generation
# This will aggregate audit logs and generate:
# - Article 12 (Transparency) documentation
# - Article 73 (Incident reporting) summaries
# - Articles 9, 11 (Risk documentation) assessments

return {
"status": "pending_implementation",
"agent_audit_handler_initialized": True,
}

async def close(self) -> None:
"""Close the audit handler and cleanup resources."""
if self._validated:
logger.info("Closing AgentAuditHandler")
self._validated = False

Loading