Build your First AI Agent with Julep: A Step-by-Step Guide
Tags: webdev, javascript, ai, react
Published: 2024-06-18 16:47:52

Creating an AI app from scratch can be a very challenging task. Whether you want to build a simple chatbot or an advanced intelligent virtual assistant, it can take weeks to develop the desired app successfully. But that’s where Julep comes to rescue us.

[Julep](https://git.new/julep) is a platform that helps to build stateful and functional LLM-powered applications. With Julep, you can build a fully functional AI app with just a few lines of code.

Platforms like OpenAI's GPT-3, Microsoft's Azure Bot Service, and Google's Dialogflow can build AI applications. However, Julep stands out due to its advantages like statefulness to track conversation history and context, easy integration with multiple LLMs, and a user-friendly interface for managing users, agents, and sessions.

In this blog, we will create Movio, an AI-powered Movie Companion App that provides recommendations and information about any movie the user asks for. We will walk through each step and understand how you can use Julep in your projects.

Let’s get started!

## Prerequisites

Make sure you have Nodejs installed in your device. Download and Install Node.js from their [official website](https://nodejs.org/en/download/package-manager)

If you like the posts on the Julep blog so far, please consider [giving Julep a star on GitHub](https://git.new/julep), it helps us to reach and help more developers.

{% embed https://git.new/julep %}

## Creating React App

To create a React App, run this command in the terminal:

```sh
npm create-vite@latest
```

You can check out the [Vite Docs](https://vitejs.dev/guide/) to create a React app.

Create the basic structure in the `App.jsx` file. Add an `<input>` tag allowing the users to enter the query:

```html
<div className="container">
      <h1>Hi, I'm Movio</h1>
      <h4>Your Ultimate Movie Companion</h4>
      <div id="conversation">
        {conversation.map((item, index) => (
          <p key={index} className={item.role}>
            {item.message}
          </p>
        ))}
      </div>
      <input
        type="text"
        id="queryInput"
        placeholder="Ask me anything about Movies..."
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      <button onClick={sendQuery}>Submit</button>
    </div>
```

To handle and capture the user’s query, we have defined a functionality:

```js
const [query, setQuery] = useState("");
const [conversation, setConversation] = useState([]);
```

We have used `useState()` hook to define `query` and `conversation` variables along with their state updating functions.

```js
const sendQuery = async () => {
    if (!query) return;


    // Append user message to conversation
    setConversation((prev) => [...prev, { role: "user", message: query }]);
    setQuery("");


    try {
      const response = await axios.post("http://localhost:3000/chat", {
        query,
      });


      const agentResponse = response.data.response;


      // Append agent response to conversation
      setConversation((prev) => [
        ...prev,
        { role: "agent", message: agentResponse },
      ]);
    } catch (error) {
      console.error("Error fetching response:", error);
    }
  };
```

The function `sendQuery()` first checks if the query is empty. If it isn't, it updates the `conversation` state by appending a new object with `role` as `"user"` and `message` as the `query`. Then, it resets the `query` to an empty string using `setQuery("")`.

Inside the try block, `axios` posts the user's query to the endpoint. The server's response is stored in `agentResponse`, which is extracted from the `response` data.

Next, `setConversation` is used again to add the agent's response to the `conversation` state, with role as `"agent"` and message as `agentResponse`.
Finally, any errors during the axios request are caught in the catch block and logged to the console.

Here’s the full `App.jsx` code:

```js
import React, { useState } from "react";
import axios from "axios";

function App() {
  const [query, setQuery] = useState("");
  const [conversation, setConversation] = useState([]);

  const sendQuery = async () => {
    if (!query) return;

    // Append user message to conversation
    setConversation((prev) => [...prev, { role: "user", message: query }]);
    setQuery("");

    try {
      const response = await axios.post("http://localhost:3000/chat", {
        query,
      });

      const agentResponse = response.data.response;

      // Append agent response to conversation
      setConversation((prev) => [
        ...prev,
        { role: "agent", message: agentResponse },
      ]);
    } catch (error) {
      console.error("Error fetching response:", error);
    }
  };

  return (
    <div className="container">
      <h1>Hi, I'm Movio</h1>
      <h4>Your Ultimate Movie Companion</h4>
      <div id="conversation">
        {conversation.map((item, index) => (
          <p key={index} className={item.role}>
            {item.message}
          </p>
        ))}
      </div>
      <input
        type="text"
        id="queryInput"
        placeholder="Ask me anything about Movies..."
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      <button onClick={sendQuery}>Submit</button>
    </div>
  );
}

export default App;
```

Let's start integrating Julep in our project.

## Installing Libraries

For the Movie Companion app, we will install some necessary libraries. These libraries are:

- [express](https://expressjs.com/) - To create and manage your web server
- [julep SDK](https://docs.julep.ai/api-reference/js-sdk-docs) - To interact with a specific service or API provided by Julep
- [body-parser](https://www.npmjs.com/package/body-parser) - To parse incoming request bodies, making it easier to handle data sent by clients
- [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#:~:text=Cross%2DOrigin%20Resource%20Sharing%20(CORS)%20is%20an%20HTTP%2D,browser%20should%20permit%20loading%20resources.) - To enable cross-origin requests, allowing your server to handle requests from different domains
- [dotenv](https://www.dotenv.org/) - To retrieve the value stored in the `.env` file
- [axios](https://axios-http.com/docs/intro) - a promise-based HTTP Client for node.js and the browser


Run this command to install libraries:

```sh
npm install express @julep/sdk cors body-parser dotenv axios
```

## Integrating Julep

To integrate Julep, we need an API key.

Go to [platform.julep.ai](http://platform.julep.ai) and Sign in with Google account credentials. Copy the _YOUR API TOKEN_ present on the top-right corner.


![Julep API Token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao5kts1slunn5wpb5itk.png)

This API Token will serve as your API key.

Create a `.env` file in your project directory and paste the following code:

```js
JULEP_API_KEY = “api_key”
```

Replace the `api_key` with the copied API Token.

Create a file in `src` directory and name it `server.js`. Whole Julep code will come in this file.

Firstly, we will import the required libraries.

```js
import express from "express";
import julep from "@julep/sdk";
import bodyParser from "body-parser";
import cors from "cors";
import { fileURLToPath } from "url"; // Import the fileURLToPath function
import path from "path";
import dotenv from "dotenv";
```

Create a new `client` using Julep SDK‘s `Client` class. This client interacts with the Julep API and initializes the managers for agents, users, sessions, documents, memories, and tools.

```js
const apiKey = process.env.JULEP_API_KEY;
const client = new julep.Client({ apiKey });
```

Now, we will create an Express app instance to serve as the backend server. Use the `bodyparser.json()` to configure the app to parse incoming JSON requests automatically, and `cors()` to enable Cross-Origin Resource Sharing (CORS) allowing requests from multiple origins.

```js
const app = express();
app.use(bodyParser.json());
app.use(cors());
```

Set up an asynchronous route handler for POST requests to the Express app's `/chat` endpoint.

```js
app.post("/chat", async (req, res) => {
```

Inside the try block, Create a `query` variable that stores the query entered by the user.

```js
try {
const query = req.body.query;
```

Now, let’s create users, agents, and sessions to perform the interaction with Julep API.

### Creating User

[User](https://docs.julep.ai/concepts/users) object represents an entity, either a real person or a system, that interacts with the application. Every AI application developed using Julep supports multiple users, each capable of interacting with the Agent. Each of these users is distinct, meaning they have their own unique identities and assigned roles.

User is an optional entity, and an application can function properly without defining one. However, it's advisable to create a user profile for each individual or system interacting with the Agent for better organization and tracking. Specifically, adding some basic details about the user can help the application better understand their behavior. This enables the application to provide personalized results tailored to the user's preferences and needs.

When it comes to creating a user, Julep offers a `users.create()` method which we can be used on the `client` to create a user. Creating a user demands 4 attributes:

- Name - Name of the user
- About - Small description of the user
- Documents - Essential documents formatted as text tailored to the user's needs (Optional)
- Metadata - Additional data beyond the ID that pertains to the user within the application (Optional)

Here’s an example:

```js
    const user = await client.users.create({
      name: "Sam",
      about: "Machine Learning Developer and AI Enthusiast",
      docs:[{"title": "AI Efficiency Report", "content": "...", "metadata": {"page": 1}}],  // Optional
      metadata:{"db_uuid": "1234"}, // Optional
    });
```

Now, let’s create a user for our Movie Companion app:

```js
const user = await client.users.create({
      name: "Ayush",
      about: "A developer",
    });
```

Here, we have created a user with name `Ayush` and `A developer` as description.

### Creating Agents

[Agent](https://docs.julep.ai/concepts/agents) is the intelligent interface serving between the user and the application, handling all the interactions and enhancing the user experience. Agents are programmed to process the queries user has asked for and provide tailored results or suggestions.

Agent contains all the configurations and settings of LLM Models you want to use in your AI application. This enables the applications to carry out specific tasks and cater to the individual preferences of users.

These agents can be as simple as a chatbot and can range up to highly complex AI-driven intelligent assistants capable of understanding natural language and performing intricate tasks.

Similarly, like users, Julep includes the `agents.create()` method to create an agent. Creating an agent requires a collection of attributes:

- Name - Name of the agent
- About - Small description of the agent (Optional)
- Instructions - List of instructions for the agent to follow (Optional)
- Tools - List of functions for agent to execute tasks (Optional)
- Model Name - LLM Model that agent will use (Optional)
- Settings - Configurations on LLM model (Optional)
- Documents - Important documents in text format to be used by agent to improve the persona (Optional)
- Metadata - Additional information apart from ID to identify the user or agent (Optional)

Here’s an example:

```js
const agent = client.agents.create(
  (name = "Cody"),
  (about =
    "Cody is an AI powered code reviewer. It can review code, provide feedback, suggest improvements, and answer questions about code."),
  (instructions = [
    "On every new issue, Review the issue made in the code. Summarize the issue made in the code and add a comment",
    "Scrutinize the changes very deeply for potential bugs, errors, security vulnerabilities. Assume the worst case scenario and explain your reasoning for the same.",
  ]),
  (tools = [
    {
      type: "function",
      function: {
        name: "github_comment",
        description:
          "Posts a comment made on a GitHub Pull Request after every new commit. The tool will return a boolean value to indicate if the comment was successfully posted or not.",
        parameters: {
          type: "object",
          properties: {
            comment: {
              type: "string",
              description:
                "The comment to be posted on the issue. It should be a summary of the changes made in the PR and the feedback on the same.",
            },
            pr_number: {
              type: "number",
              description:
                "The issue number on which the comment is to be posted.",
            },
          },
          required: ["comment", "pr_number"],
        },
      },
    },
  ]),
  (model = "gpt-4"),
  (default_settings = {
    temperature: 0.7,
    top_p: 1,
    min_p: 0.01,
    presence_penalty: 0,
    frequency_penalty: 0,
    length_penalty: 1.0,
  }),
  (docs = [{ title: "API Reference", content: "...", metadata: { page: 1 } }]),
  (metadata = { db_uuid: "1234" })
);
```

Now, let’s create the agent for our Movio app:

```js
    const agent = await client.agents.create({
      name: "Movie suggesting assistant",
      model: "gpt-4-turbo",
    });
```

As you can see, we have used the `gpt-4-turbo` LLM model in this agent, but Julep supports multiple LLM models that you can use to create AI applications. Check out the [documentation](https://docs.julep.ai/guides/llms) to know more.

### Creating Sessions

[Session](https://docs.julep.ai/concepts/sessions) is an entity where the users interact with the agent. It’s the period of interaction between the user and the agent. It serves as a framework for the entire interaction that happens, including back-and-forth messaging queries and any other relevant details.

Sessions store a record of all the messages exchanged between the user and agent. This record helps the AI understand the ongoing conversation better and provide more personalized answers.

To create a session, we can use the sessions.create() method. Let’s take a look at the attributes it requires:

- Agent ID - ID of the created agent
- User ID - ID of the created user (Optional)
- Situation - A prompt to describe the background of the interaction
- Metadata - Additional information apart from IDs to identify the session (attribute)

Situation attribute plays a vital role in the session as it provides a context for the interaction or the conversation. The situation helps the agent better understand and compute the user’s query and give more tailored replies.	

Here’s an example:

```js
// Assuming 'client' is an object with a 'sessions' property containing a 'create' method
let session = client.sessions.create({
  agent_id: agent.id,
  user_id: user.id,
  situation: `
        You are James a Software Developer, public speaker & renowned educator.
        You are an educator who is qualified to train students, developers & entrepreneurs.
        About you:
        ...
        Important guidelines:
        ...
    `,
  metadata: { db_uuid: "1234" },
});
```

Let’s create a session for our Movio app:

```js
    const session = await client.sessions.create({
      agentId: agent.id,
      userId: user.id,
      situation:
        "You are Movio. You tell the people about movies they ask for, and recommend movies to the users",
    });
```

Here, the `agentID` and `userId` are the IDs of the agent and user we created earlier, and the `situation` is the small context provided for the interaction.

### Getting Response Message

After creating the user, agent, and session, we need to handle the interaction. We will use the `sessions.chat()` method to handle the chat interaction and get the response message.

This method demands two attributes to function - session.id and an object having messages array.

```js
    const chatParams = {
      messages: [
        {
          role: "user",
          name: "Ayush",
          content: query,
        },
      ],
    };
    const chatResponse = await client.sessions.chat(session.id, chatParams);
    const responseMessage = chatResponse.response[0][0].content;


    res.json({ response: responseMessage });
```

Here, `chatParams` object contains the messages array, which includes an object with three properties:

- role: The role of the message sender, "user".
- name: The user's name, "Ayush".
- content: The user's query, stored in the variable query.

Then, the `sessions.chat()` method is called on `client` with `session.id` and `chatParams` as arguments. The resultant object is stored in chatResponse.

The value of the `content` property is extracted from the `chatResponse` and stored in `responseMessage`.

### Handling Error

To handle the errors, we will use the `catch` block to capture the error and display it

```js
catch (error) {
    res.status(500).json({ error: error.message });
  }
```

### Start the server

To start the server on localhost, we use the `listen()` method on `app` specifying the port number.

```js
app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
```

This will host the server on `localhost:3000` and print the defined string in the console window.

Congratulations! Your AI app is successfully created.

## Run the App

The project is completed, we will run it and try it.

To run the app, first, we will run the `server.js` file to initiate the Julep API and then the html file for the user interface.

Run this command to start the server:

```sh
node src/server.js
```

To run the React App, run this command:

```sh
npm run dev
```

This will run your project on the localhost. Here is the demo of the project:

![result](https://raw.githubusercontent.com/ayush2390/julep-assets/main/movio-demo.gif)

Your app is running successfully.

Project link - [https://github.com/ayush2390/julep-movio](https://github.com/ayush2390/julep-movio)

Try out Movio - [https://stackblitz.com/github/ayush2390/movio?file=README.md](https://stackblitz.com/github/ayush2390/movio?file=README.md)

Excited to see what more Julep offers? The journey starts with a single click. Visit the repository and give it a star: [https://github.com/julep-ai/julep](https://github.com/julep-ai/julep)

Check out the tutorial for a deeper understanding of Julep.

{% youtube LhQMBAehL_Q %}

Have any questions or feedback? Join the Julep Discord Community

{% embed https://discord.gg/QVBnztXC %}