DEV Community

Julia Alba
Julia Alba

Posted on Fully Autonomous

Why your Copilot Studio agent gets calculations wrong, and the agent flow that fixes it

A language model predicts the digits of a total; it does not calculate them. Put a Copilot Studio agent in front of a price list, ask it "How much for 240 units of Standard?", and sooner or later it returns a wrong number. The usual symptoms:

  • The same question returns two different totals on two runs.
  • You get "approximately" where you need cents.
  • A follow-up is answered from conversation memory instead of the data.
  • The user never gave a quantity, and the agent picked one.

A stricter prompt alone does not fix this. Moving the math does: every calculation goes into an agent flow, where the same input always gives the same figures. The model keeps the two jobs it is good at: understanding the question and wording the answer.

The example is invented. Standard costs 12.50 EUR and Premium 24.85 EUR per unit, orders of 100 units or more get 10% off, and VAT is 20%.

1. Do the math in whole cents

Create an agent flow on the Workflows page (More create options, then Agent flows) with the trigger When an agent calls the flow and two inputs: Product (Text) and Quantity (Number).

Read the unit price, the discount tiers and the VAT rate from your price list into Integer variables: prices in whole cents (12.50 EUR as 1250), rates as whole percents (10, 20).

Why integers? Flow expressions have no round() function, and div() changes behavior with its operand types. From Microsoft's expression reference:

div(11, 5)     ->  2      both Integer: the remainder is dropped
div(11, 5.0)   ->  2.2    one Float: the result is Float
Enter fullscreen mode Exit fullscreen mode

Dropping the remainder is useful: add half the divisor before you divide, and truncation becomes rounding half up. One variable per line:

ListCents      mul(variables('Qty'), variables('UnitCents'))
DiscountCents  div(add(mul(variables('ListCents'), variables('DiscountPct')), 50), 100)
NetCents       sub(variables('ListCents'), variables('DiscountCents'))
VatCents       div(add(mul(variables('NetCents'), variables('VatPct')), 50), 100)
GrossCents     add(variables('NetCents'), variables('VatCents'))
Enter fullscreen mode Exit fullscreen mode

Qty holds the quantity, and DiscountPct is 10 for 100 units or more, otherwise 0. Each discount and VAT amount is rounded half up to the cent before any total is built. That is this example's rule; use the one your finance team specifies.

The same steps for 101 units of Premium:

List      101 x 2485                =  250985  ->  2,509.85
Discount  (250985 x 10 + 50) / 100  =   25099  ->    250.99   exact: 250.985
Net       250985 - 25099            =  225886  ->  2,258.86
VAT       (225886 x 20 + 50) / 100  =   45177  ->    451.77   exact: 451.772
Gross     225886 + 45177            =  271063  ->  2,710.63
Enter fullscreen mode Exit fullscreen mode

The trap: if even one operand is a decimal, div() returns a decimal and the +50 no longer rounds, so a figure can be a cent off. Keep every amount in an Integer variable.

A price stored as 12.50 needs int(formatNumber(mul(..., 100), 'F0')), which rounds to the whole cent; int() alone converts only whole-number text. Wrap a price that arrives as text in float() first.

Format each figure once, at the end. Only here do you want a decimal, so divide by 100.0:

formatNumber(div(variables('NetCents'), 100.0), 'N2', 'en-US')   ->  2,258.86
Enter fullscreen mode Exit fullscreen mode

Check the inputs in a Condition before any math. If the price list has no row for the product, or the quantity is below 1 or not a whole number, end that branch with its own Respond to the agent, whose text names the wrong input.

2. Return one finished text

End the math branch with Respond to the agent and a single Text output, Result:

240 x Standard at 12.50 EUR = 3,000.00 EUR. Volume discount 10%: 300.00 EUR. Net total: 2,700.00 EUR. VAT 20%: 540.00 EUR. Gross total: 3,240.00 EUR.
Enter fullscreen mode Exit fullscreen mode

The model never has to add, round or format anything.

3. Make the agent copy, not compute

Add the flow to the agent as a tool named Quote. The tool description mainly decides when the agent calls it, so say what it does, when to use it and what it needs:

Calculates a price quote for one product and quantity from the current price list: list price, volume discount, net total, VAT and gross total. Use it for every question about prices, discounts, VAT or totals. Required: product and quantity. Never answer a price question without calling this tool.
Enter fullscreen mode Exit fullscreen mode

In the description of the Quantity input, write "Never assume a quantity." In the input's settings, under Prompt, select Customize and enter the question for a missing quantity: "How many units do you need?" The agent may still ask in its own words first.

Under Completion, the After running setting controls what the user sees. The default, Don't respond, lets the agent work the tool output into its own reply. Two other options matter:

  • Send specific response, with a message that holds only Result, shows the flow's text unchanged.
  • Write the response with generative AI has the tool itself word the answer at once.

When AI words the answer (the default included), add these rules to the agent's instructions:

AFTER THE QUOTE TOOL RUNS
- Restate the Quote tool's Result in the user's language, in a natural tone.
- Any number, date, currency or unit you mention must match the Result exactly. Always give the net and gross totals; show the rest of the breakdown in full or not at all. Never change a figure. Do not calculate, estimate, round, convert or add figures of your own.
- If the Result says an input is missing or wrong, ask for it. Do not guess.
- Never state a figure the Quote tool did not return for this message. If the tool fails, say the quote cannot be calculated right now; do not estimate.
- After the answer, offer only what the Quote tool can calculate.
Enter fullscreen mode Exit fullscreen mode

Keep the last two rules even with Send specific response: the agent can still answer a follow-up from memory or add a sentence of its own.

One setting to check before you go live: by default a tool runs with each user's credentials. For a price list that everyone shares, set the tool's credentials to Maker-provided.

4. Test the figures, not the tone

On the agent's Evaluation page, select New evaluation, then Single responses. Switch the toggle to Import and upload a CSV whose first row holds the headings Question and Expected response. Rerun the test set after every change.

Two test methods need care:

  • General quality, the default method, does not compare the answer with your expected response, and can flag a correct refusal. Remove it from this test set.
  • Keyword match, set to All, checks the figures whatever the wording. It does not read the expected response either: open each test case and add two of its figures as keywords. Two, because the method looks for text inside the answer, and 2,700.00 is also inside 12,700.00.

Two rows to start with:

Question,Expected response
"How much for 101 units of Premium?","101 x Premium at 24.85 EUR = 2,509.85 EUR. Volume discount 10%: 250.99 EUR. Net total: 2,258.86 EUR. VAT 20%: 451.77 EUR. Gross total: 2,710.63 EUR."
"I need a quote for Standard.","How many units do you need?"
Enter fullscreen mode Exit fullscreen mode

The keywords are 2,258.86 and 2,710.63 for the Premium question, and How many units for the Standard question.

The Premium question tells you what broke: a net of 2,258.87 means the +50 is missing from DiscountCents, and a VAT of 451.78 means the VAT step ran in decimals.

An agent that invents a quantity passes every happy-path test; the Standard question catches it. If it fails, read the answer: a question in other words is fine (change the keyword); a quote means the agent guessed.

A single-response test has no earlier message, so test follow-ups such as "And for 300 units?" in a conversation test set.

When not to use this

  • A system of record already calculates the figure: let a flow call that system.
  • The math is one formula on values the agent already holds: Power Fx in a topic is simpler, and it has Round().

How do you keep figures exact in your agents: a flow, Power Fx in a topic, or a call to the system of record?


The price list and all figures in this article are invented. The article was generated with AI under my direction. The expression behavior and the interface labels were checked against Microsoft Learn on 15 November 2026, and the figures were verified by calculation; the example as a whole has not been run end to end in a tenant, so run the two test rows before you rely on it.

References (Microsoft Learn)

Top comments (0)