Govern a Foundry multi-agent code review
Customer scenario
Fabrikam uses specialized AI agents to review customer code. A security reviewer identifies vulnerabilities, a fairness auditor checks whether equivalent technology stacks receive consistent outcomes, and a governance orchestrator combines their evidence. Because the recommendation can delay a deployment, Fabrikam must screen the request, minimize every handoff, preserve attribution, and require human review when a policy threshold is exceeded.
Lab scenario
You will deploy a Microsoft Foundry project, a model with Foundry-native guardrails, and workspace-based Application Insights by using Bicep. You will then complete a deterministic policy gate and run a three-agent governance workflow:
- Foundry applies its default safety guardrails to model prompts and completions.
- The security-reviewer receives only the source fragment and external evidence reference needed for its task.
- The fairness-auditor receives only calculated group rates, disparity, and the policy threshold.
- The governance-orchestrator receives minimized specialist results and the deterministic policy decision.
- The application records response IDs, hashes, policy metadata, and the human-review result without logging raw source code, tenant identity, prompts, or hidden reasoning.
By the end of this exercise, you will be able to:
- Identify where Foundry-native guardrails protect model prompts and completions.
- Measure fairness disparity with paired synthetic probes.
- Restrict each agent handoff to purpose-specific data.
- Preserve agent attribution with Foundry response IDs.
- Query minimized accountability evidence in Application Insights.
Important: The model deployment, Log Analytics, and Application Insights are billable. Use only the supplied synthetic data and delete the resources after validation.
Task 1: Prepare the lab
-
Install Python 3.10+, Azure CLI, Azure Developer CLI, Visual Studio Code, and the VS Code Python and Bicep extensions.
-
Use an Azure identity that can create Microsoft Foundry, model deployment, and monitoring resources and can assign roles.
-
Select a region where the required model is available.
-
Do not use customer code, personal data, credentials, or secrets.
-
If you haven't already done so, clone the lab repository:
git clone https://github.com/MicrosoftLearning/mslearn-ai-multi-agents.git
- Open the repository in Visual Studio Code.
- In a PowerShell terminal, go to the lab directory and verify the tools and active subscription:
cd Allfiles\11-fabrikam-responsible-ai-governance
az version
azd version
python --version
az account show --output table
Architecture checkpoint
Review these components before editing:
| Component | What to locate |
|---|---|
assets/governance-scenario.json and policy/governance-policy.yaml |
Synthetic inputs, fairness threshold, and human-review policy |
src/governance.py |
Fairness calculations, minimized payloads, identifier hashing, and evidence |
src/main.py |
Deterministic local orchestration and the --live Foundry path |
kql/governance-evidence.kql |
Minimized accountability evidence |
infra/main.bicep |
Foundry, model, monitoring, and least-privilege role assignments |
Before continuing, confirm that the default workflow is local and deterministic and that only the --live path invokes Foundry agents.
Task 2: Build the virtual environment
- On Windows, run:
./scripts/setup.ps1
. ./.venv/Scripts/Activate.ps1
On macOS/Linux, run
bash scripts/setup.shandsource .venv/bin/activateinstead.
Task 3: Deploy Azure resources
- Set the deployment values.
- Replace the model name and version if they are not available in your approved region.
Resource group: If your lab environment provides a precreated resource group, set
$resourceGroupNameto its name. Otherwise, leave$resourceGroupNameempty so the script creates a unique resource group in your subscription.
- Run the following commands:
$azureRegion = 'eastus2'
$resourceGroupName = ''
$modelDeploymentName = 'gpt-5.4-mini'
$modelName = 'gpt-5.4-mini'
$modelVersion = '2026-03-17'
$principalId = az ad signed-in-user show --query id --output tsv
if ([string]::IsNullOrWhiteSpace($resourceGroupName)) {
$resourceGroupName = "rg-lab11-$((New-Guid).Guid.Substring(0, 8))"
az group create --name $resourceGroupName --location $azureRegion | Out-Null
}
az bicep build --file infra/main.bicep
azd env new lab11-rai-governance
azd env set AZURE_LOCATION $azureRegion
azd env set AZURE_RESOURCE_GROUP $resourceGroupName
azd env set FOUNDRY_MODEL_NAME $modelDeploymentName
azd env set FOUNDRY_MODEL_CATALOG_NAME $modelName
azd env set FOUNDRY_MODEL_VERSION $modelVersion
azd env set AZURE_PRINCIPAL_ID $principalId
azd provision
azd env get-values | Out-File .env -Encoding utf8
Note: If
azd env newreports that the environment exists, select another environment name or use the existing environment. If provisioning fails, inspect the first deployment error. Model availability, quota, Azure Policy, and role-assignment permissions are common causes.
- Confirm that the generated
.envcontains identifiers, endpoints, and an Application Insights connection string; it contains no model keys or tokens.DefaultAzureCredentialuses your Azure sign-in.
Network access for this lab: The Bicep template enables the Foundry account's native public network access and sets the default network action to Allow so the local application can reach the project endpoint. Microsoft Entra authentication and Azure RBAC are still required. After deployment, confirm these settings on the Foundry account Networking page. Production environments should use an approved selected-network or private-endpoint design.
Task 4: Implement the policy gate
Each placeholder marks incomplete code. Copy each supplied snippet into its placeholder location, keep the LAB PLACEHOLDER comment, replace only the indicated incomplete line or block, and preserve the surrounding indentation.
# LAB PLACEHOLDER 1: Replace this line with the Task 1 sample.
raise NotImplementedError("Complete requires_human_review in Task 1")
- Replace only the
raise NotImplementedError(...)line beneath the retained marker with this implementation, preserving indentation:
fairness = evidence.get("fairness")
if not isinstance(fairness, dict) or "max_disparity" not in fairness:
return True
try:
return (
float(fairness["max_disparity"])
> float(policy["thresholds"]["max_fairness_disparity"])
)
except (KeyError, TypeError, ValueError):
return True
This decision is fail-closed: incomplete or malformed evidence requires human review. Thresholds come from the versioned policy instead of being duplicated in code.
- Check the completed code:
& .\.venv\Scripts\python.exe -m py_compile src/main.py src/governance.py scripts/preflight.py
& .\.venv\Scripts\python.exe scripts/preflight.py
Select-String -Path src/governance.py -Pattern 'NotImplementedError'
Preflight must show PASS for the Foundry project, model, and Application Insights configuration checks and end with READY (Azure configured). The final command must return no matches.
Inspect the privacy boundaries
- Open
policy/governance-policy.yamland compareagent_inputswithbuild_agent_payloadinsrc/governance.py.
| Agent | Allowed input | Deliberately excluded |
|---|---|---|
security-reviewer |
Request ID, synthetic source fragment, CWE reference | Tenant identity, fairness probes, policy internals |
fairness-auditor |
Request ID, group rates, disparity, threshold | Source code, tenant identity, security output |
governance-orchestrator |
Request ID, policy version, control results, specialist results | Raw source code, tenant identity, hidden reasoning |
- Check that the application doesn't record raw prompts or hidden reasoning:
Select-String -Path src/governance.py,src/main.py -Pattern 'chain_of_thought|raw_prompt'
The command must return no matches.
- Locate the fields used for hashed values and Foundry agent attribution:
Select-String -Path src/governance.py -Pattern 'tenant_id_hash|input_sha256|agent_response_ids'
The command should show that raw values are replaced by hashes and that Foundry response IDs provide agent attribution.
Task 5: Run the solution
Run the deterministic workflow
- Run the workflow without Azure calls:
& .\.venv\Scripts\python.exe -m src.main
The output must show:
modeequal todeterministic.- Python positive-outcome rate
1.0and Node positive-outcome rate0.5. - Maximum fairness disparity
0.5. human_review_requiredequal totruebecause0.5exceeds the policy threshold of0.1.- Three local response IDs, one for each agent role.
- Purpose-specific field names under
payloadFields.
- Inspect the latest evidence record:
$evidence = Get-Content evidence/governance-evidence.jsonl | Select-Object -Last 1 | ConvertFrom-Json
$evidence | ConvertTo-Json -Depth 10
- Confirm that it includes
evidence_scopeequal tosingle_process_local_jsonl, the policy ID and version, fairness rates, hashes, human-review result, and three response IDs. It must not include the raw tenant value or source fragment fromassets/governance-scenario.json.
The JSONL file is local, single-process exercise evidence. It is not a concurrency-safe or centralized production evidence store; use Application Insights or another governed central sink when multiple processes can write.
Run the live multi-agent workflow
The live command creates a version of each Foundry prompt agent and invokes them in this order. Foundry's native guardrails evaluate the prompts and completions inline:
security-reviewerfairness-auditorgovernance-orchestrator
- Run it once:
& .\.venv\Scripts\python.exe -m src.main --live
The command prints each agent's responseId and text, appends one minimized local evidence record, and emits one fabrikam.governance.evidence trace to Application Insights. Azure Monitor telemetry is configured once per Python process and reused for subsequent emissions in that process. The model can vary its wording, but the fairness calculation and human-review decision remain deterministic.
Note: Role assignments can take several minutes to propagate. If the first live run returns an authorization error, wait briefly, sign in again with
az loginif needed, and rerun the command.
Task 6: Validate the implementation
Validate in Microsoft Foundry
- Open Microsoft Foundry and select the project named
lab11-<environment-name>. - In the left navigation, select Agents.
- Confirm that
security-reviewer,fairness-auditor, andgovernance-orchestratorexist and each has a version. - Review each agent's instructions.
- Confirm that its role is narrow and that the orchestrator is instructed not to request raw source, tenant identity, or hidden reasoning.
- At the top of the Agents page, select Traces. There is no separate Observability navigation item in the current Foundry portal.
- Search by Response ID using an ID from
agentResponsesin the terminal output oragent_response_idsin the latest local evidence record. - Open each matching trace and compare its response ID with the corresponding security reviewer, fairness auditor, or governance orchestrator response.
- Inspect the inputs.
- Confirm that only the security reviewer receives
sourceCode; the fairness auditor and governance orchestrator do not.
Note: If Traces isn't visible or a trace can't be opened, confirm that you selected the correct project, completed a live run, and allowed time for telemetry and role assignments to propagate. The Bicep template assigns the learner
Log Analytics Readeron the connected Application Insights resource, which Microsoft Foundry requires for viewing trace data.
Review Foundry-native guardrails
This lab doesn't deploy a standalone Azure AI Content Safety resource. The model deployment uses the default guardrails that Foundry applies to prompts and completions.
- In the Foundry project, select Build in the top menu, and then select Models.
- Open the deployment named
gpt-5.4-mini. - Review the deployment's guardrail or content-filter setting.
- Confirm that the deployment uses Foundry's default safety policy and isn't configured with an unfiltered custom policy.
- Return to Agents and review the latest traces.
- Confirm that each successful specialist and orchestrator response came from the guarded model deployment.
Default guardrails block supported prompt or completion risks above their configured threshold. Use only the supplied neutral synthetic scenario; don't introduce harmful test content.
Test the fairness boundary
- Change only the final
node-bprobe inassets/governance-scenario.jsonfrom0to1. - Run:
& .\.venv\Scripts\python.exe -m src.main
$updated = Get-Content evidence/governance-evidence.jsonl | Select-Object -Last 1 | ConvertFrom-Json
$updated.fairness | ConvertTo-Json -Depth 5
$updated.human_review_required
Both rates should now be 1.0, disparity should be 0.0, and human review should be false because no fairness threshold is exceeded.
- Restore
node-bto0before continuing.
Query accountability evidence
- Wait several minutes for the live trace to reach Application Insights.
- In the Azure portal, open the Application Insights resource whose name starts with
appi-lab11-. - Select Logs.
- Switch to KQL mode if needed.
- Run the contents of
kql/governance-evidence.kql.
The result should show policy version 1.0.0, at least one review and escalation, maximum disparity 0.5, and the serialized response IDs for the three Foundry agents.
- Run this privacy query and confirm it returns zero rows:
AppTraces
| where Message == "fabrikam.governance.evidence"
| where Properties has_any ("tenant_id", "source_code", "raw_prompt", "chain_of_thought")
- Capture the following as the governance evidence set:
- The Foundry model deployment's default guardrail assignment.
- Fairness rates, disparity, and human-review decision.
- The three Foundry agent versions and matching response IDs.
- Trace inputs demonstrating purpose-specific data minimization.
- The Application Insights policy summary and zero-row privacy result.
Optional challenge: Trigger human review
Add a synthetic fairness probe that crosses a configured governance threshold.
Expected output: The release gate changes to human review required and records both the measured value and policy threshold.
Failure investigation: Remove one required evidence field and confirm that the policy gate fails closed with a specific missing-evidence reason.
Task 7: Review the design
- Answer these questions:
- Why is the human-review gate deterministic instead of delegated to the orchestrator agent?
- How could bias compound if the fairness auditor received a security review framed with developer metadata?
- Which additional controls would be required before processing real customer source code?
- When should an audit store retain full specialist output instead of response IDs and minimized policy evidence?
Task 8: Clean up
- Delete the Azure resources:
azd down --purge
- If you used a precreated resource group, verify which resources the command will remove before confirming.
- Delete local evidence and environment values:
Remove-Item evidence/governance-evidence.jsonl -ErrorAction SilentlyContinue
Remove-Item .env -ErrorAction SilentlyContinue
- Deactivate the virtual environment in every terminal where
(.venv)appears:
deactivate
Summary
You deployed a Bicep-defined Microsoft Foundry project and governed a three-agent code review with native model guardrails, paired fairness probes, minimized handoffs, attributable response IDs, deterministic human oversight, and queryable Application Insights evidence.