In T1.2 you calculated what a model costs for assessing tenders. Which model it should be was already settled there. In this lesson you make that decision yourself, for all three AI features that the Kladde gets over the course of the track: the assessment of tenders (T2), the support chat (T5) and the agent that drafts offers (T7). At the end, docs/model-choice.md holds a table with a justification for each choice.

A preliminary remark that holds for the whole lesson: whether a model is good enough for a task is decided in the end by a measurement on your own test set (T9), not by a ranking and not by a data sheet. What you settle today is a reasoned first choice that the measurement may confirm or overturn.

Large or small

Models differ above all in their size, measured in parameters, the numbers learned during training. qwen2.5-0.5b has half a billion of them, large models in the cloud many times more. As a rule of thumb:

  • Larger models solve harder tasks better: summarizing long texts, planning several steps, reading between the lines. They are slower and more expensive per token.
  • Smaller models are fast and cheap and are enough for narrow, clearly described tasks: assigning a text to one of three categories, reading fields from a form, wording a short answer from a section that comes with the request.

Many providers therefore offer families in several sizes. In Microsoft Foundry, such a family is for example gpt-5.4, gpt-5.4-mini and gpt-5.4-nano. The usual way: start with the smallest model that passes the task on the test set, and go bigger only when a measurement demands it.

Reasoning models

Reasoning models think before they answer. For that they produce tokens of their own, which don’t appear in the answer but cost time and are billed like output (T1.2). With the parameter reasoning_effort you set how much the model may think; which levels exist depends on the model, and for the newer models they include none, that is, no thinking at all. These models don’t accept the temperature from T1.1.

Reasoning pays off for tasks with several steps that build on one another: drafting a plan, weighing conditions against each other, understanding code. For simple tasks you mainly pay with wait time. The models of the gpt-5.4 family are reasoning models; for assessing a tender a low level would be a sensible start, for the agent that assembles an offer from several sources, rather a medium one.

Local or cloud

Foundry Local and Microsoft Foundry speak the same interface, so the Kladde can do both. Which environment suits which feature is decided by these points:

Consideration local (Foundry Local) cloud (Microsoft Foundry)
Data location The data doesn’t leave the computer. depending on the deployment type worldwide, in a data zone or in a region
Cost no cost per token, but hardware and electricity per token, and nothing for idle time
Quality small to medium models, as large as your computer can carry the largest models too
Speed depends on your computer; slow without a graphics card usually fast, limited by TPM and RPM
Operation every computer must have the model; according to Microsoft, Foundry Local is built for a device with one person, not as a server for many users at once Microsoft runs the model; you manage deployments and quotas
Protection no content filters, no Prompt Shields guardrails and Prompt Shields (T6)

For the Kladde that means: development and testing happen locally, so nobody pays for experiments. Whether a feature runs locally or in the cloud in operation is a decision per feature. A feature with content that must never leave the computer can stay local on purpose, even if a cloud model would be better.

Reading a model card

For every model there is a description, the model card. In the model catalog of the Foundry portal, every model has a page of its own, and on Microsoft Learn the page “Foundry Models sold by Azure” summarizes the Azure OpenAI models in tables. For local models, foundry model info <alias> shows the details, and foundry model list --verbose names the license. Look out for:

  • Capabilities: Can the model call tools? Does it deliver structured responses according to a JSON schema? Does it understand images?
  • Context window and longest output: how many tokens go in and how many come out at most.
  • Training data cutoff: What happened afterward, the model doesn’t know.
  • Version and retirement: models in the cloud have a version and an announced end. After that you can’t use them any more. Whoever chooses a model plans the switch too (T10.4).
  • License: for models with open weights that you run locally, the license says whether and how you may use them in a product.
  • Deployment types and regions: not every model exists in every region and every deployment type.

An example from Microsoft Learn: gpt-5.4-mini is a reasoning model with Chat Completions and the Responses API, structured responses, text and image input and tool calls. Its context window has 400,000 tokens, of which at most 272,000 are for the input and 128,000 for the output, and its training data runs to August 2025. Always look such details up at the source; they change with every new version.

Deployment types in Foundry

You use a model in the cloud through a deployment. Its type determines where your requests are processed and how you pay. The most important types:

Type where it’s processed payment what for
Global Standard in any Azure region per token Microsoft’s recommendation for the start: newest models, lowest price, highest quota
Data Zone Standard only within a data zone (US, EU or Asia-Pacific) per token when processing should stay in the EU
Standard within one Azure geography per token when processing must stay in one country; not for every model
Global or Data Zone Batch as above half the price, answer with a target of 24 hours large volumes without hurry
Provisioned as above reserved capacity (PTU) steady high load with predictable wait time

Two details from Microsoft’s description matter for the Kladde:

  • Stored data stays in the Azure geography of your resource for all types. The deployment type affects where the request is processed.
  • The EU data zone follows Azure’s EU Data Boundary. According to Microsoft, that can include EFTA countries such as Norway and Switzerland.

An office that processes tenders and customer inquiries and wants to keep them in the EU therefore usually chooses Data Zone Standard in a region of the EU. For gpt-5.4-mini, according to T1.2, that costs about 10 percent more than Global Standard. Whether a deployment of this type exists for your model in your region is shown by Microsoft’s page on availability by region (under “Read more”); whether your subscription has quota there, only the portal shows. The price list proves only that a price exists.

Capabilities first

The Kladde’s three AI features need different capabilities, and you have to check them before you talk about quality and price:

  • The assessment needs structured responses: the Kladde wants a suitability, reasons and a score in fixed fields, not free text (T2.3). In the cloud, models with “Structured outputs” enforce a JSON schema strictly. Whether Foundry Local does too isn’t in the documentation; T2.3 checks it, and until then the code checks every answer itself.
  • The support chat needs a context window large enough for the help pages it sends along, a short wait time and good German and English.
  • The offer agent needs tool calls (T7). Locally, the Task column in foundry model list shows with tools which models can do that. Small models often call tools unreliably; Microsoft explicitly describes forcing a tool call in Foundry Local only as “best effort”.

A model without the necessary capability drops out, however cheap it is.

Comparing costs

For the comparison, you extend the calculation from T1.2 to three features and three models. The volumes are assumptions for a small office; they stand as data at the start of the program so you can change them easily:

Feature Calls per month Input per call Output per call Reason for the assumption
Assessment 1,000 430 400 as in T1.2
Support chat 2,000 3,000 250 instruction, three sections of the help and the history so far; short answers
Offer agent 300 4,000 600 50 offers with six steps each, in which the agent reads data and writes on the draft
C#
// comparison.cs – what the Kladde's three AI features would cost per month with three models, at today's prices.
using System.Globalization;
using System.Text.Json;

const string Region = "swedencentral";
const string PricesApi = "https://prices.azure.com/api/retail/prices";

// The names exactly as the price list writes them (case matters), all Data Zone Standard.
(string Name, string InputSku, string OutputSku)[] models =
[
    ("gpt-5.4", "5.4 inp Dz", "5.4 opt Dz"),
    ("gpt-5.4-mini", "5.4 mini Inp Dz", "5.4 mini Opt Dz"),
    ("gpt-5.4-nano", "5.4 nano Inp Dz", "5.4 nano Opt Dz"),
];

// Assumptions per feature: calls per month, input and output tokens per call. Measured from T2.4 on.
(string Name, int Calls, int Input, int Output)[] features =
[
    ("Assessment", 1_000, 430, 400),
    ("Support chat", 2_000, 3_000, 250),
    ("Offer agent", 300, 4_000, 600),
];

using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
var prices = new Dictionary<string, decimal>();
try
{
    foreach (var model in models)
    {
        prices[model.InputSku] = await PricePerMillionAsync(http, model.InputSku);
        prices[model.OutputSku] = await PricePerMillionAsync(http, model.OutputSku);
    }
}
catch (Exception e) when (e is HttpRequestException or TaskCanceledException or JsonException or KeyNotFoundException or InvalidDataException)
{
    Console.Error.WriteLine($"No price read ({e.Message}). Without current prices I don't compare.");
    return 1;
}

var usd = CultureInfo.InvariantCulture;
Console.WriteLine($"USD per month, Data Zone Standard, {Region}, read {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC");
Console.WriteLine($"{"",-16}" + string.Concat(models.Select(m => $"{m.Name,14}")));
foreach (var feature in features)
{
    string row = string.Concat(models.Select(m =>
    {
        decimal perCall = feature.Input * prices[m.InputSku] / 1_000_000m + feature.Output * prices[m.OutputSku] / 1_000_000m;
        return (perCall * feature.Calls).ToString("0.00", usd).PadLeft(14);
    }));
    Console.WriteLine($"{feature.Name,-16}{row}");
}

return 0;

static async Task<decimal> PricePerMillionAsync(HttpClient http, string sku)
{
    string filter = $"serviceName eq 'Foundry Models' and armRegionName eq '{Region}' "
        + $"and priceType eq 'Consumption' and skuName eq '{sku}'";
    string url = $"{PricesApi}?api-version=2023-01-01-preview&$filter={Uri.EscapeDataString(filter)}";
    using JsonDocument json = JsonDocument.Parse(await http.GetStringAsync(url));
    JsonElement[] items = [.. json.RootElement.GetProperty("Items").EnumerateArray()];
    if (items.Length != 1
        || items[0].GetProperty("currencyCode").GetString() != "USD"
        || items[0].GetProperty("unitOfMeasure").GetString() != "1M")
    {
        throw new InvalidDataException($"did not get exactly one price in USD per 1M for '{sku}'");
    }

    return items[0].GetProperty("retailPrice").GetDecimal();
}
Bash
dotnet run comparison.cs

With the prices of the day this lesson was written; in the first line you’ll see the time of your run, and the amounts follow today’s prices:

USD per month, Data Zone Standard, swedencentral, read YYYY-MM-DD hh:mm UTC
                       gpt-5.4  gpt-5.4-mini  gpt-5.4-nano
Assessment                7.78          2.33          0.64
Support chat             24.75          7.43          2.01
Offer agent               6.27          1.88          0.51

PricePerMillionAsync is the same method as in costs.cs. Only the two tables at the start are new. That the names of the prices are written inconsistently (5.4 inp Dz versus 5.4 mini Inp Dz) is no typo but is how the price list has it; that’s why the comment sits above them.

Between the three sizes there’s a factor of about three to four each time. In absolute amounts, all three features are cheap for a small office. The choice of model is therefore decided here less by price than by quality, wait time and data location. That changes as soon as the volumes grow, as exercise 2 shows.

Exercise 1: One model for each feature

Choose one model and one deployment type for the assessment, the support chat and the offer agent. Justify each choice in a table with these columns:

  • Quality: What must the model be able to do, and why is the chosen one enough (or why does it need a bigger one)?
  • Cost: the amount per month from comparison.cs, with the reading date.
  • Data location: Which data goes in, where is it processed, and does the chosen deployment type exist for the model in your region?
  • Possible locally: Would it work with Foundry Local, and with what drawback?

A table without a justification doesn’t count. How to check your result: every choice names at least one capability from the section “Capabilities first” that the model must have, and every row says what a measurement in T9 would have to confirm.

Solution 1

One possible solution. Other decisions are right if their justification holds.

Feature Choice Quality Cost Data location possible locally
Assessment gpt-5.4-mini, Data Zone Standard, low reasoning level needs structured responses and a sure judgment on selection criteria; nano is a candidate if the test set in T9 shows it’s enough about $2.33 a month public tenders, processing in the EU yes, with a small model for development; whether the strict schema takes effect locally, T2.3 checks
Support chat gpt-5.4-nano, Data Zone Standard, reasoning none answers only from help pages that come with the request (T5), so a narrow task; needs a short wait time and German and English about $2.01 a month users’ questions, which may contain personal details: EU, in a region where nano exists as Data Zone Standard (see below) for development yes; in operation no, because Foundry Local isn’t built as a server for many users at once
Offer agent gpt-5.4-mini, Data Zone Standard, medium reasoning level needs reliable tool calls and several steps; gpt-5.4 only if T7 shows that mini picks tools wrongly about $1.88 a month the office’s contacts and day rates: EU for development with a local model whose Task says tools; forced tool calls are only “best effort” locally

The support chat’s row shows why the price list isn’t enough. On the day this lesson was written, Microsoft’s page on availability by region listed gpt-5.4-nano as Data Zone Standard in Europe only for northeurope, not for swedencentral. The price list nevertheless had a price for swedencentral. Whoever wants nano for the chat therefore creates the resource in a region where this deployment exists, or uses gpt-5.4-mini for the chat in swedencentral too. Read the page for your own choice yourself; it changes with every new model.

Three thoughts are in this solution:

  • Start small, let the measurement decide. No row chooses the largest model without a measurement having demanded it. Every row names the next candidate in case the measurement fails.
  • The data location follows the data. Even the public tenders run in the EU data zone. One single rule for all features is easier to explain and check than three.
  • Reasoning is a setting, not a different model. The same family with different levels of reasoning_effort covers all three cases.

Exercise 2: When the volumes grow

Assume the Kladde becomes a success: the support chat gets 20,000 questions a month instead of 2,000, and the agent reads 12,000 tokens per step instead of 4,000 because it also sends the whole tender and earlier offers along. Change the assumptions in comparison.cs and run it again. Which feature now determines the cost? Does that change your choice from exercise 1?

Solution 2

Two lines are changed:

C#
    ("Support chat", 20_000, 3_000, 250),
    ("Offer agent", 300, 12_000, 600),
USD per month, Data Zone Standard, swedencentral, read YYYY-MM-DD hh:mm UTC
                       gpt-5.4  gpt-5.4-mini  gpt-5.4-nano
Assessment                7.78          2.33          0.64
Support chat            247.50         74.25         20.08
Offer agent              12.87          3.86          1.04

Now the support chat determines the cost. With gpt-5.4 it would cost over $247 a month, with nano a good $20. Choosing a small model for the chat was therefore not just a question of wait time. For the agent, three times the input has only about doubled the cost, because the output stayed the same.

Two more levers aren’t visible in this table, and both come later: a large part of the chat’s input is the same with every question (the instruction). With the prompt cache, this part costs only a tenth (T3.4). And a monthly cap makes sure that an unexpected success doesn’t become expensive unnoticed (T10.3).

Exercise 3: Reading two model cards

Read the model cards of gpt-5.4-mini (on Microsoft Learn, page “Foundry Models sold by Azure”) and of qwen2.5-0.5b (with foundry model info qwen2.5-0.5b and foundry model list --verbose; whoever doesn’t have the tool takes the model card of the original model Qwen2.5-0.5B-Instruct at Hugging Face). For both, note: capabilities, context window, longest output, training data cutoff (if given), license.

How to check it: for each of the five fields there’s a value or expressly “not specified”, and you know where you found it.

Solution 3

Field gpt-5.4-mini (Microsoft Learn) qwen2.5-0.5b (original model at Hugging Face)
Capabilities reasoning, Chat Completions and Responses API, structured responses, text and images, tool calls text; tool calls according to Foundry Local, Task column
Context window 400,000 tokens, of which 272,000 input 32,768 tokens
longest output 128,000 tokens 8,192 tokens
Training data until August 2025 not specified
License use as a service of Azure, under Microsoft’s terms Apache 2.0

The version of qwen2.5-0.5b in Foundry Local is a variant prepared for your hardware. Its limits can differ from those of the original model; what counts for the Kladde is what foundry model info shows on your computer. The difference in the table is big: a context window of 32,768 tokens is enough for one tender, but not for many help pages at once. That, too, is a reason to develop locally and to keep an eye on the size of requests.

The Kladde: justify the model choice

Extend docs/model-choice.md from T1.1 with your table from exercise 1 and the assumptions the costs are based on:

## Models per feature (first choice, measured in T9)

| Feature | Model | Deployment | Reasoning | Justification | next candidate |
| --- | --- | --- | --- | --- | --- |
| Assessment | ... | Data Zone Standard | ... | ... | ... |
| Support chat | ... | Data Zone Standard | ... | ... | ... |
| Offer agent | ... | Data Zone Standard | ... | ... | ... |

## Assumptions for the costs

- Assessment: 1,000 calls a month, 430 tokens input, 400 output
- Support chat: 2,000 questions a month, 3,000 tokens input, 250 output
- Offer agent: 300 steps a month, 4,000 tokens input, 600 output
- Prices: Azure Retail Prices API, region swedencentral, read on <date>

## Local

- Development and tests: Foundry Local, alias qwen2.5-0.5b
- For the agent a local model with Task "tools": <alias from foundry model list>

Put comparison.cs in your exercise folder. Submit the change as a pull request. In the review, one question is worth asking: Would someone who doesn’t know the Kladde understand from the justification why this choice was made?

What you can do now

  • You tell large, small and reasoning models apart and start with the smallest model that can pass a measurement.
  • You weigh local against cloud: data location, cost, quality, speed, operation and protection.
  • You read a model card and find capabilities, context window, training data cutoff, version and license.
  • You know the deployment types in Foundry and justify Data Zone Standard for processing in the EU.
  • You check first the capabilities a feature needs, then quality and price.
  • You have a reasoned first choice for every AI feature of the Kladde in docs/model-choice.md.

Read more