Ctrl K
ComunidadGuías…

plans/agent-context-compaction-2025-03-25-concise.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Context Compaction Logic Update Plan (Revised)

## Objective

Modify the context compaction logic in `crates/forge_domain/src/compaction.rs` to identify and compress only one continuous sequence of assistant messages at a time, rather than processing all sequences at once or compressing the entire context.

## Current Implementation Analysis

The current implementation in `compaction.rs` performs compaction on the entire context as a single unit:

1. It checks if compaction is needed via `should_perform_compaction`
2. If needed, it generates a summary of the entire context
3. It then builds a new compacted context with just the summary

This approach doesn't distinguish between different message roles or sequences, treating all messages equally in the compaction process.

## Revised Implementation Plan

### 1. Identify Single Compressible Sequence

Add functionality to identify only the first sequence of assistant messages that qualify for compression:

```rust
/// Identifies the first sequence of assistant messages that can be compressed (2+ consecutive messages)
fn identify_first_compressible_sequence(&self, context: &Context) -> Option<(usize, usize)> {
    let messages = context.messages();
    let mut current_sequence_start: Option<usize> = None;
    
    for (i, message) in messages.iter().enumerate() {
        if message.is_assistant() {
            // Start a new sequence or continue current one
            if current_sequence_start.is_none() {
                current_sequence_start = Some(i);
            }
        } else {
            // End of a potential sequence
            if let Some(start) = current_sequence_start {
                // Only compress sequences with more than 1 assistant message
                if i - start > 1 {
                    return Some((start, i - 1));
                }
                current_sequence_start = None;
            }
        }
    }
    
    // Check for a sequence at the end
    if let Some(start) = current_sequence_start {
        let end = messages.len() - 1;
        if end - start > 0 {  // More than 1 message
            return Some((start, end));
        }
    }
    
    None // No compressible sequence found
}
```

### 2. Update Main Compaction Method

Modify the main compaction method to identify and compress just one sequence:

```rust
pub async fn compact_context(&self, agent: &Agent, context: Context) -> Result<Context> {
    if !self.should_perform_compaction(agent, &context) {
        return Ok(context);
    }
    
    debug!(
        agent_id = %agent.id,
        "Context compaction triggered"
    );
    
    // Identify the first compressible sequence
    if let Some(sequence) = self.identify_first_compressible_sequence(&context) {
        debug!(
            agent_id = %agent.id,
            sequence_start = sequence.0,
            sequence_end = sequence.1,
            "Compressing assistant message sequence"
        );
        
        // Compress just this sequence
        self.compress_single_sequence(agent, context, sequence).await
    } else {
        debug!(agent_id = %agent.id, "No compressible sequences found");
        Ok(context)
    }
}
```

### 3. Implement Single Sequence Compression

Create a method to handle the compression of a single identified sequence:

```rust
async fn compress_single_sequence(
    &self, 
    agent: &Agent, 
    original_context: Context, 
    sequence: (usize, usize)
) -> Result<Context> {
    let messages = original_context.messages();
    let (start, end) = sequence;
    
    // Extract the sequence to summarize
    let sequence_messages = &messages[start..=end];
    
    // Generate summary for this sequence
    let summary = self.generate_summary_for_sequence(agent, sequence_messages).await?;
    
    // Build a new context with the sequence replaced by the summary
    let mut compacted_messages = Vec::new();
    
    // Add messages before the sequence
    compacted_messages.extend(messages[0..start].to_vec());
    
    // Add the summary as a single assistant message
    compacted_messages.push(ContextMessage::assistant(summary, None));
    
    // Add messages after the sequence
    if end + 1 < messages.len() {
        compacted_messages.extend(messages[end+1..].to_vec());
    }
    
    // Build the new context
    let mut compacted_context = Context::default();
    
    // Add system message if present in original context
    if let Some(system_msg) = original_context.system_message() {
        compacted_context = compacted_context.set_first_system_message(system_msg.clone());
    }
    
    // Add all the processed messages
    for msg in compacted_messages {
        compacted_context = compacted_context.add_message(msg);
    }
    
    Ok(compacted_context)
}
```

### 4. Implement Sequence-Based Summary Generation

Create a method to generate summaries for a specific sequence:

```rust
async fn generate_summary_for_sequence(
    &self, 
    agent: &Agent, 
    messages: &[ContextMessage]
) -> Result<String> {
    let compact = agent.compact.as_ref().unwrap();

    // Create a temporary context with just the sequence for summarization
    let mut sequence_context = Context::default();
    for msg in messages {
        sequence_context = sequence_context.add_message(msg.clone());
    }
    
    // Render the summarization prompt
    let prompt = self
        .services
        .template_service()
        .render_summarization(agent, &sequence_context)
        .await?;

    let message = ContextMessage::user(prompt);
    let summary_context = Context::default().add_message(message);

    // Get summary from the provider
    let response = self
        .services
        .provider_service()
        .chat(&compact.model, summary_context)
        .await?;

    self.collect_completion_stream_content(response).await
}
```

### 5. Update Helper Methods

The `should_perform_compaction` method remains unchanged since it's still applicable. The `collect_completion_stream_content` also remains as is.

### 6. Remove Unused Methods

The original `generate_summary` and `build_compacted_context` methods will be replaced by our new sequence-based methods, so they can be removed or repurposed.

## Implementation Considerations

1. **Processing Only One Sequence**: This approach only compresses one sequence at a time, which means that if there are multiple compressible sequences, only the first one will be compressed in a single call to `compact_context`. 

2. **Repeated Compaction**: If desired, the caller can repeatedly call `compact_context` to compress additional sequences over multiple iterations.

3. **Processing Order**: Sequences are identified from the beginning of the context, so the first eligible sequence found will be compressed first.

4. **Message Order Preservation**: This approach preserves the order of all non-compressed messages, maintaining the conversation flow.

## Verification Criteria

1. **Correctness**: The compacted context should:
   - Preserve all user messages in their original positions
   - Replace only the first sequence of 2+ assistant messages with a summary
   - Keep single assistant messages unchanged
   - Keep other sequences of assistant messages unchanged (they are not processed in this call)

2. **Functionality**:
   - The compaction should trigger under the same conditions as before
   - Summary generation should work properly for the identified sequence
   - The context structure should be preserved

3. **Edge Cases**:
   - Empty contexts should be handled properly
   - Contexts with no compressible sequences should be returned unchanged
   - Boundary conditions (sequence at start/end of context) should work correctly

4. **Performance**:
   - This approach should be more efficient as it only processes one sequence per call
   - Only one API call for summarization is made per call to `compact_context`