15. Conversation Summaries

Chapter 15 of 24 · 15 min

Long conversations with AI systems generate context that exceeds model context windows. OpenCLaw implements automatic conversation summarization to compress historical context while preserving essential information for continuity.

Summary Triggers

Summarization occurs at three trigger conditions: token count thresholds (configurable, default 4K tokens), conversation segment boundaries (user indicates topic change), and time gaps (pause exceeding threshold).

# conversation_summarizer.py
from typing import List, Tuple, Optional
from dataclasses import dataclass
from datetime import datetime, timedelta

@dataclass
class ConversationSegment:
    messages: List[Message]
    start_time: datetime
    end_time: datetime
    topic: Optional[str] = None
    summary: Optional[str] = None

class ConversationSummarizer:
    def __init__(
        self,
        token_threshold: int = 4000,
        time_gap_threshold: timedelta = timedelta(minutes=30),
        llm_client: LLMClient = None
    ):
        self.token_threshold = token_threshold
        self.time_gap_threshold = time_gap_threshold
        self.llm = llm_client
    
    def should_summarize(self, conversation: Conversation) -> bool:
        if self.count_tokens(conversation) > self.token_threshold:
            return True
        
        if self.has_topic_boundary(conversation):
            return True
        
        if self.has_time_gap(conversation):
            return True
        
        return False
    
    def summarize_segment(
        self, 
        segment: ConversationSegment
    ) -> str:
        prompt = f"""Summarize the following conversation segment.
        Preserve: key decisions, user preferences expressed, 
        tasks mentioned, important context.
        
        Conversation:
        {self.format_segment(segment)}
        
        Summary:"""
        
        response = self.llm.generate(prompt, max_tokens=200)
        return response.content
    
    def compress_conversation(
        self, 
        conversation: Conversation
    ) -> Tuple[List[Message], List[ConversationSegment]]:
        segments = self.segment_conversation(conversation)
        summaries = []
        
        for segment in segments:
            if self.should_summarize_segment(segment):
                summary_text = self.summarize_segment(segment)
                summary_msg = Message(
                    role="system",
                    content=f"[Earlier conversation summary: {summary_text}]"
                )
                summaries.append(summary_msg)
            else:
                summaries.extend(segment.messages)
        
        return summaries, segments

Hierarchical Summaries

For very long conversations, OpenCLaw implements hierarchical summarization. Individual exchanges summarize to segment summaries, segments summarize to conversation arc summaries, and arcs summarize to full context narratives.

class HierarchicalSummarizer:
    def __init__(self, levels: int = 3):
        self.levels = levels
        self.summarizers = [
            FineGrainedSummarizer(),
            MidLevelSummarizer(),
            ArcSummarizer()
        ][:levels]
    
    def compress(self, messages: List[Message]) -> Message:
        current_level = messages
        
        for level, summarizer in enumerate(self.summarizers):
            compressed = summarizer.compress(current_level)
            current_level = [Message(role="system", content=compressed)]
        
        return current_level[0]

Entity and Fact Tracking

Summarization preserves not just narrative flow but specific entities and facts. The system maintains an entity registry that gets updated across summaries, ensuring that referenced people, dates, and decisions remain accessible even when their originating messages are compressed away.

EXERCISE

Implement a summary coherence checker that validates whether a compressed conversation preserves logical consistency. Design metrics for measuring summary quality beyond simple token reduction.