# 上下文压缩流程文档

## 整体流程概览

当前的上下文压缩系统采用**多层次渐进式压缩策略**，在token不足时自动触发，确保长期运行时不会因上下文溢出而中断。

## 触发条件

### 1. **Token阈值触发**（主要触发方式）
- **阈值**: 剩余token < 输入窗口的25%
- **检查时机**: 每次调用模型前
- **位置**: `AgentRunLoop.run()` 方法中

### 2. **对话轮次阈值触发**
- **阈值**: 对话轮次达到配置值（默认200轮）
- **检查时机**: 每次对话轮次检查
- **位置**: `AgentRunLoop.run()` 方法中

### 3. **手动触发**
- **标记**: LLM输出 `!!!SUMMARY!!!` 标记
- **位置**: `AgentRunLoop.run()` 方法中

### 4. **用户指令触发**
- **方式**: 用户通过输入处理器触发
- **位置**: `builtin_input_handler.py`

## 压缩策略执行流程

### 阶段1：渐进式压缩（在调用模型前）

当检测到剩余token低于阈值时，按以下顺序执行压缩策略：

```
开始
  ↓
检查剩余token < 25%阈值？
  ↓ 是
自适应压缩（根据任务类型选择策略）
  ↓
  ├─ 成功？
  │   ├─ 是 → 检查剩余token
  │   │        ├─ 仍不足 → 尝试其他策略（滑动窗口 → 重要性评分）
  │   │        └─ 足够 → 继续执行
  │   └─ 否 → 回退到固定策略顺序
  │            ├─ 滑动窗口压缩（优先）
  │            ├─ 重要性评分压缩
  │            ├─ 关键事件提取压缩
  │            └─ 增量摘要压缩
  ↓
调用模型
```

#### 1.1 自适应压缩（主要策略）

**任务类型识别**：
- 代码任务（code）：CodeAgent 或包含代码相关工具调用
- 分析任务（analysis）：包含分析相关工具调用
- 对话任务（conversation）：主要是短的用户输入和响应
- 混合任务（mixed）：包含多种类型的工具调用
- 未知类型（unknown）：无法明确识别

**策略选择**（优先使用滑动窗口压缩）：
- **代码任务**: 滑动窗口 → 关键事件提取 → 增量摘要
- **分析任务**: 滑动窗口 → 重要性评分 → 增量摘要
- **对话任务**: 滑动窗口 → 重要性评分 → 增量摘要
- **混合任务**: 滑动窗口 → 增量摘要 → 重要性评分
- **未知类型**: 滑动窗口 → 重要性评分 → 增量摘要

#### 1.2 回退策略（如果自适应压缩失败）

如果自适应压缩失败或仍不足，按以下顺序尝试（优先使用滑动窗口）：

1. **滑动窗口压缩**（优先）
   - 保留最近的用户/工具消息4条和助手消息5条（共9条，奇数以避免连续的同role消息）
   - 压缩更早的对话为摘要
   - 保留最近的完整上下文

2. **重要性评分压缩**
   - 根据消息重要性评分，保留高分消息
   - 压缩低分消息为摘要
   - 保留最近5条低分消息作为上下文

3. **关键事件提取压缩**
   - 提取关键事件（任务完成、错误修复、工具调用等）
   - 压缩非关键事件为摘要
   - 保留最近5条非关键消息作为上下文

4. **增量摘要压缩**
   - 将历史分成多个chunk
   - 压缩前面的chunks为摘要
   - 保留最后一个chunk的完整内容
   - 压缩更早的对话为摘要

### 阶段2：完整摘要压缩（最后手段）

如果渐进式压缩后仍然不足，或者达到对话轮次阈值，执行完整摘要压缩：

```
触发完整摘要压缩
  ↓
生成对话摘要（使用LLM）
  ↓
清空所有对话历史
  ↓
保留关键信息：
  - 原始任务目标（original_user_input）
  - 用户固定内容（pin_content）
  - 最近记忆（recent_memories）
  - 任务列表状态
  - Git diff统计信息（CodeAgent）
  ↓
将摘要作为新上下文添加
  ↓
重置对话长度计数器
```

## 详细流程说明

### 流程1：模型调用前的压缩检查

```python
# 位置：AgentRunLoop.run()

while True:
    # 1. 检查剩余token
    remaining_tokens = model.get_remaining_token_count()
    
    # 2. 如果低于阈值，触发压缩
    if remaining_tokens < summary_remaining_token_threshold:
        # 2.1 尝试自适应压缩
        success = agent._adaptive_compression()
        
        if success:
            # 2.2 重新计算剩余token
            remaining_tokens = model.get_remaining_token_count()
            
            # 2.3 如果仍不足，尝试其他策略
            if remaining_tokens < threshold:
                # 尝试重要性评分压缩
                # 尝试滑动窗口压缩
        else:
            # 2.4 回退到固定策略顺序
            # 尝试重要性评分压缩
            # 尝试关键事件提取压缩
            # 尝试增量摘要压缩
            # 尝试滑动窗口压缩
    
    # 3. 调用模型
    response = agent._call_model(...)
```

### 流程2：自适应压缩内部流程

```python
# 位置：Agent._adaptive_compression()

def _adaptive_compression():
    # 1. 检测任务类型
    task_type = _detect_task_type()
    
    # 2. 根据任务类型选择策略（优先使用滑动窗口）
    if task_type == "code":
        # 代码任务：滑动窗口 → 关键事件提取 → 增量摘要
        return _sliding_window_compression() or
               _key_event_extraction_compression() or
               _incremental_summarization_compression()
    
    elif task_type == "analysis":
        # 分析任务：滑动窗口 → 重要性评分 → 增量摘要
        return _sliding_window_compression() or
               _importance_scoring_compression() or
               _incremental_summarization_compression()
    
    elif task_type == "conversation":
        # 对话任务：滑动窗口 → 重要性评分 → 增量摘要
        return _sliding_window_compression() or
               _importance_scoring_compression() or
               _incremental_summarization_compression()
    
    # ... 其他任务类型
```

### 流程3：完整摘要压缩流程

```python
# 位置：Agent._summarize_and_clear_history()

def _summarize_and_clear_history():
    # 1. 生成摘要
    summary = generate_summary()
    
    # 2. 获取关键信息
    - 原始任务目标（original_user_input）
    - 用户固定内容（pin_content）
    - 最近记忆（recent_memories）
    - 任务列表状态
    - Git diff统计信息
    
    # 3. 格式化摘要消息
    formatted_summary = _format_summary_message(summary)
    
    # 4. 清空历史
    model.reset()
    session.clear_history()
    
    # 5. 添加摘要作为新上下文
    session.addon_prompt = formatted_summary
    
    # 6. 重置计数器
    conversation_length = 0
```

## 压缩策略对比

| 策略 | 压缩强度 | 保留内容 | 适用场景 |
|------|---------|---------|---------|
| 重要性评分压缩 | 轻 | 高分消息完整保留 | 通用场景 |
| 关键事件提取压缩 | 中 | 关键事件完整保留 | 代码任务、问题排查 |
| 增量摘要压缩 | 中 | 最近chunk完整保留 | 长期对话 |
| 滑动窗口压缩 | 中 | 最近N轮完整保留 | 对话任务 |
| 完整摘要压缩 | 重 | 仅保留摘要和关键信息 | 最后手段 |

## 关键信息保留机制

无论使用哪种压缩策略，以下信息始终被保留：

1. **原始任务目标**（`original_user_input`）
   - 第一次运行时的用户输入
   - 在总结后始终保留在最前面

2. **用户固定内容**（`pin_content`）
   - 用户通过 `<Pin>` 标记的内容
   - 非交互模式下的完整需求

3. **系统消息**
   - 系统提示词
   - 始终不压缩

4. **最近记忆**（`recent_memories`）
   - 最近10条记忆内容

5. **任务列表状态**
   - 任务列表的摘要信息
   - 任务执行进度

6. **Git diff统计**（CodeAgent）
   - 代码变更统计信息

## 压缩效果评估

每次压缩后，系统会：
1. 重新计算剩余token数量
2. 检查是否达到阈值
3. 如果仍不足，继续尝试下一个策略
4. 如果所有策略都失败或仍不足，触发完整摘要压缩

## 配置参数

### 环境变量配置

- `sliding_window_size`: 滑动窗口大小（默认9条：用户/工具消息4条+助手消息5条，奇数以避免连续的同role消息）
- `importance_score_threshold`: 重要性评分阈值（默认3.0）
- `incremental_summary_chunk_size`: 增量摘要chunk大小（默认20轮）
- `conversation_turn_threshold`: 对话轮次阈值（默认200轮）

### 内置配置

- `summary_remaining_token_threshold`: 剩余token阈值（输入窗口的25%）
- 自动计算，无需配置

## 流程图

```
┌─────────────────────────────────────────────────────────────┐
│                    每次调用模型前检查                         │
└─────────────────────────────────────────────────────────────┘
                            ↓
        ┌───────────────────────────────────┐
        │  剩余token < 25%阈值？            │
        └───────────────────────────────────┘
                    ↓ 是              ↓ 否
        ┌──────────────────┐         继续执行
        │  自适应压缩      │
        └──────────────────┘
                    ↓
        ┌───────────────────────────────────┐
        │  检测任务类型                     │
        │  (code/analysis/conversation/...) │
        └───────────────────────────────────┘
                    ↓
        ┌───────────────────────────────────┐
        │  根据任务类型选择策略              │
        │  - 代码任务: 关键事件提取          │
        │  - 分析任务: 重要性评分            │
        │  - 对话任务: 滑动窗口              │
        └───────────────────────────────────┘
                    ↓
        ┌───────────────────────────────────┐
        │  执行选择的压缩策略                │
        └───────────────────────────────────┘
                    ↓
        ┌───────────────────────────────────┐
        │  压缩成功？                        │
        └───────────────────────────────────┘
        ↓ 是              ↓ 否
重新计算token        回退到固定策略顺序
        ↓                    ↓
   仍不足？              重要性评分压缩
        ↓                    ↓
    是   否              关键事件提取压缩
     ↓    │              增量摘要压缩
     │    │              滑动窗口压缩
     │    │                    ↓
     │    │              重新计算token
     │    │                    ↓
     │    │              仍不足？
     │    │              是   否
     │    │               ↓    │
     │    └───────────────┘    │
     │                         │
     └─────────────────────────┘
              ↓
    ┌─────────────────────────┐
    │  完整摘要压缩（最后手段） │
    │  - 生成摘要              │
    │  - 清空历史              │
    │  - 保留关键信息          │
    │  - 添加摘要为新上下文    │
    └─────────────────────────┘
              ↓
         继续执行
```

## 最佳实践

1. **优先使用自适应压缩**：系统会自动选择最适合的策略
2. **配置合理的阈值**：根据实际使用场景调整窗口大小和评分阈值
3. **保留关键信息**：使用 `<Pin>` 标记重要内容，确保不被压缩
4. **监控压缩效果**：观察压缩后的剩余token，评估压缩效果
5. **定期检查**：长期运行时，定期检查对话历史，必要时手动触发摘要

## 注意事项

1. **压缩不可逆**：压缩后的历史无法完全恢复，建议启用会话文件保存
2. **关键信息优先**：原始任务目标始终保留，确保不丢失
3. **渐进式压缩**：优先使用轻量级压缩，避免一次性清空所有历史
4. **容错机制**：压缩失败不影响主流程，会自动回退到其他策略
5. **性能考虑**：压缩操作本身会消耗token，但相比上下文溢出更经济
