Exercise - Use a plan template

Completed

You can tune the plans generated by the Handlebars planner. In this exercise, you generate a plan, tune the template, and use it as a function prompt. Let's try it out!

  1. In your 'Program.cs' file, update your code to the following:

    var planner = new HandlebarsPlanner(new HandlebarsPlannerOptions() { AllowLoops = true });
    
    string location = "Redmond WA USA";
    string goal = @$"Based on the user's recently played music, suggest a 
        concert for the user living in ${location}";
    
    var concertPlan = await planner.CreatePlanAsync(kernel, goal);
    // output the plan result
    Console.WriteLine("Concert Plan:");
    Console.WriteLine(concertPlan);
    

    Now you're able to see the generated plan. Next, you add a quick function to suggest a song to the user.

  2. Add the following code to your 'Program.cs' file:

    var songSuggesterFunction = kernel.CreateFunctionFromPrompt(
        promptTemplate: @"Based on the user's recently played music:
            {{$recentlyPlayedSongs}}
            recommend a song to the user from the music library:
            {{$musicLibrary}}",
        functionName: "SuggestSong",
        description: "Suggest a song to the user"
    );
    
    kernel.Plugins.AddFromFunctions("SuggestSongPlugin", [songSuggesterFunction]);
    
    var songSuggestPlan = await planner.CreatePlanAsync(kernel, @"Suggest a song from the 
        music library to the user based on their recently played songs");
    
    Console.WriteLine("Song Plan:");
    Console.WriteLine(songSuggestPlan);
    

    In this code, you create a function from a prompt that suggests a song to the user. Then you add it to the kernel Plugins. Finally, you tell the planner to create a plan to accomplish the goal of suggesting a song to the user.

  3. Enter dotnet run in the terminal to see the output of the plans you created.

    You should see a template similar to the following output:

    Concert Plan:
    {{!-- Step 1: Identify key values --}}
    {{set "location" "Redmond, WA, USA"}}
    
    {{!-- Step 2: Call the 'MusicLibraryPlugin-GetRecentPlays' helper to get the recently played music --}}     
    {{set "recentlyPlayedSongs" (MusicLibraryPlugin-GetRecentPlays)}}
    
    {{!-- Step 3: Call the 'MusicConcertsPlugin-GetConcerts' helper to get the list of upcoming concerts --}}   
    {{set "upcomingConcerts" (MusicConcertsPlugin-GetConcerts)}}
    
    {{!-- Step 4: Call the 'Prompts-SuggestConcert' helper to suggest a concert based on the provided inputs --}}
    {{set "suggestedConcert" (Prompts-SuggestConcert input=location recentlyPlayedSongs=recentlyPlayedSongs upcomingConcerts=upcomingConcerts)}}
    
    {{!-- Step 5: Output the suggested concert --}}
    {{json suggestedConcert}}
    SongPlan
    {{!-- Step 1: Identify key values --}}
    {{set "recentlyPlayedSongs" (MusicLibraryPlugin-GetRecentPlays)}}
    {{set "musicLibrary" (MusicLibraryPlugin-GetMusicLibrary)}}
    
    {{!-- Step 2: Call custom helper to suggest a song --}}
    {{set "suggestedSong" (SuggestSongPlugin-SuggestSong recentlyPlayedSongs musicLibrary)}}
    
    {{!-- Step 3: Output the suggested song --}}
    {{json suggestedSong}}
    

    Next, you use these generated templates to create your own Handlebars plan. You want to use the steps from the Concert Plan if the user requests a concert, or the steps from the Song Plan if the user requests a song.

  4. Create a new file named 'handlebarsTemplate.txt' with the following text:

    {{#if suggestConcert}}
        {{!-- Step 1: Identify key values --}}
        {{set "location" location}} 
    
        {{!-- Step 2: Call the 'MusicLibraryPlugin-GetRecentPlays' helper to get the recently played music --}}     
        {{set "recentlyPlayedSongs" (MusicLibraryPlugin-GetRecentPlays)}}
    
        {{!-- Step 3: Call the 'MusicConcertsPlugin-GetConcerts' helper to get the list of upcoming concerts --}}   
        {{set "upcomingConcerts" (MusicConcertsPlugin-GetConcerts)}}
    
        {{!-- Step 4: Call the 'Prompts-SuggestConcert' helper to suggest a concert based on the provided inputs --}}
        {{set "suggestedConcert" (Prompts-SuggestConcert input=location recentlyPlayedSongs=recentlyPlayedSongs upcomingConcerts=upcomingConcerts)}}
    
        {{!-- Step 5: Output the suggested concert --}}
        {{json suggestedConcert}}
    {{else}}
        {{!-- Step 1: Identify key values --}}
        {{set "recentlyPlayedSongs" (MusicLibraryPlugin-GetRecentPlays)}}
        {{set "musicLibrary" (MusicLibraryPlugin-GetMusicLibrary)}}
    
        {{!-- Step 2: Call custom helper to suggest a song --}}
        {{set "suggestedSong" (SuggestSongPlugin-SuggestSong recentlyPlayedSongs musicLibrary)}}
    
        {{!-- Step 3: Output the suggested song --}}
        {{json suggestedSong}}
    {{/if}}
    

    Notice the {{#if ...}} syntax. This syntax acts as a conditional statement that the Handlebars planner can use, similar to a traditional if-else block in C#. The if statement must be closed with {{/if}}. You also add a {{set ...}} statement that sets the user's location to the location variable.

    In this template, if the suggestConcert variable is true, the planner completes the steps to suggest a concert. Otherwise, it completes the steps to suggest a song. Let's try it out!

  5. Remove the handlebars plans by modifying your existing code:

    var builder = Kernel.CreateBuilder();
    builder.Services.AddAzureOpenAIChatCompletion(
        "your-resource-name",
        "your-endpoint",
        "your-resource-key",
        "deployment-model");
    var kernel = builder.Build();
    kernel.ImportPluginFromType<MusicLibraryPlugin>();
    kernel.ImportPluginFromType<MusicConcertsPlugin>();
    kernel.ImportPluginFromPromptDirectory("Prompts");
    
    var songSuggesterFunction = kernel.CreateFunctionFromPrompt(
        promptTemplate: @"Based on the user's recently played music:
        {{$recentlyPlayedSongs}}
        recommend a song to the user from the music library:
        {{$musicLibrary}}",
        functionName: "SuggestSong",
        description: "Suggest a song to the user"
    );
    
    kernel.Plugins.AddFromFunctions("SuggestSongPlugin", [songSuggesterFunction]);
    
  6. Read your template file and create a function with the following code:

    string dir = Directory.GetCurrentDirectory();
    string template = File.ReadAllText($"{dir}/handlebarsTemplate.txt");
    
    var handlebarsPromptFunction = kernel.CreateFunctionFromPrompt(
        new() {
            Template = template,
            TemplateFormat = "handlebars"
        }, new HandlebarsPromptTemplateFactory()
    );
    

    In this code, you pass a Template object to the kernel method CreateFunctionFromPrompt along with the TemplateFormat. CreateFunctionFromPrompt also accepts an IPromptTemplateFactory type that tells the kernel how to parse a given template. Since you're using a Handlebars template, you use the HandlebarsPromptTemplateFactory type.

    Next let's run the function with some arguments and check out the results!

  7. Add the following code to your Program.cs file:

    string location = "Redmond WA USA";
    var templateResult = await kernel.InvokeAsync(handlebarsPromptFunction,
        new() {
            { "location", location },
            { "suggestConcert", false }
        });
    
    Console.WriteLine(templateResult);
    
  8. Enter dotnet run in the terminal to see the output of your planner template.

    You should see a response similar to the following output:

    Based on your recently played music, I recommend you listen to the song "Luv(sic)" by Chishin. It falls under the genres of hiphop and rap, which aligns with some of your recently played songs. Enjoy!  
    

    The prompt was able to suggest a song to the user based on the list of recently played music. You can also try setting the suggestConcert variable to true and see what happens!

Important

Be sure not to delete any of the code you wrote so far since it's needed for the next exercise.