Leveraging LangChain Agents with Function Calling to Build DTDL's Autonomous Customer Care Assistant

Welcome to the final episode of this blog series. We are now prepared to work with our agent to develop an autonomous customer care solution.

Here are the key takeaways from this blog:

  • Working with Langchain agents 🤖
  • Understanding function calling concepts 🧠

Langchain is a open-source framework that simplifies the development of LLM applications. It is all about having set of tools and abstractions that makes it easy to integrate LLM into your workflows, connect them to external data sources and build complex applciations.

Langchain Agents are key component of the framework enabling LLMs to acts as reasoning engines. Agents use LLM to determine the best course of action based on user input and available tools. This helps us to build autonomous applications.

For more info visit here

Latest version of GPT models are fine-tuned to understand and work with functions and have the reasoning engine to enable LLM to decide when and how a function should be called. Say for example if there are more than one function included in the request, model determines which to call based on the context of the prompt.

This allows us to connect with external data source and bring in more context to the LLM securely.

  • When a user asks a product-related query, such as “Can you compare the various TV packages available?”, the system begins processing the request.
  • Along with the user’s prompt, a list of available tools is sent to the Language Learning Model (LLM).
  • LLM decides to pick Products_retriever_tool
  • Then LLM recognizes the need to generate vector embeddings for the query, and these embeddings are sent back to the application.
  • The generated embeddings are used to perform a vector search against Azure Cosmos DB to retrieve relevant product information from the database.
  • The retrieved documents are then sent back to the LLM.
  • The LLM responds to the user, providing information based on the query and the relevant data fetched from the database.
1
2
3
4
5
6
        const productsRetrieverTool = new DynamicTool({
            name: "products_retriever_tool",
            description: `Searches DTDL customercare product information for similar products based on the question. 
                    Returns the product information in JSON format.`,
            func: async (input) => await retrieverChain.invoke(input),
        });       
  • The user initiates the process by prompting the system with a query related to their past order, specifying the order ID. This prompt sets the stage for retrieving personal order information.

  • Along with the user’s query, the system sends a list of available tools to the Language Learning Model (LLM). This list helps the LLM determine which actions to take next based on the user’s request.

  • Recognizing that the query involves sensitive personal information, the LLM understands from the system prompt that authentication is necessary to proceed. This step ensures that only the rightful user can access their order details.

  • To authenticate the user, the LLM asks the customer to provide their phone number. This is a crucial step for initiating the verification process.

  • The user then provides their phone number, which the system will use to send an OTP (One-Time Password) for verification purposes.

  • The provided phone number, along with the list of available tools, is sent back to the LLM. This allows the LLM to make an informed decision on which tool to use next.

  • The LLM selects the otp_generation_tool for user authentication and passes the phone number as a parameter to this tool. This tool is responsible for generating the OTP.

  • The otp_generation_tool generates an OTP and sends it to the user’s phone number via the Twilio SMS API. It also stores the OTP in the database alongside the phone number for later verification.

  • Twilio sends the OTP to the customer’s phone number via SMS. This step ensures that the user receives the OTP promptly.

  • The customer receives the OTP and inputs it into the chatbot. This input is necessary for verifying their identity.

  • The entered OTP, along with the previous chat history and the list of available tools, is sent back to the LLM. This allows the LLM to decide on the next action based on the current context.

  • The LLM then selects the otp_verification_tool to validate the OTP. It passes both the phone number and the OTP as parameters to this function. The verification tool checks if the provided OTP matches the one stored in the database.

  • The otp_verification_tool verifies the OTP against the stored information. If the OTP is valid, the LLM proceeds to retrieve the user’s order details.

  • To fetch the order details, the LLM uses the specific_order_tool, providing the phone number and order ID as inputs. This tool queries Azure CosmosDB for the relevant order information.

  • The specific_order_tool retrieves the order details from Azure CosmosDB using the provided phone number and order ID. This step ensures that the correct order information is obtained.

  • Once the order details are retrieved, they are sent back to the LLM. This information is necessary for the LLM to formulate a response.

  • Finally, the LLM formats the response with the fetched order details and sends it back to the customer. This completes the process, providing the customer with the information they requested in a secure and efficient manner.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        const customerSpecificOrderTool = new DynamicTool({
            name: "specific_order_tool",
            description: `Fetches a specific order item based on the customer's phone number and order ID. Returns the order item as a JSON string.`,
            func: async (input) => {
                // Split the input to get phone number and order ID
                const [phoneNumber, orderId] = input.split(',');
        
                if (!phoneNumber || !orderId) {
                    throw new Error("Invalid input format. Expected 'phoneNumber,orderId'");
                }
        
                // Connect to the database
                const db = this.dbClient.db("dtdl-customercare");
                const customers = db.collection("customers");
                const sales = db.collection("sales");
        
                // Fetch the customer entry for the given phone number
                const customer = await customers.findOne({ "phoneNumber": phoneNumber.trim() });
        
                if (!customer) {
                    throw new Error("Customer not found");
                }
        
                // Fetch the specific order for the customer using the customer ID and order ID
                const customerId = customer.customerId;
                const order = await sales.findOne({ "customerId":customerId, "_id": orderId.trim() });
        
                if (!order) {
                    throw new Error("Order not found");
                }
        
                // Convert the order data to a JSON string
                return JSON.stringify(order, null, '\t');
            },
        });
        
         const otpGenerationTool = new DynamicTool({
            name: "otp_generation_tool",
            description: `Generates and sends an OTP with the provided user's phone.`,
            func: async (input) => {                               
                const OTP_EXPIRATION_TIME = 5 * 60 * 1000;       
                const otp = crypto.randomInt(100000, 999999).toString();
                const expiresAt = Date.now() + OTP_EXPIRATION_TIME;

                const db = this.dbClient.db("dtdl-customercare");
                const otps = db.collection("otps");

                await otps.insertOne({ input, otp, expiresAt });

                    try {
                        await this.twilioClient.messages.create({
                            body: `Your OTP code is ${otp}`,
                            from: process.env.TWILIO_PHONE_NUMBER,
                            to: input
                        });
                    } catch (error) {
                        console.error('Error sending OTP:', error);
                        return "OTP send is failed";
                    }
                    return "OTP is sent successfully";
                }                
        });
        
        const otpValidationTool = new DynamicTool({
            name: "otp_validation_tool",
            description: `Validates an OTP for the user by its phonenumber and OTP. Input should be formatted as comma separated values. Returns true if valid, otherwise throws an error.`,
            func: async (input) => {
                const [phoneNumber, otp] = input.split(',');
                if (!phoneNumber || !otp) {
                    throw new Error('Phone number and OTP are required');
                }
        
                const db = this.dbClient.db("dtdl-customercare");
                const otps = db.collection("otps");
        
                const otpEntry = await otps.findOne({"input": phoneNumber, "otp":otp });
        
                if (!otpEntry) {
                    return 'Invalid OTP';
                }
        
                if (Date.now() > otpEntry.expiresAt) {
                    return 'OTP expired';
                }        
                return "OTP is valid";
            }
        });

DTDL-CustomerCare

https://dghx6mmczrmj4mk-web.azurewebsites.net/

CAUTION : If the site is slow, it is due to the serverless infrastructure used for this application.