Skip to content

.NET: Add IDecisionClient (experimental), DecisionLoopEvaluator, and the Microsoft.Agents.AI.TypeSafe (Jev) provider - #8563

Open
Jose Luis Latorre Millas (joslat) wants to merge 6 commits into
microsoft:mainfrom
joslat:joslat/adr-0042-decision-loop-evaluator
Open

Jose Luis Latorre Millas (joslat) wants to merge 6 commits into
microsoft:mainfrom
joslat:joslat/adr-0042-decision-loop-evaluator

Conversation

@joslat

@joslat Jose Luis Latorre Millas (joslat) commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

Several decision points in the .NET runtime are bounded judgments, not text generation: should a loop continue, has the task been completed, which authorized tools or Skills are relevant, which participant acts next, should a result be escalated. A new category of model answers exactly that shape: it evaluates state against typed questions and returns probabilities directly. TypeSafe AI's Jev is the first such model, and it is fast and cheap enough to run on every loop iteration.

Today the only way to make such a judgment is a generative IChatClient judge (AIJudgeLoopEvaluator), which costs a full model call per iteration and flattens the probability into generated JSON. The provider-neutral abstraction proposed for Microsoft.Extensions.AI in dotnet/extensions#7764 is untriaged and absent from the MEAI version this repository references, so waiting on it blocks the loop integration, provider work, and evaluation frameworks that want one shared contract. This PR makes decision-model inference usable now while keeping Core provider-neutral and keeping the eventual move to MEAI a deletion rather than a migration. Design and alternatives are recorded in ADR-0042.

Description & Review Guide

  • What are the major changes?
    • Microsoft.Agents.AI.Abstractions/Decisions/: an [Experimental(MAAI001)] reference copy of the [API Proposal]: Add a provider-neutral abstraction for decision-oriented AI models dotnet/extensions#7764 contract, name for name: IDecisionClient, DecisionRequest (JsonElement state, unique question ids), DecisionOptions, BinaryDecisionQuestion / ChoiceDecisionQuestion / ScoreDecisionQuestion, DecisionResponse, the three answer kinds with range validation, DecisionClientMetadata, and a JsonTypeInfo<TState> convenience overload. Two additions beyond the proposal: DecisionClientException with a DecisionFailureKind and IsTransient, and answer validation. Provider limits are not part of the contract.
    • Microsoft.Agents.AI/Harness/Loop/DecisionLoopEvaluator: a LoopEvaluator that asks one binary question per iteration, applies a completion threshold (default 0.90), continues with deterministic feedback, rejects non-text request content unless a StateFactory is supplied, and handles provider failure through FailureBehavior (Throw by default, Continue, DeferToNextEvaluator) plus a separate TransientFailureBehavior. Optional ILoggerFactory logs one debug line per decision and a warning when a failure policy is applied; the state is never logged. Ordered before AIJudgeLoopEvaluator it forms a cheap-then-strong cascade using existing LoopAgent semantics, with no new abstraction.
    • Microsoft.Agents.AI.TypeSafe: TypeSafeDecisionClient over TypeSafe's System One API or OpenRouter's relay; strict parsing, HTTP failure classification, no silent retries, API key never in exception messages, feature index 75 registered.
    • Harness_Step06_DecisionLoop sample (decision-only loop with a cumulative StateFactory, Jev-then-AI-judge cascade, direct mixed batch of all three primitives), Harness README, solution registration, PublicAPI baselines for every target framework, unit tests for the contract consumer and the provider.
  • What is the impact of these changes?
    • Additive and opt-in. No existing agent, evaluator, tool, approval, or workflow behavior changes unless a DecisionLoopEvaluator or IDecisionClient is configured. AIJudgeLoopEvaluator is untouched. Core and Abstractions gain no vendor dependency; the TypeSafe transport is a separate preview package following the existing provider-package precedent.
    • The Abstractions types are a staging ground: when MEAI ships the abstraction they are deleted and consumers retarget the namespace; the shape is the upstream proposal's so that stays mechanical.
    • Verified live against Jev jev-1.13.0 with an OpenAI-compatible primary model: the cascade finished a three-part task in four iterations with a single generative judge call; a mixed batch returned a binary probability, a choice distribution, and a weighted score in one call.
  • What do you want reviewers to focus on?
    • Whether an experimental reference copy of the MEAI proposal is acceptable in Microsoft.Agents.AI.Abstractions (ADR-0042, Decisions 1 and 2), or should live in a satellite package.
    • The failure-policy split: general FailureBehavior defaults to Throw like AIJudgeLoopEvaluator; TransientFailureBehavior covers rate limits, overload and outages (.NET: [Feature]: Integrate Microsoft.Extensions.AI decision-model inference into agent decision points #8545 asked for "continue" as the default).
    • The Microsoft.Agents.AI.TypeSafe package as the provider's home versus a provider-owned package.
    • Security framing in the XML docs and sample README: a second external inference boundary, minimal state projection, probabilities never used for authorization or approval.

Related Issue

Fixes #8562

Related: #8545 (.NET decision-model integration request, MEAI-gated), #8556 (Python Jev request), dotnet/extensions#7764 (MEAI proposal this contract mirrors). No other open PR addresses these issues.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

…sion-model inference

Adds a provider-neutral decision-model contract to Microsoft.Agents.AI.Abstractions
as an experimental reference implementation of the shape proposed in
dotnet/extensions#7764: IDecisionClient, DecisionRequest/Options/Response, the
binary, choice and score question and answer kinds, DecisionClientMetadata, a
JsonTypeInfo-based convenience overload, and a classified DecisionClientException
with IsTransient. Answers validate their probability ranges at construction.

ADR-0042 records the decision: MEAI is the long-term owner; MAF hosts a verbatim,
experimental copy until it ships so the loop evaluator, evaluation frameworks and
providers share one contract, and removes it when MEAI lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion

Adds a LoopEvaluator that asks an IDecisionClient one binary question ("has the
agent fully addressed the original request?") against a minimal, source-generated
text projection of the loop state and applies a configurable completion threshold.
It complements AIJudgeLoopEvaluator: a decision model reports a probability, not
prose, so continuation carries deterministic feedback. Ordered before an AI judge
it forms a cheap-then-strong cascade using existing LoopAgent semantics.

Provider failure is never treated as "incomplete": FailureBehavior (Throw by
default, Continue, DeferToNextEvaluator) and a separate TransientFailureBehavior
for rate limits, overload and outages. Non-text request content is rejected
unless a StateFactory is supplied. Optional ILoggerFactory logs one debug line per
decision and a warning when a failure policy is applied, never the state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n loop sample

Adds TypeSafeDecisionClient, an IDecisionClient over TypeSafe's System One API
(or OpenRouter's relay of it), mapping binary/choice/score questions to
noul/choice/score with strict parsing, HTTP failure classification, no silent
retries and no API key in exception messages. Provider limits (255 choices, 10
levels) are enforced in the transport, not the contract. Allocates feature index
75 (typesafe) in the .NET feature-usage registry.

Adds the Harness_Step06_DecisionLoop sample: a decision-only loop with a
cumulative StateFactory, a Jev-then-AI-judge cascade, and a direct mixed batch of
all three primitives, against OpenAI or any OpenAI-compatible endpoint.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 20, 2026 19:56
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net labels Sep 20, 2026

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Credential routing, logging exposure, protocol validation, public invariants, and ADR ownership must be corrected before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 6 Medium severity · 2 Low severity

Open (9)
What changed in this PR

Adds experimental decision-model inference to .NET Agent Framework through a provider-neutral abstraction, loop evaluator, and TypeSafe Jev provider.

Changes:

  • Introduces IDecisionClient, typed questions/answers, failure classification, and public API baselines.
  • Adds DecisionLoopEvaluator with configurable thresholds, failure policies, logging, and tests.
  • Adds the TypeSafe provider, feature registration, ADR, and demonstration sample.
File Description
dotnet/​tests/​Microsoft.Agents.AI.TypeSafe.UnitTests/​TypeSafeProtocolTests.cs Tests protocol serialization, parsing, and failure mapping.
dotnet/​tests/​Microsoft.Agents.AI.TypeSafe.UnitTests/​TypeSafeDecisionClientTests.cs Tests client construction, HTTP behavior, and services.
dotnet/​tests/​Microsoft.Agents.AI.TypeSafe.UnitTests/​Microsoft.Agents.AI.TypeSafe.UnitTests.csproj Defines the TypeSafe test project.
dotnet/​tests/​Microsoft.Agents.AI.FeatureRegistry.UnitTests/​FeatureRegistryTests.cs Registers expected TypeSafe feature ownership.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​netstandard2.0/​PublicAPI.Unshipped.txt Baselines evaluator APIs for netstandard2.0.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net9.0/​PublicAPI.Unshipped.txt Baselines evaluator APIs for net9.0.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net8.0/​PublicAPI.Unshipped.txt Baselines evaluator APIs for net8.0.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net472/​PublicAPI.Unshipped.txt Baselines evaluator APIs for net472.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net10.0/​PublicAPI.Unshipped.txt Baselines evaluator APIs for net10.0.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​LoopJsonContext.cs Registers the decision-loop state serializer.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​LoopEvaluator.cs Documents the new evaluator.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​LoopAgent.cs Documents decision/judge cascading.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​DecisionLoopState.cs Defines the default state projection.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​DecisionLoopFailureBehavior.cs Defines evaluator failure policies.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​DecisionLoopEvaluatorOptions.cs Exposes evaluator configuration.
dotnet/​src/​Microsoft.Agents.AI/​Harness/​Loop/​DecisionLoopEvaluator.cs Implements decision-backed loop evaluation.
dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeProtocol.cs Implements the System One wire protocol.
dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeDecisionClientOptions.cs Defines endpoint, model, and timeout options.
dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeDecisionClient.cs Implements the TypeSafe HTTP client.
dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​Microsoft.Agents.AI.TypeSafe.csproj Defines the preview provider package.
dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​FeatureIndex.cs Records TypeSafe feature usage.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​PublicAPI/​netstandard2.0/​PublicAPI.Unshipped.txt Baselines decision APIs for netstandard2.0.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​PublicAPI/​net9.0/​PublicAPI.Unshipped.txt Baselines decision APIs for net9.0.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​PublicAPI/​net8.0/​PublicAPI.Unshipped.txt Baselines decision APIs for net8.0.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​PublicAPI/​net472/​PublicAPI.Unshipped.txt Baselines decision APIs for net472.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​PublicAPI/​net10.0/​PublicAPI.Unshipped.txt Baselines decision APIs for net10.0.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​ScoreDecisionQuestion.cs Defines ordered score questions.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​IDecisionClient.cs Defines the client abstraction.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionResponse.cs Defines decision responses.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionRequest.cs Defines state and question requests.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionQuestion.cs Defines the question base type.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionOptions.cs Defines per-call options.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionClientMetadata.cs Defines provider metadata.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionClientExtensions.cs Adds typed-state and service helpers.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionClientException.cs Defines classified client failures.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionAnswer.cs Defines binary, choice, and score answers.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​ChoiceDecisionQuestion.cs Defines categorical questions.
dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​BinaryDecisionQuestion.cs Defines binary questions and criteria.
dotnet/​samples/​02-agents/​Harness/​README.md Lists the new sample and data boundary.
dotnet/​samples/​02-agents/​Harness/​Harness_Step06_DecisionLoop/​README.md Documents sample patterns and security.
dotnet/​samples/​02-agents/​Harness/​Harness_Step06_DecisionLoop/​Program.cs Demonstrates loops, cascading, and batching.
dotnet/​samples/​02-agents/​Harness/​Harness_Step06_DecisionLoop/​Harness_Step06_DecisionLoop.csproj Defines sample dependencies.
dotnet/​samples/​02-agents/​Harness/​Harness_Step06_DecisionLoop/​ConsoleObservingDecisionClient.cs Adds console decision observation.
dotnet/​agent-framework-dotnet.slnx Registers provider, tests, and sample.
docs/​specs/​feature-usage-bit-registry.md Allocates feature index 75.
docs/​decisions/​0042-decision-model-inference.md Records the architectural decision.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread dotnet/samples/02-agents/Harness/Harness_Step06_DecisionLoop/Program.cs Outdated
Comment thread dotnet/src/Microsoft.Agents.AI.Abstractions/Decisions/DecisionAnswer.cs Outdated
Comment thread dotnet/src/Microsoft.Agents.AI.TypeSafe/TypeSafeDecisionClient.cs
Comment thread dotnet/src/Microsoft.Agents.AI.TypeSafe/TypeSafeProtocol.cs
Comment thread dotnet/src/Microsoft.Agents.AI.TypeSafe/TypeSafeProtocol.cs Outdated
Comment thread dotnet/src/Microsoft.Agents.AI.TypeSafe/TypeSafeProtocol.cs Outdated
Comment thread docs/decisions/0042-decision-model-inference.md Outdated
Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Loop/DecisionLoopEvaluatorOptions.cs Outdated
- Sample: the chat API key is bound to the chosen endpoint; a credential for
  one service is never sent to the other.
- Answers: distributions supplied at construction are validated (non-blank
  keys, unit probabilities); docs state the collection stays mutable to match
  the proposed MEAI shape.
- TypeSafe client: a blank per-call ModelId override is rejected before sending.
- TypeSafe protocol: choice and score questions are revalidated at call time
  (at least two members, unique non-blank names, no nulls) since their
  collections are mutable; a selected choice must be one of the requested
  choices, not merely present in the returned map; a score outside 0..N-1 is
  rejected.
- DecisionLoopEvaluator: the failure warning logs only the exception type,
  kind, status and transience, never the exception message.
- DecisionLoopEvaluatorOptions.CompletionQuestion is string? (null restores
  the default); PublicAPI baselines updated.
- ADR-0042: deciders set to the .NET Harness/Loop owners (maintainers may adjust).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Provider validation, endpoint handling, usage parsing, and the cascade sample contain correctness issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (9)
Previously missed (5)

In code that hasn't changed since last review

Medium severity Instruct agent to recap answers after final question

dotnet/​samples/​02-agents/​Harness/​Harness_Step06_DecisionLoop/​Program.cs:218

This prompt never permits the complete recap that pattern 2 depends on: after the three one-question responses, it still says to answer exactly one unanswered question even though none remain. With the evaluator's latest-response-only projection, the cascade can therefore run to MaxIterations instead of demonstrating a successful final verification. Explicitly instruct the agent to recap all answers once no questions remain.

Medium severity Reject unsupported state JSON value kinds locally

dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeProtocol.cs:45

System One accepts state only as a JSON string, object, or array, but this converts Undefined to null and also sends Null, number, and boolean states. Those requests are guaranteed provider validation failures; reject unsupported ValueKinds locally as InvalidRequest instead.

Medium severity Require non-negative Int64 token counts

dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeProtocol.cs:445

Token counts are integer fields, but this fallback truncates fractional JSON numbers (for example, 12.9 becomes 12) and accepts negative values, producing inaccurate usage/cost accounting. Require a non-negative Int64 and classify anything else as InvalidResponse.

Low severity Complete the phrase with “when one was returned”

dotnet/​src/​Microsoft.Agents.AI.Abstractions/​Decisions/​DecisionClientException.cs:77

Correct the incomplete phrase to “when one was returned.”

Low severity Clarify state-size limits remain provider-enforced

dotnet/​src/​Microsoft.Agents.AI.TypeSafe/​TypeSafeDecisionClient.cs:25

This claims every listed provider limit is rejected before sending, but the implementation validates only choice and score cardinality; it does not enforce the stated state-token limit. Clarify that state-size limits remain provider-enforced so callers are not promised local rejection.

This issue also appears on line 75 of the same file.

Comment thread docs/decisions/0042-decision-model-inference.md Outdated
The Probabilities collections on choice and score answers stay mutable to match
the proposed MEAI shape, so the ADR no longer claims an out-of-range value
cannot exist as an object; it states what is validated and who owns the
invariant afterwards, matching the API documentation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@joslat

Copy link
Copy Markdown
Contributor Author

westey (@westey-m) Roger Barreto (@rogerbarreto) — you own the Harness/Loop code this builds on, so I'd value your look at this PR together with ADR-0042 (docs/decisions/0042-decision-model-inference.md); the code is the implementation of that ADR.

Pure move, no behavior or public API change: BinaryDecisionCriteria,
DecisionChoice, DecisionScoreLevel, DecisionFailureKind, and the three answer
types get their own files, matching the surrounding folders and the file
layout of the Microsoft.Extensions.AI staging branch so the two trees diff
file for file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mo3in

Copy link
Copy Markdown

I think TypeSafe provider is belongs to Microsoft.Extensions.Ai and in MAF we should use only IDecisionClient from MEAI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: .NET: [Feature]: Add IDecisionClient (reference of dotnet/extensions#7764), DecisionLoopEvaluator, and a TypeSafe (Jev) provider package

3 participants