WEBVTT

1
00:00:00.000 --> 00:00:08.880
Hi, and welcome to an ART-Sharp video on how you convert Semantic Kernel into Agent Framework.

2
00:00:08.880 --> 00:00:14.739
Lots of people out there right now have Semantic Kernel, it's in maintenance mode, you should

3
00:00:14.739 --> 00:00:22.479
definitely upgrade to Agent Framework when it comes out of preview in start of the year.

4
00:00:22.479 --> 00:00:26.040
So now's the time to have a look at how you can do that.

5
00:00:26.040 --> 00:00:34.740
On this video, I'm going to convert a sample project I have, I have a video of a live upgrade

6
00:00:34.740 --> 00:00:41.080
as well in my channel, but if you want to see it a bit more structured and easy to look

7
00:00:41.080 --> 00:00:44.439
at, this is the one for you.

8
00:00:44.439 --> 00:00:51.400
Because in this, we are going to see how to take normal chats, streaming, structured output,

9
00:00:51.400 --> 00:00:59.680
tool calling, RAG, and everything from Semantic Kernel, throwing the NuGet package away, putting

10
00:00:59.680 --> 00:01:03.240
in the others, getting a lot of errors, and getting rid of them.

11
00:01:03.240 --> 00:01:06.879
So let's get started.

12
00:01:06.879 --> 00:01:13.760
So I have a folder here in my sample repo, and the repo is linked in the descriptions.

13
00:01:13.760 --> 00:01:17.519
So I have a before and a finished.

14
00:01:17.519 --> 00:01:23.580
So this is the one that actually uses Agent Framework, while this one uses Semantic Kernel.

15
00:01:23.580 --> 00:01:35.000
So I'm gonna change this live, but you might want to instead look at this in the end.

16
00:01:35.000 --> 00:01:46.720
So what I'm gonna do here is show you how the thing works, and what code we have here.

17
00:01:46.720 --> 00:01:56.319
So we have two NuGet packages, Semantic Kernel, Agents Core, and Azure AI, and then we have the in-memory.

18
00:01:56.319 --> 00:02:00.400
So these are the three packages we use.

19
00:02:00.400 --> 00:02:08.520
And this is a web API, so we can do a little dependency injection on things here, but it's

20
00:02:08.520 --> 00:02:13.320
not needed, both for Semantic Kernel or Agent Framework.

21
00:02:13.320 --> 00:02:19.399
I have some credentials that I grab from a secret manager, and that's part of any of

22
00:02:19.399 --> 00:02:22.559
the two, and it will remain the same.

23
00:02:22.559 --> 00:02:28.399
And then I have something called a MyKernelFactory, which can build different kernels.

24
00:02:28.399 --> 00:02:34.080
So we have the normal Kernel CreateBuilder, we add a chat completion to it, we add some

25
00:02:34.080 --> 00:02:42.639
middleware in terms of IAutoFunctionInvocationFilter, and we build our kernel so we can give it

26
00:02:42.639 --> 00:02:45.759
to the various objects here.

27
00:02:45.759 --> 00:02:51.360
And then we use the Azure OpenAI Embedding Generator, where we can get the embedding

28
00:02:51.360 --> 00:02:55.720
generator for the Rack stuff.

29
00:02:55.720 --> 00:03:03.240
Then comes six map posts, one for chat, where we try to use the kernel directly, just getting

30
00:03:03.240 --> 00:03:05.639
a question back.

31
00:03:05.639 --> 00:03:11.160
And this happens via a chat request object, which is just a question.

32
00:03:11.160 --> 00:03:17.399
MyKernelFactory is dependency injected here, and we get a response back which is just the

33
00:03:17.399 --> 00:03:21.080
answer back, nothing special there.

34
00:03:21.080 --> 00:03:25.080
We do something similar with streaming here, it's not really streaming because it's just

35
00:03:25.080 --> 00:03:33.720
a map post, but we are doing some streaming here where we go in and just do streaming.

36
00:03:33.720 --> 00:03:41.240
Then we do a map post with chat history, where we use the chat history object, we actually

37
00:03:41.240 --> 00:03:51.600
don't need that one here, and we don't need these, they are just in the way.

38
00:03:51.600 --> 00:03:56.759
We do a chat history, add a question, add it to the history, add another question, add

39
00:03:56.759 --> 00:04:03.399
to the history, and we'll see how that will be changed in Agent Framework.

40
00:04:03.399 --> 00:04:06.679
In the same manner, we have tool calling.

41
00:04:06.679 --> 00:04:13.160
So we have a tool where we import the tool from object, and this tool is down here.

42
00:04:13.160 --> 00:04:19.239
In general, this is in one big file, and you can see there's 273 lines of code.

43
00:04:19.239 --> 00:04:24.720
That's on purpose so we can both tell how many lines of code there are after the fact.

44
00:04:24.720 --> 00:04:36.279
So 273 is our starting, and we'll see in the end how much there will be left with Agent Framework.

45
00:04:36.279 --> 00:04:42.079
But down here we have two tools, they are dummy tools more or less, with no real implementation

46
00:04:42.079 --> 00:04:48.519
of a getWeather, it's always sunny and 90 degrees, and in terms of a getDate and time,

47
00:04:48.519 --> 00:04:52.160
it's really in the real life way.

48
00:04:52.160 --> 00:04:57.640
And the weather result is just a record here.

49
00:04:57.640 --> 00:05:11.880
So in the tool call example, we call this tool, we give the agent the option to do tool calling,

50
00:05:11.880 --> 00:05:17.600
and we just get our answer back like normal.

51
00:05:17.600 --> 00:05:23.320
Then we have a structured output example, where we give our agent its response format,

52
00:05:23.320 --> 00:05:31.720
and this response format is just a movie result with movies inside, where we then ask the

53
00:05:31.720 --> 00:05:39.600
agent to give us the JSON back, and Deo serialize that JSON to the movie result.

54
00:05:39.600 --> 00:05:48.040
Then finally we have the RAG part, where we go through and make some RAG ingestion here.

55
00:05:48.040 --> 00:05:52.880
You wouldn't normally do that in the call itself, but just to keep it simple for the

56
00:05:52.880 --> 00:06:01.040
demo, we ingest all the data, so all of this will not change, because that is not part

57
00:06:01.040 --> 00:06:05.959
of the Agent Framework, we'll get back to that.

58
00:06:05.959 --> 00:06:14.239
We get our kernel, we ask for the RAG, we do a search in the RAG, and we give that data

59
00:06:14.239 --> 00:06:18.559
next to the question for our agent.

60
00:06:18.559 --> 00:06:20.500
So that is all the code there is.

61
00:06:20.500 --> 00:06:30.959
We can run it, and we can see we have a little HTTP file here, so we can test it.

62
00:06:30.959 --> 00:06:35.440
We can ask for the capital of France, it will come back and say it's Paris.

63
00:06:35.440 --> 00:06:41.399
We can ask streaming how to make soup, and after a little while it's been cooking up

64
00:06:41.399 --> 00:06:47.239
the recipe, it will give us back a long answer about that.

65
00:06:47.239 --> 00:06:53.920
We can do the chat history, where we see a complete chat history back.

66
00:06:53.920 --> 00:06:59.440
Not really the real way you would do it, but that was the best way I could do it with just a web API.

67
00:07:00.160 --> 00:07:08.160
Then we have the tool calling, and the tool calling will show that the auto-invocation

68
00:07:08.160 --> 00:07:13.920
filter will happen, and show that the thing was called, and being used to find that it's

69
00:07:13.920 --> 00:07:19.040
sunny, and what the time is in Paris right now.

70
00:07:19.040 --> 00:07:23.720
Again, the input and output is not that important here.

71
00:07:23.720 --> 00:07:31.359
Then we have structured output, where we can see we get our movies back, and we have

72
00:07:31.359 --> 00:07:36.119
a rack, where we ask for what is the Wi-Fi password, which was one of the ten things.

73
00:07:36.119 --> 00:07:44.239
So doing rack search, finding that, and giving us back what the Wi-Fi password is.

74
00:07:44.239 --> 00:07:50.559
So all good and well, now let's do the conversion.

75
00:07:50.559 --> 00:07:55.200
And in order to do that, we of course need to go to our NuGet packages, and get rid of

76
00:07:55.200 --> 00:08:00.559
some, and add some.

77
00:08:00.559 --> 00:08:15.839
So first we will get rid of agents core here, we don't need semantic kernel agents.

78
00:08:15.920 --> 00:08:21.320
And we don't need the Azure OpenAI of semantic kernel.

79
00:08:21.320 --> 00:08:28.480
We do however need this one, because this is actually just semantic kernel by name.

80
00:08:28.480 --> 00:08:35.400
In real life it's actually Microsoft extension vector data, and at some point Microsoft will

81
00:08:35.400 --> 00:08:41.599
actually rename this package to vector data dot in memory instead.

82
00:08:41.599 --> 00:08:45.479
But right now it's just confusing, it's called semantic kernel, it really has nothing to

83
00:08:45.479 --> 00:08:51.000
do with semantic kernel other than the source code is in their repo.

84
00:08:51.000 --> 00:08:59.760
So we don't get rid of that, but what we get is we take our Azure dot AI dot OpenAI.

85
00:08:59.760 --> 00:09:05.919
This package was behind the scenes in semantic kernel, but now we control it ourselves as

86
00:09:05.919 --> 00:09:13.960
the raw connection.

87
00:09:14.880 --> 00:09:22.880
The other package is the Microsoft agent framework package, which is the OpenAI package we need.

88
00:09:22.880 --> 00:09:24.719
So let's put that in as well.

89
00:09:24.719 --> 00:09:30.080
So two packets removed, two packets added.

90
00:09:30.080 --> 00:09:42.359
And once we do that, the code will be very sad.

91
00:09:42.359 --> 00:09:48.440
So we get, there will be more errors in one second.

92
00:09:48.440 --> 00:09:57.919
There's actually more, there's like 42 errors.

93
00:09:57.919 --> 00:10:03.440
So let's just start from the top, let's get rid of stuff that doesn't exist anymore.

94
00:10:03.440 --> 00:10:06.799
Let's get rid of this, let's get rid of this.

95
00:10:06.799 --> 00:10:12.760
We don't need this one either, because that was just a warning from semantic kernel.

96
00:10:12.760 --> 00:10:21.760
So what we first need is we actually need a builder dot services dot add singleton.

97
00:10:21.760 --> 00:10:31.059
And we need a singleton for the Azure OpenAI client.

98
00:10:31.059 --> 00:10:36.859
And this might feel strange for a semantic kernel person that we need to do this on our

99
00:10:36.859 --> 00:10:44.460
own, but it will make much more sense down the line.

100
00:10:44.460 --> 00:10:51.739
So we get that in, and then we don't need this kernel factory anymore.

101
00:10:51.739 --> 00:10:58.539
So let's just delete it entirely. There we go.

102
00:10:58.539 --> 00:11:04.219
And get rid of its registration, because what we are going to do is, every time we have

103
00:11:04.219 --> 00:11:14.539
a kernel factory here in the different methods, we instead want to do an Azure OpenAI client of type client.

104
00:11:14.539 --> 00:11:21.340
So let's replace those in all the six methods, so we don't have this anymore.

105
00:11:21.340 --> 00:11:28.700
But because it's dependency injected here, we can do this in the various map posts.

106
00:11:28.700 --> 00:11:40.219
We still need embedding generator, but we need to new it up in a slightly different way.

107
00:11:40.219 --> 00:11:48.739
We need to say add embedding generator, and it will be constructed on the fly.

108
00:11:48.739 --> 00:12:00.859
And we do that by checking our provider.getRequiredService.

109
00:12:00.859 --> 00:12:11.260
And we need the required service of the Azure OpenAI client. There we go.

110
00:12:11.260 --> 00:12:20.179
So I could have made it out here and given it in, but let's do it in the proper manner.

111
00:12:20.179 --> 00:12:28.859
So on that, we need to get the embedding general client, and that client should get the text

112
00:12:28.859 --> 00:12:31.979
embedding small.

113
00:12:31.979 --> 00:12:37.380
And then we need to say SI embedding generator.

114
00:12:37.380 --> 00:12:41.700
And that one we can just return, and we have our new embedding generator.

115
00:12:41.700 --> 00:12:51.780
So slightly more difficult to set up this part, but a lot of other places, it will become much simpler.

116
00:12:51.780 --> 00:12:54.659
So let's tackle the first map post here.

117
00:12:54.659 --> 00:13:05.539
We have a client now instead of our factory, and we can say get chat client, we get this.

118
00:13:05.539 --> 00:13:10.780
And then we can say create AI agent.

119
00:13:10.780 --> 00:13:15.619
Once we do that, we get an agent.

120
00:13:15.619 --> 00:13:19.859
And an agent can, of course, give the answer back as well.

121
00:13:19.859 --> 00:13:27.940
We just simply say agent.runAsync with our chat, our requested question.

122
00:13:27.940 --> 00:13:32.739
That's a wait, and we get a response back.

123
00:13:33.020 --> 00:13:39.820
Instead of all this, we can simply just say we want to give our response.text back.

124
00:13:39.820 --> 00:13:45.859
This is the text it will give back to us.

125
00:13:45.859 --> 00:13:52.859
So first one done, and I'm just going to copy paste this, because we will use these for

126
00:13:52.859 --> 00:13:58.179
all the other ones, and I will just put one into each one of them, and then we will modify

127
00:13:58.299 --> 00:14:04.299
it slightly after that.

128
00:14:04.299 --> 00:14:09.340
You could, of course, make a helper method to give such an agent, but I like it being

129
00:14:09.340 --> 00:14:16.700
on an individual basis, so I can have different models for different scenarios.

130
00:14:16.700 --> 00:14:23.979
But back to number two here, which was our streaming, and streaming is fairly simple as well.

131
00:14:23.979 --> 00:14:34.419
We can simply say agent.runStreamingAsync with our chat, request.question.

132
00:14:34.419 --> 00:14:40.859
It also needs a wait for each, and give an update back.

133
00:14:40.859 --> 00:14:53.020
So we can take our answer, say answer.equal.update.

134
00:14:54.020 --> 00:14:57.820
This is the streaming update.

135
00:14:57.820 --> 00:15:03.380
And once we have that, we can simply just give the answer back.

136
00:15:03.380 --> 00:15:06.739
So number two is done.

137
00:15:06.739 --> 00:15:13.020
Next we have the chat history, and that chat history doesn't really exist anymore, but

138
00:15:13.020 --> 00:15:17.739
what it does here in our agent, we also have an instruction, and those instructions we

139
00:15:17.739 --> 00:15:27.859
just give to this instructions, and we can give our instruction up here.

140
00:15:27.859 --> 00:15:36.340
So we can get rid of this, and instead of a chat history, we say agent.getNewThread,

141
00:15:36.340 --> 00:15:41.140
which is a new name for chat history, being a thread.

142
00:15:41.140 --> 00:15:43.020
So we don't need our history.

143
00:15:43.020 --> 00:15:49.539
We don't need to give the history our first question.

144
00:15:49.539 --> 00:15:58.020
Instead we can simply just say agent.runAsync with the chat, request.question, which was

145
00:15:58.020 --> 00:16:09.900
who is Barack Obama, get our response number one back here, and we don't really use it

146
00:16:09.900 --> 00:16:15.979
for anything here, but in real life you of course begin to write it out, because we just

147
00:16:15.979 --> 00:16:22.539
wanted to simulate a follow-up question, and our follow-up question, in order to work,

148
00:16:22.539 --> 00:16:32.580
we need to give the thread in here on the various questions we want to ask.

149
00:16:32.580 --> 00:16:41.260
So we want to ask us a follow-up question, how tall is he, to get our response to.

150
00:16:41.260 --> 00:16:46.820
We can get rid of all this code, and we can just say that the thread here, because it's

151
00:16:46.820 --> 00:16:53.820
an open-ended system with multiple versions of threads, there's multiple of them, but

152
00:16:53.820 --> 00:17:00.539
we know in our case it's a chat client agent, so it has a chat client thread, and when we

153
00:17:00.539 --> 00:17:13.540
get one of those, we can say chatClientThread.messageStore.getMessages, and we know that this is not null,

154
00:17:13.540 --> 00:17:20.939
so we can await this, and get our messages back that has been asked between them, because

155
00:17:20.939 --> 00:17:27.739
the thread will take care of putting the input and the output, the input and output, into this,

156
00:17:27.739 --> 00:17:32.739
and we can go in and just return the messages, because they have the same structure back.

157
00:17:35.339 --> 00:17:43.339
On to the tool calling, so we have our agent again, that agent needed some instructions,

158
00:17:46.339 --> 00:17:53.939
so we just take the instructions, the old one needed a function calling behavior, so

159
00:17:53.939 --> 00:17:55.939
it would actually call the functions.

160
00:17:55.939 --> 00:18:01.339
By default, Agent Framework could do it, so we don't need to, but instead we need to give

161
00:18:01.339 --> 00:18:08.939
the tools, and the tools is just a collection of AI tools, as they're called.

162
00:18:09.939 --> 00:18:27.939
So, we need our tools out, so we can work with them a bit here, so let's put them up here.

163
00:18:28.939 --> 00:18:40.939
This is our tool class, not the tools themselves.

164
00:18:43.939 --> 00:18:55.339
Mr. Right buttons, and down here we need to say we want an AI function factory to create a new tool,

165
00:18:55.339 --> 00:18:58.339
which can take a delegate or method info.

166
00:18:58.339 --> 00:19:06.339
In our case, we just have two tools, so it's easier to just say, let's get the two tools in,

167
00:19:06.339 --> 00:19:12.339
but you could also use reflection on the class itself in order to get your data.

168
00:19:12.339 --> 00:19:19.339
So, down here we want the name of the tool, and we don't need the kernel function anymore.

169
00:19:19.339 --> 00:19:29.339
So, we put that in, and we make one more of these, one for the get city and date,

170
00:19:29.339 --> 00:19:44.339
and we take the name down here, and get rid of this, so we have the right names here.

171
00:19:45.339 --> 00:19:49.339
So, now we have our tools in place.

172
00:19:49.339 --> 00:19:56.339
We don't need our kernel, we don't need to import, we don't need to make this agent.

173
00:19:56.339 --> 00:20:07.339
All we need to do is answer back, so we don't need the answer here, we just say agents.runAsync,

174
00:20:07.339 --> 00:20:11.339
and we ask our question,

175
00:20:15.339 --> 00:20:19.339
and we can await the answer.

176
00:20:23.339 --> 00:20:30.339
And once we have that, we can just say response.txt.

177
00:20:30.339 --> 00:20:37.339
We do, however, need to have the function calling middleware, the invocation part.

178
00:20:37.339 --> 00:20:43.339
We had it down here, and a slightly different way of doing it,

179
00:20:43.339 --> 00:20:46.339
where we had the context and call and stuff.

180
00:20:46.339 --> 00:20:52.339
Like normal, we can get rid of that, and I will just bring in the new way of doing it.

181
00:20:52.339 --> 00:20:57.339
It's something you, in real life, will never really remember,

182
00:20:57.339 --> 00:21:02.339
but just take from a sample or the documentation,

183
00:21:02.339 --> 00:21:07.339
and it needs to be put in here, so we can see it.

184
00:21:07.339 --> 00:21:13.339
It's just a basic function called middleware.

185
00:21:13.339 --> 00:21:17.339
We can get the calling agent, we can have the context,

186
00:21:17.339 --> 00:21:21.339
we can stop the next if we want to, just like the other one,

187
00:21:21.339 --> 00:21:25.339
with it just a slightly different nature.

188
00:21:25.339 --> 00:21:31.339
And what we need to do is we, of course, need to have the agent know about this middleware,

189
00:21:31.339 --> 00:21:37.339
so the way you do that is you take your agent and say

190
00:21:37.339 --> 00:21:43.339
asBuilder.use in order to get middleware,

191
00:21:43.339 --> 00:21:48.339
so we can take our function called middleware, and then we can build the agent again.

192
00:21:48.339 --> 00:21:53.339
And now it turns from a chat client agent into something called an AI agent,

193
00:21:53.339 --> 00:21:57.339
because it's a more lower level fraction, but once we have that,

194
00:21:57.339 --> 00:22:01.339
everything works again.

195
00:22:01.339 --> 00:22:05.339
Then we have the structured output. We need to do something similar.

196
00:22:05.339 --> 00:22:11.339
We saw that our agent had some instructions, so we need to put those up.

197
00:22:11.339 --> 00:22:19.339
Instructions, and it was about asking movie questions.

198
00:22:19.339 --> 00:22:22.339
We then told it the response format.

199
00:22:22.339 --> 00:22:28.339
In agent framework, we don't need to tell the entire agent that they can only do that.

200
00:22:28.339 --> 00:22:32.339
It will be done on a call-by-call basis,

201
00:22:32.339 --> 00:22:37.339
so we don't need to do this at all,

202
00:22:37.339 --> 00:22:39.339
and we don't need JSON or anything.

203
00:22:39.339 --> 00:22:45.339
Instead, we just need to say agent.runAsync,

204
00:22:45.339 --> 00:22:51.339
but with this version that takes a movie result.

205
00:22:51.339 --> 00:22:58.339
And then we have our chatRequests.question, get our weight,

206
00:22:58.339 --> 00:23:04.339
and we get our response back.

207
00:23:04.339 --> 00:23:11.339
And instead of doing the call here and doing the deserialization,

208
00:23:11.339 --> 00:23:16.339
we can simply say response.result, which will be a movie result

209
00:23:16.339 --> 00:23:21.339
because of the generic nature of this.

210
00:23:21.339 --> 00:23:29.339
Finally, we need the rack setup, so all the knowledge here will stay,

211
00:23:29.339 --> 00:23:32.339
but we just have our agent. We can pull it down here

212
00:23:32.339 --> 00:23:37.339
so it's a little easier to see where things are happening.

213
00:23:37.339 --> 00:23:41.339
So we need our agent. The agent had some instructions again,

214
00:23:41.339 --> 00:23:48.339
so let's skip that.

215
00:23:48.339 --> 00:23:53.339
But beyond that, we don't need any of this code.

216
00:23:53.339 --> 00:23:56.339
We still need to do our rack search up front,

217
00:23:56.339 --> 00:24:05.339
and then we need to call our agent, so we can say agent.runAsync,

218
00:24:05.339 --> 00:24:11.339
and we need to give it the question with the augmented part of the rack,

219
00:24:11.339 --> 00:24:14.339
like we did before.

220
00:24:14.339 --> 00:24:19.339
We say a weight, we get our response like before,

221
00:24:19.339 --> 00:24:26.339
we get rid of all this, and just say response.text.

222
00:24:27.339 --> 00:24:35.339
Once we have that, we have thrown away all the things we didn't use,

223
00:24:35.339 --> 00:24:39.339
and we should be ready to go.

224
00:24:39.339 --> 00:24:45.339
So if we run this, let's see.

225
00:24:45.339 --> 00:24:53.339
The code is running, and again, we can try and use the same test over here.

226
00:24:53.339 --> 00:24:57.339
We asked what is the capital of France for semantic kernel,

227
00:24:57.339 --> 00:25:02.339
and now we get the capital of France from agent framework instead.

228
00:25:02.339 --> 00:25:10.339
We did it with streaming on how to make soup.

229
00:25:10.339 --> 00:25:13.339
Let's take a little longer to answer like normal,

230
00:25:13.339 --> 00:25:17.339
and we get our entire soup recipe back.

231
00:25:17.339 --> 00:25:21.339
We had our chat history where we got the entire back and forward,

232
00:25:21.339 --> 00:25:28.339
and now we get it back as an agent thread instead with the same format.

233
00:25:28.339 --> 00:25:32.339
We have the tool calling,

234
00:25:32.339 --> 00:25:39.339
and the tool calling can see again that our middleware was called on the fly,

235
00:25:39.339 --> 00:25:43.339
and we get back that the weather in Paris is sunny and 90 degrees,

236
00:25:43.339 --> 00:25:46.339
as the tool always told,

237
00:25:46.339 --> 00:25:51.339
and since it's usually sea time 12.40 right now,

238
00:25:51.339 --> 00:26:00.339
it's 1.40 in the afternoon in Paris right now, which is correct.

239
00:26:00.339 --> 00:26:02.339
Then we have the structured output,

240
00:26:02.339 --> 00:26:08.339
which now comes back in exactly the same format, just with agent framework,

241
00:26:08.339 --> 00:26:13.339
and rack where we asked for the Wi-Fi password is again exactly the same.

242
00:26:13.339 --> 00:26:17.339
We get our Wi-Fi back.

243
00:26:17.339 --> 00:26:20.339
This is actually everything that is to it,

244
00:26:20.339 --> 00:26:25.339
and we saw that we had 273 lines of code or something like that,

245
00:26:25.339 --> 00:26:30.339
and now we are down to 202 lines.

246
00:26:30.339 --> 00:26:36.339
So roughly 70 lines of code can be saved using a better,

247
00:26:36.339 --> 00:26:41.339
more condensed agent framework compared to semantic kernel.

248
00:26:41.339 --> 00:26:46.339
So you also get more concise, better named, in my opinion,

249
00:26:46.339 --> 00:26:50.339
easier to work with, especially around structured output

250
00:26:50.339 --> 00:26:55.339
where you on the fly can ask for the data.

251
00:26:55.339 --> 00:26:58.339
So overall, quite easy to do.

252
00:26:58.339 --> 00:27:05.339
Let me check my time here. It has taken 27 minutes to do this live,

253
00:27:05.339 --> 00:27:08.339
including introduction and telling what the code does.

254
00:27:08.339 --> 00:27:14.339
So it should be fairly easy for you as well to go from this.

255
00:27:14.339 --> 00:27:19.339
And as mentioned, what we just did is down here in the finished version.

256
00:27:19.339 --> 00:27:22.339
I will undo this just after this,

257
00:27:22.339 --> 00:27:27.339
but all exactly the same we did before is here as well,

258
00:27:27.339 --> 00:27:33.339
so you can see it and use it for your own migrations.

259
00:27:33.339 --> 00:27:36.339
So good luck with those, and see you in the next one.

