DEV Community

Cover image for Build your First AI Agent with Julep: A Step-by-Step Guide
Ayush Thakur for Julep

Posted on • Updated on

Build your First AI Agent with Julep: A Step-by-Step Guide

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 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

If you like the posts on the Julep blog so far, please consider giving Julep a star on GitHub, it helps us to reach and help more developers.

GitHub logo julep-ai / julep

Open-source alternative to Assistant's API with a managed backend for memory, RAG, tools and tasks. ~Supabase for building AI agents.

English | 中文翻译

julep

💸🤑 Announcing our Bounty Program: Help the Julep community fix bugs and ship features and get paid. More details here.


Start your project with conversation history, support for any LLM, agentic workflows, integrations & more


Explore the docs »

Report Bug · Request Feature · Join Our Discord · X · LinkedIn

NPM Version   PyPI - Version   Docker Image Version   GitHub License


Why Julep?

We've built a lot of AI apps and understand how difficult it is to evaluate hundreds of tools, techniques, and models, and then make them work well together.

The Problems

  1. The barrier to making LLM apps with memory, knowledge & tools is too high.
  2. Agentic behaviour is hard to control when done through multi-agent frameworks.

Features

  • Statefulness By Design: Manages conversation history by default. Use simple flags; remember & recall to tune whether to save or retrieve conversation history.
  • Support for Users & Agents: Allows creating different user <-> agent…

Creating React App

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

npm create-vite@latest
Enter fullscreen mode Exit fullscreen mode

You can check out the Vite Docs 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:

<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>
Enter fullscreen mode Exit fullscreen mode

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

const [query, setQuery] = useState("");
const [conversation, setConversation] = useState([]);
Enter fullscreen mode Exit fullscreen mode

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

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);
    }
  };
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

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 - To create and manage your web server
  • julep SDK - To interact with a specific service or API provided by Julep
  • body-parser - To parse incoming request bodies, making it easier to handle data sent by clients
  • cors - To enable cross-origin requests, allowing your server to handle requests from different domains
  • dotenv - To retrieve the value stored in the .env file
  • axios - a promise-based HTTP Client for node.js and the browser

Run this command to install libraries:

npm install express @julep/sdk cors body-parser dotenv axios
Enter fullscreen mode Exit fullscreen mode

Integrating Julep

To integrate Julep, we need an API key.

Go to platform.julep.ai and Sign in with Google account credentials. Copy the YOUR API TOKEN present on the top-right corner.

Julep API Token

This API Token will serve as your API key.

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

JULEP_API_KEY = api_key
Enter fullscreen mode Exit fullscreen mode

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.

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";
Enter fullscreen mode Exit fullscreen mode

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.

const apiKey = process.env.JULEP_API_KEY;
const client = new julep.Client({ apiKey });
Enter fullscreen mode Exit fullscreen mode

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.

const app = express();
app.use(bodyParser.json());
app.use(cors());
Enter fullscreen mode Exit fullscreen mode

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

app.post("/chat", async (req, res) => {
Enter fullscreen mode Exit fullscreen mode

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

try {
const query = req.body.query;
Enter fullscreen mode Exit fullscreen mode

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

Creating User

User 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:

    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
    });
Enter fullscreen mode Exit fullscreen mode

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

const user = await client.users.create({
      name: "Ayush",
      about: "A developer",
    });
Enter fullscreen mode Exit fullscreen mode

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

Creating Agents

Agent 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:

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" })
);
Enter fullscreen mode Exit fullscreen mode

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

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

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 to know more.

Creating Sessions

Session 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:

// 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" },
});
Enter fullscreen mode Exit fullscreen mode

Let’s create a session for our Movio app:

    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",
    });
Enter fullscreen mode Exit fullscreen mode

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.

    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 });
Enter fullscreen mode Exit fullscreen mode

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

catch (error) {
    res.status(500).json({ error: error.message });
  }
Enter fullscreen mode Exit fullscreen mode

Start the server

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

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
Enter fullscreen mode Exit fullscreen mode

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:

node src/server.js
Enter fullscreen mode Exit fullscreen mode

To run the React App, run this command:

npm run dev
Enter fullscreen mode Exit fullscreen mode

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

result

Your app is running successfully.

Project link - https://github.com/ayush2390/julep-movio

Try out Movio - 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

Check out the tutorial for a deeper understanding of Julep.

Have any questions or feedback? Join the Julep Discord Community

Julep AI Community

Check out the Julep AI Community community on Discord - hang out with 185 other members and enjoy free voice and text chat.

favicon discord.com

Top comments (6)

Collapse
 
sidbin profile image
Siddharth Balyan

super coooll!

love the idea. can someone make a discord bot with this?

Collapse
 
ayush2390 profile image
Ayush Thakur

My next blog will be on creating a discord bot

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

Awesome article!

Collapse
 
ayush2390 profile image
Ayush Thakur

Thank you Nathan

Collapse
 
ijindal1 profile image
ijindal1

Super neat. This should be part of iMDB or other apps like Novio.
Would love to see a book recommendation bot as well.

Collapse
 
ayush2390 profile image
Ayush Thakur

Glad you like it Ishita