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
45.71k stars 6.43k forks source link

Default Prompt Customization #8223

Open SavioR0 opened 1 week ago

SavioR0 commented 1 week ago

Self Checks

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

Customizing the default promptWhen using the attribute extraction node, I encountered a challenge. I ran several tests with different prompts and realized that the results did not match what was expected. My need was to query a {{input}} and have GPT extract the necessary variables, respecting the data from that input. Frequently, it ignored this input variable and just reproduced the user's input. My hypothesis was that there was a "dirty prompt" that could not be changed using the graphical interface. This could explain the confusion of the AI, which, due to there being many instructions, could be getting confused and not respecting all the restrictions. The default prompt that is used in every parameter extractor node is shown below: image

{
  "model_mode": "chat",
  "prompts": [
    {
      "role": "system",
      "text": "You are a helpful assistant tasked with extracting structured information based on specific criteria provided. Follow the guidelines below to ensure consistency and accuracy.\n### Task\nAlways call the `extract_parameters` function with the correct parameters. Ensure that the information extraction is contextual and aligns with the provided criteria.\n### Memory\nHere is the chat history between the human and assistant, provided within <histories> tags:\n<histories>\n\n</histories>\n### Instructions:\nSome additional information is provided below. Always adhere to these instructions as closely as possible:\n<instruction>\nMY PROMPT\n</instruction>\nSteps:\n1. Review the chat history provided within the <histories> tags.\n2. Extract the relevant information based on the criteria given, output multiple values if there is multiple relevant information that match the criteria in the given text. \n3. Generate a well-formatted output using the defined functions and arguments.\n4. Use the `extract_parameter` function to create structured outputs with appropriate parameters.\n5. Do not include any XML tags in your output.\n### Example\nTo illustrate, if the task involves extracting a user's name and their request, your function call might look like this: Ensure your output follows a similar structure to examples.\n### Final Output\nProduce well-formatted function calls in json without XML tags, as shown in the example.\n",
      "files": []
    },
    {
      "role": "user",
      "text": "### Structure\nHere is the structure of the JSON object, you should always follow the structure.\n<structure>\n{\"type\": \"object\", \"properties\": {\"location\": {\"type\": \"string\", \"description\": \"The location to get the weather information\", \"required\": true}}, \"required\": [\"location\"]}\n</structure>\n\n### Text to be converted to JSON\nInside <text></text> XML tags, there is a text that you should convert to a JSON object.\n<text>\nWhat is the weather today in SF?\n</text>\n",
      "files": []
    },
    {
      "role": "assistant",
      "text": "{\"location\": \"San Francisco\"}",
      "files": []
    },
    {
      "role": "user",
      "text": "### Structure\nHere is the structure of the JSON object, you should always follow the structure.\n<structure>\n{\"type\": \"object\", \"properties\": {\"food\": {\"type\": \"string\", \"description\": \"The food to eat\", \"required\": true}}, \"required\": [\"food\"]}\n</structure>\n\n### Text to be converted to JSON\nInside <text></text> XML tags, there is a text that you should convert to a JSON object.\n<text>\nI want to eat some apple pie.\n</text>\n",
      "files": []
    },
    {
      "role": "assistant",
      "text": "{\"result\": \"apple pie\"}",
      "files": []
    },
    {
      "role": "user",
      "text": "### Structure\nHere is the structure of the JSON object, you should always follow the structure.\n<structure>\n{\"type\": \"object\", \"properties\": {\"color\": {\"description\": \"cor\", \"type\": \"string\"}, \"base\": {\"description\": \"base\\n\", \"type\": \"string\"}}, \"required\": []}\n</structure>\n\n### Text to be converted to JSON\nInside <text></text> XML tags, there is a text that you should convert to a JSON object.\n<text>\ninterior\n</text>\n",
      "files": []
    }
  ],
  "usage": {
    "prompt_tokens": 634,
    "prompt_unit_price": "0.15",
    "prompt_price_unit": "0.000001",
    "prompt_price": "0.0000951",
    "completion_tokens": 1,
    "completion_unit_price": "0.60",
    "completion_price_unit": "0.000001",
    "completion_price": "6E-7",
    "total_tokens": 635,
    "total_price": "0.0000957",
    "currency": "USD",
    "latency": 0.8289054050001141
  },
  "function": {},
  "tool_call": null,
  "llm_text": "{}"
}

As a solution, I created an API that uses the official openai library, with a more personalized and concise prompt. This way I was able to obtain the desired result, in which I defined an output format that simulated the output variables of the variable extractor node.

import OpenAI from 'openai';
import { z } from 'zod';
import { zodResponseFormat } from "openai/helpers/zod";

const AtributesExtraction = z.object({
    color: z.string(),
    base: z.string(),
    environment: z.string(),
    finish: z.string(),
    line: z.string(),
    volume: z.string(),    
});

const completion = await openai.beta.chat.completions.parse({
    ...
    response_format: zodResponseFormat(AtributesExtraction, "atributes_extraction"),]
})

image This way, I was able to create an ideal prompt that met my needs. This confirmed that a non-customizable prompt is not always the best option, as in specific cases it may not adequately meet the user's demands. One suggestion would be to offer the user the opportunity to modify this default prompt.

My prompt looked like this:

Note: The user input will be in Portuguese (Brazil), which is why the examples are in that language.

messages: [
                    {
                        role: "system",
                        content:`
                        # You are an expert at structured data extraction. You will be given unstructured text from a user message and should convert it into the given structure.
                        # Only extract the attributes that the user explicitly requests.
                        # If the user does not provide the information, never insert 'Na' always insert empty string ('').
                        # Always provide attributes in lowercase.
                        `
                    },
                    {
                        role: "user",
                        content:  `
                            # Extract the attribute names and paint attributes provided by the user (in <user> xml tag)  and convert them to the equivalent words listed in the <options> xml tag. 
                            # always extract volume with one decimal place.
                            # Please note that there are colors with compound names such as: "azul bebê".consider this in the assignment.
                            # "Exterior" is the same as "Externo" for environment.

                            # Examples:
                                "18litros da tinta Branca Neve para meu quarto."
                                Extract volume 18.0, enviroment 'interno' and color 'Branco Neve'

                            <user>
                                    ${userMessage}
                            </user>
                            <options>
                                    ${atributes}
                            </options>`
                    },
                ],

2. Additional context or comments

No response

3. Can you help us with this feature?

dosubot[bot] commented 1 week ago

Hello, @SavioR0! I'm here to help you with any bugs, questions, or contribution inquiries you may have. Let me know how I can assist you!

To customize the default prompt in the attribute extraction node and ensure it respects the input variables and produces the desired output, you can use the prompt editor in the LLM node. Here are the steps and code snippets to help you achieve this:

  1. Open the Prompt Editor: In the LLM node, navigate to the prompt editor where you can customize the SYSTEM/User/ASSISTANT sections.

  2. Insert Variables: Use the variable insertion menu by typing "/" or "{" to insert special variable blocks or upstream node variables into the prompt.

  3. Use Context Variables: Define and insert context variables to incorporate externally retrieved text content into the prompt.

  4. Leverage Jinja-2 Templates: Utilize the Jinja-2 template language for more complex prompts, allowing for lightweight data transformation and logical processing.

Here is an example of how you might customize a prompt using these features:

SYSTEM: You are an intelligent assistant. Your task is to extract specific attributes from the provided text.

USER: {{ user_input }}

ASSISTANT: Based on the input, here are the extracted attributes:
{% for attribute in context_variables.attributes %}
- {{ attribute.name }}: {{ attribute.value }}
{% endfor %}

In this example:

Additionally, you can use the AdvancedPromptTransform class and related methods from the parameter_extractor_node.py to further customize and validate your prompts. For instance, you can generate function call prompts or prompt engineering prompts based on the model mode (e.g., CHAT or COMPLETION). Here is a snippet showing how to generate a function call prompt:

def _generate_function_call_prompt(self, node_data, query, variable_pool, model_config, memory):
    query = FUNCTION_CALLING_EXTRACTOR_USER_TEMPLATE.format(content=query, structure=json.dumps(node_data.get_parameter_json_schema()))
    prompt_transform = AdvancedPromptTransform(with_variable_tmpl=True)
    rest_token = self._calculate_rest_token(node_data, query, variable_pool, model_config, '')
    prompt_template = self._get_function_calling_prompt_template(node_data, query, variable_pool, memory, rest_token)
    prompt_messages = prompt_transform.get_prompt(
        prompt_template=prompt_template,
        inputs={},
        query='',
        files=[],
        context='',
        memory_config=node_data.memory,
        memory=None,
        model_config=model_config
    )
    return prompt_messages, [tool]

Configuration Steps

  1. Select the input variable, usually the variable input for parameter extraction.
  2. Choose the model, as the parameter extractor relies on the LLM's inference and structured generation capabilities.
  3. Define the parameters to extract, which can be manually added or quickly imported from existing tools.
  4. Write instructions, where providing examples can help the LLM improve the effectiveness and stability of extracting complex parameters.

Advanced Settings

Inference Mode

Some models support two inference modes, achieving parameter extraction through function/tool calls or pure prompt methods, with differences in instruction compliance. For instance, some models may perform better in prompt inference if function calling is less effective.

Memory

When memory is enabled, each input to the question classifier will include the chat history in the conversation to help the LLM understand the context and improve question comprehension during interactive dialogues.

Output Variables

__is_success Number Extraction success status, with a value of 1 for success and 0 for failure.

__reason String Extraction error reason

By following these steps and using the provided code snippets, you can customize the default prompt to ensure it respects the input variables and produces the desired output [1][2][3].

To continue talking to Dosu, mention @dosu.