DEV Community

Cover image for Give your LLM Vibe Coding Superpowers using .Net 10
Peter Bons
Peter Bons

Posted on

Give your LLM Vibe Coding Superpowers using .Net 10

This blog post demonstrates how to build an AI agent using dotnet that is capable of writing and executing C# code to perform complex tasks.

Introduction

It is an exciting time to live in when it comes to generative AI. Long gone are the days when an LLM is merely capable of generating responses based on the data they are trained on. Nowadays they are capable of doing much more by giving them the proper tools for example. Initiatives like the Model Context Protocol (MCP) are driving a new era and MCP servers are popping up everywhere.

Another term you might have heard of is "vibe coding". According to ChatGPT it means:

Vibe coding with AI is like whispering your app dreams to a super-fast coder who never sleeps—you describe what you want (“make the button red,” “add cat purring”), and the AI writes it. You review, tweak, rinse, repeat… no need to grok every line. It’s part brainstorming session, part code genie — fast, fun, and sometimes buggy magic! 😜

And, in other news, Microsoft has already started working on .Net 10. Lots of new goodies and stuff but one thing that stands out is the ability to do something like dotnet run app.cs. Yes, that's right: single file scripts are finally coming our way.

With those 3 ingredients, LLMs, tool calling and .Net 10, we can create our own vibe coding agent to accomplish tasks that go beyond answering simple questions based on the data it is trained on. So let's get dive in! For the magic we will use the Microsoft.Extensions.AI libraries.

Setting up the stage

Let's start with importing some required packages and setting up the chat client. I use a model hosted on Azure AI Foundry but it can be any model hosted anywhere as long as it supports tool calling.

#:package Azure.AI.OpenAI
#:package Microsoft.Extensions.AI
#:package Microsoft.Extensions.Logging.Console@10.0.0-preview.5.25277.114
#:package Microsoft.Extensions.AI.OpenAI@9.5.0-preview.1.25265.7

using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using System.ClientModel;
using System.Diagnostics;

var runId = Guid.NewGuid();

var azureOpenAIEndpoint = "https://<your-endpoint>.cognitiveservices.azure.com/";
var azureOpenAIModelId = "gpt-4.1";

using var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Information).AddConsole());

var client =
    new ChatClientBuilder(
        new AzureOpenAIClient(new Uri(azureOpenAIEndpoint), new ApiKeyCredential("<your-api-key>"))
        .GetChatClient(azureOpenAIModelId).AsIChatClient())
    .UseLogging(loggerFactory)
    .UseFunctionInvocation(loggerFactory)
    .Build();
Enter fullscreen mode Exit fullscreen mode

So now we got the basic stuff, what's next? We already know that LLMs are capable of writing code, so that is covered. However, we need to give the LLM code execution capabilities as well if we want to succeed. Let's write a tool for that. In the code below we have defined a method that takes a string for input, write that string to a file and execute the code using dotnet run. There is some ceremony to be able to track the different executions in there as well so we can see how the code evolves when the model encounters compile time or runtime errors.

var chatOptions = new ChatOptions
{
    Tools = [AIFunctionFactory.Create((string code) =>
    {
        var logger = loggerFactory.CreateLogger("CodeExecution");

        var codeFileName = @$"c:\temp\{DateTime.Now.ToString("HHmmssfff")}-{runId}.cs";
        File.WriteAllText(codeFileName, code);
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "dotnet",
                Arguments = $"run {codeFileName}",
                RedirectStandardOutput = true
            }
        };

        process.Start();
        process.WaitForExit(TimeSpan.FromMinutes(1));

        var output = process.StandardOutput.ReadToEnd();
        logger.LogInformation("Code execution output: {Output}", output);

        return output;
    },
    description: "Execute the provided code.")]
};
Enter fullscreen mode Exit fullscreen mode

The general idea is that we provide the LLM with a tool using ChatOptions, ask the LLM to perform a task using a prompt and have it automatically invoke the tool if it thinks it is needed to generate an answer. We enable this by calling UseFunctionInvocation.

Now all we need is a system prompt that explains the model how to handle a given task. Finally, we need a prompt defining the task at hand:

var history = new List<ChatMessage>
    {
    new(ChatRole.System, """
        Write code to answer the question. 

        If you need to use a NuGet package, specify the package name in the code like this: #:package PackageName. Some examples:
        #:package Humanizer@9.5.0-preview.1.25265.7
        #:package SemanticKernel
        #:package Newtonsoft.Json

        A code block should start with the required NuGet packages, if any, followed by one or more using statements, followed by the code.
        You can directly write code statements without a program class with a main method.

        ### Example 1
        #:package Humanizer@1.2.8
        #:package SomePackage

        using System;

        Console.WriteLine("123".Humanize());

        ### Example 2
        #:package Microsoft.Extensions.Logging.Console

        using System.IO;

        var text = await File.ReadAllTextAsync("c:\\temp\\file.txt");
        Console.WriteLine(text);

        ###

        Write and execute code to answer the question. Do not ask the user for any input, just complete all the steps below
        by writing and executing the code without asking for clarification or consent.

        """),
    new(ChatRole.User,
        """
        1. Fetch the csv file from the url 'https://raw.githubusercontent.com/fgeorges/star-wars-dataset/refs/heads/master/csv/films/films.csv'
        2. Analyze the content and determine the delimiter used in the csv file. Keep in mind that text columns may contain commas as well, e.g. "A New Hope, Episode IV".
        3. Determine which columns are present in the csv file and what data the columns contain.
        4. Order the movies by release date, Newest first.
        5. Write a .docx word document at 'c:\\temp\\star_wars_movie.docx' with one line per page with the following information:
            - release date
            - title
            - description
        """
        )
    };
Enter fullscreen mode Exit fullscreen mode

If we take a closer look at the system prompt you'll see that it has clear instructions on how to reference NuGet packages and how to structure the code, accompanied with some examples. The reason for this is that LLMs are trained on lots of existing C# code that is written before .Net 10 was introduced. Without that it will generate an old school Program class with a Main method and won't be able to reference packages. It took some prompt tweaking to "unlearn the old way" of writing a simple program.

The final step is to feed the model the prompts and wait for it to complete its vibe coding session. 😄

var result = await client.GetResponseAsync(history, chatOptions);
Console.WriteLine($"LLM response: {result}");
Enter fullscreen mode Exit fullscreen mode

Reviewing the performance

The whole code file can be found as a gist here. Save it and then run the code using dotnet run vibecoding_agent.cs

If everything is ok you will see one or more files popping up in C:\Temp. If the LLM is in a good mood it will be just one but more often it needs a few iterations before a correct code file is written. Since the model is fed with the output of the dotnet run command it can detect compile time and runtime errors and rewrite the code to handle that.

The output of dotnet run is the response of the LLM, my last execution resulted in this response:

LLM response: Analysis and Results:

  1. Delimiter: The delimiter used in the CSV is a comma (,).

  2. Columns present:

    • title
    • episode_id
    • opening_crawl
    • director
    • producer
    • release_date
    • created
    • edited
    • url
    • desc
  3. Data in Columns:

    • title: The movie title (e.g. "A New Hope").
    • episode_id: The Star Wars episode number.
    • opening_crawl: The long Star Wars intro text that appears at the start of the film.
    • director: Film director name(s).
    • producer: Producer name(s).
    • release_date: Release date of the movie (e.g. "1977-05-25").
    • created, edited: Timestamps (likely for internal dataset management).
    • url: URL reference for the film.
    • desc: Another description, but appears redundant with opening_crawl.
  4. Word Document Output:

    The movies are ordered by release date, newest first, and written one per page. Each page includes release date, title, and description (using opening_crawl).

File written:

c:/temp/star_wars_movie.docx

Summary of document:

  • The document contains 7 movies, each on its own page, with release date, title, and description.

Let me know if you want to see the content or need a different format!

The full output, including the final word document and all the iterations can be found here.

Final Thoughts

I've tried different models and the results vary. Some models stop half way, present the code to the user and ask whether to continue or not. Others will write the code but ask you to execute it, they seems to ignore the tools presented even though they are capable of tool calling. It takes some trial and error to craft a good prompt and fix this behavior.

Interesting as it might be, it comes with a strong warning: the code should only ever run in an isolated, sandboxed environment because there is absolutely no guarantee that malicious code is executed or flawed dependencies are used.

So where does that leave us? What would be the use case for these kind of agents? I don't know. I was inspired by AutoGen for .Net, which has the capability to run code snippets. I think .Net 10 helps lower the barrier to create Agents with coding capabilities.

So, what do you think? Do you see this as a useful feature or .. ? I am really curious about your opinion, so don't hesitate to drop a comment below!

Top comments (0)