Hi, and welcome to this AI in C#
video on the Microsoft Agent Framework.
In this video, we're going to look at
a very cool concept for adding user memory
to your agents.
It's called AIContextProvider, and we're using a custom
version of it in this video.
And to let you see what it is,
I will start actually with running it a
few times and then show you the code.
Because here I have a normal agent, but
I have added memory to it.
But if I ask it, for example, what
is my name, it doesn't know.
That is quite normal for an agent.
Of course, I need to tell it in
a conversation thread, and then it will begin
to understand.
So I could, for example, say, my name
is Rasmus.
And now it knows, so if I say,
what is my name, it now knows.
But we all know that when we run
these and then just close it and start
it again, it normally ends up not knowing
again, because we don't have any persistence in
any way.
We could persist the entire conversation, but let's
say I just wanted to talk with the
agent again for something new.
But now if I say, what is my
name, it actually knows.
And I can also give it other information.
For example, my favourite is blue,
and I live in Denmark.
And again, if I stop the code and
I start it again, I can now ask,
what do you know about me?
It now knows three facts, that my name
is Rasmus, my favourite colour is blue, and
I live in Denmark.
And I could say, forget where I live,
and start and stop again.
And now we can see it only knows
two things.
So how is this achieved?
Let's go into the code.
The sample is up here in an AI
context provider factory, where we use the custom
version.
There's other versions, but we will save them
for other videos.
So what I have done here is, I
have made user memory.
And in order to do user memory in
my book, then we have a user ID.
So this is me simulating that I have
logged in with this user, rwj1234.
Then I do a normal OpenAI client.
And then I make something called a memory
extractor agent.
And I use the cheapest method possible, because
it will run fairly often.
And I've given it an instruction that says,
look at the user's message and extract any
memory that we do not already know, or
none if there aren't any memories to store.
So this agent is by itself, and we're
not really calling it here.
Instead, we are adding our agent with custom
memory.
And we are just making the agent like
normal, and giving instructions, but that's just your
nice AI.
So we're not telling this user memory upfront
or anything.
But what we are providing is this AI
context factory, which sits on the chat client
agent options.
And if we give one of these, we
can make one of them ourself, where we
then give the memory extractor agent and the
user for it.
And before we go and look at that,
we just see that else we do a
normal thread, and we just ask the user
again and again.
So we're not putting in, we're not saving
anything in the normal code.
But this one is special in that it
receives an agent, and it receives the user
ID.
And what I do here is I tell
that this user's memory is in this specific
file in my temp folder.
So if I look in my temp folder,
there's actually a file here with my memory,
which is the two things we had.
And this is just, of course, simulating some
kind of data store.
It could have been a database, a blob
storage, or whatever we wanted to save this
in.
But in our case, if we find a
file, we add that as user facts to
this custom context provider.
And then we overwrite two things here.
We overwrite an invoking async and an invoked
async.
So invoking async is actually a call that
it will do just before, when we run
this, before it actually goes to the LLM,
we can give further instructions.
So if we set a breakpoint here and
run again, and also set a breakpoint here
in order to see it.
So here, because when we new up the
agent, it will go to this.
We'll see our user ID is here.
And it will go in and fetch the
memory we have already.
So our user facts is now up in
memory for this agent.
And then it's done creating the agent.
And then when we ask a question, what
is my name?
It will happen this invoking async as part
of the run async here.
So this part has not happened yet.
The AI have not been asked what is
my name, but it's been given on the
fly here some more instructions.
And in my case, it's just a user
facts.
I string concatenate together, so it have these
two strings.
You could give it in XML, in JSON,
whatever you want.
I'm just giving it simple facts here.
So whenever this happens, and let me also
set a breakpoint down here, now the agent
have actually responded back already.
We can't see it yet, but just after
it's finished doing its thing, and it releases
us back to us here, we get the
invoked, meaning now the request and the response
have happened.
And we can see that in the context
here, where we can see that the request
message was what is my name.
And we can see in response messages.
I'm pretty sure we can see those as
well.
Yeah, response messages are here.
And it said, your name is Rasmus.
So what we're doing is we're just extracting
what it was the user actually said.
In this case, what is my name?
And we're going and run the agent that
is our memory extractor.
And we already know the following about the
user.
We know what their name is, and we
know that their favourite colour is blue.
But based on the message we have right
now, what is my name, extract further memory
either to add or to remove.
In this case, it will come back with
a response where there's nothing to add, nothing
to remove, because our message is just what
is my name.
So it will do nothing special here.
It will just update live the user's memory
with what we already had, and we get
our response back.
But if I go into the agent now
and ask it something more, it will give
it some information.
Again, I live in Denmark.
Now the invoking will still go on, because
it doesn't know if it needs to give
the facts or not, so it will always
be given up front, which is not important
here.
But the important thing here is that the
message was, I live in Denmark, and the
memory extractor will now see that, okay, based
on that, I probably should make a memory
update here that says, I need to add
one memory, I live in Denmark, and nothing
to remove, because I didn't ask to remove.
So it now adds that memory.
So we now have three memories in the
file, and we can see that by just
opening it and see we have three data.
And it will just confirm that it has
saved that.
So when I say, or stop this and
start it again, it now will get the
tree information.
In this case, it's going in and grabbing
them now, and it will have the tree
information.
And when I say, what do you know
about me?
Again, it will go in on the fly,
give the three user facts up front.
And again, this could be done by making
the agent and grabbing this.
But this is a nice way of making
something that works for every agent about the
user facts, for example.
And again, in this case, it will not
grab anything new out of memory, because it
was just a question and not something to
quit remembering.
So now it knows this, and I can
say, forget my favourite colour.
It will go in, extract that I want
to remove, that my favourite colour is blue.
Remove that from the list, so now only
have the two things again, and come back
to us.
So again, all of this could have been
done by giving instructions here again and again
based on the facts, and after the response,
the normal response down here, we could have
extracted and so on.
But this is just a nice way of
putting it sort of in process.
So now this custom thing can be used
across multiple agents and just be reused, because
now I've built a system that can store
memories, and I can plug that into any
agent across my system if I need to.
Of course, I probably wouldn't save it in
a temp folder, save it in a database,
and so on.
So this is what AI context can do.
There's other AI contexts where Microsoft has built
something by itself.
They have a chat history memory that uses
vector stores for this data, and they have
a mem0 that does something similar.
But I like this fact that you can
control it yourself, how it can be done,
that you can forget stuff again, or whatever
you want to use this for, because it's
essentially just before and after any LLM call
do something.
So that is actually everything.
See you in the next one.