-
Notifications
You must be signed in to change notification settings - Fork 50
feat: add Agent Governance Toolkit sample — policy enforcement for ADK agents #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
imran-siddique
wants to merge
4
commits into
google:main
Choose a base branch
from
imran-siddique:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
9a2d08b
feat: add Agent Governance Toolkit sample for ADK
imran-siddique 108df9c
feat: add GovernancePlugin implementation
imran-siddique ff5d84d
feat: add governance example main.py
imran-siddique 3876da4
feat: add example governance policy
imran-siddique File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # Agent Governance Toolkit — GovernancePlugin for Google ADK | ||
|
|
||
| A governance plugin for [Google ADK](https://github.com/google/adk-python) that enforces | ||
| policy-as-code rules before tool execution, verifies agent identity, and produces | ||
| tamper-evident audit trails. | ||
|
|
||
| Built on the [Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit) | ||
| (v3.2.0, 9,500+ tests, MIT licensed). | ||
|
|
||
| ## Features | ||
|
|
||
| - **Policy enforcement** — Evaluate YAML/OPA/Cedar policies before every tool call (<5ms) | ||
| - **Agent identity** — Zero-trust verification via Ed25519 + SPIFFE | ||
| - **Audit logging** — Merkle-chained tamper-evident action logs | ||
| - **Configurable** — Allow/deny/warn/require-approval actions per policy rules | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| pip install agentmesh-platform[server] | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ```python | ||
| from google.adk.agents import Agent | ||
| from governance_plugin import GovernancePlugin | ||
|
|
||
| governance = GovernancePlugin( | ||
| policy_dir="./policies", | ||
| agent_did="did:mesh:my-agent", | ||
| ) | ||
|
|
||
| agent = Agent( | ||
| name="governed-agent", | ||
| model="gemini-2.0-flash", | ||
| tools=[my_tool], | ||
| plugins=[governance], | ||
| ) | ||
| ``` | ||
|
|
||
| ## Policy Example (policies/default.yaml) | ||
|
|
||
| ```yaml | ||
| apiVersion: governance.toolkit/v1 | ||
| name: adk-agent-policy | ||
| rules: | ||
| - name: block-dangerous-tools | ||
| condition: "action in ['shell_exec', 'file_delete']" | ||
| action: deny | ||
| - name: rate-limit-api-calls | ||
| condition: "action == 'api_call'" | ||
| action: allow | ||
| limit: "100/hour" | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| The plugin hooks into ADK's `before_tool_call` lifecycle: | ||
|
|
||
| 1. **Before tool execution** — GovernancePlugin evaluates the tool name + arguments against loaded policies | ||
| 2. **Identity check** — Optionally verifies the calling agent's DID | ||
| 3. **Decision** — Allow, deny, warn, or require approval | ||
| 4. **Audit** — Logs the decision with Merkle hash chaining | ||
|
|
||
| ## Links | ||
|
|
||
| - [Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit) | ||
| - [Policy-as-Code Tutorial](https://github.com/microsoft/agent-governance-toolkit/tree/main/docs/tutorials/policy-as-code) | ||
| - [OWASP Agentic Compliance](https://github.com/microsoft/agent-governance-toolkit/blob/main/docs/OWASP-COMPLIANCE.md) |
125 changes: 125 additions & 0 deletions
125
contributing/samples/agent_governance/governance_plugin.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| # Copyright 2026 Microsoft Corporation | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why Microsoft? |
||
| # | ||
| # Licensed under the MIT License. | ||
| """ | ||
| GovernancePlugin for Google ADK. | ||
|
|
||
| Enforces policy-as-code rules before tool execution, verifies agent | ||
| identity, and produces tamper-evident audit trails using the Agent | ||
| Governance Toolkit (https://github.com/microsoft/agent-governance-toolkit). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class GovernancePlugin: | ||
| """ADK plugin that enforces governance policies before tool execution. | ||
|
|
||
| Usage:: | ||
|
|
||
| from governance_plugin import GovernancePlugin | ||
|
|
||
| governance = GovernancePlugin(policy_dir="./policies") | ||
| agent = Agent(name="my-agent", plugins=[governance]) | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| policy_dir: str | Path = "./policies", | ||
| agent_did: str = "did:mesh:adk-agent", | ||
| sponsor_email: str = "adk-operator@example.com", | ||
| default_action: str = "allow", | ||
| ) -> None: | ||
| self._policy_dir = Path(policy_dir) | ||
| self._agent_did = agent_did | ||
| self._sponsor_email = sponsor_email | ||
| self._default_action = default_action | ||
| self._engine = None | ||
| self._audit = None | ||
| self._setup() | ||
|
|
||
| def _setup(self) -> None: | ||
| """Initialize AGT policy engine and audit service.""" | ||
| try: | ||
| from agentmesh.governance.policy import PolicyEngine | ||
| from agentmesh.services.audit import AuditService | ||
|
|
||
| self._engine = PolicyEngine() | ||
| self._audit = AuditService() | ||
|
|
||
| if self._policy_dir.exists(): | ||
| for f in sorted(self._policy_dir.glob("*.yaml")): | ||
| try: | ||
| self._engine.load_yaml(f.read_text()) | ||
| logger.info("Loaded policy: %s", f.name) | ||
| except Exception as exc: | ||
| logger.warning("Skipped %s: %s", f.name, exc) | ||
|
|
||
| logger.info( | ||
| "GovernancePlugin initialized (agent=%s, policies=%s)", | ||
| self._agent_did, | ||
| self._policy_dir, | ||
| ) | ||
| except ImportError: | ||
| logger.warning( | ||
| "agentmesh-platform not installed. " | ||
| "Install with: pip install agentmesh-platform" | ||
| ) | ||
|
|
||
| def before_tool_call( | ||
| self, | ||
| tool_name: str, | ||
| args: dict[str, Any] | None = None, | ||
| **kwargs: Any, | ||
| ) -> dict[str, Any]: | ||
| """Evaluate governance policy before a tool call. | ||
|
|
||
| Returns: | ||
| Dict with 'allowed' (bool), 'decision', 'reason', and | ||
| 'audit_entry_id'. | ||
| """ | ||
| if self._engine is None: | ||
| return {"allowed": True, "decision": "allow", "reason": "AGT not installed"} | ||
|
|
||
| context = { | ||
| "action": tool_name, | ||
| "tool_args": args or {}, | ||
| **kwargs, | ||
| } | ||
|
|
||
| result = self._engine.evaluate( | ||
| agent_did=self._agent_did, | ||
| context=context, | ||
| ) | ||
|
|
||
| if self._audit: | ||
| entry = self._audit.log_policy_decision( | ||
| agent_did=self._agent_did, | ||
| action=tool_name, | ||
| decision=result.action, | ||
| policy_name=result.policy_name or "", | ||
| data={"tool_args": args or {}, "reason": result.reason}, | ||
| ) | ||
| audit_id = entry.entry_id | ||
| else: | ||
| audit_id = None | ||
|
|
||
| return { | ||
| "allowed": result.allowed, | ||
| "decision": result.action, | ||
| "reason": result.reason, | ||
| "matched_rule": result.matched_rule, | ||
| "audit_entry_id": audit_id, | ||
| } | ||
|
|
||
| def get_audit_summary(self) -> dict[str, Any]: | ||
| """Return audit service summary.""" | ||
| if self._audit: | ||
| return self._audit.summary() | ||
| return {"error": "Audit service not initialized"} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Copyright 2026 Microsoft Corporation | ||
| # | ||
| # Licensed under the MIT License. | ||
| """ | ||
| Example: Google ADK agent with Agent Governance Toolkit policy enforcement. | ||
|
|
||
| Demonstrates: | ||
| 1. Loading YAML governance policies | ||
| 2. Evaluating policies before tool calls | ||
| 3. Producing tamper-evident audit trails | ||
| """ | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| from google.adk.agents import Agent | ||
| from governance_plugin import GovernancePlugin | ||
|
|
||
|
|
||
| def create_governed_agent() -> Agent: | ||
| """Create an ADK agent with governance controls.""" | ||
| governance = GovernancePlugin( | ||
| policy_dir=Path(__file__).parent / "policies", | ||
| agent_did="did:mesh:adk-demo-agent", | ||
| ) | ||
|
|
||
| # Example: check policy before a tool call | ||
| result = governance.before_tool_call( | ||
| tool_name="web_search", | ||
| args={"query": "latest AI safety research"}, | ||
| ) | ||
| print(f"Policy decision: {result['decision']} — {result['reason']}") | ||
|
|
||
| # Check audit trail | ||
| summary = governance.get_audit_summary() | ||
| print(f"Audit: {summary['total_entries']} entries, chain valid: {summary['chain_valid']}") | ||
|
|
||
| return Agent( | ||
| name="governed-research-agent", | ||
| model="gemini-2.0-flash", | ||
| instruction="You are a research assistant with governance controls.", | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| agent = create_governed_agent() | ||
| print(f"Agent '{agent.name}' created with governance enabled.") |
25 changes: 25 additions & 0 deletions
25
contributing/samples/agent_governance/policies/default.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| apiVersion: governance.toolkit/v1 | ||
| name: adk-demo-policy | ||
| description: Example governance policy for Google ADK agents | ||
| rules: | ||
| - name: block-shell-execution | ||
| condition: "action in ['shell_exec', 'code_exec', 'file_delete']" | ||
| action: deny | ||
| description: Block dangerous system-level tool calls | ||
| priority: 100 | ||
|
|
||
| - name: rate-limit-api-calls | ||
| condition: "action == 'api_call'" | ||
| action: allow | ||
| limit: "100/hour" | ||
| description: Rate limit external API calls | ||
| priority: 50 | ||
|
|
||
| - name: require-approval-for-payments | ||
| condition: "action == 'process_payment'" | ||
| action: require_approval | ||
| approvers: ["admin@example.com"] | ||
| description: Payment actions require human approval | ||
| priority: 90 | ||
|
|
||
| default_action: allow |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can have a plugins/ folder under src/google/adk_community to host the plugin