mirror of
https://github.com/james-m-jordan/openai-cookbook.git
synced 2025-05-09 19:32:38 +00:00
Update title and reformat with prettier
This commit is contained in:
parent
ec414ee67b
commit
6d3db1374b
@ -1,4 +1,4 @@
|
|||||||
# How to build an agent with the Node.js SDK and function-calling
|
# How to build an agent with the Node.js SDK
|
||||||
|
|
||||||
OpenAI functions enable your app to take action based on user inputs. This means that it can, e.g., search the web, send emails, or book tickets on behalf of your users, making it more powerful than a regular chatbot.
|
OpenAI functions enable your app to take action based on user inputs. This means that it can, e.g., search the web, send emails, or book tickets on behalf of your users, making it more powerful than a regular chatbot.
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ If you prefer watching screencasts over reading, then you can check out [this sc
|
|||||||
Our app is a simple agent that helps you find activities in your area.
|
Our app is a simple agent that helps you find activities in your area.
|
||||||
It has access to two functions, `getLocation()` and `getCurrentWeather()`,
|
It has access to two functions, `getLocation()` and `getCurrentWeather()`,
|
||||||
which means it can figure out where you’re located and what the weather
|
which means it can figure out where you’re located and what the weather
|
||||||
is at the moment.
|
is at the moment.
|
||||||
|
|
||||||
At this point, it's important to understand that
|
At this point, it's important to understand that
|
||||||
OpenAI doesn't execute any code for you. It just tells your app which
|
OpenAI doesn't execute any code for you. It just tells your app which
|
||||||
@ -27,12 +27,12 @@ internal knowledge to suggest suitable local activities for you.
|
|||||||
|
|
||||||
We start by importing the OpenAI SDK at the top of our JavaScript file and authenticate with our API key, which we have stored as an environment variable.
|
We start by importing the OpenAI SDK at the top of our JavaScript file and authenticate with our API key, which we have stored as an environment variable.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
import OpenAI from "openai";
|
import OpenAI from "openai";
|
||||||
|
|
||||||
const openai = new OpenAI({
|
const openai = new OpenAI({
|
||||||
apiKey: process.env.OPENAI_API_KEY,
|
apiKey: process.env.OPENAI_API_KEY,
|
||||||
dangerouslyAllowBrowser: true
|
dangerouslyAllowBrowser: true,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -44,11 +44,11 @@ Next, we'll create the two functions. The first one - `getLocation` -
|
|||||||
uses the [IP API](https://ipapi.co/) to get the location of the
|
uses the [IP API](https://ipapi.co/) to get the location of the
|
||||||
user.
|
user.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
async function getLocation() {
|
async function getLocation() {
|
||||||
const response = await fetch('https://ipapi.co/json/');
|
const response = await fetch("https://ipapi.co/json/");
|
||||||
const locationData = await response.json();
|
const locationData = await response.json();
|
||||||
return locationData;
|
return locationData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -58,12 +58,12 @@ function `getCurrentWeather`. It uses the [Open Meteo
|
|||||||
API](https://open-meteo.com/) to get the current weather data, like
|
API](https://open-meteo.com/) to get the current weather data, like
|
||||||
this:
|
this:
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
async function getCurrentWeather(latitude, longitude) {
|
async function getCurrentWeather(latitude, longitude) {
|
||||||
const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=apparent_temperature`;
|
const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=apparent_temperature`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const weatherData = await response.json();
|
const weatherData = await response.json();
|
||||||
return weatherData;
|
return weatherData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -74,49 +74,51 @@ describe them using a specific schema. We'll create an array called
|
|||||||
`functionDefinitions` that contains one object per function. Each object
|
`functionDefinitions` that contains one object per function. Each object
|
||||||
will have three keys: `name`, `description`, and `parameters`.
|
will have three keys: `name`, `description`, and `parameters`.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
const functionDefinitions = [
|
const functionDefinitions = [
|
||||||
{
|
{
|
||||||
name: "getCurrentWeather",
|
name: "getCurrentWeather",
|
||||||
description: "Get the current weather in a given location",
|
description: "Get the current weather in a given location",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
longitude: {
|
longitude: {
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
latitude: {
|
latitude: {
|
||||||
type: "string",
|
type: "string",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
required: ["longitude", "latitude"]
|
required: ["longitude", "latitude"],
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "getLocation",
|
{
|
||||||
description: "Get the user's location based on their IP address",
|
name: "getLocation",
|
||||||
parameters: {
|
description: "Get the user's location based on their IP address",
|
||||||
type: "object",
|
parameters: {
|
||||||
properties: {}
|
type: "object",
|
||||||
}
|
properties: {},
|
||||||
}
|
},
|
||||||
]
|
},
|
||||||
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setting up the messages array
|
## Setting up the messages array
|
||||||
|
|
||||||
We also need to define a `messages` array. This will keep track of all of the messages back and forth between our app and OpenAI.
|
We also need to define a `messages` array. This will keep track of all of the messages back and forth between our app and OpenAI.
|
||||||
|
|
||||||
The first object in the array should always have the `role` property set to `"system"`, which tells OpenAI that this is how we want it to behave.
|
The first object in the array should always have the `role` property set to `"system"`, which tells OpenAI that this is how we want it to behave.
|
||||||
``` js
|
|
||||||
const messages = [{
|
```js
|
||||||
|
const messages = [
|
||||||
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content: "You are a helpful assistant. Only use the functions you have been provided with."
|
content:
|
||||||
}];
|
"You are a helpful assistant. Only use the functions you have been provided with.",
|
||||||
|
},
|
||||||
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Creating the agent function
|
## Creating the agent function
|
||||||
|
|
||||||
We are now ready to build the logic of our app, which lives in the
|
We are now ready to build the logic of our app, which lives in the
|
||||||
@ -125,18 +127,20 @@ We are now ready to build the logic of our app, which lives in the
|
|||||||
|
|
||||||
We start by pushing the `userInput` to the messages array. This time, we set the `role` to `"user"`, so that OpenAI knows that this is the input from the user.
|
We start by pushing the `userInput` to the messages array. This time, we set the `role` to `"user"`, so that OpenAI knows that this is the input from the user.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
async function agent(userInput) {
|
async function agent(userInput) {
|
||||||
messages.push([{
|
messages.push([
|
||||||
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
content: userInput,
|
content: userInput,
|
||||||
}]);
|
},
|
||||||
const response = await openai.chat.completions.create({
|
]);
|
||||||
model: "gpt-4",
|
const response = await openai.chat.completions.create({
|
||||||
messages: messages,
|
model: "gpt-4",
|
||||||
functions: functionDefinitions
|
messages: messages,
|
||||||
});
|
functions: functionDefinitions,
|
||||||
console.log(response);
|
});
|
||||||
|
console.log(response);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -145,47 +149,46 @@ Next, we'll send a request to the Chat completions endpoint via the
|
|||||||
configuration object as an argument. In it, we'll specify three
|
configuration object as an argument. In it, we'll specify three
|
||||||
properties:
|
properties:
|
||||||
|
|
||||||
- `model` - Decides which AI model we want to use (in our case,
|
- `model` - Decides which AI model we want to use (in our case,
|
||||||
GPT-4).
|
GPT-4).
|
||||||
- `messages` - The entire history of messages between the user and the
|
- `messages` - The entire history of messages between the user and the
|
||||||
AI up until this point.
|
AI up until this point.
|
||||||
- `functions` - A description of the functions our app has access to.
|
- `functions` - A description of the functions our app has access to.
|
||||||
Here, we'll we use the `functionDefinitions` array we created
|
Here, we'll we use the `functionDefinitions` array we created
|
||||||
earlier.
|
earlier.
|
||||||
|
|
||||||
|
|
||||||
## Running our app with a simple input
|
## Running our app with a simple input
|
||||||
|
|
||||||
Let's try to run the `agent` with an input that requires a function call to give a suitable reply.
|
Let's try to run the `agent` with an input that requires a function call to give a suitable reply.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
agent("Where am I located right now?");
|
agent("Where am I located right now?");
|
||||||
```
|
```
|
||||||
|
|
||||||
When we run the code above, we see the response from OpenAI logged out
|
When we run the code above, we see the response from OpenAI logged out
|
||||||
to the console like this:
|
to the console like this:
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
{
|
{
|
||||||
id: "chatcmpl-84ojoEJtyGnR6jRHK2Dl4zTtwsa7O",
|
id: "chatcmpl-84ojoEJtyGnR6jRHK2Dl4zTtwsa7O",
|
||||||
object: "chat.completion",
|
object: "chat.completion",
|
||||||
created: 1696159040,
|
created: 1696159040,
|
||||||
model: "gpt-4-0613",
|
model: "gpt-4-0613",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
message: {
|
message: {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: null,
|
content: null,
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "getLocation", // The function OpenAI wants us to call
|
name: "getLocation", // The function OpenAI wants us to call
|
||||||
arguments: "{}"
|
arguments: "{}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
finish_reason: "function_call" // OpenAI wants us to call a function
|
finish_reason: "function_call" // OpenAI wants us to call a function
|
||||||
}],
|
}],
|
||||||
usage: {
|
usage: {
|
||||||
prompt_tokens: 134,
|
prompt_tokens: 134,
|
||||||
completion_tokens: 6,
|
completion_tokens: 6,
|
||||||
total_tokens: 140
|
total_tokens: 140
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,10 +206,10 @@ Now that we have the name of the function as a string, we'll need to
|
|||||||
translate that into a function call. To help us with that, we'll gather
|
translate that into a function call. To help us with that, we'll gather
|
||||||
both of our functions in an object called `availableFunctions`:
|
both of our functions in an object called `availableFunctions`:
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
const availableFunctions = {
|
const availableFunctions = {
|
||||||
getCurrentWeather,
|
getCurrentWeather,
|
||||||
getLocation
|
getLocation,
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -214,17 +217,16 @@ This is handy because we'll be able to access the `getLocation` function
|
|||||||
via bracket notation and the string we got back from OpenAI, like this:
|
via bracket notation and the string we got back from OpenAI, like this:
|
||||||
`availableFunctions["getLocation"]`.
|
`availableFunctions["getLocation"]`.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
|
|
||||||
const { finish_reason, message } = response.choices[0];
|
const { finish_reason, message } = response.choices[0];
|
||||||
|
|
||||||
if (finish_reason === "function_call") {
|
if (finish_reason === "function_call") {
|
||||||
const functionName = message.function_call.name;
|
const functionName = message.function_call.name;
|
||||||
const functionToCall = availableFunctions[functionName];
|
const functionToCall = availableFunctions[functionName];
|
||||||
const functionArgs = JSON.parse(message.function_call.arguments);
|
const functionArgs = JSON.parse(message.function_call.arguments);
|
||||||
const functionArgsArr = Object.values(functionArgs);
|
const functionArgsArr = Object.values(functionArgs);
|
||||||
const functionResponse = await functionToCall.apply(null, functionArgsArr);
|
const functionResponse = await functionToCall.apply(null, functionArgsArr);
|
||||||
console.log(functionResponse);
|
console.log(functionResponse);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -232,25 +234,26 @@ We're also grabbing ahold of any arguments OpenAI wants us to pass into
|
|||||||
the function: `message.function_call.arguments`.
|
the function: `message.function_call.arguments`.
|
||||||
However, we won't need any arguments for this first function call.
|
However, we won't need any arguments for this first function call.
|
||||||
|
|
||||||
|
|
||||||
If we run the code again with the same input
|
If we run the code again with the same input
|
||||||
(`"Where am I located right now?"`), we'll see that `functionResponse`
|
(`"Where am I located right now?"`), we'll see that `functionResponse`
|
||||||
is an object filled with location about where the user is located right
|
is an object filled with location about where the user is located right
|
||||||
now. In my case, that is Oslo, Norway.
|
now. In my case, that is Oslo, Norway.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
{ip: "193.212.60.170", network: "193.212.60.0/23", version: "IPv4", city: "Oslo", region: "Oslo County", region_code: "03", country: "NO", country_name: "Norway", country_code: "NO", country_code_iso3: "NOR", country_capital: "Oslo", country_tld: ".no", continent_code: "EU", in_eu: false, postal: "0026", latitude: 59.955, longitude: 10.859, timezone: "Europe/Oslo", utc_offset: "+0200", country_calling_code: "+47", currency: "NOK", currency_name: "Krone", languages: "no,nb,nn,se,fi", country_area: 324220, country_population: 5314336, asn: "AS2119", org: "Telenor Norge AS"}
|
{ip: "193.212.60.170", network: "193.212.60.0/23", version: "IPv4", city: "Oslo", region: "Oslo County", region_code: "03", country: "NO", country_name: "Norway", country_code: "NO", country_code_iso3: "NOR", country_capital: "Oslo", country_tld: ".no", continent_code: "EU", in_eu: false, postal: "0026", latitude: 59.955, longitude: 10.859, timezone: "Europe/Oslo", utc_offset: "+0200", country_calling_code: "+47", currency: "NOK", currency_name: "Krone", languages: "no,nb,nn,se,fi", country_area: 324220, country_population: 5314336, asn: "AS2119", org: "Telenor Norge AS"}
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll add this data to a new item in the `messages` array, where we also
|
We'll add this data to a new item in the `messages` array, where we also
|
||||||
specify the name of the function we called.
|
specify the name of the function we called.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
messages.push({
|
messages.push({
|
||||||
role: "function",
|
role: "function",
|
||||||
name: functionName,
|
name: functionName,
|
||||||
content: `The result of the last function was this: ${JSON.stringify(functionResponse)}
|
content: `The result of the last function was this: ${JSON.stringify(
|
||||||
`
|
functionResponse
|
||||||
|
)}
|
||||||
|
`,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -279,33 +282,34 @@ the next iteration of the loop, triggering a new request.
|
|||||||
If we get `finish_reason: "stop"` back, then GPT has found a suitable
|
If we get `finish_reason: "stop"` back, then GPT has found a suitable
|
||||||
answer, so we'll return the function and cancel the loop.
|
answer, so we'll return the function and cancel the loop.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
const response = await openai.chat.completions.create({
|
const response = await openai.chat.completions.create({
|
||||||
model: "gpt-4",
|
model: "gpt-4",
|
||||||
messages: messages,
|
messages: messages,
|
||||||
functions: functionDefinitions
|
functions: functionDefinitions,
|
||||||
});
|
});
|
||||||
const { finish_reason, message } = response.choices[0];
|
const { finish_reason, message } = response.choices[0];
|
||||||
|
|
||||||
if (finish_reason === "function_call") {
|
if (finish_reason === "function_call") {
|
||||||
const functionName = message.function_call.name;
|
const functionName = message.function_call.name;
|
||||||
const functionToCall = availableFunctions[functionName];
|
const functionToCall = availableFunctions[functionName];
|
||||||
const functionArgs = JSON.parse(message.function_call.arguments);
|
const functionArgs = JSON.parse(message.function_call.arguments);
|
||||||
const functionArgsArr = Object.values(functionArgs);
|
const functionArgsArr = Object.values(functionArgs);
|
||||||
const functionResponse = await functionToCall.apply(null, functionArgsArr);
|
const functionResponse = await functionToCall.apply(null, functionArgsArr);
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
role: "function",
|
role: "function",
|
||||||
name: functionName,
|
name: functionName,
|
||||||
content: `
|
content: `
|
||||||
The result of the last function was this: ${JSON.stringify(functionResponse)}
|
The result of the last function was this: ${JSON.stringify(
|
||||||
`
|
functionResponse
|
||||||
});
|
)}
|
||||||
} else if (finish_reason === "stop") {
|
`,
|
||||||
|
});
|
||||||
|
} else if (finish_reason === "stop") {
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
return message.content;
|
return message.content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "The maximum number of iterations has been met without a suitable answer. Please try again with a more specific input.";
|
return "The maximum number of iterations has been met without a suitable answer. Please try again with a more specific input.";
|
||||||
@ -319,22 +323,24 @@ we'll return a message saying we couldn’t find a suitable answer.
|
|||||||
At this point, we are ready to try our app! I'll ask the agent to
|
At this point, we are ready to try our app! I'll ask the agent to
|
||||||
suggest some activities based on my location and the current weather.
|
suggest some activities based on my location and the current weather.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
const response = await agent("Please suggest some activities based on my location and the current weather.");
|
const response = await agent(
|
||||||
|
"Please suggest some activities based on my location and the current weather."
|
||||||
|
);
|
||||||
console.log(response);
|
console.log(response);
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's what we see in the console (formatted to make it easier to read):
|
Here's what we see in the console (formatted to make it easier to read):
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
Based on your current location in Oslo, Norway and the weather (15°C and snowy),
|
Based on your current location in Oslo, Norway and the weather (15°C and snowy),
|
||||||
here are some activity suggestions:
|
here are some activity suggestions:
|
||||||
|
|
||||||
1. A visit to the Oslo Winter Park for skiing or snowboarding.
|
1. A visit to the Oslo Winter Park for skiing or snowboarding.
|
||||||
2. Enjoy a cosy day at a local café or restaurant.
|
2. Enjoy a cosy day at a local café or restaurant.
|
||||||
3. Visit one of Oslo's many museums. The Fram Museum or Viking Ship Museum offer interesting insights into Norway’s seafaring history.
|
3. Visit one of Oslo's many museums. The Fram Museum or Viking Ship Museum offer interesting insights into Norway’s seafaring history.
|
||||||
4. Take a stroll in the snowy streets and enjoy the beautiful winter landscape.
|
4. Take a stroll in the snowy streets and enjoy the beautiful winter landscape.
|
||||||
5. Enjoy a nice book by the fireplace in a local library.
|
5. Enjoy a nice book by the fireplace in a local library.
|
||||||
6. Take a fjord sightseeing cruise to enjoy the snowy landscapes.
|
6. Take a fjord sightseeing cruise to enjoy the snowy landscapes.
|
||||||
|
|
||||||
Always remember to bundle up and stay warm. Enjoy your day!
|
Always remember to bundle up and stay warm. Enjoy your day!
|
||||||
@ -349,7 +355,7 @@ to call the `getCurrentWeather` function with
|
|||||||
`"longitude": "10.859", "latitude": "59.955"` passed in as the
|
`"longitude": "10.859", "latitude": "59.955"` passed in as the
|
||||||
arguments. This is data it got back from the first function call we did.
|
arguments. This is data it got back from the first function call we did.
|
||||||
|
|
||||||
``` js
|
```js
|
||||||
{role: "assistant", content: null, function_call: {name: "getLocation", arguments: "{}"}}
|
{role: "assistant", content: null, function_call: {name: "getLocation", arguments: "{}"}}
|
||||||
{role: "assistant", content: null, function_call: {name: "getCurrentWeather", arguments: " { "longitude": "10.859", "latitude": "59.955" }"}}
|
{role: "assistant", content: null, function_call: {name: "getCurrentWeather", arguments: " { "longitude": "10.859", "latitude": "59.955" }"}}
|
||||||
```
|
```
|
||||||
|
@ -1029,13 +1029,14 @@
|
|||||||
- tiktoken
|
- tiktoken
|
||||||
- completions
|
- completions
|
||||||
|
|
||||||
- title: How to build an agent with the Node.js SDK and function-calling
|
- title: How to build an agent with the Node.js SDK
|
||||||
path: examples/How_to_build_an_agent_with_the_node_sdk.md
|
path: examples/How_to_build_an_agent_with_the_node_sdk.md
|
||||||
date: 2023-10-05
|
date: 2023-10-05
|
||||||
authors:
|
authors:
|
||||||
- perborgen
|
- perborgen
|
||||||
tags:
|
tags:
|
||||||
- completions
|
- completions
|
||||||
|
- functions
|
||||||
|
|
||||||
- title: What makes documentation good
|
- title: What makes documentation good
|
||||||
path: articles/what_makes_documentation_good.md
|
path: articles/what_makes_documentation_good.md
|
||||||
|
Loading…
x
Reference in New Issue
Block a user