langgenius / dify

Dify is an open-source LLM app development platform. Dify's intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
https://dify.ai
Other
50.03k stars 7.15k forks source link

Implement Conversation State Management and Menu-Based Navigation in Dify Chatbot Orchestration Tool #7043

Closed Delgerskhn closed 1 month ago

Delgerskhn commented 2 months ago

Self Checks

1. Is this request related to a challenge you're experiencing? Tell me about your story.

Description: I would like to request a feature enhancement for the Dify chatbot orchestration tool. Currently, the tool follows a linear workflow that doesn't persist the conversation state, requiring the LLM to process each step from the beginning for every user query. This can lead to misinterpretation of user intentions and incorrect responses.

Proposed Feature: Implement a conversation state management feature that allows for menu-based navigation. This would involve: Menu-Based Navigation:

State Persistence:

Action Declaration:

Benefits:

Thank you for considering this feature request.

Example Usage:

  1. User starts at the ROOT menu.
  2. User selects "Contact Support" and is taken to the CONTACT_SUPPORT menu.
  3. User can then choose "Branches" to get information about branches or "Contact Info" to view contact details.
  4. At any point, the user can choose to go back to the previous menu or return to the start.

2. Additional context or comments

I have provided an example of how I manage state in my messenger chatbot. This could serve as a reference for implementing this feature in Dify.

Example state management state machine I use in messenger chatbot:

export const states = {
  ROOT: "ROOT",
  CONTACT_SUPPORT: "CONTACT_SUPPORT",
  CONTACT_INFO: "CONTACT_INFO",
  ABOUT_US: "ABOUT_US",
  BRANCHES: "BRANCHES",
  BRANCH_INFO: "BRANCH_INFO",
  GET_AID: "GET_AID"
};

export const transitions = {
  [states.ROOT]: [states.CONTACT_SUPPORT, states.ABOUT_US, states.GET_AID],
  [states.ABOUT_US]: [states.ROOT],
  [states.CONTACT_SUPPORT]: [states.BRANCHES, states.CONTACT_INFO, states.ROOT],
  [states.BRANCHES]: [states.BRANCH_INFO, states.ROOT],
  [states.BRANCH_INFO]: [states.ROOT, states.BRANCHES],
  [states.GET_AID]: [states.ROOT]
};

export const messages = {
  [states.ROOT]: {
    attachment: {
      type: "template",
      payload: {
        template_type: "button",
        text: "Сайн байна уу? Хууль зүйн туслалцааны төвтэй холбогдлоо. Та доорх цэснээс сонгоно уу.",
        buttons: [
          {
            type: "postback",
            title: "Холбоо барих",
            payload: states.CONTACT_SUPPORT
          },
          {
            type: "postback",
            title: "Үйлчилгээ, үйл ажиллагаа",
            payload: states.ABOUT_US
          },
          {
            type: "postback",
            title: "Хууль зүйн зөвлөгөө",
            payload: states.GET_AID
          }
        ]
      }
    }
  },
  [states.ABOUT_US]: {
    attachment: {
      type: "template",
      payload: {
        template_type: "button",
        text: "Та лавлахыг хүссэн асуултаа асууна уу?",
        buttons: [
          {
            type: "postback",
            title: "Буцах",
            payload: "BACK"
          }
        ]
      }
    }
  }
};

import { callSendAPI } from "./callSendApi"; // Adjust the path as needed
import { messages, states, transitions } from "./chat_state";

export const stateMachine = {
  userStates: {},

  getState(userId) {
    return this.userStates[userId] || states.ROOT;
  },

  setState(userId, newState) {
    if (transitions[this.getState(userId)].includes(newState)) {
      this.userStates[userId] = newState;
    } else {
      console.error(
        `Invalid transition from ${this.getState(userId)} to ${newState}`
      );
    }
  },

  /**
   * Presents options to the user based on their current state.
   *
   * @param {string} userId - The ID of the user.
   * @return {Promise<void>} A promise that resolves when the options are presented.
   */
  async presentOptions(userId) {
    const currentState = this.getState(userId);
    console.log("state>", currentState);
    const response = messages[currentState];

    await callSendAPI(userId, response);
  }
};

// Handle Postback
export async function handlePostback(senderPsid, receivedPostback) {
  const payload = receivedPostback.payload;
  stateMachine.setState(senderPsid, payload);
  await stateMachine.presentOptions(senderPsid);
}

3. Can you help us with this feature?

Delgerskhn commented 2 months ago

Example messenger postback buttons that I use for state management.

Screenshot 2024-08-07 at 14 02 49

Also menu selection can be like this one used in telegram.

Screenshot 2024-08-07 at 14 03 45
crazywoola commented 2 months ago

This sounds good and introduces a state machine, but it can be quite complicated to implement.