feat(context): static-doc compression — prune CLAUDE.md/AGENTS.md doc entries
The big win the pipeline missed: projectProfile + agentInstructions doc dumps (~14k chars) are L0-SYSTEM -> STATIC -> never pruned, re-sent verbatim every turn. Now pruned via the same TokenPruner at a gentler ratio (keep 60% vs 45% freeform), protected spans (code fences/paths/keys) kept so directives survive. Gated by TOKEN_PRUNE (level 3+); base systemPrompt stays verbatim.
This commit is contained in:
+16
-2
@@ -51,7 +51,13 @@ class DefaultContextPackBuilder(
|
||||
const val CHARS_PER_TOKEN = 4
|
||||
const val FACT_SHEET_ORDINAL = -1
|
||||
const val DEFAULT_PRUNE_RATIO = 0.45
|
||||
const val DOC_PRUNE_RATIO = 0.6
|
||||
const val DOC_MIN_CHARS = 1200
|
||||
const val TIER0_TURNS = 3
|
||||
// Large static instruction docs (CLAUDE.md project profile, AGENTS.md / DOX framework)
|
||||
// injected verbatim every turn. Pruned at DOC_PRUNE_RATIO — gentler than freeform, with
|
||||
// protected spans (code fences, paths, config keys) kept, so directives survive.
|
||||
val DOC_SOURCE_TYPES = setOf("projectProfile", "agentInstructions")
|
||||
}
|
||||
|
||||
override suspend fun build(
|
||||
@@ -142,10 +148,18 @@ class DefaultContextPackBuilder(
|
||||
.map { it.ordinal }.sortedDescending()
|
||||
val tier0 = if (tierSplit) freeformOrdinals.take(TIER0_TURNS).toSet() else emptySet()
|
||||
return entries.map { entry ->
|
||||
if (classifier.classify(entry) != ContextClass.FREEFORM || entry.ordinal in tier0) entry
|
||||
else reencode(entry, tokenPruner.prune(entry.content, tagger.protectedSpans(entry.content), DEFAULT_PRUNE_RATIO))
|
||||
val isFreeform = classifier.classify(entry) == ContextClass.FREEFORM && entry.ordinal !in tier0
|
||||
val isLargeDoc = entry.sourceType in DOC_SOURCE_TYPES && entry.content.length >= DOC_MIN_CHARS
|
||||
when {
|
||||
isFreeform -> reencode(entry, tokenPruner.prune(entry.content, spansOf(entry), DEFAULT_PRUNE_RATIO))
|
||||
// Static instruction docs: gentler ratio so directives survive the classifier.
|
||||
isLargeDoc -> reencode(entry, tokenPruner.prune(entry.content, spansOf(entry), DOC_PRUNE_RATIO))
|
||||
else -> entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun spansOf(entry: ContextEntry) = tagger.protectedSpans(entry.content)
|
||||
|
||||
private fun applyToMe(entries: List<ContextEntry>): List<ContextEntry> {
|
||||
val (freeform, rest) = entries.partition { classifier.classify(it) == ContextClass.FREEFORM }
|
||||
|
||||
@@ -137,6 +137,28 @@ class CompressionPipelineStagesTest {
|
||||
Unit
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `large static doc entries are pruned at level 3 while base system prompt is untouched`() = kotlinx.coroutines.runBlocking {
|
||||
val pruner = object : com.correx.core.context.compression.TokenPruner {
|
||||
override suspend fun prune(content: String, protectedSpans: List<String>, targetRatio: Double): String = "DOC-PRUNED"
|
||||
}
|
||||
val builder = com.correx.core.context.builder.DefaultContextPackBuilder(
|
||||
com.correx.core.context.compression.DefaultContextCompressor(), CompressionPolicy(3), tokenPruner = pruner,
|
||||
)
|
||||
val bigDoc = "# CLAUDE.md\n" + "guidance line ".repeat(120) // > DOC_MIN_CHARS
|
||||
val entries = listOf(
|
||||
entry("proj", ContextLayer.L0, EntryRole.SYSTEM, bigDoc).copy(sourceType = "projectProfile"),
|
||||
entry("sys", ContextLayer.L0, EntryRole.SYSTEM, "you are an agent").copy(sourceType = "systemPrompt"),
|
||||
)
|
||||
val flat = builder.build(
|
||||
com.correx.core.events.types.ContextPackId("p"), com.correx.core.events.types.SessionId("s"),
|
||||
com.correx.core.events.types.StageId("st"), entries, com.correx.core.context.model.TokenBudget(limit = 4000),
|
||||
).layers.values.flatten()
|
||||
assertEquals("DOC-PRUNED", flat.first { it.sourceType == "projectProfile" }.content)
|
||||
assertEquals("you are an agent", flat.first { it.sourceType == "systemPrompt" }.content)
|
||||
Unit
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `classifier separates static structured freeform`() {
|
||||
val c = ContextClassifier()
|
||||
|
||||
Reference in New Issue
Block a user