Main

Dynamic Chaining in #CopilotStudio ✨| 🎟️ Win EPPC tickets 🎉

In this video, I explore the differences between two powerful features: Plugin Actions and Dynamic Chaining Topics. I explain why you may use one over the other. 🚀 Also, learn how to leverage Adaptive Cards within Copilot Studio. I demonstrate how to choose records and execute plugin actions on selected items. I also delve into three strategies for handling translations for multi-language copilots. #powerapps #powerplatform #copilotstudio #tutorial #ai #multilingual 📺Watch part 1: https://youtu.be/OQsvSmXI2As?si=mqS0DFdCDENBcjn4 🎫Win a 2-Day Ticket to the European Power Platform Conference! 🎉 I have the privilege to give away a 2-day ticket to the European Power Platform Conference in Brussels. Details on how to participate are in the video – don’t miss out! https://bit.ly/win-eppc-ticket-with-scott 🌐 European Power Platform Conference 2024: Join us in Brussels from June 11-13, 2024, at the Square. Connect with fellow enthusiasts, learn from global experts, and immerse yourself in the Power Platform community. 🚀 ----------------------------- 00:00 Introduction 02:05 Plugin Action Recap 02:50 Validating Inputs using Topics 05:13 Adaptive card to show results and to perform an action 07:19 Multi-language copilots with validation of inputs 08:58 Translating list and dynamic text 11:31 Translating adaptive cards and static text 13:12 News and competition time! ----------------------------- Code snips: Translations: { OrderStatus:[ { key: "Pending", Name: "Pending", Language:"English" }, // ... // Norwegian Translations { key: "Pending", Name: "Avventer", Language:"Norwegian" }, // ... ], Date:[ { Name:"Date", Language:"English" }, { Name:"Daddel", Language:"Norwegian" } ] // ... } Adaptive card: { '$schema': "http://adaptivecards.io/schemas/adaptive-card.json", type: "AdaptiveCard", version: "1.5", body: [ { type: "Container", items: [ { type: "TextBlock", text: "Order List", weight: "Bolder", size: "Large" } ] }, { type: "Container", items: ForAll(Topic.OrderListByStatus, { type: "Container", items: [ { type: "FactSet", facts: [ { title: "ID", value: Text(ThisRecord.id) }, { title: LookUp(Global.Translations.Date,Language=Text(System.User.Language)).Name, value: Text(ThisRecord.orderDate,"dd MMM yyyy") }, { title: LookUp(Global.Translations.Amount,Language=Text(System.User.Language)).Name, value: Text(ThisRecord.orderTotal,"$#,####") }, { title: LookUp(Global.Translations.Status,Language=Text(System.User.Language)).Name, value: LookUp(Global.Translations.OrderStatus,Language=Text(System.User.Language) && key = status).Name } ] }, { type: "ActionSet", actions: [ { type: "Action.Submit", title: $"{LookUp(Global.Translations.MarkAs,Language=Text(System.User.Language)).Name} {LookUp(Global.Translations.OrderStatus,Language=Text(System.User.Language) && key = "Shipped").Name}", data: { orderId: Text(ThisRecord.id), action: "Shipped" } }, { type: "Action.Submit", title: $"{LookUp(Global.Translations.MarkAs,Language=Text(System.User.Language)).Name} {LookUp(Global.Translations.OrderStatus,Language=Text(System.User.Language) && key = "Delivered").Name}", data: { orderId: Text(ThisRecord.id), action: "Delivered" } } ] } ] } ) } ] }

Scott Durow

8 days ago

Hi, Hi friends. Well, in part one of this video, I showed you how to use plugin actions in Copilot Studio to call an API and then use the magic of AI to pass the parameters and then show the response in a human readable form. If you've not seen that video, go and check it out here. And it does feel like magic, right? But we do need to use AI responsibly and appropriately. So if you want an example of this here inside Copilot Studio, I can create a prompt plugin, ask a very simple question. How m
any letter Ns are there in the word enter? I'll actually create a little parameter here to put the word in and then test it out. Oddly enough, it gives us the wrong answer, two. And for another example, we ask how long does it take to dry four towels if it takes two hours to dry two towels? And what happens is it thinks it's a proportional maths problem and it says four hours. When it's very obvious to us it's only going to take two hours. Well, unless you take air humidity and surface area into
account, maybe it's got a point. The plug in actions allow us to interact with external data and use the AI to interpret what we're asking it to do and then interpret the response. And it's great for doing that because it doesn't really require any significant understanding of the context. So in this video, I'm going to compare plugin actions with another feature of Copilot Studio, which is dynamic chaining topics. And I'm also going to show how to use adaptive cards to choose records. And then
some of the nuances of creating multi language copilots. The key to understanding the difference between plugin actions and topics is that topics have more control. You get to wrap up your logic in a more orchestrated way using Power Fx and branching logic. Whilst plugin actions represent a single call to a custom connector API all wrapped up in that AI powered parsing of parameters and showing the response. You do get a little bit of control in plugin actions by being able to specify the descr
iption which acts essentially as a system prompt and you can also supply a custom message as a response or even an adaptive card but it's still a single operation. Topics can be a lot more orchestrated and have many many different steps. So how do you get those topics to act in the same way as those plugin actions and automatically extract the parameters rather than the old fashioned bot approach of having this step based question and answer conversation? And stick around to the end of the video
where you can win a two day ticket to EPPC. So here's this little app that we created last time where it lists the orders and this is all using a custom connector and it's calling Visual Studio. So if I go over here and I put a little breakpoint. Get Orders. And then go back to my app here, I can ask a question, list all orders that have the status shipped. So you can see here it's calling my custom connector, which is calling my Web API in Visual Studio, via a Dev Tunnel. And check out my last
video to see how I did that! So what it's doing here is it's formatting this in a Human Readable Form even though it's coming back from that Web API in a JSON object. If we go down to our Plugin Action here, we've got Inputs and we've got outputs and we're giving a little bit of information to the AI, how to interpret the input. So it's great because very little effort. You can then call your own custom connector and actually interact with it. What happens if I want to validate perhaps the inpu
t, more control over it to check that the status is one of the statuses that is available. Now entities gives us the ability to create lists of things. So you can see here, I've got a list of all the different statuses and I've called it order status. It's like a choice. Now if we go into plugins and topics and I deactivate my get orders by status plugin, I go and create another topic. Create it from blank here. Give it a description. Allows the user to list orders and it also gives an example.
And next we can add in a question here. And this is where we can now select order status. And then we can select which ones we want to display. And I'm going to set. Store it in a order status choice. So saving that, we can then test that out. And so we can see here that it is asking us for these various different options. So I can select processing and then it set that variable. But what we want to do is make it the same as the plugin action, where processing is automatically or In this case, s
hipped is automatically picked up from that initial message. So the way we can do that is if we go into settings and if we come down to generative AI, we need to make sure that the dynamic chaining is turned on. And then if we go back into our topic, select the variable, and there's this option here to receive values from other topics. That's very important that we check that. So if we save that, now if we do the same thing again, now what we, what we're seeing here is this error message. And th
at's because closed list option sets, entity lists in this case, are not supported at the moment for that dynamic chaining extraction. So what we can do is we just need to change this identify to be user's entire response. So it's only gonna, it's gonna pick up a string. And of course we now need to change that variable to be a, a string type. And we'll select that receive values from other topics again. And then let's output the value from that we've actually got. So send a message here. Let's
try again. And there we go. We've actually now extracted shipped from that initial message. So it's acting the same as our plugin action. So now we've actually got that. We can go and make a call to our connector, list orders by status, and we can specify the variable to use as the status that we want to list. And then it's going to give us this output. So of course we needed to show this output. Now before with our plugin action, That was just automatically working for us because it was using t
he AI to just create an answer. But in this case, what we're going to do is we're going to have to use an adaptive card. We can use the adaptive card to perform actions on the results. Well, I can come into the adaptive card designer, which is pretty good. You can go and drag and drop items. And since we're talking about the magic of AI, let's use Copilot inside VS Code to generate our adaptive cards. I've got a little prompt here to ask it to create an adaptive card. So you can see here it's do
ne it and it's also creating an action set to mark as shipped or delivered for each of these items. So I'm gonna go and copy that. I'm gonna paste this into here, and then we can change this into a formula which allows us to use Power Fx, which is really cool. Inside our adaptive card, the first thing we need to do is need to make each of the items in here its own little container that can be re repeated. Now we can wrap that up in a for all. so that it gets repeated for each of the individual o
rder statuses. And so we got here, we've now got the action. And we've got the Order ID that's coming out of that Adaptive Card. And so each of these correspond to the Action Items. Order ID and Action. So the next thing is we can now, the great thing is we can now call our Plugin Action. Which is Update Order. And then we can set a value which is the ID of the order which has come from that submit button, and we have another one, which again comes from the Submit button. So let's test that. Oh,
we've got one error, and that's because it's getting a string when it's actually expecting a number. Let's put, change that to be a expression. We can just simply say value. But we also need to, of course, make the adaptive card dynamic. So let's go down here. At the moment this is all hard coded, so let's try this out then. And actually the order ID here needs to go to be the same thing. The order ID, like that. Let's save that and let's give it a go. So, listing orders that are status shipped
, so we should see all of the orders. in that status, looking promising. There we go, we've got two orders and status shipped. Now if we mark as delivered, now that should call our plug in action. And you can see here we've got an order, and it's got delivered. So if we carry on, and we get order updated, because we've just called that plug in action, which we've already implemented. So what about creating a multi language copilot? If I come over into languages here, I can very easily add anothe
r language. I'm going to add in Norwegian. And that means our plugin actions now automatically speak Norwegian, which is pretty cool. So I'm going to look at this plugin action, which is get orders by ID. And one of the inputs is just simply order ID. So let's switch over to Norwegian. Details for 10001. And you can see it's translated the response and it's translated shipped into sent. Let's try a different order. The problem now is it hasn't translated processing, so you can see it's a little
bit unpredictable. But what we can do is we can add in a little prompt here to say always translate the status into the current user's language. But I do need to switch that over to English to do that, to update, because you always need to update your copilot in the primary language. So I'll save that and we'll go back into Norwegian. And now let's see what it comes up with. So we're asking for the same details. And you can see now that prompt that we've said translate into the user's language i
s actually meant that it responds with the correct language. So that's great. I mean, you can see that is kind of magic. Well, what about updating with statuses or retrieving with statuses where I actually need to provide that status? So you can see here, I'm actually asking it to retrieve all the orders in the status shipped. So what you can see here is that the copilot's translated what the user provided, but it's translated into the wrong value. Translated it to sent, not shipped. So we need
a mechanism to make sure that the copilot is sending the right values to our custom connector. Now there is a very easy way of downloading a file of translation strings that we can then re upload for static text. But in this case we have dynamic text the user is entering. So if we go into the Conversation Start, we can create a variable that can be used throughout our custom copilot, which is going to hold all of our translations. So we'll set a variable called translations, and we're going to m
ake this variable, a global variable, so that it can be used in any different topics. And we're going to set it to an expression value. And this expression contains translations for order status. So there's the English versions. And then we've got the Norwegian versions. And of course they could also include different synonyms, but we've also got translations for date, amount, status, other things that we're going to use in our adaptive card. And this is different to downloading the resource fil
e, the JSON or ResX, because this is going to be dynamic. We'll save that. And then if we go into our GetOrderByStatus, now we can add in a validation step. The users should have provided an order status, but we can now check that that matches one of the values in our translations. So we'll select the value to say it's going to set the order status string that's already been identified, but we're going to use a expression to look up against our translation variable, where the OrderStatusString e
quals the name in that OrderStatus collection. And then we're going to pick up the key and the key is the system value, not the name that the user has provided. So we'll insert that. What happens if they haven't entered a value that's recognized? Well, here, what we need to do is provide a branch. So we'll add a condition to check if the value that's being looked up, it's been found or not. Check the OrderStatusString if it. Now what we need to do is ask a question to identify from a list of val
ues. Options from a list variable, and the values that we're going to provide are going to come from that translation. So let's put in a formula. So we're filtering, only giving us the order status names where the language equals the user's language. So we'll insert that. Now I've just typed in English there, but what I'm going to show in a minute is how you then translate that into the user's language using the resource file. We'll change this variable name to be status choice. That's going to
give us the value, which is the translated value, but we also need to, if we copy this. translate that back into the system version. So we'll add in another step here and change this expression to use the order status choice. So we'll save that and let's test our copilot. So list all orders in the status back order. So this now should give us a list of order statuses to select from because back order doesn't exist. And there we go. We've got the list of order statuses. So I select one and there
we go. It gives us the list. Now let's try this in Norwegian. List all orders in the status shipped, which is sent. So it's correctly listed them, but you can see here that there are still bits of text here, which are in English. The first one to tackle is the adaptive card, and then we'll go and tackle the text. If we go back into English and open up this adaptive card here, at the moment these are all hard coded, but what we can do is replace all the bits of text with a lookup to the translati
on object that we created. So you can see here we're looking up date, and then the amount, and the status, and we're also looking up the actual status. Value in the local language. And we're doing the same for the actions, providing the button label with a mark as and for ship to mark as for delivered. And the last piece of the jigsaw is we go into languages. We can now download a file which contains a row for all the pieces of text inside our copilot. And interestingly, we do have a row for all
the different values in our closed list items or order items. We could translate that if we were using it. But in this case, we want to translate the values for the get order status. And I'm gonna use copilot to do that. We'll accept that. And we're also going to translate the welcome message as well. And this is amazing, this is using GitHub Copilot, which really speeds up your day to day work. And we'll accept and then save. And back inside Copilot we can go and select that file that we've ju
st saved and updated with the Norwegian translations and upload those. Let's switch back to Norwegian and give it a go. So you can immediately see that the welcome message is in Norwegian. We've got a message there that we're listing them in the status shipped. And we've got all the different translations for the different labels here. The last piece we do need to provide a lookup to shipped to show it in Norwegian, but I can show you, you can work out how to do that. And the other thing is, is
that you should probably want to do some lookups to make sure it's not case sensitive as well, but you can get the idea of how this all works. So we've seen. Three ways to create your custom multi language copilots. And of course that bonus content of creating a multi language adaptive card that allows you to select from multiple records. So a little bit of news then. There is the third European Power Platform Conference, EPPC, that's happening in Brussels on June the 11th to the 13th in my poss
ession I have a two day conference ticket to give away free to a lucky winner. It's a fantastic event. There are a hundred plus sessions on all range of Power Platform topics, including Copilot Studio and some amazing speakers. Check out the link below for more information. So are you ready for the question to win a two day ticket to this event? What did I ask you to forget about for a moment in my most recent blog post on the Microsoft Power Apps blog? Is it A. No Code, B. Low Code, C. Pro Code
, or D. All of the above? If you know the answer and you fancy a chance at winning a two day ticket to EPVC, you can click on the link below and submit your answer and a win will be picked at random. And I'll also be at the MVP Summit in Seattle and then the Canadian Power Platform Summit in Vancouver the next few weeks, so look out for social media posts from me on news from those events. And that's all for today, so until next time, cheers, thanks for watching.

Comments

@ArafatTehsin

Finally we get some pro videos on this topic 😊

@wdalciva

Thanks for sharing Scott, I watch your videos as a source of inspiration to create cool stuff like the drag and drop PCF in canvas app 👏

@shomari169

And still waiting for pcf course to drop!😅😅😇

@Monic_ic

Thanks for sharing this information ! Is very usefull!!

@rwchecking

Thank you and I love it!!!

@absyoutube1792

Thank you for the information. It helped me a lot with Copilot. I also want to learn how to customize Dynamics Copilot with custom entities(Context based).