Document extraction is relatively straightforward when every source has a known structure and a predictable role. The problem gets harder when multiple documents contain overlapping information, and the workflow needs to decide which records are primary, which provide supporting evidence, and which shouldn’t contribute to the final result.
Hardcoding every possible combination into deterministic rules becomes difficult to maintain. Sending every ambiguous case to a general-purpose reasoning model introduces another trade-off in cost, latency, and complexity.
Microsoft-Decision-1 offers another option; use a model designed for bounded decisions when the workflow needs to choose among defined outcomes.
In this article, I’ll walk through how to integrate Decision-1 into a document processing pipeline, including the decision configuration, evidence supplied to the model, acceptance thresholds, and safeguards that prevent uncertain classifications from silently changing the result.
Reference Implementation: The complete codebase, infrastructure templates, and deployment guide are available on my GitHub: jonathanscholtes/Azure-AI-Foundry-Remittance-Intelligence.
Start with the Workflow
The pipeline combines deterministic extraction, AI-powered document understanding, and decision logic. Each component has a defined responsibility:
- Deterministic readers parse structured tables and must prove themselves: every row has to parse, and the rows have to sum exactly to the table’s own Total row.
- Content Understanding in Microsoft Foundry extracts candidate fields and records from everything else, and each line is checked against the source text.
- Decision-1 classifies source roles when two or more sources read by Content Understanding return candidate records and the rules can’t settle how they relate.
- Validation and reconciliation check values, totals, and consistency before data moves downstream.
- Microsoft Foundry agents and human review handle cases that require investigation or judgment beyond the available classifications.
- Orchestration and monitoring coordinate execution and track outcomes, failures, and service usage.
Extraction produces candidate data and preserves its source context. It doesn’t determine which information belongs in the final result.
That distinction matters when several documents contain overlapping records. One source might provide the primary information, another might corroborate it, and a third might simply summarize information already present elsewhere.
In the reference implementation, those documents are ACH remittance emails. A customer pays by ACH and sends the detail separately: a table in the email body, a PDF or spreadsheet attachment, a bank transfer confirmation, sometimes a statement, and a thread of replies restating the totals. Several of them can carry the same references and amounts.
Where Content Understanding Fits
Content Understanding in Microsoft Foundry turns document content into structured information the workflow can process. It extracts candidate fields and records from documents whose content or layout needs deeper analysis than a deterministic reader can provide. Every line it returns must be found in the source text before the workflow accepts it, so a reference paired with the wrong amount is flagged rather than accepted.
The deterministic readers and Content Understanding feed the same downstream workflow. When several sources contain candidate records, however, extraction alone doesn’t reveal the role each one plays.
That’s where Decision-1 fits in this implementation. It classifies the sources so the workflow can apply its own selection rules before validating the resulting data.
The Escalation Path
The workflow doesn’t call Decision-1 for every document set. After extraction, it checks whether at least two sources read by Content Understanding returned candidate records. Sources the deterministic readers handled don’t count, because they’ve already proven themselves against their own totals. If not, classification is unnecessary and the pipeline proceeds directly to validation. Otherwise, it calls Decision-1 and evaluates the returned classifications against the configured acceptance policy.

If classification fails or the results don’t meet that policy, the workflow leaves the candidate records unchanged and continues through its existing validation and review paths.
This keeps model calls focused on the cases that need them, and it ensures an uncertain decision can’t silently change the data.
If you read Optimizing AI Agents for Scale, the pattern will look familiar: a deterministic check decides when a model call is warranted, and some of the time it isn’t.
What Decision-1 Does
Decision-1 supports bounded decisions such as classification, routing, ranking, grading, and yes/no checks. The workflow supplies a state and one or more typed questions. Decision-1 returns structured results rather than a free-form explanation the application has to interpret.
The question types include:
| Type | Use |
|---|---|
noul | Evaluate a yes/no condition |
choice | Select from a defined set of options |
score | Place a result on an ordered scale |
Source-role classification uses the choice type. The workflow defines the available roles and their descriptions. Decision-1 selects among them, and application code determines what that selection is allowed to change.
The model can do more than this, but classification is its only responsibility here. The workflow needs to resolve one specific ambiguity: how multiple candidate sources relate to each other.
A classification isn’t proof. It doesn’t establish that a source is authoritative or that its contents are correct. The workflow still needs an explicit acceptance policy, deterministic checks, and a defined way to handle uncertain results.
Why Use a Decision Model?
Decision-1 isn’t valuable simply because it can classify documents; other models can do that too. Its architectural value is that it’s designed for bounded decisions and returns structured choices with probabilities, while the workflow keeps control over what happens next.
Cost and latency matter. At the Azure list prices in the reference implementation’s rate card (westus3, October 2026), Decision-1 costs $0.042 per million tokens. GPT-5.6 Terra, the reasoning model the workflow reserves for failed verification, costs $2.20 per million input tokens and $13.20 per million output tokens. Every run records its Decision-1 usage and prices it, so the savings are measured per run rather than assumed.
Control matters just as much. The workflow defines the available categories, sets an acceptance threshold, and applies deterministic rules to decide which candidate records proceed. Validation and reconciliation remain separate steps.
The goal isn’t to add a model to every decision. It’s to use an appropriate mechanism for each task and reserve broader reasoning or human review for the cases that need it.
Define the Questions
When that condition is met, the workflow sends questions about those eligible sources and the overall document set in a single request:
- What role does each candidate source play?
- What kind of document set is this, taking all of its sources together?
Each question has a fixed set of options. For source roles, the structure matters more than the names: one role for the primary detail, one for proof that corroborates it, roles that should never contribute records, and an explicit way to say the evidence isn’t enough. The keys change with the domain:
| Role | ACH remittances (reference implementation) | Invoice processing | Claims intake |
|---|---|---|---|
| Primary detail | payment_detail: remittance advice or line list | Invoice | Claim form |
| Proof | payment_proof: ACH transfer confirmation | Proof of delivery or goods receipt | Receipts, photos, or police report |
| Summary | statement: open items or AR export | Vendor statement | Policy or coverage summary |
| Correspondence | correspondence: questions and disputes | Vendor emails | Claimant or adjuster emails |
| Neither | other, cannot_tell | Other, cannot tell | Other, cannot tell |
The document-set question works the same way. In the remittance implementation it classifies the email thread as a single_remittance, multiple_payments, dispute_or_inquiry, no_remittance, or cannot_tell.
The keys and descriptions are specific to each implementation; the structure is what carries over. They need to be explicit enough to distinguish plausible alternatives while still letting the model indicate when the evidence is insufficient.
Keep the Questions in Configuration
The question wording, option descriptions, and minimum probability live in configuration rather than being scattered through orchestration code. That makes the classification policy easier to review and test without changing the workflow itself.
In this implementation, the minimum probability is 0.75. The evidence sent for each source is also bounded: excerpts are limited to 1,200 characters, and each source contributes at most 10 payment lines.
The classification configuration looks like this:
{
"minProbability": 0.75,
"maxExcerptChars": 1200,
"maxLinesPerSource": 10,
"sourceRole": {
"instructions": "What is source {id} ({name}) in this remittance email? Judge from its text excerpt and the payment lines found in it.",
"criteria": {
"payment_detail": "Lists the invoices or PROs a payment is applied to, with amounts - a remittance advice, payment summary or line list.",
"payment_proof": "Confirms that a transfer happened - a bank or ACH transfer receipt or confirmation for one payment - rather than listing what it pays.",
"statement": "Lists amounts owed or open items (a statement or AR export); it does not record a payment.",
"correspondence": "Discussion about payments - questions, disputes or proposals such as which invoice to apply money to - without being the remittance itself.",
"other": "Anything else: signatures, logos, disclaimers, unrelated content.",
"cannot_tell": "Not enough information to decide."
}
},
"threadKind": {
"instructions": "What kind of email thread is this, taking all of its sources together?",
"criteria": {
"single_remittance": "One payment and the lines it pays.",
"multiple_payments": "Several separate payments, each with its own amount or date.",
"dispute_or_inquiry": "A collections inquiry or dispute about payments (e.g. an unapplied or already-paid amount) rather than a plain remittance.",
"no_remittance": "No payment information at all.",
"cannot_tell": "Not enough information to decide."
}
}
}
The configuration is business-owned. The option keys are part of the code contract, so changing one requires a corresponding code change. The descriptions can be adjusted in configuration and evaluated against labeled examples before deployment, which is what the repo’s golden-set evaluation is for.
A probability threshold is a policy choice, not a guarantee. Calibrate it against representative data, weighted by what an incorrect classification would cost.
Send the Evidence the Decision Needs
The workflow builds a structured state containing document context and the eligible sources. Each source includes its identifier, name, kind, relationship to the containing document, a bounded excerpt, and a limited number of extracted records.
The goal is to provide enough context to distinguish source roles without sending the entire document set. Too much text introduces irrelevant information; too little can omit the clue that separates primary information from supporting evidence or general correspondence.
A simplified version of the state looks like this:
{
"context": "Remittance email with multiple candidate sources",
"sources": [
{
"id": "source_1",
"name": "remittance_advice.pdf",
"kind": "attachment",
"embeddedIn": "email_123",
"excerpt": "Remittance advice for ACH payment. Invoices INV-1042 and INV-1043...",
"records": [
{ "reference": "INV-1042", "value": 1250.00 },
{ "reference": "INV-1043", "value": 875.00 }
]
},
{
"id": "source_2",
"name": "ach_confirmation.pdf",
"kind": "attachment",
"embeddedIn": "email_123",
"excerpt": "ACH credit transfer confirmation. Amount: 2,125.00...",
"records": [
{ "reference": "ACH-TRACE-0047", "value": 2125.00 }
]
}
]
}
Both sources contain relevant information, but they serve different purposes. One provides primary records; the other provides supporting evidence. The workflow needs to distinguish those roles before deciding which candidate records should proceed.
For each eligible source, the workflow generates a choice question, then adds one for the email thread as a whole. Sending them together means the source roles and the overall context are evaluated against the same state in one request.
The workflow calls Decision-1 through its Microsoft Foundry endpoint, passing the deployment name, state, and questions. The client authenticates with Microsoft Entra ID and applies bounded retries for transient throttling and server errors. A service failure is treated as a failure of the classification step, not as a classification result.
Apply Classifications Conservatively
Classification is only useful if its effect on the workflow is clearly defined. The workflow checks each returned probability against the configured threshold before applying a classification, then applies these selection rules:
- A source confidently classified as a summary, correspondence, or other content doesn’t contribute candidate records.
- When a confident primary-detail source exists, records from a proof source are treated as corroboration rather than additional records.
- When no primary-detail source exists, proof records aren’t automatically discarded.
- If the proposed exclusions would remove every candidate record, the workflow keeps the candidates rather than returning an empty result because of classification alone.
In simplified form, the threshold check looks like this:
confident_roles = {
source_id: role
for source_id, (role, probability) in classifications.items()
if probability is not None
and probability >= min_probability
}
The workflow uses those confident roles to propose exclusions, checks that they wouldn’t remove every candidate record, and records each exclusion and its reason for auditing. The model never deletes data or decides the final result. Its classifications feed a deterministic policy, and that policy can reject them.
That’s the practical difference between asking a model for an answer and integrating a model into a controlled workflow.
Key Takeaways
Escalate only when sources overlap. Decision-1 runs only when at least two Content Understanding sources return candidate records. Everything else goes straight to validation.
Let the workflow define the outcomes. Roles, descriptions, and the 0.75 threshold live in configuration, so the model chooses from options the application controls.
Treat classifications as input, not authority. Deterministic rules decide what a confident classification can change, and the workflow can reject the result.
Fail toward unchanged data. A failed call or a low-confidence result leaves the candidate records exactly as they were.
Production AI systems don’t have to choose between rigid rules and unrestricted reasoning. Give the model one bounded job, and let the rules decide what that job is allowed to change.

Leave a Reply