Wrong order of the history prompts in multi-round ReAct agent chat mode #4099

Closed
opened 2026-02-21 18:04:44 -05:00 by yindo · 1 comment
Owner

Originally created by @sinomoe on GitHub (Jun 14, 2024).

Originally assigned to: @laipz8200 on GitHub.

Self Checks

  • This is only for bug report, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • Please do not modify this template :) and fill in all the required fields.

Dify version

0.6.10

Cloud or Self Hosted

Self Hosted (Source)

Steps to reproduce

multi-round chat with an ReAct mode agent

image

✔️ Expected Behavior

history prompts should be organized in proper way

Actual Behavior

history prompts' order is wrong

Originally created by @sinomoe on GitHub (Jun 14, 2024). Originally assigned to: @laipz8200 on GitHub. ### Self Checks - [X] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [X] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones. - [X] I confirm that I am using English to submit this report (我已阅读并同意 [Language Policy](https://github.com/langgenius/dify/issues/1542)). - [X] Please do not modify this template :) and fill in all the required fields. ### Dify version 0.6.10 ### Cloud or Self Hosted Self Hosted (Source) ### Steps to reproduce multi-round chat with an ReAct mode agent ![image](https://github.com/langgenius/dify/assets/18367114/8d333d76-67a7-47dd-af43-1bde8818e764) ### ✔️ Expected Behavior history prompts should be organized in proper way ### ❌ Actual Behavior history prompts' order is wrong
yindo added the 🤖 feat:agent label 2026-02-21 18:04:44 -05:00
yindo closed this issue 2026-02-21 18:04:44 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jun 14, 2024):

To resolve the issue of history prompts appearing in the wrong order in multi-round ReAct agent chat mode in Dify version 0.6.10, you need to ensure that the history messages are appended in the correct order. The AgentHistoryPromptTransform class in the api/core/prompt/agent_history_prompt_transform.py file is responsible for handling the history prompts.

Here is the relevant code snippet from api/core/prompt/agent_history_prompt_transform.py:

class AgentHistoryPromptTransform(PromptTransform):
    """
    History Prompt Transform for Agent App
    """
    def __init__(self,
                 model_config: ModelConfigWithCredentialsEntity,
                 prompt_messages: list[PromptMessage],
                 history_messages: list[PromptMessage],
                 memory: Optional[TokenBufferMemory] = None,
                 ):
        self.model_config = model_config
        self.prompt_messages = prompt_messages
        self.history_messages = history_messages
        self.memory = memory

    def get_prompt(self) -> list[PromptMessage]:
        prompt_messages = []
        num_system = 0
        for prompt_message in self.history_messages:
            if isinstance(prompt_message, SystemPromptMessage):
                prompt_messages.append(prompt_message)
                num_system += 1

        if not self.memory:
            return prompt_messages

        max_token_limit = self._calculate_rest_token(self.prompt_messages, self.model_config)

        model_type_instance = self.model_config.provider_model_bundle.model_type_instance
        model_type_instance = cast(LargeLanguageModel, model_type_instance)

        curr_message_tokens = model_type_instance.get_num_tokens(
            self.memory.model_instance.model,
            self.memory.model_instance.credentials,
            self.history_messages
        )
        if curr_message_tokens <= max_token_limit:
            return self.history_messages

        num_prompt = 0
        for prompt_message in self.history_messages[::-1]:
            if isinstance(prompt_message, SystemPromptMessage):
                continue
            prompt_messages.append(prompt_message)
            num_prompt += 1
            if isinstance(prompt_message, UserPromptMessage):
                curr_message_tokens = model_type_instance.get_num_tokens(
                    self.memory.model_instance.model,
                    self.memory.model_instance.credentials,
                    prompt_messages
                )
                if curr_message_tokens > max_token_limit:
                    prompt_messages = prompt_messages[:-num_prompt]
                    break
                num_prompt = 0

        message_prompts = prompt_messages[num_system:]
        message_prompts.reverse()

        prompt_messages = prompt_messages[:num_system]
        prompt_messages.extend(message_prompts)
        return prompt_messages

Additionally, the BaseAgentRunner class in the api/core/agent/base_agent_runner.py file has a method organize_agent_history that organizes the history messages. This method ensures that the messages are retrieved and ordered correctly from the database:

def organize_agent_history(self, prompt_messages: list[PromptMessage]) -> list[PromptMessage]:
    """
    Organize agent history
    """
    result = []
    # check if there is a system message in the beginning of the conversation
    for prompt_message in prompt_messages:
        if isinstance(prompt_message, SystemPromptMessage):
            result.append(prompt_message)

    messages: list[Message] = db.session.query(Message).filter(
        Message.conversation_id == self.message.conversation_id,
    ).order_by(Message.created_at.asc()).all()

    for message in messages:
        if message.id == self.message.id:
            continue

        result.append(self.organize_agent_user_prompt(message))
        agent_thoughts: list[MessageAgentThought] = message.agent_thoughts
        if agent_thoughts:
            for agent_thought in agent_thoughts:
                tools = agent_thought.tool
                if tools:
                    tools = tools.split(';')
                    tool_calls: list[AssistantPromptMessage.ToolCall] = []
                    tool_call_response: list[ToolPromptMessage] = []
                    try:
                        tool_inputs = json.loads(agent_thought.tool_input)
                    except Exception as e:
                        tool_inputs = { tool: {} for tool in tools }
                    try:
                        tool_responses = json.loads(agent_thought.observation)
                    except Exception as e:
                        tool_responses = { tool: agent_thought.observation for tool in tools }

                    for tool in tools:
                        # generate a uuid for tool call
                        tool_call_id = str(uuid.uuid4())
                        tool_calls.append(AssistantPromptMessage.ToolCall(
                            id=tool_call_id,
                            type='function',
                            function=AssistantPromptMessage.ToolCall.ToolCallFunction(
                                name=tool,
                                arguments=json.dumps(tool_inputs.get(tool, {})),
                            )
                        ))
                        tool_call_response.append(ToolPromptMessage(
                            content=tool_responses.get(tool, agent_thought.observation),
                            name=tool,
                            tool_call_id=tool_call_id,
                        ))

                    result.extend([
                        AssistantPromptMessage(
                            content=agent_thought.thought,
                            tool_calls=tool_calls,
                        ),
                        *tool_call_response
                    ])
                if not tools:
                    result.append(AssistantPromptMessage(content=agent_thought.thought))
        else:
            if message.answer:
                result.append(AssistantPromptMessage(content=message.answer))

    db.session.close()

    return result

To ensure the history messages are in the correct order, you should verify that both the get_prompt method in the AgentHistoryPromptTransform class and the organize_agent_history method in the BaseAgentRunner class correctly process and append the history messages. If the messages are still not in the correct order, you may need to debug these methods to ensure they retrieve and return the messages in the correct sequence [1][2].

To continue talking to Dosu, mention @dosu.

@dosubot[bot] commented on GitHub (Jun 14, 2024): <!-- Answer --> To resolve the issue of history prompts appearing in the wrong order in multi-round ReAct agent chat mode in Dify version 0.6.10, you need to ensure that the history messages are appended in the correct order. The `AgentHistoryPromptTransform` class in the `api/core/prompt/agent_history_prompt_transform.py` file is responsible for handling the history prompts. Here is the relevant code snippet from `api/core/prompt/agent_history_prompt_transform.py`: ```python class AgentHistoryPromptTransform(PromptTransform): """ History Prompt Transform for Agent App """ def __init__(self, model_config: ModelConfigWithCredentialsEntity, prompt_messages: list[PromptMessage], history_messages: list[PromptMessage], memory: Optional[TokenBufferMemory] = None, ): self.model_config = model_config self.prompt_messages = prompt_messages self.history_messages = history_messages self.memory = memory def get_prompt(self) -> list[PromptMessage]: prompt_messages = [] num_system = 0 for prompt_message in self.history_messages: if isinstance(prompt_message, SystemPromptMessage): prompt_messages.append(prompt_message) num_system += 1 if not self.memory: return prompt_messages max_token_limit = self._calculate_rest_token(self.prompt_messages, self.model_config) model_type_instance = self.model_config.provider_model_bundle.model_type_instance model_type_instance = cast(LargeLanguageModel, model_type_instance) curr_message_tokens = model_type_instance.get_num_tokens( self.memory.model_instance.model, self.memory.model_instance.credentials, self.history_messages ) if curr_message_tokens <= max_token_limit: return self.history_messages num_prompt = 0 for prompt_message in self.history_messages[::-1]: if isinstance(prompt_message, SystemPromptMessage): continue prompt_messages.append(prompt_message) num_prompt += 1 if isinstance(prompt_message, UserPromptMessage): curr_message_tokens = model_type_instance.get_num_tokens( self.memory.model_instance.model, self.memory.model_instance.credentials, prompt_messages ) if curr_message_tokens > max_token_limit: prompt_messages = prompt_messages[:-num_prompt] break num_prompt = 0 message_prompts = prompt_messages[num_system:] message_prompts.reverse() prompt_messages = prompt_messages[:num_system] prompt_messages.extend(message_prompts) return prompt_messages ``` Additionally, the `BaseAgentRunner` class in the `api/core/agent/base_agent_runner.py` file has a method `organize_agent_history` that organizes the history messages. This method ensures that the messages are retrieved and ordered correctly from the database: ```python def organize_agent_history(self, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: """ Organize agent history """ result = [] # check if there is a system message in the beginning of the conversation for prompt_message in prompt_messages: if isinstance(prompt_message, SystemPromptMessage): result.append(prompt_message) messages: list[Message] = db.session.query(Message).filter( Message.conversation_id == self.message.conversation_id, ).order_by(Message.created_at.asc()).all() for message in messages: if message.id == self.message.id: continue result.append(self.organize_agent_user_prompt(message)) agent_thoughts: list[MessageAgentThought] = message.agent_thoughts if agent_thoughts: for agent_thought in agent_thoughts: tools = agent_thought.tool if tools: tools = tools.split(';') tool_calls: list[AssistantPromptMessage.ToolCall] = [] tool_call_response: list[ToolPromptMessage] = [] try: tool_inputs = json.loads(agent_thought.tool_input) except Exception as e: tool_inputs = { tool: {} for tool in tools } try: tool_responses = json.loads(agent_thought.observation) except Exception as e: tool_responses = { tool: agent_thought.observation for tool in tools } for tool in tools: # generate a uuid for tool call tool_call_id = str(uuid.uuid4()) tool_calls.append(AssistantPromptMessage.ToolCall( id=tool_call_id, type='function', function=AssistantPromptMessage.ToolCall.ToolCallFunction( name=tool, arguments=json.dumps(tool_inputs.get(tool, {})), ) )) tool_call_response.append(ToolPromptMessage( content=tool_responses.get(tool, agent_thought.observation), name=tool, tool_call_id=tool_call_id, )) result.extend([ AssistantPromptMessage( content=agent_thought.thought, tool_calls=tool_calls, ), *tool_call_response ]) if not tools: result.append(AssistantPromptMessage(content=agent_thought.thought)) else: if message.answer: result.append(AssistantPromptMessage(content=message.answer)) db.session.close() return result ``` To ensure the history messages are in the correct order, you should verify that both the `get_prompt` method in the `AgentHistoryPromptTransform` class and the `organize_agent_history` method in the `BaseAgentRunner` class correctly process and append the history messages. If the messages are still not in the correct order, you may need to debug these methods to ensure they retrieve and return the messages in the correct sequence <sup>[[1]](https://github.com/langgenius/dify/blob/main/api/core/prompt/agent_history_prompt_transform.py)</sup><sup>[[2]](https://github.com/langgenius/dify/blob/main/api/core/agent/base_agent_runner.py)</sup>. <!-- Dosu Comment Footer --> *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#4099