Back to contents

Chapter 3C

Decisions with Reusable Prompts and Native .NET Functions

Static reading edition generated from the Decisions with Reusable Prompts and Native .NET Functions notebook.

Workshop (AI Extensions) - Decisions with Reusable Prompts and Native .NET Functions

Decision Intelligence applied in this module:

  • Listing various decision-making frameworks with their descriptions
  • Listing types of decision-making frameworks dynamically based on the decision type
  • Reusing AI Extensions prompt templates with dynamic inputs
  • Selecting a decision-making framework dynamically using native .NET code
  • Decision Scenario: Using a Monte Carlo Simulation to estimate decision uncertainty

AI Extensions prompt templates are reusable instructions that help guide GenAI models toward a specific decision workflow. A prompt template usually has one responsibility: list useful decision frameworks, recommend a reasoning approach for a high-stakes decision, summarize a tradeoff, or personalize a recommendation from structured context.

What makes reusable prompt functions useful? GenAI models can already respond to basic prompts, but decision workflows usually need repeatable instructions, consistent formatting, and dynamic inputs. By combining Microsoft.Extensions.AI with simple C# helper methods, a prompt becomes a reusable part of an AI orchestration flow instead of a one-off string.

For example, imagine you want to prepare a great Thanksgiving dinner and you ask a GenAI cooking application to create a recipe. It may produce a delicious plan, but it may also use ingredients or cooking appliances you do not own. You could keep prompting until it narrows the recipe, but it is better to provide available ingredients, time availability, kitchen appliances, and allergy preferences as dynamic inputs. The same pattern applies to Decision Intelligence: reusable prompt templates let the model consider specific business context, user constraints, and decision criteria each time the prompt runs.

In this notebook, reusable prompt functions are implemented with:

  • IChatClient from Microsoft.Extensions.AI
  • ChatMessage roles for system and user instructions
  • ChatOptions for model settings such as temperature, top-p, and maximum output tokens
  • C# raw string interpolation for dynamic prompt inputs

Below is an example of a reusable prompt template that can be used for facilitating decision-making. Note the {decisionToMake} input where the parameter can be dynamically passed in to provide additional specificity.

var decisionPrompt = $"""
[DECISION FRAMEWORKS TO USE]
Price's Law
Pareto Principle
Laplace Rule of Succession
Eisenhower Matrix
Median Rule of Five
Second Order Thinking

[DECISION GUIDANCE]
Try to use the best fitting framework.  
Prefer using quantitative decision frameworks rather than qualitative ones.  
Use calculations or code-based validation when quantitative reasoning is required.

Use the decision frameworks above to help the user make the following decision:
{decisionToMake}
""";

The core value is that the prompt can be reused with different inputs while still flowing through the same AI Extensions IChatClient API.


Step 1 - Initialize Configuration Builder & Build the AI Extensions Responses API Orchestration

Execute the next two cells to:

  • Use the Configuration Builder to load the API secrets.
  • Use the API configuration to build a Responses API-backed AI Extensions IChatClient orchestration.
In [11]:
#r "nuget: Microsoft.Extensions.Configuration, 10.0.8"
#r "nuget: Microsoft.Extensions.Configuration.Json, 10.0.8"
#r "nuget: System.Text.Json, 10.0.8"

using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.Configuration;
using System.IO;
using System;

var configurationBuilder = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
    .AddJsonFile("secrets.settings.json", optional: true, reloadOnChange: true);
var config = configurationBuilder.Build();

// IMPORTANT: You ONLY NEED either Azure OpenAI or OpenAI connection info, not both.
// Azure OpenAI Connection Info
var azureOpenAIEndpoint = config["AzureOpenAI:Endpoint"];
var azureOpenAIAPIKey = config["AzureOpenAI:APIKey"];
var azureOpenAIModelDeploymentName = config["AzureOpenAI:ModelDeploymentName"];
// OpenAI Connection Info 
var openAIAPIKey = config["OpenAI:APIKey"];
var openAIModelId = config["OpenAI:ModelId"];
Installed Packages
  • Microsoft.Extensions.Configuration, 10.0.8
  • Microsoft.Extensions.Configuration.Json, 10.0.8
  • System.Text.Json, 10.0.8
In [12]:
// Import the Microsoft Extensions AI NuGet Packages
#r "nuget: Microsoft.Extensions.AI, 10.6.0"
#r "nuget: Microsoft.Extensions.AI.Abstractions, 10.6.0"
#r "nuget: Microsoft.Extensions.AI.OpenAI, 10.6.0"
// Import Azure & OpenAI NuGet Packages
#r "nuget: Azure.AI.OpenAI, 2.9.0-beta.1"
#r "nuget: Azure.Identity, 1.21.0"
#r "nuget: OpenAI, 2.10.0"

using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Text.Json;

// Set the flag to use Azure OpenAI or OpenAI. False to use OpenAI, True to use Azure OpenAI
var useAzureOpenAI = true;

// Create the IChatClient based on the selected service
IChatClient chatClient;

#pragma warning disable OPENAI001

if (useAzureOpenAI)
{
    Console.WriteLine("Using Azure OpenAI Service");

    var apiKeyCredential = new ApiKeyCredential(azureOpenAIAPIKey);
    var azureOpenAIClient = new AzureOpenAIClient(new Uri(azureOpenAIEndpoint), apiKeyCredential);

    // Get the ResponsesClient from AzureOpenAIClient and adapt it to Microsoft.Extensions.AI.
    var responsesClient = azureOpenAIClient.GetResponsesClient();
    chatClient = responsesClient.AsIChatClient(defaultModelId: azureOpenAIModelDeploymentName);
}
else
{
    Console.WriteLine("Using OpenAI Service");

    var apiKeyCredential = new ApiKeyCredential(openAIAPIKey);
    var openAIClient = new OpenAIClient(apiKeyCredential);

    // Get the ResponsesClient from OpenAIClient and adapt it to Microsoft.Extensions.AI.
    var responsesClient = openAIClient.GetResponsesClient();
    chatClient = responsesClient.AsIChatClient(defaultModelId: openAIModelId);
}

#pragma warning restore OPENAI001

var decisionIntelligenceSystemPrompt = """
You are a Decision Intelligence assistant.
Help the user explore options, evaluate tradeoffs, reason through uncertainty, solve problems,
and apply systems thinking to personal, professional, strategic, and operational decisions.

Provide responses that are structured, logical, and thorough.
Aim to improve the user's judgment rather than make choices for them.
Be balanced, analytical, and pragmatic.
Adapt depth and complexity to the user's context.
""";

async Task<string> RunPromptAsync(string prompt, ChatOptions? options = null)
{
    ChatResponse response = await chatClient.GetResponseAsync(prompt, options);
    return response.Text ?? string.Empty;
}

async Task<string> RunDecisionPromptAsync(string userPrompt, ChatOptions? options = null)
{
    var chatMessages = new List<ChatMessage>
    {
        new(ChatRole.System, decisionIntelligenceSystemPrompt),
        new(ChatRole.User, userPrompt)
    };

    ChatResponse response = await chatClient.GetResponseAsync(chatMessages, options);
    return response.Text ?? string.Empty;
}

static string GetFunctionResultText(object? result)
{
    if (result is null)
    {
        return string.Empty;
    }

    if (result is JsonElement jsonElement)
    {
        return jsonElement.ValueKind == JsonValueKind.String
            ? jsonElement.GetString() ?? string.Empty
            : jsonElement.ToString();
    }

    return result.ToString() ?? string.Empty;
}
Installed Packages
  • Azure.AI.OpenAI, 2.9.0-beta.1
  • Azure.Identity, 1.21.0
  • Microsoft.Extensions.AI, 10.6.0
  • Microsoft.Extensions.AI.Abstractions, 10.6.0
  • Microsoft.Extensions.AI.OpenAI, 10.6.0
  • OpenAI, 2.10.0
Using Azure OpenAI Service
(59,61): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

(65,73): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

(77,43): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

warning CS1701: Assuming assembly reference 'OpenAI, Version=2.9.1.0, Culture=neutral, PublicKeyToken=b4187f3e65366280' used by 'Azure.AI.OpenAI' matches identity 'OpenAI, Version=2.10.0.0, Culture=neutral, PublicKeyToken=b4187f3e65366280' of 'OpenAI', you may need to supply runtime policy

warning CS1701: Assuming assembly reference 'System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8' used by 'Azure.AI.OpenAI' matches identity 'System.ClientModel, Version=1.10.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8' of 'System.ClientModel', you may need to supply runtime policy

warning CS1701: Assuming assembly reference 'Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8' used by 'Azure.AI.OpenAI' matches identity 'Azure.Core, Version=1.53.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8' of 'Azure.Core', you may need to supply runtime policy

warning CS1701: Assuming assembly reference 'OpenAI, Version=2.9.1.0, Culture=neutral, PublicKeyToken=b4187f3e65366280' used by 'Azure.AI.OpenAI' matches identity 'OpenAI, Version=2.10.0.0, Culture=neutral, PublicKeyToken=b4187f3e65366280' of 'OpenAI', you may need to supply runtime policy


Step 2 - Create an AI Extensions Reusable Prompt Function

📜 "Decision frameworks are like maps. Use them to navigate complex decision-making terrain.

-- Unknown

Reusable prompt functions in this notebook are simple C# helper methods that submit prompt templates through IChatClient. This keeps the prompt reusable while using the same AI Extensions API surface shown in the previous workshop notebooks.

In [13]:
// Simple prompt to list some decision frameworks this GenAI LLM is familiar with
var decisionFrameworksPromptTemplate = """
Identify and list various decision-making frameworks that can enhance the quality of decisions. 
Briefly describe how each framework supports better analysis and reasoning in different decision-making scenarios.

Output Format Instructions: 
When generating Markdown, do not use any headings higher than ###. 
Avoid # and ## headers. Use only ###, ####, or lower-level headings if necessary. 
All top-level section headers should start at ### or lower. 
Never use ---, ***, or ___ for horizontal lines. There should be no horizontal lines in the output.
For separation, use extra extra spacing. Do not any render horizontal lines.
Format the response using only a Markdown table. Only return a Markdown table. 
Do not enclose the table in triple backticks.
""";

// Treat the prompt template as a reusable local function that invokes the AI Extensions IChatClient.
async Task<string> RunDecisionFrameworksPromptAsync(ChatOptions? options = null)
{
    return await RunDecisionPromptAsync(decisionFrameworksPromptTemplate, options);
}

var decisionFrameworksResponseText = await RunDecisionFrameworksPromptAsync();
decisionFrameworksResponseText.DisplayAs("text/markdown");
Framework How it supports better analysis and reasoning Best fit / typical scenarios
Cost-Benefit Analysis Compares expected benefits against costs to clarify whether a choice creates net value. Helps make tradeoffs explicit and prevents decisions based only on intuition. Investments, projects, policy choices, purchasing decisions
SWOT Analysis Organizes internal strengths and weaknesses alongside external opportunities and threats. Supports structured situational awareness and strategic thinking. Strategy, competitive positioning, planning, career moves
Decision Matrix / Weighted Scoring Assigns criteria and weights to options, making comparisons more objective and transparent. Useful when multiple factors matter and choices are hard to compare directly. Vendor selection, hiring, product choices, prioritization
Expected Value Analysis Combines probabilities with outcomes to estimate average long-run value. Improves reasoning under uncertainty by comparing risk-adjusted outcomes. Gambling-like decisions, finance, forecasting, risk choices
Decision Trees Maps sequential choices, probabilities, and outcomes in a visual branching structure. Helps reason through contingent decisions and identify optimal paths. Multi-stage decisions, investment strategy, medical decisions
OODA Loop Observe, Orient, Decide, Act; emphasizes fast feedback and adaptation. Improves decision quality in dynamic environments where conditions change quickly. Operations, competitive environments, crisis response
Cynefin Framework Categorizes problems as simple, complicated, complex, or chaotic to match the decision approach to the situation. Reduces misapplication of overly rigid analysis to messy problems. Organizational decisions, strategy, incident response
Premortem Analysis Assumes a decision failed and asks why, surfacing hidden risks and blind spots. Strengthens risk identification and contingency planning. Projects, launches, major commitments
Red Team / Devil’s Advocate Intentionally challenges assumptions, arguments, and plans from an opposing perspective. Improves robustness by exposing weaknesses and alternative interpretations. Strategy, security, policy, high-stakes decisions
Six Thinking Hats Separates thinking into distinct modes such as facts, emotions, risks, creativity, and process. Prevents one-sided reasoning and improves group discussion quality. Team meetings, brainstorming, complex group decisions
Pareto Analysis (80/20) Focuses attention on the few causes or options that produce most of the impact. Helps prioritize effort where it matters most. Process improvement, problem solving, resource allocation
Root Cause Analysis Looks beyond symptoms to identify underlying causes of a problem. Improves long-term decision quality by addressing system drivers rather than quick fixes. Quality issues, operational problems, recurring failures
Scenario Planning Builds several plausible future scenarios and tests decisions against them. Enhances resilience and prepares decision-makers for uncertainty and change. Strategy, forecasting, long-term planning
Satisficing Chooses the first option that meets acceptable criteria rather than optimizing endlessly. Useful when time, information, or cognitive resources are limited. Routine decisions, time-sensitive choices, hiring under constraints
Bayesian Reasoning Updates beliefs as new evidence arrives, rather than treating initial assumptions as fixed. Supports probabilistic thinking and continuous learning from new information. Forecasting, diagnostics, iterative decisions
Influence Diagram Shows relationships among decisions, uncertainties, and outcomes in a compact model. Helps clarify what variables matter and how they interact. Complex decision analysis, risk management, operations
Moral / Ethical Frameworks Apply principles such as utilitarianism, rights, duties, or virtue ethics to evaluate what ought to be done. Improves reasoning when decisions have value conflicts or stakeholder impacts. Leadership, healthcare, public policy, people decisions
Five Whys Repeatedly asks “why” to trace a problem back through causal layers. Supports deeper diagnosis and reduces superficial problem solving. Troubleshooting, operational improvement, incident analysis
RAPID / DACI / RACI Clarifies who recommends, decides, contributes, and executes. Improves decision quality by reducing ambiguity, delays, and role conflict. Team governance, organizational decision processes
Heuristic-Driven Decision Making Uses tested rules of thumb when full analysis is too costly or unavailable. Can improve speed and effectiveness when applied with awareness of bias and context. Real-time decisions, expert judgment, high-frequency choices
(17,64): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


Step 3 - AI Extensions Dynamic Decision Intelligence

📜 "If the only tool you have is a hammer, you tend to see every problem as a nail."

-- Abraham Maslow (Renowned American psychologist)

Prompt templates can be dynamically composed using C# variables and raw string interpolation. This allows the ease of passing in parameters and execution settings into reusable prompt functions. This is not groundbreaking, but it does allow you to dynamically compose AI prompts (context-engineering, prompt-engineering dynamically).

Execute the cell below to view how the prompt can dynamically instruct the LLM to retrieve different types of decision-making frameworks.

In [14]:
// Takes input variables and creates a dynamic prompt that can be used to invoke the GenAI model.
string CreateDecisionFrameworkPrompt(int numberOfFrameworks, string decisionType)
{
    return $"""
        Identify and list {numberOfFrameworks} decision-making frameworks that can enhance the quality of decisions. 
        Briefly describe how each framework supports better analysis and reasoning in {decisionType} decision-making scenarios.

        Output Format Instructions: 
        When generating Markdown, do not use any headings higher than ###. 
        Avoid # and ## headers. Use only ###, ####, or lower-level headings if necessary. 
        All top-level section headers should start at ### or lower. 
        Never use ---, ***, or ___ for horizontal lines. There should be no horizontal lines in the output.
        For separation, use extra extra spacing. Do not any render horizontal lines.
        Format the response using only a Markdown table. Only return a Markdown table. 
        Do not enclose the table in triple backticks.
        """;
}

// Create the chat options. 
//Try changing the settings to see how they affect the quality of generated text.
#pragma warning disable OPENAI001

var decisionFrameworkChatOptions = new ChatOptions
{
    RawRepresentationFactory = _ => new OpenAI.Responses.CreateResponseOptions
    {
        Model = useAzureOpenAI ? azureOpenAIModelDeploymentName : openAIModelId,
        ReasoningOptions = new OpenAI.Responses.ResponseReasoningOptions
        {
            ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.Medium,
            ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
        },
        StoredOutputEnabled = false
    }
};

#pragma warning restore OPENAI001

// Dynamically set the number of frameworks and decision type -> strategic
var strategicDecisionFrameworkPrompt = CreateDecisionFrameworkPrompt(
    numberOfFrameworks: 3,
    decisionType: "Long-Term strategic with probabilistic outcomes");

var strategicDecisionFrameworkResponseText = await RunDecisionPromptAsync(
    strategicDecisionFrameworkPrompt,
    decisionFrameworkChatOptions);

strategicDecisionFrameworkResponseText.DisplayAs("text/markdown");
Framework Core idea How it supports better long-term strategic decision-making with probabilistic outcomes
Expected Utility / Expected Value Analysis Compares options by estimating the probability-weighted outcomes of each choice, often adjusted for risk preferences. Helps decision-makers quantify tradeoffs under uncertainty, compare alternatives on a common basis, and avoid being misled by the most likely or most dramatic outcome. Useful when strategic choices depend on long-run payoff distributions rather than single-point forecasts.
Decision Trees with Scenario Analysis Maps choices, uncertainties, and possible consequences in a branching structure across multiple future states. Makes complex strategic decisions more transparent by showing how early choices lead to different future paths. Supports better reasoning by clarifying assumptions, revealing critical uncertainties, and identifying where probabilities and payoffs have the greatest impact over time.
Bayesian Updating Revises beliefs and probabilities as new evidence becomes available. Improves long-term decision quality by treating strategy as adaptive rather than fixed. It helps decision-makers continuously update forecasts, reduce overconfidence, and respond rationally to new information in environments where outcomes are uncertain and conditions evolve.

In the example below, the number of frameworks to return has been changed and the type has been changed to Quick to Implement for rapid Decision-Making.

In [15]:
// Dynamically set the number of frameworks and decision type -> requiring fast action
var rapidDecisionFrameworkPrompt = CreateDecisionFrameworkPrompt(
    numberOfFrameworks: 5,
    decisionType: "Quick to Implement for rapid Decision-Making");

// Note: The invocation has NOT changed, only the dynamic prompt inputs have changed.
var rapidDecisionFrameworkResponseText = await RunDecisionPromptAsync(
    rapidDecisionFrameworkPrompt,
    decisionFrameworkChatOptions);

rapidDecisionFrameworkResponseText.DisplayAs("text/markdown");
Framework Core Idea How It Supports Better Analysis and Reasoning in Quick-to-Implement Decision-Making Scenarios
OODA Loop (Observe–Orient–Decide–Act) Make decisions by rapidly cycling through observation, context-setting, choice, and action. Helps you move quickly without freezing on analysis. It encourages fast information gathering, real-time interpretation, and short feedback loops, which is useful when conditions change fast and decisions must be adjusted on the fly.
80/20 Rule (Pareto Principle) Focus on the small number of factors that create most of the impact. Improves speed by directing attention to the highest-leverage variables instead of overanalyzing everything. This makes it easier to identify the few actions most likely to drive results in time-sensitive situations.
SWOT Analysis Evaluate Strengths, Weaknesses, Opportunities, and Threats. Provides a fast, structured snapshot of internal and external factors. It supports clearer reasoning by separating what you control from what you face in the environment, helping you compare options quickly and avoid blind spots.
Decision Matrix / Weighted Scoring Compare options against criteria and assign weights and scores. Reduces bias by making tradeoffs explicit and comparable. In rapid decisions, it helps you evaluate alternatives systematically, even under time pressure, by highlighting the option with the strongest overall fit.
Eisenhower Matrix (Urgent vs. Important) Prioritize tasks based on urgency and importance. Helps you quickly distinguish what needs immediate action from what truly matters strategically. This prevents reactive decisions, improves prioritization, and ensures limited attention goes to the most consequential choices first.

Step 4 - Decision Scenario with Dynamic Decision Intelligence

In the below code a decision-analysis scenario is introduced that uses dynamic inputs to personalize the decision recommendation dynamically. The more specific information that provides contextual information to the Generative AI model can greatly improve the specific recommendations.

Decision Scenario: Michael is a 35-year-old professional chef who is considering opening his own restaurant. This is a significant life decision that could greatly impact his career and personal life. Michael is looking for a recommendation for an approach (decision) for this potentially life-changing decision.

Three factors will be considered for this decision scenario that the user will be prompted for:

  1. Michael's Total Net Worth in dollars (enter number)
  2. Level of competition with other restaurants (low, medium, high)
  3. Level of support from Michael's friends and family (low, medium, high)

Various Decision Inputs: You can simulate different What-If scenarios by varying the input of net worth, restaurant competition and level of family support. Note the different decision recommendations based on this provided by Artificial Intelligence. The recommendations in this example are quite simple, but even on the extreme inputs Generative AI has a concept of understanding the quality of inputs.

In [16]:
// Import the Microsoft.DotNet.Interactive namespace for user input
using Microsoft.DotNet.Interactive;

var totalNetWorth = await Microsoft.DotNet.Interactive.Kernel.GetInputAsync("Michael's total net worth in dollars: ");
var levelOfCompetition = await Microsoft.DotNet.Interactive.Kernel.GetInputAsync("Level of competition with other restaurants (Low, Medium, High): ");
var levelOfFamilySupport = await Microsoft.DotNet.Interactive.Kernel.GetInputAsync("Level of support from Michael's friends and family (Low, Medium, High): ");

Console.WriteLine($"Michael's Net Worth: {totalNetWorth}");
Console.WriteLine($"Level of Restaurant Competition: {levelOfCompetition}");
Console.WriteLine($"Michael's level of Family Support: {levelOfFamilySupport}");
Console.WriteLine();

// Takes dynamic inputs and creates a personalized prompt that can be used to invoke the model.
var restaurantDecisionRecommendationPrompt = $"""
Michael is a 35-year-old professional chef who is considering opening his own restaurant. 
This is a significant life decision that could greatly impact his career and personal life. 
Michael is looking for a recommendation on how to approach this.
Some key information about Michael:
- Michael's total net worth is (in US dollars) ${totalNetWorth}.
- The level of competition with other restaurants is {levelOfCompetition}.
- The level of support from Michael's friends and family is {levelOfFamilySupport}.

Based on this information, what recommendation would you give to Michael regarding opening his own restaurant?

Output Format Instructions: 
When generating Markdown, do not use any headings higher than ###. 
Avoid # and ## headers. Use only ###, ####, or lower-level headings if necessary. 
All top-level section headers should start at ### or lower. 
Never use ---, ***, or ___ for horizontal lines. There should be no horizontal lines in the output.
For separation, use extra extra spacing. Do not any render horizontal lines.
Format the response using only a Markdown table. Only return a Markdown table. 
Do not enclose the table in triple backticks.
Provide a small Decision Summary of the recommendation below the table.
""";


#pragma warning disable OPENAI001

// Create the chat options. Try changing the settings to see how they affect the quality of generated text.
var restaurantDecisionChatOptions = new ChatOptions
{
    RawRepresentationFactory = _ => new OpenAI.Responses.CreateResponseOptions
    {
        Model = useAzureOpenAI ? azureOpenAIModelDeploymentName : openAIModelId,
        ReasoningOptions = new OpenAI.Responses.ResponseReasoningOptions
        {
            ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.Medium,
            ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
        },
        StoredOutputEnabled = false
    }
};

var restaurantDecisionRecommendationResponseText = await RunDecisionPromptAsync(
    restaurantDecisionRecommendationPrompt,
    restaurantDecisionChatOptions);

restaurantDecisionRecommendationResponseText.DisplayAs("text/markdown");

#pragma warning restore OPENAI001
Michael's Net Worth: 10000000
Level of Restaurant Competition: low
Michael's level of Family Support: low

Factor Assessment Implication Recommendation
Financial capacity Michael’s net worth is very high at $10,000,000 He can absorb startup costs, early losses, and operational risk more comfortably than most people Financially, he is well positioned to pursue the restaurant
Market competition Competition is low The business may have a better chance of standing out and capturing demand This is a favorable condition for opening, assuming demand is real and sustainable
Personal support Support from friends and family is low He may face less emotional support and possibly more stress during launch Build a strong external support system: mentors, advisors, and experienced operators
Career fit He is a professional chef He has relevant expertise and likely understands kitchen operations and food quality His background makes this a credible next step, especially if he has a strong concept
Overall risk High personal and operational commitment required Restaurant ownership can affect lifestyle, time, and relationships Use a phased approach rather than a full leap if possible
Recommendation Proceed, but carefully The opportunity looks promising, with manageable financial risk and favorable market conditions Michael should consider opening his own restaurant, ideally after validating the concept, creating a detailed business plan, and securing strong operational support
Decision Summary Recommended approach Best path is a cautious “yes” rather than an impulsive one Open the restaurant only if Michael can test demand, control costs, and surround himself with experienced support; his finances and low competition make this a viable opportunity, despite limited family/friend backing

Native .NET Functions with Microsoft.Extensions.AI

The first part of this notebook used reusable prompt templates. The next section adds native .NET functions with AIFunctionFactory, which lets AI workflows use deterministic C# logic for business rules, calculations, simulations, API calls, and other capabilities that should not be left to prompting alone.

Step 5 - Create a Simple Native .NET Function with AI Extensions

Execute the cell below to create a very simple native .NET function using a C# inline method. Notice the function takes no parameters. It retrieves the name of a productivity decision framework to use. In this case, that return name is hard-coded to "Price's Law".

The function returns instantly because it is not calling a GenAI service. The point is to show how Microsoft.Extensions.AI can wrap deterministic .NET logic as a first-class AIFunction.

In [17]:
// Create an AIFunction from an inline C# method.
var nameOfProductivityFramework = AIFunctionFactory.Create(
    (Func<string>)(() => "Price's Law"),
    new AIFunctionFactoryOptions
    {
        Name = "GetNameOfProductivityFramework",
        Description = "Retrieves the name of the Productivity Framework to use."
    });

// Invoke the function directly with AI Extensions.
var response = await nameOfProductivityFramework.InvokeAsync(new AIFunctionArguments());
Console.WriteLine(GetFunctionResultText(response));
Price's Law

Step 6 - Create a Native .NET Function with Dynamic Parameters

Execute the cell below to create a native .NET function that takes a parameter as input. This shows that C# native functions can have different execution paths. The execution paths can obviously be quite complex. Basically, any C# logic flow will work.

In [18]:
// Create an AIFunction from an inline C# method with parameters.
var nameOfProductivityFrameworkByType = AIFunctionFactory.Create(
    (Func<string, string>)(typeOfProductivity => typeOfProductivity == "Sales" ? "Price's Law" : "Pareto Principle"),
    new AIFunctionFactoryOptions
    {
        Name = "GetNameOfProductivityFramework",
        Description = "Retrieves the name of the Productivity Framework to use."
    });

// Pass the "Sales" parameter to the function.
var salesResponse = await nameOfProductivityFrameworkByType.InvokeAsync(new AIFunctionArguments
{
    ["typeOfProductivity"] = "Sales"
});
Console.WriteLine(GetFunctionResultText(salesResponse));

// Pass the "Other" parameter to the function.
var otherResponse = await nameOfProductivityFrameworkByType.InvokeAsync(new AIFunctionArguments
{
    ["typeOfProductivity"] = "Other"
});
Console.WriteLine(GetFunctionResultText(otherResponse));
Price's Law
Pareto Principle

Step 7 - Use Native .NET Functions To Simulate the Uncertainty of a Decision

📜 "The best way to predict the future is to simulate it. And the best way to simulate it is with Monte Carlo."

-- Nassim Nicholas Taleb (Lebanese-American essayist, scholar, best known for his work on probability)

For more complex decisions, native .NET functions can use statistics, advanced probabilistic algorithms, analytics, machine learning, AI, and other approaches that have been relied on in software for decades. One such method is Monte Carlo Simulation. These powerful Monte Carlo simulation techniques are used everywhere: risk management, sports gambling, medical decision-making, game theory, energy market forecasting, and more. In simple terms, a Monte Carlo simulation is a series of many runs testing different plausible parameters.

A simple use case for a Monte Carlo simulation is to visualize the distribution of possible outcomes for an average probability. Imagine you want to illustrate the uncertainty of a decision that you have calculated to be 70% successful. On "average" the probability of success can be interpreted as 70%. However, if that 70% decision model is run 100 times, the actual number of successful outcomes will vary. A Monte Carlo simulation can show that variation as a distribution.

Run the cell below to create a native .NET function that takes the confidence parameter and returns a Markdown distribution. The output uses a simple horizontal bar chart so the likely outcomes can be scanned quickly, similar to a compact sales distribution chart.

In [19]:
string RetrieveMonteCarloDistributionMarkdown(
    [Description("Claimed Probability Percentage")] int probability)
{
    if (probability < 0 || probability > 100)
    {
        throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between 0 and 100.");
    }

    const int NUMBEROFSIMULATIONS = 100000; // 100,000 simulations, make this smaller for faster results
    const int NUMBEROFDECISIONS = 100;
    const int MAXIMUMBARWIDTH = 24;

    var random = new Random(42); // Fixed seed keeps notebook output reproducible
    var successfulOutcomesPerSimulation = new List<int>();
    var outcomeDistribution = new Dictionary<int, int>();

    for (int i = 0; i != NUMBEROFSIMULATIONS; i++) // Bootstrap Simulations (bootstrap estimates)
    {
        var successfulDecisions = 0;
        for (int j = 0; j != NUMBEROFDECISIONS; j++)
        {
            var randomIndex = random.Next(0, 100);

            if (randomIndex < probability)
            {
                successfulDecisions++;
            }
        }

        successfulOutcomesPerSimulation.Add(successfulDecisions);

        if (!outcomeDistribution.ContainsKey(successfulDecisions))
        {
            outcomeDistribution[successfulDecisions] = 0;
        }

        outcomeDistribution[successfulDecisions]++;
    }

    // Sort the success counts to calculate the central 95% simulated range.
    var successfulOutcomesSorted = successfulOutcomesPerSimulation.OrderBy(a => a).ToList();
    var lowerPercentileIndex = Convert.ToInt32(0.025 * NUMBEROFSIMULATIONS);
    var topPercentileIndex = Convert.ToInt32(0.975 * NUMBEROFSIMULATIONS);
    var lowerPercentile = successfulOutcomesSorted[lowerPercentileIndex];
    var upperPercentile = successfulOutcomesSorted[topPercentileIndex];

    var visibleOutcomes = Enumerable.Range(lowerPercentile, upperPercentile - lowerPercentile + 1).ToList();
    var maximumOutcomeCount = visibleOutcomes.Max(outcome =>
        outcomeDistribution.ContainsKey(outcome) ? outcomeDistribution[outcome] : 0);

    var distributionMarkdown = new System.Text.StringBuilder();
    distributionMarkdown.AppendLine($"### Monte Carlo Distribution ({probability}% Success Model)");
    distributionMarkdown.AppendLine();
    distributionMarkdown.AppendLine("```text");
    distributionMarkdown.AppendLine($"{NUMBEROFSIMULATIONS:n0} simulations, {NUMBEROFDECISIONS} decisions per simulation");
    distributionMarkdown.AppendLine($"Showing central 95% simulated range: {lowerPercentile} to {upperPercentile} successful outcomes");
    distributionMarkdown.AppendLine();

    foreach (var outcome in visibleOutcomes)
    {
        var simulationsAtOutcome = outcomeDistribution.ContainsKey(outcome) ? outcomeDistribution[outcome] : 0;
        var barWidth = simulationsAtOutcome == 0
            ? 0
            : Math.Max(1, Convert.ToInt32(Math.Round((double)simulationsAtOutcome / maximumOutcomeCount * MAXIMUMBARWIDTH)));
        var bar = new string('█', barWidth).PadRight(MAXIMUMBARWIDTH);
        distributionMarkdown.AppendLine($"{outcome,3} successes | {bar} {simulationsAtOutcome:n0}");
    }

    distributionMarkdown.AppendLine("```");
    return distributionMarkdown.ToString();
}

Run the cell below to wrap the native .NET method as an AIFunction and invoke it directly. This will run a Monte Carlo Simulation with 100,000 simulations of a decision with a confidence (probability) of 70% being run 100 times. The result is displayed as a Markdown distribution so the uncertainty pattern is easier to scan than a single interval sentence.

In [21]:
var retrieveMonteCarloDistribution = AIFunctionFactory.Create(
    (Func<int, string>)RetrieveMonteCarloDistributionMarkdown,
    new AIFunctionFactoryOptions
    {
        Name = "RetrieveMonteCarloDistributionMarkdown",
        Description = "Generates a Markdown distribution from a Monte Carlo simulation."
    });

// Change the probability to another integer and invoke the function to see other outcome distributions.
var monteCarloDistribution70 = await retrieveMonteCarloDistribution.InvokeAsync(new AIFunctionArguments
{
    ["probability"] = 70
});

var monteCarloDistributionMarkdown70 = GetFunctionResultText(monteCarloDistribution70);
monteCarloDistributionMarkdown70.DisplayAs("text/markdown");

Monte Carlo Distribution (70% Success Model)

100,000 simulations, 100 decisions per simulation
Showing central 95% simulated range: 61 to 79 successful outcomes

 61 successes | ████                     1,343
 62 successes | █████                    1,955
 63 successes | ███████                  2,653
 64 successes | ██████████               3,516
 65 successes | █████████████            4,655
 66 successes | ████████████████         5,713
 67 successes | ██████████████████       6,654
 68 successes | █████████████████████    7,764
 69 successes | ███████████████████████  8,301
 70 successes | ████████████████████████ 8,717
 71 successes | ███████████████████████  8,480
 72 successes | ██████████████████████   7,969
 73 successes | ████████████████████     7,311
 74 successes | █████████████████        6,184
 75 successes | ██████████████           5,016
 76 successes | ███████████              3,919
 77 successes | ████████                 2,891
 78 successes | █████                    1,911
 79 successes | ████                     1,275