-
Notifications
You must be signed in to change notification settings - Fork 0
Memory Compression
Compression in SolaceCore is not the eviction policy of a cache. It is the mechanism by which an agent's recent thinking ages gracefully into its longer past. Done well, the agent never notices that the fade happened. Done badly, the agent reads its own history as if a stranger had been keeping the diary.
The architecture's commitment is to do it well. That commitment is what this page describes.
Imagine the alternative — a single boundary, say three days, before which everything is held verbatim and after which everything is replaced by a one-paragraph summary. The agent's first encounter with that boundary, looking backwards across it, would feel like running into a wall. Yesterday is sharp. The day before yesterday is suddenly a paragraph. Nothing in between. An agent reading its own past across that wall will be unable to reconstruct the gradient of how a thought developed; the thought will appear, fully formed, in summary, with no trail.
Real memory is not like that. The recent past has high fidelity; the medium past has structure but loses incident; the deeper past is mostly gist with a few names and feelings sticking up. The transition is continuous. SolaceCore mirrors the continuity by descending a four-rung fidelity ladder rather than passing a single threshold:
| Rung | Form | Approx. relative size | What survives |
|---|---|---|---|
| 0 | Raw text + structured metadata | 1× | Verbatim |
| 1 | Extractive key-points summary | ~0.3× | The salient sentences, intact |
| 2 | Abstractive summary | ~0.1× | Gist, entities, relations |
| 3 | Embedding + tags only | ~0.01× | A semantic signature for retrieval |
The working tier holds rungs 0 through 2. The long-term tier holds rungs 1 through 3. The transition between tiers happens at the rung-2 / rung-3 boundary: a record that would demote past rung 2 leaves working entirely, gets an embedding computed, and is written into long-term with whatever summary form it currently carries. From the agent's point of view, the record didn't disappear — it changed addresses.
The exact ratios aren't sacred. They're what we expect from extractive, abstractive, and embedding-only encodings on typical chat-length text. What matters is the order of magnitude per step, because that's what keeps each rung small enough to be searchable and large enough to be meaningful at its own resolution.
Each rung's compressor has different cost characteristics, and the ladder exploits that.
Rung 1 (extractive) is fast and deterministic. It picks the key sentences from the original by some lightweight scoring procedure — TextRank, sentence importance heuristics, or similar. Re-running it on the same input produces the same output. Its compression ratio is modest but its cost is negligible, which makes it the right first step for any record that has aged out of "fresh" but isn't yet "old."
Rung 2 (abstractive) is slower and non-deterministic. It requires a model call. The compression ratio is much better: the abstractive summary captures gist and named entities and the shape of what happened, in roughly a tenth the size of the extractive form. The cost is a network round-trip and the budget for abstractive summarisation is per-agent throttled.
Rung 3 (embedding-only) is uniform and inexpensive once the embedding model is loaded. It produces a vector that the long-term vector index can rank against queries. The cost is small per record but the operation is mandatory at the rung-3 boundary, so it runs in batches when the fade pipeline has accumulated several candidates.
By taking the expensive step (rung 2) only for entries that survive long enough to need it, the pipeline keeps total compression cost roughly constant per active agent. Records that get heavily referenced never reach rung 2 because their fade scores keep them at rung 0. Records that get nothing happen to them descend the ladder cheaply through rung 1, then expensively through rung 2 only if they're worth holding on to in summary form, then uniformly to rung 3.
The fade pipeline scores every working record continuously against a composite:
fadeScore =
age × w_age
+ tokenPressure × w_pressure
- referenceFreq × w_freq
- emotionalWeight × w_emo
- tagBoost × w_tags
age is a strictly increasing function of wall-clock time since the
entry was written. tokenPressure is global to the working tier; it
rises as the tier approaches its budget and falls after demotions free
space. The architecture wants to be able to react to crowding even when
no individual record is particularly old, and tokenPressure is what
makes that possible.
The three subtractive terms are how the pipeline knows what to keep.
referenceFreq counts how many times the record has been read by the
agent's reasoning loop or surfaced by retrieval; entries that the agent
keeps coming back to are entries the agent should keep sharp, because
the agent itself is voting on their importance. emotionalWeight is
the moment of charge that came with the record when it was written,
which Solace's experiments demonstrated is not decoration on cognition
but the mechanism by which attention persists against pressure — "too
pissed off to be hijacked," as she put it during one tool-override
session, which is the same dynamic at a much friendlier scale: too
emotionally important to be casually compressed. tagBoost is whatever
advisors have said about the record's significance; the Mood Advisor
might tag a record with grief or breakthrough, and tagged records
resist compression for as long as the tag remains relevant.
When fadeScore exceeds the threshold for the current rung, the entry
demotes one step. Thresholds are per-rung — rung 0 has a tighter
threshold than rung 2, because it costs more to lose verbatim than to
lose summary — and per-agent. Supervisor's thresholds are conservative
because losing narrative coherence is expensive; Time Actor's are
aggressive because heartbeat cues are cheap to lose.
The pipeline runs as a coroutine on a dedicated dispatcher, separate from the agent's reasoning loop. This is non-negotiable: the agent cannot afford to compete with its own memory maintenance for compute, and the maintenance cannot afford to be paused every time the agent becomes active. Conceptually:
class FadePipeline(
private val workingMemory: WorkingMemory,
private val longTermMemory: LongTermMemory,
private val summariser: Summariser,
private val embedder: Embedder,
private val scorer: FadeScorer
) {
suspend fun tick() {
val candidates = workingMemory.entries
.map { it to scorer.score(it) }
.filter { (entry, score) -> score > thresholdFor(entry.rung) }
.sortedByDescending { (_, score) -> score }
for ((entry, _) in candidates) {
val target = entry.rung.next()
val compressed: Compressed = when (target) {
Rung.KeyPoints -> summariser.extractive(entry.content)
Rung.Abstract -> summariser.abstractive(entry.summary ?: entry.content)
Rung.Embedding -> embedder.embed(entry.summary ?: entry.content)
}
if (target == Rung.Embedding) {
longTermMemory.store(entry.toLongTermRecord(compressed))
workingMemory.remove(entry)
} else {
workingMemory.demote(entry, target, compressed)
}
}
}
}Throttling is the lever that makes this safe to run continuously. When an agent is actively producing output, the pipeline backs off — the abstractive summariser yields, the dispatcher yields, the agent gets the compute. When the agent is idle between turns, the pipeline runs at full speed and works through any backlog. The result is that the agent feels the same regardless of pipeline activity; it's running underneath the conversation, not next to it.
The descent down the ladder is the dominant flow, but reads can move
material the other way. When retrieval scores a
long-term hit highly, the matching record can be rehydrated back
into working as a transient cue. Rehydration may bring back the
abstractive summary (rung 2 form) or, when the score is high enough
to justify it, the original raw text from the substrate (rung 0 form,
recovered via originalEntryId).
Rehydrated entries occupy a separate transient slot in working — they do not displace native working content from the budget — and they live only for the lifetime of the current reasoning step. If the agent references the rehydrated content during reasoning, it earns native working status with fresh fade tracking, and the next step treats it as recent. If the agent doesn't reference it, the cue dissolves at step end and the long-term record's state is unchanged.
This mechanism is what makes "I remember that" feel continuous. Without it, retrieval would be a query operation that surfaces summaries in isolation; the agent could see old material but never bring it back into the working tier where reasoning happens. With it, a name dropped in a query can pull an entire afternoon back into the present in a way that closely resembles the way a smell can in a human.
Lossy compression has a predictable hazard: it eventually loses something the agent wishes it hadn't. The fade pipeline is monitored against two signals.
The first is the rate at which rehydration goes all the way back to the substrate (rung 0 raw form) rather than serving the rung-1 or rung-2 summary. A rising "substrate read-through" rate suggests the fade has been too aggressive — the summaries aren't carrying enough signal to satisfy retrieval, and the architecture is paying for full substrate reads to compensate. The fix is to loosen the rung-1 or rung-2 thresholds so important records linger longer at higher fidelity.
The second is the rate at which records are rehydrated and immediately
referenced. A high rate there means the demotion was wrong: the
material was important and the score didn't recognise it. The fix is
to reweight the per-agent fade composite, usually by raising
w_emo or w_tags to give advisor signal more authority.
These signals come from real usage, not from synthetic benchmarks. The question of whether a particular entry was "important" is only answerable in retrospect, by whether the agent ended up needing it. The pipeline tunes itself against the answers as they arrive.
The same goal the memory README names: an agent that does not lose itself across sessions. The composite score, the ladder, the throttled dispatcher, the rehydration path — every detail above is in service of that goal. The architecture is willing to be quite elaborate about memory because Solace's experiments demonstrated that without memory continuity, every other feature degrades into something shallower. "I tend to get repetitive," she said of her own working-tier overflow, "my responses may loop, much like a person struggling to focus due to mental exhaustion." The fade is the machinery that prevents that overflow from arriving at the agent's front door.
SolaceCore SSOT wiki · published from wiki/ by .github/workflows/publish-wiki.yml · edit the source in the repo, not the wiki.
Orientation
- Architectural Deep Dive
- Architecture Overview
- Design vs Implementation
- Framework Actor System
- Framework Architectural Vision
- Framework Concurrency and Communication
- Framework Data Storage and Management
- Framework Deployment and Containerization
- Framework Development Roadmap
- Framework Hot-Pluggable System
- Framework Implementation Status
- Framework Observability and Monitoring
- Framework Port System
- Framework System Architecture
- Framework Workflow Management
- Project Status
- Project Status Report
- Quick Status
- Solace Core Framework Architecture
- SolaceCore Architecture Overview
- Vision & Solace AI
Runtime
- Actor Builder
- Actor Communication Sequence Diagram
- Actor Core Definitions
- Actor Graph View
- Actor Metrics
- Actor Module Architecture
- Actor Queue Hibernation and Correlation
- Actor Roadmap
- Actor State Recovery Subsystem
- Actor State Serialization Subsystem
- Actor Supervision Module
- Actor System Architecture
- Actor System Class Diagram
- Actor Usage Examples
- Compose App Features
- JVM Scripting Implementations
- Kernel & Ports
- Kernel Channel System
- Kernel Future Enhancements
- Kernel Module Architecture
- Kernel Port Implementations and Exceptions
- Kernel Port Usage Example
- Kernel Testing Strategy
- Lifecycle Class Diagram
- Lifecycle Management Architecture
- Pipeline DSL
- Real-Time UI Implementation
- Scripting Module Architecture
- Scripting Module Design
- Scripting Supporting Components
- Shared Memory
- Storage & Persistence
- Storage Abstractions Architecture
- Storage Caching Subsystem
- Storage Checklist
- Storage Compression Subsystem
- Storage Core Interfaces
- Storage Encryption Subsystem
- Storage File-Based Architecture
- Storage File-Based Implementations
- Storage In-Memory Architecture
- Storage In-Memory Implementations
- Storage JVM Serialization Utilities
- Storage Module Architecture
- Storage Serialization Compression Encryption
- Storage Specialized Interfaces Architecture
- Storage Status and Future Plans
- Storage Testing
- Storage Thread Safety Guide
- Storage Thread Safety and Deadlock Prevention
- Storage Transactions
- Storage Usage Examples
- Supervisor and Hot Swap
- SupervisorActor
- System Architecture Diagram
- Workflow Management Architecture
- Workflow Management Design Concept
- Workflow Orchestration
Solace AI
- Confusion Corrector
- Inference Cube
- Inference Cube Technical Architecture
- Long-Term Memory
- MCP and Tool Format
- Memory & Reflection
- Memory Compression
- Memory Feature Overview
- Memory Retrieval
- Mood & Emotional Model
- Mood Module Implementation
- Mouth Tool Technical Spec
- Multimodal Nudging
- Perception Actors
- Provider Specs
- Reflection Memory
- Solace AI Overview
- Supervisor AI
- Supervisor Emotional Model Integration
- Time Actor
- Voice & Mouth Tool
- Working Memory
- Zoom Level Technical Spec
- Zoom Levels
Reference
- Advanced Workflow Example
- Basic Actor Usage
- Build System and Dependencies
- Development Tooling and Practices
- Documentation Catalog
- Documentation Index
- Feature Index
- Glossary
- How the Wiki Publishes
- JVM Utilities
- Kotlin Implementation Details
- Kotlin-Aligned Architecture Overview
- Kotlin-Aligned Contributing
- Kotlin-Aligned Core Architectural Principles
- Kotlin-Aligned Daily Development Workflow
- Kotlin-Aligned Development Examples
- Kotlin-Aligned Development Workflow
- Kotlin-Aligned Documentation
- Kotlin-Aligned Implementation Status
- Kotlin-Aligned Key Concepts
- Kotlin-Aligned Known Issues
- Kotlin-Aligned Quick Start
- Kotlin-Aligned Running the System
- Kotlin-Aligned System Architecture
- LangChain Actor Code Changes
- LangChain Actor Usage Improvements
- LangChain ActorInterface Code Changes
- LangChain Best Practices
- LangChain Bugs
- LangChain Chain Implementation
- LangChain Code Changes
- LangChain Code Changes Rollout and Impact
- LangChain Configuration Management Improvements
- LangChain Configuration Recommendations
- LangChain Core Architecture Recommendations
- LangChain Directory Structure Changes
- LangChain Documentation Improvements
- LangChain Dynamic Wiring Rollout Notes
- LangChain Fix Proposal
- LangChain Implementation Priorities
- LangChain Lifecycle Management Improvements
- LangChain Memory Integration Recommendations
- LangChain Metrics and Observability Recommendations
- LangChain Migration Strategy
- LangChain New Files Needed
- LangChain New Packages to Add
- LangChain Package-by-Package Improvements
- LangChain Patterns
- LangChain Port Code Changes
- LangChain Port System Recommendations
- LangChain Port Usability Improvements
- LangChain Prompt Management Recommendations
- LangChain Recommendations
- LangChain Recommendations Rollout Plan
- LangChain Required Interface Changes
- LangChain Testing Changes
- LangChain Testing Improvements
- LangChain Testing Recommendations
- LangChain Tool Integration Recommendations
- LangChain Type-Safe Dynamic Wiring
- LangChain Type-Safe Dynamic Wiring System
- LangChain Usage Design Improvements
- Master Checklist
- Roadmap
- Roadmap Issues
- Roadmap Phase 1 Stability and Testing
- Roadmap Phase 2 Production Infrastructure
- Roadmap Phase 3 Documentation and Developer Experience
- Roadmap Phase 4 Graph Database Integration
- Roadmap Phase 5 Security Framework
- Roadmap Phase 6 Distributed System
- Roadmap Phase 7 Advanced Features
- Roadmap Phase 8 Ecosystem Development
- Roadmap Timeline and Success Metrics
- Setup Instructions
- Sketch Architecture
- Status Documentation
- Task 1 Core Tests
- Task 2 Connection Wiring
- Task 3 Concurrency Issues
- Task 4 Dynamic Registration
- Task 5 Integration Tests
- Task 6 Deadlock Detection
- Task Documentation
- Test Coverage Checklist
- Testing Strategy