.NETOpenAIC#Tutorial

How to Use Function Calling with OpenAI in C#

Function calling lets an OpenAI model trigger your C# code. A step-by-step guide to defining functions, letting the model call them, and returning the result — in .NET.

Function calling is how you let an OpenAI model do things instead of just talking. You describe some functions, the model decides when one is needed, and your C# code runs it and hands back the result. It’s the mechanism behind every tool-using assistant. This guide walks through it end to end in .NET.

If you’re building full agents, the Microsoft Agent Framework wraps this loop for you — but understanding the raw mechanism is worth it.

The idea in one sentence

You send the model your message plus a list of function definitions (name, description, parameter schema). The model either answers directly, or replies “call getWeather with {city: 'Chennai'}.” You run that function, send the result back, and the model uses it to form its final answer.

Step 1: Define a function the model can call

Describe the function so the model knows what it does and what arguments it needs — the description quality directly affects whether the model calls it correctly:

using OpenAI.Chat;

ChatTool getWeatherTool = ChatTool.CreateFunctionTool(
    functionName: "get_weather",
    functionDescription: "Gets the current weather for a city.",
    functionParameters: BinaryData.FromString("""
    {
      "type": "object",
      "properties": {
        "city": { "type": "string", "description": "The city name" }
      },
      "required": ["city"]
    }
    """));

Step 2: Send the request with the tool attached

var client = new ChatClient("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

var options = new ChatCompletionOptions();
options.Tools.Add(getWeatherTool);

List<ChatMessage> messages = [new UserChatMessage("What's the weather in Chennai?")];

ChatCompletion completion = await client.CompleteChatAsync(messages, options);

Step 3: Handle the model’s tool call

If the model decided to call your function, completion.ToolCalls is populated. Run the real function, then send the result back so the model can finish:

if (completion.FinishReason == ChatFinishReason.ToolCalls)
{
    messages.Add(new AssistantChatMessage(completion));  // record the model's request

    foreach (var call in completion.ToolCalls)
    {
        if (call.FunctionName == "get_weather")
        {
            var args = JsonDocument.Parse(call.FunctionArguments);
            string city = args.RootElement.GetProperty("city").GetString()!;

            string result = GetWeather(city);   // your real C# method
            messages.Add(new ToolChatMessage(call.Id, result));
        }
    }

    // Ask again — now the model has the tool result and can answer
    completion = await client.CompleteChatAsync(messages, options);
}

Console.WriteLine(completion.Content[0].Text);

That’s the whole loop: request → model asks for a tool → you run it → send the result → model answers. For multiple or chained calls, you repeat until FinishReason is no longer ToolCalls.

Validate the arguments

The model’s arguments are model-generated, so treat them as untrusted input: validate types and ranges before doing anything with side effects. A model asking to call delete_account doesn’t mean your code should skip its own authorization checks — see securing AI agents.

Function calling vs. just asking for JSON

Function calling is also a reliable way to get structured output: the parameter schema is a JSON schema the model conforms to. If all you need is structured data (not an action), that’s a valid use of the same mechanism.

Note: OpenAI SDK type names (ChatTool, ToolChatMessage) shift between versions. Verify against the OpenAI .NET SDK; the loop — define, send, handle tool call, return result, repeat — is stable, and it’s what higher-level frameworks automate.

Takeaway

Function calling turns an OpenAI model into something that can act: define functions with clear descriptions and schemas, attach them to the request, and when the model asks, run the real C# method and hand back the result. Validate the arguments, loop until the model stops calling tools, and you have the core of every AI assistant — the same loop the Agent Framework wraps so you don’t write it by hand.


Have a correction or a topic you want covered? Email mani.bc72@gmail.com.