Skip to content

[Bug]:AutoContextMemory#summaryPreviousRoundMessages implementation and comment inconsistent #1178

@maybob

Description

@maybob

AgentScope-Java is an open-source project. To involve a broader community, we recommend asking your questions in English.

Describe the bug
AutoContextMemory#summaryPreviousRoundMessages implementation and comment inconsistent.
Original comment:

/**
* Summarize all previous rounds of conversation messages before the latest assistant.
*
*

This method finds the latest assistant message and summarizes all conversation rounds
* before it. Each round consists of messages between a user message and its corresponding
* assistant message (typically including tool calls/results and the assistant message itself).
*
*

Example transformation:
* Before: "user1-tools-assistant1, user2-tools-assistant2, user3-tools-assistant3, user4"
* After: "user1-summary, user2-summary, user3-summary, user4"
* Where each summary contains the compressed information from tools and assistant of that round.
*
*

Strategy:
* 1. Find the latest assistant message (this is the current round, not to be summarized)
* 2. From the beginning, find all user-assistant pairs before the latest assistant
* 3. For each pair, summarize messages between user and assistant (including assistant message)
* 4. Replace those messages (including assistant) with summary (process from back to front to avoid index shifting)
*
* @param rawMessages the list of messages to process
* @return true if summary was actually performed, false otherwise
*/

According to 'Summarize all previous rounds of conversation messages before the latest assistant' in comment and code implementation, for 'the 'Example transformation', 'user3-tools-assistant3' will not be summarized. The correct content is as follows:

  • Before: "user1-tools-assistant1, user2-tools-assistant2, user3-tools-assistant3, user4"
    * After: "user1-summary, user2-summary, user3-tools-assistant3, user4"

AutoContextMemory#summaryPreviousRoundMessages will not summary last <user, assistant> pair, when last <user, assistant> pair has too many messages, it will trigger a large amount of context.

To Reproduce
Steps to reproduce the behavior:

Branch: release/1.0.10
Method: AutoContextMemory#summaryPreviousRoundMessages

  1. You code
private boolean summaryPreviousRoundMessages(List<Msg> rawMessages) {
        if (rawMessages == null || rawMessages.isEmpty()) {
            return false;
        }

        // Step 1: Find the latest assistant message that is a final response (not a tool call)
        int latestAssistantIndex = -1;
        for (int i = rawMessages.size() - 1; i >= 0; i--) {
            Msg msg = rawMessages.get(i);
            if (MsgUtils.isFinalAssistantResponse(msg)) {
                latestAssistantIndex = i;
                break;
            }
        }

        // If no assistant message found, nothing to summarize
        if (latestAssistantIndex < 0) {
            return false;
        }

        // Step 2: Find all user-assistant pairs before the latest assistant
        // We'll collect them as pairs: (userIndex, assistantIndex)
        List<Pair<Integer, Integer>> userAssistantPairs = new ArrayList<>();
        int currentUserIndex = -1;

        for (int i = 0; i < latestAssistantIndex; i++) {
            Msg msg = rawMessages.get(i);
            if (msg.getRole() == MsgRole.USER) {
                currentUserIndex = i;
            } else if (MsgUtils.isFinalAssistantResponse(msg) && currentUserIndex >= 0) {
                // Found a user-assistant pair (assistant message is a final response, not a tool
                // call)
                if (i - currentUserIndex != 1) {
                    userAssistantPairs.add(new Pair<>(currentUserIndex, i));
                }

                currentUserIndex = -1; // Reset to find next pair
            }
        }

        // If no pairs found, nothing to summarize
        if (userAssistantPairs.isEmpty()) {
            return false;
        }

        log.info(
                "Found {} user-assistant pairs to summarize before latest assistant at index {}",
                userAssistantPairs.size(),
                latestAssistantIndex);

        // Step 3: Process pairs from back to front to avoid index shifting issues
        boolean hasSummarized = false;
        for (int pairIdx = userAssistantPairs.size() - 1; pairIdx >= 0; pairIdx--) {
            Pair<Integer, Integer> pair = userAssistantPairs.get(pairIdx);
            int userIndex = pair.first();
            int assistantIndex = pair.second();

            // Messages to summarize: from user to assistant (inclusive of both)
            // Include user message for context, but we'll only remove messages after user
            int startIndex = userIndex + 1; // Messages to remove start after user
            int endIndex = assistantIndex; // Include assistant message in removal

            // If no messages between user and assistant (including assistant), skip
            if (startIndex > endIndex) {
                log.info(
                        "No messages to summarize between user at index {} and assistant at index"
                                + " {}",
                        userIndex,
                        assistantIndex);
                continue;
            }

            // Include user message in messagesToSummarize for context, but keep it in the final
            // list
            List<Msg> messagesToSummarize = new ArrayList<>();
            messagesToSummarize.add(rawMessages.get(userIndex)); // Include user message for context
            for (int i = startIndex; i <= endIndex; i++) {
                messagesToSummarize.add(rawMessages.get(i));
            }

            log.info(
                    "Summarizing round {}: user at index {}, messages [{}, {}], totalCount={}"
                            + " (includes user message for context)",
                    pairIdx + 1,
                    userIndex,
                    startIndex,
                    endIndex,
                    messagesToSummarize.size());

            // Step 4: Check if original token count is sufficient for compression
            // Skip compression if tokens are below threshold to avoid compression overhead
            int originalTokens = TokenCounterUtil.calculateToken(messagesToSummarize);
            int threshold = autoContextConfig.getMinCompressionTokenThreshold();
            if (originalTokens < threshold) {
                log.info(
                        "Skipping conversation summary for round {}: original tokens ({}) is below"
                                + " threshold ({})",
                        pairIdx + 1,
                        originalTokens,
                        threshold);
                continue;
            }

            log.info(
                    "Proceeding with conversation summary for round {}: original tokens: {},"
                            + " threshold: {}",
                    pairIdx + 1,
                    originalTokens,
                    threshold);

            // Step 5: Offload original messages if contextOffLoader is available
            String uuid = UUID.randomUUID().toString();
            offload(uuid, messagesToSummarize);
            log.info("Offloaded messages to be summarized: uuid={}", uuid);

            // Step 6: Generate summary
            Msg summaryMsg = summaryPreviousRoundConversation(messagesToSummarize, uuid);

            // Build metadata for compression event
            Map<String, Object> metadata = new HashMap<>();
            if (summaryMsg.getChatUsage() != null) {
                metadata.put("inputToken", summaryMsg.getChatUsage().getInputTokens());
                metadata.put("outputToken", summaryMsg.getChatUsage().getOutputTokens());
                metadata.put("time", summaryMsg.getChatUsage().getTime());
            }

            // Record compression event (before removing messages to preserve indices)
            recordCompressionEvent(
                    CompressionEvent.PREVIOUS_ROUND_CONVERSATION_SUMMARY,
                    startIndex,
                    endIndex,
                    rawMessages,
                    summaryMsg,
                    metadata);

            // Step 7: Remove the messages between user and assistant (including assistant), then
            // replace with summary
            // Since we're processing from back to front, the indices are still accurate
            // for the current pair (indices of pairs after this one have already been adjusted)

            // Remove messages from startIndex to endIndex (including assistant, from back to front
            // to avoid index shifting)
            int removedCount = endIndex - startIndex + 1;
            rawMessages.subList(startIndex, endIndex + 1).clear();

            // After removal, the position where assistant was is now: assistantIndex - removedCount
            // + 1
            // But since we removed everything including assistant, we insert summary at the
            // position after user
            int insertIndex = userIndex + 1;

            // Insert summary after user (replacing the removed messages including assistant)
            rawMessages.add(insertIndex, summaryMsg);

            log.info(
                    "Replaced {} messages [indices {}-{}] with summary at index {}",
                    removedCount,
                    startIndex,
                    endIndex,
                    insertIndex);

            hasSummarized = true;
        }

        return hasSummarized;
    }
  1. How to execute
  2. See error
    Expected behavior

According to 'Summarize all previous rounds of conversation messages before the latest assistant' in comment and code implementation, for 'the 'Example transformation', 'user3-tools-assistant3' will not be summarized. The correct content is as follows:

  • Before: "user1-tools-assistant1, user2-tools-assistant2, user3-tools-assistant3, user4"
    * After: "user1-summary, user2-summary, user3-tools-assistant3, user4"

AutoContextMemory#summaryPreviousRoundMessages will not summary last <user, assistant> pair, when last <user, assistant> pair has too many messages, it will trigger a large amount of context.

Error messages
Detailed error messages.

Environment (please complete the following information):

  • AgentScope-Java Version: release/1.0.10
  • Java Version: [e.g. 17]
  • OS: [e.g. macos]

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions