WEBVTT

00:00.210 --> 00:01.380
-: Hey, welcome back.

00:01.380 --> 00:02.610
And in this short video,

00:02.610 --> 00:04.740
what we're gonna cover and start introducing

00:04.740 --> 00:08.040
is this concept called agents inside a LangChain.

00:08.040 --> 00:10.020
Now, agents are really fascinating,

00:10.020 --> 00:14.640
and they're very good at doing task planning, for example,

00:14.640 --> 00:16.710
and executing lots of different things

00:16.710 --> 00:18.660
in a very dynamic way.

00:18.660 --> 00:20.430
But before we go into any code,

00:20.430 --> 00:21.810
I think it's worthwhile us thinking

00:21.810 --> 00:25.020
about some high-level concepts that LangChain provides.

00:25.020 --> 00:27.780
So you've got this idea of an agent action,

00:27.780 --> 00:31.110
which is what you might decide a tool that's gonna be used,

00:31.110 --> 00:32.940
and we'll talk about tools in a bit

00:32.940 --> 00:34.350
when the agent is finished.

00:34.350 --> 00:36.300
So there's this idea of the eventually,

00:36.300 --> 00:38.130
the agent is gonna finish,

00:38.130 --> 00:40.410
and that it will return some result.

00:40.410 --> 00:43.110
Now there's some series of intermediate steps.

00:43.110 --> 00:45.360
And if I scroll down a bit and show you,

00:45.360 --> 00:47.610
you can almost think of an agent like this,

00:47.610 --> 00:50.220
where the agent gets an action,

00:50.220 --> 00:52.920
and when the action isn't a finish,

00:52.920 --> 00:55.950
we then run the next action.

00:55.950 --> 01:00.360
And then we get an observation, and with the observation,

01:00.360 --> 01:03.510
we then decide to get another action.

01:03.510 --> 01:08.400
So we've provided it almost like an endless loop of actions,

01:08.400 --> 01:11.400
observations, getting the next action,

01:11.400 --> 01:13.680
and then whilst the next action isn't a finish,

01:13.680 --> 01:15.720
we keep running in this endless loop.

01:15.720 --> 01:17.370
So that's a little bit on agents

01:17.370 --> 01:19.470
and the concepts that are happening here.

01:19.470 --> 01:21.330
Now, as you're probably all aware of,

01:21.330 --> 01:23.820
agents use something called tools.

01:23.820 --> 01:25.650
And tools, if you can think of it,

01:25.650 --> 01:27.630
are basically just Python functions,

01:27.630 --> 01:30.060
or some way to interact with the environment.

01:30.060 --> 01:31.740
Now if we have a look at tools,

01:31.740 --> 01:33.660
there's a variety of different ways

01:33.660 --> 01:35.340
that you can define tools.

01:35.340 --> 01:37.650
You've got some default tools that are provided to you

01:37.650 --> 01:40.140
from the LangChain documentation,

01:40.140 --> 01:41.910
but essentially, in essence,

01:41.910 --> 01:44.280
a tool is basically a function

01:44.280 --> 01:47.820
that you can have an input into and there's an output

01:47.820 --> 01:51.780
that gets added, right, from using the tool.

01:51.780 --> 01:54.270
A LangChain provides some really great ways for us

01:54.270 --> 01:55.950
to create our own tools.

01:55.950 --> 01:57.630
And so what we're gonna do is have a look at

01:57.630 --> 01:59.400
how we can create tools,

01:59.400 --> 02:02.880
and how we can attach those tools to LangChain agents.

02:02.880 --> 02:05.130
And then the agent can then use those tools

02:05.130 --> 02:06.420
to answer questions.

02:06.420 --> 02:08.790
So let's have a look at this notebook.

02:08.790 --> 02:10.230
And to start with,

02:10.230 --> 02:12.930
we're gonna use the LangChain_community.tools,

02:12.930 --> 02:14.550
and you've got two tools here,

02:14.550 --> 02:16.530
so I'm just gonna make sure my notebook's running

02:16.530 --> 02:17.970
and I'll just run that.

02:17.970 --> 02:19.800
So let's have a look at this.

02:19.800 --> 02:23.280
So what we're gonna do is have a look at this one here.

02:23.280 --> 02:26.610
All right, so in this example, we're using a community tool

02:26.610 --> 02:29.790
which allows us to query Wikipedia.

02:29.790 --> 02:32.070
And you can see there's this API wrapper.

02:32.070 --> 02:34.050
And then we have this tool

02:34.050 --> 02:36.780
that's being made from the Wikipedia query run.

02:36.780 --> 02:39.900
And this eventually will basically make a tool.

02:39.900 --> 02:42.000
Now we know it's a tool in LangChain

02:42.000 --> 02:43.890
because if it's got a name, a description,

02:43.890 --> 02:46.860
and some input arguments, you can see,

02:46.860 --> 02:48.840
the tool name is called Wikipedia.

02:48.840 --> 02:51.660
This is the description, you've got the query.

02:51.660 --> 02:54.150
So this is the args that will go in

02:54.150 --> 02:57.630
and this bit here is whether the tool will automatically

02:57.630 --> 02:59.220
be returned to the user,

02:59.220 --> 03:02.160
or whether the agent will take the output of the tool

03:02.160 --> 03:04.560
and then decide what to return to the user.

03:04.560 --> 03:06.570
Now how do you call a tool?

03:06.570 --> 03:08.280
Well, you can just do the tool.invoke,

03:08.280 --> 03:09.630
and you can see if I run this,

03:09.630 --> 03:11.487
it's gonna start using that tool

03:11.487 --> 03:14.910
and it gives us some information about digital marketing.

03:14.910 --> 03:17.790
Now you can see it's got a doc content chars_max,

03:17.790 --> 03:19.890
so we could also set this to 1,000,

03:19.890 --> 03:21.949
and then we're gonna get a lot more content back

03:21.949 --> 03:24.240
about that example.

03:24.240 --> 03:26.730
Now one thing that's often the case

03:26.730 --> 03:29.070
is you'll want to create your own tools.

03:29.070 --> 03:31.620
And your tools are gonna need a name, a description,

03:31.620 --> 03:34.530
and an arg schema, which is optional.

03:34.530 --> 03:38.070
The name is required and so the two optional ones

03:38.070 --> 03:39.979
are the description and the argschema,

03:39.979 --> 03:42.450
but it can be really nice to include those.

03:42.450 --> 03:44.940
Now the first way that you can easily create a tool

03:44.940 --> 03:46.530
is you could have a Python function,

03:46.530 --> 03:48.930
like where you gonna do a Google search?

03:48.930 --> 03:50.460
And we could take a query in here now.

03:50.460 --> 03:52.170
For now we're just returning LangChain,

03:52.170 --> 03:53.940
which is a type of string,

03:53.940 --> 03:57.180
and we wrap that specific Python function

03:57.180 --> 03:59.310
with this tool decorator.

03:59.310 --> 04:04.170
And then based on that, you will then from the doc string,

04:04.170 --> 04:06.570
it will add that into the description.

04:06.570 --> 04:07.860
So your doc strings,

04:07.860 --> 04:09.870
you want to make them very descriptive

04:09.870 --> 04:13.050
about what kind of tools your agents can use.

04:13.050 --> 04:16.410
The name of the tool comes from the Python function

04:16.410 --> 04:17.940
that you can see here.

04:17.940 --> 04:21.270
And the argschema or the .args in this case,

04:21.270 --> 04:23.460
get associated with the query.

04:23.460 --> 04:25.620
And this is basically the function signature.

04:25.620 --> 04:28.380
So you've got one input parameter here, which is a query,

04:28.380 --> 04:30.300
which is the type of string.

04:30.300 --> 04:33.960
Now you could also modify your inputs.

04:33.960 --> 04:36.099
And so you can see here we've got a didactic model,

04:36.099 --> 04:39.660
and we have one input which is a query type of string,

04:39.660 --> 04:42.060
but we've also added on a description

04:42.060 --> 04:44.220
to make it a bit more descriptive.

04:44.220 --> 04:46.440
And you can now see we've also changed the name

04:46.440 --> 04:48.120
of the tools to search-tool.

04:48.120 --> 04:50.160
And we've also said that this output

04:50.160 --> 04:53.520
should be immediately returned to the user,

04:53.520 --> 04:56.460
and not the agent if it's the final finish.

04:56.460 --> 05:01.020
So you can also make tools from the search function

05:01.020 --> 05:03.900
in terms of a structured tool .from function,

05:03.900 --> 05:06.840
where the function is here, you give it the name,

05:06.840 --> 05:10.224
and the description and you can even add on the argschema

05:10.224 --> 05:12.300
and all that good stuff.

05:12.300 --> 05:14.400
Now this shows you how to make tools,

05:14.400 --> 05:17.100
but let's see how do we add these onto agents?

05:17.100 --> 05:19.470
The easiest way to do that is you would have some function.

05:19.470 --> 05:21.600
So for example, we've got the get_word_length,

05:21.600 --> 05:25.710
we'd make our list of tools and we'd call that a tools list.

05:25.710 --> 05:27.630
And so what you end up here with is you've got

05:27.630 --> 05:32.430
that tools-list, which has a bunch of tools.

05:32.430 --> 05:34.410
Now once you have your bunch of tools,

05:34.410 --> 05:36.450
I personally find that the best way to use

05:36.450 --> 05:39.750
these LangChain agents is to write your own custom agents.

05:39.750 --> 05:41.790
Now it's quite easy to do that.

05:41.790 --> 05:43.650
There's a couple of different things that you'll need.

05:43.650 --> 05:46.500
So the first is you'll need a chat prompt template,

05:46.500 --> 05:48.240
and then you can have whatever you want

05:48.240 --> 05:49.770
in the system message.

05:49.770 --> 05:53.610
You need some form of input to take the original query.

05:53.610 --> 05:55.500
And then this is the most important bit

05:55.500 --> 05:56.970
that you have to have if you're building

05:56.970 --> 06:00.690
your own custom LangChain expression language agents.

06:00.690 --> 06:03.060
And basically this agent scratch pad

06:03.060 --> 06:07.980
is how LangChain will store all of those actions

06:07.980 --> 06:12.180
and observations so that it can use that in memory

06:12.180 --> 06:13.860
to then decide what to do next.

06:13.860 --> 06:16.290
'Cause remember there was that endless feedback loop

06:16.290 --> 06:19.440
of do an action, get an observation,

06:19.440 --> 06:22.170
decide on the next action, have we finished?

06:22.170 --> 06:23.490
Oh, we haven't finished.

06:23.490 --> 06:25.920
Let's go and run that next action again,

06:25.920 --> 06:28.530
until you end up finishing.

06:28.530 --> 06:30.540
So that agent scratch pad is really important

06:30.540 --> 06:33.118
because that stores all of the kind of intermediate steps

06:33.118 --> 06:34.710
that LangChain uses.

06:34.710 --> 06:37.140
And remember the input here is basically

06:37.140 --> 06:40.770
what you can use to specifically decide on the first query

06:40.770 --> 06:42.300
that hits this agent.

06:42.300 --> 06:45.030
Now after defining the chat prompt template,

06:45.030 --> 06:47.940
you'll then need to bind tools to your agent.

06:47.940 --> 06:50.970
And basically OpenAI have recently moved away

06:50.970 --> 06:52.260
from function calling,

06:52.260 --> 06:54.720
and they've moved to binding tools

06:54.720 --> 06:56.580
and using tools directly.

06:56.580 --> 06:59.250
So we use the .bind_tools function

06:59.250 --> 07:02.910
and that takes a list of tools and you've got a model

07:02.910 --> 07:06.390
and your model also needs to be capable of calling tools.

07:06.390 --> 07:08.040
Now once you've got your model

07:08.040 --> 07:10.860
and you've bound the tools to that specific model,

07:10.860 --> 07:12.810
it knows about those tools,

07:12.810 --> 07:15.030
but we still need to set up the formatting

07:15.030 --> 07:18.390
to easily convert the intermediate steps

07:18.390 --> 07:20.580
that are inside the agent scratch pad

07:20.580 --> 07:22.950
into something that OpenAI understands.

07:22.950 --> 07:25.380
So what's happening is these intermediate steps

07:25.380 --> 07:27.030
that the agents are scratch pad down,

07:27.030 --> 07:29.190
I've taken this action and run these tools,

07:29.190 --> 07:32.452
it's formatting those to the OpenAI tool messages

07:32.452 --> 07:35.730
and then after that gets put into the prompt,

07:35.730 --> 07:38.310
we then have the model that is with the tools

07:38.310 --> 07:41.940
and we also pass the tools outputs

07:41.940 --> 07:43.860
back into these intermediate steps

07:43.860 --> 07:47.280
so that the agent executor then can easily understand those

07:47.280 --> 07:49.410
and see what's happening with those.

07:49.410 --> 07:53.520
So after you've got your agent set up in an LCL chain,

07:53.520 --> 07:56.550
you would then add that onto the agent executor.

07:56.550 --> 07:58.020
You also pass the tools here

07:58.020 --> 08:00.660
and you can also put in verbose-True

08:00.660 --> 08:02.790
to make sure that comes across

08:02.790 --> 08:04.740
and you can see all the various steps.

08:04.740 --> 08:07.440
Now if I just .stream the output

08:07.440 --> 08:09.090
and you can see the input

08:09.090 --> 08:12.936
is gonna be mapped if we scroll back up to this input here

08:12.936 --> 08:16.650
in the user message, and we're saying something like,

08:16.650 --> 08:19.080
how many letters in the word data.

08:19.080 --> 08:21.360
And by doing agent_executed.stream,

08:21.360 --> 08:23.730
we can actually see all the different steps

08:23.730 --> 08:25.440
that happen inside of the agency.

08:25.440 --> 08:26.310
You've got the actions,

08:26.310 --> 08:28.560
it decided to take a tool agent action,

08:28.560 --> 08:30.990
and it used this get_word_length tool

08:30.990 --> 08:33.500
and we can also see what the final output is,

08:33.500 --> 08:35.820
so the word data is four letters

08:35.820 --> 08:38.340
and this is the messages that ended with.

08:38.340 --> 08:40.830
Now you wouldn't always stream these

08:40.830 --> 08:42.600
and the way that you would normally use these

08:42.600 --> 08:44.340
if you just wanted to get the final output

08:44.340 --> 08:46.530
is you would use that .invoke function.

08:46.530 --> 08:48.450
And by changing that to the .invoke,

08:48.450 --> 08:51.030
you'll see that we just get the final output,

08:51.030 --> 08:52.590
which will come through in just a second.

08:52.590 --> 08:55.800
So you get this input, how many letters is in the word data,

08:55.800 --> 08:58.590
and you get the output, the word data has four letters.

08:58.590 --> 09:00.210
Now that's great,

09:00.210 --> 09:03.570
but we don't currently have any memory inside of this agent.

09:03.570 --> 09:05.340
It can just use a tool,

09:05.340 --> 09:08.070
and it can use that to help solve a question.

09:08.070 --> 09:11.070
Now one thing that we can do to add memory in

09:11.070 --> 09:14.460
is we can add another messages placeholder,

09:14.460 --> 09:16.650
and you'll see we've got two messages placeholders.

09:16.650 --> 09:18.030
So one for the memory key,

09:18.030 --> 09:20.340
which is we've called chat history.

09:20.340 --> 09:22.320
And that chat history, that key name,

09:22.320 --> 09:25.050
the variable name here is very important.

09:25.050 --> 09:28.530
And you'll see this has also got our agent scratch pad.

09:28.530 --> 09:32.400
Now we create a Python list called Chat History,

09:32.400 --> 09:35.520
and we updated the LCL chain.

09:35.520 --> 09:39.990
So basically we're getting the chat history

09:39.990 --> 09:43.350
and then that's then being formatted into the prompt.

09:43.350 --> 09:45.540
So you've got both the chat history

09:45.540 --> 09:47.280
and the agent scratch pad

09:47.280 --> 09:48.660
that are now being associated

09:48.660 --> 09:51.120
with these message placeholders, yeah?

09:51.120 --> 09:54.240
So you can add as many message placeholders as you want,

09:54.240 --> 09:55.620
but you have to make sure that

09:55.620 --> 09:57.570
before that enters the prompt,

09:57.570 --> 09:59.430
you've got some output for those

09:59.430 --> 10:03.060
inside of your prompt template that goes into the prompt.

10:03.060 --> 10:03.990
Then it goes into the model

10:03.990 --> 10:06.510
and we pass back the agent tools agent

10:06.510 --> 10:07.980
using an output passer.

10:07.980 --> 10:10.290
The agent executor's exactly the same.

10:10.290 --> 10:11.850
But you'll see we've got this first input,

10:11.850 --> 10:13.860
how many letters in the word data,

10:13.860 --> 10:16.620
and we've now had to add on that chat history

10:16.620 --> 10:19.680
as the extra one, which is just a Python list.

10:19.680 --> 10:23.550
Now we then extend the chat history with a human message

10:23.550 --> 10:25.260
with the input one,

10:25.260 --> 10:29.160
and the AI's message that it came back with a result output.

10:29.160 --> 10:31.500
And then we can say, is that a real word?

10:31.500 --> 10:34.140
And so we can have a look. So firstly it's gonna figure out

10:34.140 --> 10:37.641
that it needs to invoke this tool called the get_word_length

10:37.641 --> 10:41.160
and it uses the input for that as data

10:41.160 --> 10:44.010
and then it basically work,

10:44.010 --> 10:45.300
this is the output it came back with,

10:45.300 --> 10:47.130
so the word data is four letters.

10:47.130 --> 10:48.210
And then after that,

10:48.210 --> 10:50.550
then it entered a new agent executed chain.

10:50.550 --> 10:52.980
But rather than deciding to invoke a tool,

10:52.980 --> 10:55.680
it decided to just reply with text.

10:55.680 --> 10:57.210
And that's quite an interesting thing,

10:57.210 --> 11:00.660
because you don't always have to invoke tools

11:00.660 --> 11:01.890
when using agents.

11:01.890 --> 11:04.800
They can use a mixture of just replying in text

11:04.800 --> 11:06.180
or using a tool.

11:06.180 --> 11:07.980
And in OpenAI tools,

11:07.980 --> 11:10.920
you can also run multiple tools in parallel,

11:10.920 --> 11:13.281
which can make agents incredibly versatile

11:13.281 --> 11:16.020
at not only being able to handle certain queries,

11:16.020 --> 11:18.570
but also being able to handle a mixture of queries.

11:18.570 --> 11:20.672
And we can see that the second response

11:20.672 --> 11:23.430
is saying yes, data is a real word.

11:23.430 --> 11:25.860
It refers to facts and statistics collected together

11:25.860 --> 11:26.940
for reference or analysis.

11:26.940 --> 11:29.370
So it is actually referring back to the fact

11:29.370 --> 11:33.630
that we asked it a question about what's the data?

11:33.630 --> 11:36.540
And so what's the count for that word.

11:36.540 --> 11:39.000
Now one thing that can be quite nice to do

11:39.000 --> 11:40.680
is obviously we've done this,

11:40.680 --> 11:43.830
but I just wanna show you another way of doing the memory.

11:43.830 --> 11:47.970
You could create, so when we're building these chains,

11:47.970 --> 11:50.070
they are something called a runnable.

11:50.070 --> 11:52.980
And a runnable is a something that can be ran.

11:52.980 --> 11:55.230
It's a LangChain expression

11:55.230 --> 11:57.990
that's basically compiled into a graph.

11:57.990 --> 11:59.430
Now if we go back down here,

11:59.430 --> 12:01.170
we can also do something like this,

12:01.170 --> 12:03.060
where we can create an empty store,

12:03.060 --> 12:05.460
which is gonna store our chat messages

12:05.460 --> 12:08.460
and we can do something with runnable with message history,

12:08.460 --> 12:10.770
and we can specify a session ID.

12:10.770 --> 12:12.030
And so what you'll see here is

12:12.030 --> 12:14.190
we've changed the variable name to history,

12:14.190 --> 12:16.230
just to show you how this works.

12:16.230 --> 12:18.660
The agent chain therefore has to update

12:18.660 --> 12:20.250
and it has to use history.

12:20.250 --> 12:22.620
The agent scratch pad stays the same.

12:22.620 --> 12:27.120
And what we've also got here is a get_session history

12:27.120 --> 12:28.980
that takes a session ID,

12:28.980 --> 12:32.550
looks inside of that Python dictionary, which is the store.

12:32.550 --> 12:34.080
And then after we've looked inside

12:34.080 --> 12:36.870
of that Python dictionary, we add on that session ID

12:36.870 --> 12:39.030
and we create this chat message history.

12:39.030 --> 12:42.480
And then we return the session ID.

12:42.480 --> 12:45.900
Now we can then wrap the agent executor

12:45.900 --> 12:47.280
with that Python function

12:47.280 --> 12:50.520
and the input messages key is gonna be the input.

12:50.520 --> 12:52.050
So remember that's our input here,

12:52.050 --> 12:55.650
and you can see that in the ACL inside of this agent chain.

12:55.650 --> 12:59.520
And the history messages key is the history.

12:59.520 --> 13:00.960
Now once we've done that,

13:00.960 --> 13:02.910
now what we can do is we can invoke,

13:02.910 --> 13:04.770
put our input, the history,

13:04.770 --> 13:07.620
set the config for the runnable config,

13:07.620 --> 13:09.769
and we say it's configurable with a session ID,

13:09.769 --> 13:10.860
some_session_ID.

13:10.860 --> 13:14.040
And what's happening now is the store,

13:14.040 --> 13:15.570
if we have a look at it,

13:15.570 --> 13:19.440
is actually gonna start to be populated with a session ID

13:19.440 --> 13:22.140
which has a chat message history attached to it.

13:22.140 --> 13:24.060
So this is an in-memory way

13:24.060 --> 13:27.180
of doing configurable chat message histories,

13:27.180 --> 13:30.960
which is being associated with this specific dictionary,

13:30.960 --> 13:33.540
this Python dictionary, this store Python dictionary.

13:33.540 --> 13:37.800
Now if we then say, I said, I've told it my name is James,

13:37.800 --> 13:40.080
and now we can also say what is my name?

13:40.080 --> 13:41.820
And we're using the same session ID,

13:41.820 --> 13:43.650
which we've called some_session_ID,

13:43.650 --> 13:46.770
and the agent executor hasn't used any tools yet.

13:46.770 --> 13:49.680
It's still got the ability to count with the word length,

13:49.680 --> 13:51.360
but it's decided to just reply in text.

13:51.360 --> 13:52.980
And I think that's an important point.

13:52.980 --> 13:54.480
And you can also see here

13:54.480 --> 13:56.460
that because we are using the same session ID

13:56.460 --> 13:58.080
in our Runnable config,

13:58.080 --> 14:01.440
we've then also been able to then get the LLM

14:01.440 --> 14:06.030
to store that chat history in a in-memory Python dictionary.

14:06.030 --> 14:08.220
And the output then says, your name is James.

14:08.220 --> 14:11.130
So it is able to remember who we are

14:11.130 --> 14:14.250
by using that with message history runnable.

14:14.250 --> 14:18.060
Now if I change the session ID to something different,

14:18.060 --> 14:20.280
you'll see that we then get a very different response

14:20.280 --> 14:21.113
from the agent,

14:21.113 --> 14:23.490
where it decides you haven't mentioned your name yet,

14:23.490 --> 14:26.190
please could you tell me your name, and I'll remember it.

14:26.190 --> 14:29.520
So hopefully this gives you a good introduction to agents.

14:29.520 --> 14:30.600
In future videos,

14:30.600 --> 14:32.970
we'll be exploring how you can build agents

14:32.970 --> 14:35.190
directly inside of things like line graph,

14:35.190 --> 14:38.010
and also how you could build multiple tools

14:38.010 --> 14:39.873
and looking at parallel tool usage.
