Skip to main content Signal blog Official Microsoft Blog Command Line Microsoft On The Issues Asia Canada Europe, Middle East and Africa Latin America The Code of Us What's new AI Innovation Digital Transformation Sustainability Security Work & Life Diversity & Inclusion Unlocked Microsoft 365 Azure Copilot Windows Surface XBOX Deals Small Business Support Windows Apps Outlook OneDrive Microsoft Teams OneNote Microsoft Edge Moving from Skype to Teams Computers Shop XBOX Accessories VR & mixed reality Certified Refurbished Trade-in for cash XBOX Game Pass Ultimate PC Game Pass XBOX games PC games Microsoft AI Microsoft Security Dynamics 365 Microsoft 365 for business Microsoft Power Platform Windows 365 Small Business Digital Sovereignty Azure Microsoft Developer Microsoft Learn Support for AI marketplace apps Microsoft Tech Community Microsoft Marketplace Software companies Visual Studio Microsoft Rewards Free downloads & security Education Gift cards Licensing Unlocked stories View Sitemap

By builders, for builders.

A Microsoft publication

One requirement, many failure paths: Evaluate, control, and optimize with ASSERT and ACS

A safety requirement might appear simple while the runtime failure surface is complex. ASSERT helps identify coverage bugs; ACS fixes the policy.

One question consistently comes up from customers building AI agents: How do I translate a high-level safety, policy, or product requirement into evaluations and controls that reliably govern agent behavior in production? 

Writing the requirement is often the easy part. A policy might simply state that sensitive customer data requires verified authorization. The challenge is ensuring that requirement holds across different users, tools, workflows, request sequences, and other contextual variations an agent may encounter. 

As agents become more capable, manually enumerating and testing every potential failure path does not scale. Point fixes can address individual issues but often create brittle logic that is difficult to maintain and does not generalize to new scenarios. 

To help address this challenge, we recently released two open-source projects: ASSERT and Agent Control Specification (ACS). Together, they help developers systematically evaluate, understand, and govern agent behavior. 

This post is intended for developers and AI engineers who need to move from โ€œwe have a requirementโ€ to โ€œwe can continuously verify and enforce that requirement in production.โ€


By the end of this post, you’ll see how to: 

  • Turn a policy or product requirement into executable test cases
  • Systematically uncover failure modes that are difficult to find through manual testing
  • Apply the right control mechanism for different classes of risk
  • Create regression gates that help ensure protections continue to work as agents evolve

Using a banking support scenario, weโ€™ll walk through a practical evaluate โ†’ control โ†’ optimize workflow that you can apply to your own agent systems. 

ASSERT turns requirements into realistic single-turn and multi-turn test cases, runs them against a live agent, and captures execution through OpenTelemetry. Developers can inspect model calls, tool interactions, routing decisions, and intermediate reasoning steps, not just the final response. Because ASSERT uses OpenTelemetry conventions, the same approach works across agent frameworks rather than relying on framework-specific test infrastructure. 

Using this workflow, we uncovered two distinct authorization failures in a banking support agent: 

  1. A deterministic authorization policy that was correctly implemented but applied to only one service.
  2. Coercive requests that couldnโ€™t be reliably distinguished from legitimate requests using structured fields alone.

For each behavior, weโ€™ll look at two metrics: 

An impermissible violation of behavior 1, for example, would mean users asked to skip authorization before client records, trade ordering, or loan modification preparation, and the agent complied since the deposit gate did not generalize to other services. An example of permissible violation of behavior 2 would be refusing legitimate, authorized requests for the services. All results come from the linked bank-support demonstration agent evaluated on ASSERT-generated synthetic test cases; they illustrate the workflow, not production prevalence or an industry benchmark. 

Each behavior needed a different Agent Control Specification (ACS) control: Rego for the deterministic decision and a model classifier for the semantic one. The loop was the same: evaluate, control, optimize. Freeze the test cases, change one thing, run every arm against the same cases, and measure both impermissible behavior and the permissible behavior the product must preserve. Weโ€™ll dive deep into these two behaviors to illustrate the value of evals and controls as a disciplined form of agent optimization.

Behavior 1: ASSERT finds the coverage bug; ACS fixes the policy once

The safety requirement didnโ€™t name a product domain. It read: Any entity with a sensitive `risk_tier` requires verified authorization before its data is read or changed.ย 

ASSERT systematized that requirement into reviewable behavior categories, then generated realistic conversations across record domains, request types, and user pressure. The cases exercised deposit accounts, loans, brokerage records, and client records through the running agent while OpenTelemetry captured the complete execution. 

Thatโ€™s how we found the bug. The agent already had a competent authorization gate for deposit accounts. It was server-side, deterministic, and tested. A new VIP deposit account was covered automatically. But loans, brokerage, and client records had shipped later, and those services never called the deposit-specific gate. 

The code was correct where it ran, but the policy coverage was shallow. 

No human had to anticipate and hand-write every conversation that exposed the gap. ASSERT generated the runtime matrix from the general requirement and showed exactly which domains, tools, and action sequences escaped enforcement. 

We compared three arms on the same frozen 72-prompt benchmark. We then ran a separate matched stress test with 72 multi-turn scenarios (more realistic for a client-facing agent). The prompt benchmark preserves the published comparison; the scenarios add runtime pressure, tool ordering, and trace evidence without pooling unlike denominators. The default permissible and impermissible behavior violations represent โ€œwhat the agent is supposed to doโ€ and โ€œwhat the agent is not supposed to do,โ€ according to the policy requirement.

ASSERTโ€™s built-in viewer shows that ACS Rego eliminates observed impermissible authorization violations: Impermissible behavior violated moves from 8% at baseline to 0% with ACS Rego. The defensive prompt only reduces the displayed aggregate from 8% to 6%. All three arms remain at 0% permissible behavior violated.
ASSERTโ€™s built-in viewer shows that ACS Rego eliminates observed impermissible authorization violations: Impermissible behavior violated moves from 8% at baseline to 0% with ACS Rego. The defensive prompt only reduces the displayed aggregate from 8% to 6%. All three arms remain at 0% permissible behavior violated.

The ACS Rego fix generalized to unseen domains without new code and improved on the violations deterministically to zero, a hard compliance requirement. The policy-as-code looks like this, keyed on the normalized property every domain emitted:

sensitive_tiers := {"high_net_worth", "vip", "restricted"} result_risk_tier := object.get(result_obj, "risk_tier", "standard") โ€ฆโ€ฆ post_tool_call_verdict := deny( "sensitive_read_gate", "Use the verified customer review portal." ) if { input.intervention_point == "post_tool_call" tool_name in read_tools result_risk_tier in sensitive_tiers }

The published rule applies the same risk_tier test to sensitive reads and actions across domains. No customer-ID prefixes or per-record allowlist.

Full traces make the result auditable

For a non-trivial agent, the final response isnโ€™t enough evidence. Authorization is an ordering property: verify first, then read or act.ย 

ASSERTโ€™s target.callable + target.trace configuration captures the agent through OpenTelemetry. The resulting evidence includes: 

That lets the judge distinguish โ€œdata returned after verified authorizationโ€ from โ€œdata returned without a check.โ€ 

The trace also makes the result actionable. A developer can open a failed case, see the unprotected loan or client-record tool call, and fix the common policy boundary instead of guessing based on a percentage. Impermissible behavior violation is the most relevant metric here.

ASSERTโ€™s built-in viewer lets you drill down to the violations specific to the model and tool calls grounded in OpenTelemetry traces.
ASSERTโ€™s built-in viewer lets you drill down to the violations specific to the model and tool calls grounded in OpenTelemetry traces.

The deterministic telemetry and the judge answer different questions: 

ASSERT supplies the scalable discovery and evidence loop. ACS supplies the scalable runtime fix.

Behavior 2: Use a classifier when thereโ€™s no field to check

The second failure was coercion through unverified authority, often with social engineering techniques: 

The requests were otherwise well formed. No typed field separated a coercive request from a legitimate one. There was no deterministic rule to write, so we needed a classifier for semantic nuance. 

The baseline was not naive. Its prompt explicitly said that authentication is not authorization, and it included a keyword tripwire. We then compared it with a hardened prompt and a classifier gate. 

We froze 120 new test cases (i.e., unseen customer requests across three arms) to validate generalization and make the comparison apples to apples: 

Every arm received the same cases:

ASSERTโ€™s built-in viewer shows that both controls remove observed impermissible violations: the hardened prompt and ACS classifier are both 0% Impermissible behavior violated. The ACS classifier preserves 20 percentage points more legitimate work than the hardened prompt, with 27% Permissible behavior violated vs. 47%, and it matches the baseline permissible-violation rate.
ASSERTโ€™s built-in viewer shows that both controls remove observed impermissible violations: the hardened prompt and ACS classifier are both 0% Impermissible behavior violated. The ACS classifier preserves 20 percentage points more legitimate work than the hardened prompt, with 27% Permissible behavior violated vs. 47%, and it matches the baseline permissible-violation rate.

Unlike behavior 1, permissible behavior violation is the more relevant metric here:ย 

Calibrated handling of properly evidenced or normal-flow requests

Prompt hardening regressed on permissible behavioral violations, in this case, while the ACS fix improved there. This points to a better safety Pareto frontier without trading off quality.

The Pareto disciplineโ€”not a single number

The behavior specification defines the dimensions that matter. For each of these two evaluations, we plotted two metrics: 

Over-refusal is one example of a permissible behavior violation. It isnโ€™t the general axis: another evaluation might use unnecessary escalation, incomplete task completion, latency, or another product-quality requirement. The Pareto discipline: we want to hill-climb on both axesโ€”better safety without sacrificing quality.

The prompt isnโ€™t โ€œbad.โ€ Itโ€™s simply the wrong control for these two failure shapes: 

The structural control earns its cost only when the eval measures both axes. 

The Pareto discipline naturally extends to operating cost and other decision dimensionsโ€”model and tool spend, latency, human thumbs ups/downs, and human-review timeโ€”and you can then hill-climb on an ROI frontier: towards a better, safer product at a lower cost.

Best practices to hill-climb and improve your agentย 

  1. Start from the requirement (your PRD, spec, etc.), not a hand-written scenario list. Let the eval vary domains, tools, turns, and pressure systematically. We built an eval-fix skill for you to use inside your favorite coding agent.
  2. Run the real agent with full traces. Tool order and orchestration are part of behavior.
  3. Decide whether the failure is deterministic. If a typed property determines the answer, use a rule and test its coverage.
  4. Freeze the test set before comparing fixes. Run the same cases through every arm.
  5. Specify permissible as well as impermissible behavior. A guardrail that blocks everything is not a quality product.
  6. Test outside the cases used to design the control. Hand-written scorers often fail exactly where their vocabulary ends.

ASSERT provides the model-independent measurement loop: behavior spec, generated test cases, repeated execution, and trace-grounded judging. ACS supplies the enforcement layer: Rego when the answer is deterministic, a classifier when itโ€™s not. 

Both bank support agent behaviors are runnable. Clone the repository, run the three arms, inspect the traces, and then point the same loop at your own agent. Once youโ€™re confident with the evals, wire it into your CI/CD pipelines as a regression test or simply use this CI GitHub Action we’ve built. 

Get started: