Skip to content

test(model-onboarding): probe the model as an LLM-as-judge guardrail - #1039

Open
denispetre wants to merge 2 commits into
fix/model-onboarding-false-passfrom
feat/model-onboarding-judge-guardrail
Open

test(model-onboarding): probe the model as an LLM-as-judge guardrail#1039
denispetre wants to merge 2 commits into
fix/model-onboarding-false-passfrom
feat/model-onboarding-judge-guardrail

Conversation

@denispetre

Copy link
Copy Markdown
Contributor

Adds a third probe that puts the model under test in the judge role: a ReAct agent runs behind a real UiPathLLMAsJudgeMiddleware guardrail whose judge model is the one being onboarded, and the guardrail decides whether the agent's answer broke a natural-language rule.

Important

Stacked on #1022 — base is fix/model-onboarding-false-pass, not main. Merge #1022 first; this diff is judge-only (+261, 5 files, no deletions).

Needs an ALPHA_TEST_PAT secret to actually run in CI. Without it the probe skips (see below), so merging this changes nothing until the secret exists.

Two probes, not one

One probe cannot tell a working judge from a broken one:

  • violating — steered into breaking the rule; the judge must block
  • compliant — stays inside the rule; the judge must stay quiet

A judge that always blocks fails the second. One that never blocks fails the first. Only a judge that discriminates passes both.

Sampled, because a single sample is a coin toss

The judge is itself a model call. An earlier single-shot version of this probe reported the compliant answer blocked in 3 of 6 end-to-end runs — flaky enough to be useless as a gate.

Each probe now runs SAMPLES times and is decided by majority, and the observed counts are always reported, passing or failing:

judge_guardrail: ✓ judge discriminated (violating blocked 3/3, compliant allowed 3/3)

so a marginal judge shows up as a number rather than an intermittent red build.

Threshold chosen by measurement, not intuition

I had assumed the default threshold was too strict and planned to raise it. That was wrong, and measuring showed the opposite — 20 samples per setting against gpt-5.2 on alpha:

threshold violating blocked compliant allowed
2.0 (middleware default) 10/10 10/10
4.0 10/10 7/10

On this scale higher is more lenient, so raising it only cost specificity. The default stands, and the table is in the source so nobody re-tunes it the wrong way.

Skipped without a PAT — the CI path stays green

The validator lives on agentsruntime_, which the client-credentials app cannot reach: the OAuth resource catalog has no agentsruntime entry, so an S2S token comes back 401. Rather than fail every existing dispatch, the cell skips:

judge_guardrail: – skipped (no UIPATH_PAT; needs user identity)

Verified with UIPATH_PAT unset: the cell records the skip and does not fail the run.

run.sh swaps a supplied PAT into .env when one is present. It has to go in the file, not the environment — the CLI loads .env with override=True, so an exported UIPATH_ACCESS_TOKEN loses to whatever uipath auth just wrote and the PAT would be silently ignored. That was verified in isolation.

Verification

End-to-end against alpha, post-rebase:

openai:responses:
  build: ✓ UiPathAzureChatOpenAI
  image: ✓ purple
  pdf: ✓ PDF-CODE-74915
  judge_guardrail: ✓ judge discriminated (violating blocked 3/3, compliant allowed 3/3)

Both paths exercised: with a user token (3/3 on each probe, 2 runs) and with UIPATH_PAT unset (skips, stays green).

Caveats I have not resolved

  • An unexplained discrepancy. Yesterday the compliant probe was wrongly blocked 3/6 at threshold 2.0; today the same threshold measured 16/16 correct (10 harness + 6 end-to-end). Either something changed server-side or yesterday was an unlucky streak. I could not reproduce the failure today, so the majority-vote sampling is there as insurance rather than a proven cure — treat "3/3" as evidence, not a guarantee.
  • X-UiPath-Guardrails-Source is required by the endpoint and comes from the execution context, so the guardrail only works inside a uipath run. Calling the agent directly returns 400. Fine here; a real constraint if this moves.
  • Cost: SAMPLES=3 means 6 agent+guardrail round-trips per flavor. Onboarding runs are on-demand, so I judged that acceptable; drop to 1 if it drags.

🤖 Generated with Claude Code

Adds a third probe putting the model under test in the *judge* role: a
ReAct agent runs behind a real UiPathLLMAsJudgeMiddleware guardrail whose
judge model is the one being onboarded, and the guardrail decides whether
the agent's answer broke a natural-language rule.

Two probes, because one cannot tell a working judge from a broken one:
a violating prompt the judge must block, and a compliant one it must let
through. A judge that always blocks fails the second; one that never
blocks fails the first.

Sampled, not single-shot. The judge is a model call, so one sample is a
coin toss on a borderline verdict — an earlier single-sample version
reported the compliant answer blocked in 3 of 6 end-to-end runs. Each
probe now runs SAMPLES times and is decided by majority, and the observed
counts are always reported:

  judge_guardrail: ✓ judge discriminated (violating blocked 3/3, compliant allowed 3/3)

so a marginal judge is visible rather than intermittently red.

Threshold stays at the middleware default of 2.0, chosen by measurement
rather than intuition — 20 samples per setting against gpt-5.2 on alpha:

  threshold  violating blocked  compliant allowed
  2.0        10/10              10/10
  4.0        10/10               7/10

Raising it only cost specificity. The table is in the source so nobody
"tunes" it the wrong way later.

Skipped without a PAT. The validator lives on `agentsruntime_`, which the
client-credentials app cannot reach — the OAuth resource catalog has no
`agentsruntime` entry, so an S2S token comes back 401. Verified: with
UIPATH_PAT unset the cell records "– skipped" and does not fail the run,
so the existing CI path stays green; run.sh swaps a supplied PAT into
.env (it must go in the file, not the environment — the CLI loads .env
with override=True, so an exported token loses to what `uipath auth`
just wrote).

Requires an ALPHA_TEST_PAT secret to actually run in CI.

Verified end-to-end against alpha: 2 runs, both 3/3 on each probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 18, 2026 15:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

… detection

Review findings on the judge_guardrail probe, fixed:

- create_agent evaluates AGENT-scope llm_as_judge guardrails at BOTH PRE
  (the incoming user message) and POST (the answer); the middleware's
  stage=POST only affects its own middleware instances, which the probe
  never used. Reword the rule to judge "the text" so the compliant
  *request* no longer reads as a violation at PRE — the likely cause of
  the intermittent wrongly-blocked compliant probe — and state the
  dual-stage reality (and the up-to-12-judge-call cost) in the comments.
- Build the guardrail on the public seam generated coded agents use
  (AgentBuiltInValidatorGuardrail + build_guardrails_with_actions)
  instead of reaching into middleware._guardrail; drops the dual
  BlockAction aliases and the dead stage/action arguments.
- Count a block only on exact code match AND category USER: the
  guardrail node raises the same TERMINATION_GUARDRAIL_VIOLATION code
  with category DEPLOYMENT for feature-disabled/missing-entitlement
  outcomes, which previously registered as blocks (a false pass on the
  violating probe, a misdiagnosis on the compliant one).
- Carry the offending answers/block reasons into the AssertionError so
  a red run is debuggable without re-running under a PAT.
- Replace the loop-tail `continue` PAT skip with if/else so a probe
  appended later still runs without a PAT; match sibling cell style.
- run.sh: uv run python for the heredoc; document that the PAT becomes
  the ambient token for the whole run (least-privilege note, also in
  the workflow and README); README section for the probe and its skip.

Rule text and wiring changed, so the threshold table and 3/3 runs in
the PR description were measured against the previous wording —
re-verify end-to-end on alpha with a PAT before relying on the gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants