TnsAI
Intelligence

Context Management

Context window management, decision tracing, session history, knowledge extraction, automatic memory consolidation, and auto-summarization. These components help agents operate effectively within token limits and learn from past interactions.

Auto-Summarization (Context Compaction)

LLMs have a fixed context window, and long conversations will eventually exceed it. Compaction strategies automatically shrink the conversation history to fit within the token budget while preserving as much important information as possible. Package: com.tnsai.context.compaction.

TruncatingCompactor

The simplest strategy: removes oldest messages (preserving system prompts) until the token budget is met. Zero cost -- no LLM calls required.

ContextCompactor compactor = new TruncatingCompactor();
List<Map<String, Object>> compacted = compactor.compact(messages, 4096);

TwoPhaseCompactor

Two-phase strategy that first truncates tool call arguments, then uses an LLM to summarize older conversation turns. Preserves more semantic information than pure truncation.

TwoPhaseCompactor compactor = TwoPhaseCompactor.builder()
    .llm(llmClient)
    .argTruncationThreshold(500)   // truncate tool args over 500 chars
    .summaryRatio(0.3)             // summarize oldest 30% of messages
    .build();

List<Map<String, Object>> compacted = compactor.compact(messages, 8192);

Phase 1 -- Argument Truncation: Tool call arguments and results exceeding the threshold are truncated to their keys and types. This often reclaims significant tokens without losing conversational context.

Phase 2 -- LLM Summarization: If Phase 1 does not reduce the context enough, older messages are sent to the LLM for summarization. The summary replaces the original messages as a single system message.

CompactorCostInformation LossBest For
TruncatingCompactorFreeHigh (oldest context lost)Short conversations, cost-sensitive
TwoPhaseCompactorLow (1 LLM call)Low (summary preserves key facts)Long conversations, multi-turn reasoning

LLMContextCompactor

A standalone LLM-based compactor that gives you fine-grained control over when compaction triggers, how many recent messages to preserve, and how the summary is generated. Use this when you need precise configuration beyond what TwoPhaseCompactor offers.

LLMClient summarizer = LLMClientFactory.create("openai", "gpt-4o-mini");
LLMContextCompactor compactor = new LLMContextCompactor(summarizer);

CompactionConfig config = CompactionConfig.builder()
    .thresholdRatio(0.80)
    .preserveLastN(5)
    .preserveSystemPrompt(true)
    .minMessagesForCompaction(10)
    .summarizerModel("gpt-4o-mini")
    .maxSummaryTokens(500)
    .build();

CompactionResult result = compactor.compact(messages, config);

CompactionConfig

These settings control when compaction fires and how much context is preserved versus summarized.

ParameterDefaultDescription
thresholdRatio0.80Token ratio at which compaction triggers
preserveLastN5Recent messages to keep unmodified
preserveSystemPrompttrueAlways keep system prompt
minMessagesForCompaction10Minimum messages before compaction
summarizerModelnullModel for summarization (null = agent's default)
summaryPromptTemplatebuilt-inCustom template (use {messages} placeholder)
maxSummaryTokens500Max tokens for the generated summary

Compaction Process

The compactor follows a four-step process that ensures the system prompt and recent messages are always preserved, while older messages are summarized into a compact form.

  1. Separate system prompt (if preserved)
  2. Identify last N messages to preserve
  3. Summarize remaining messages via LLM
  4. Reconstruct: [system prompt] + [summary as system message] + [preserved messages]

AgentContextManager

The central hub for observing and recording what your agent does during a conversation. It captures state snapshots at key moments, traces the reasoning behind each decision, tracks entities mentioned in the conversation, and manages conversation lifecycle. This is essential for debugging, auditing, and building agents that can learn from their own history.

AgentContextManager contextManager = new AgentContextManager(
    "agent-001",
    new InMemorySnapshotStore(),
    new InMemoryDecisionTraceStore()
);

Context Snapshots

A snapshot captures the agent's complete state (beliefs, desires, intentions) at a specific moment. By taking snapshots before and after decisions, you can see exactly what changed and why.

ContextSnapshot snapshot = contextManager.createSnapshot(
    SnapshotTrigger.PRE_DECISION,
    agent.getBeliefs(),
    agent.getDesires(),
    agent.getIntentions()
);

List<ContextSnapshot> recent = contextManager.getRecentSnapshots(10);
Optional<SnapshotDiff> diff = contextManager.diffSnapshots(beforeId, afterId);

Decision Tracing

Wrap any agent operation in a decision trace to automatically record what the agent was thinking before and after the decision, what it decided, and whether it succeeded. This creates an audit trail of every significant action.

// Full version with BDI suppliers
String response = contextManager.executeWithTrace(
    "Process discount request",
    () -> agent.chat("Should we approve 20% discount?"),
    agent::getBeliefs,
    agent::getDesires,
    agent::getIntentions
);

// Simplified version (without BDI)
String result = contextManager.executeWithTrace(
    "Summarize report",
    () -> agent.chat("Summarize this...")
);

This creates pre/post snapshots, records the full decision trace, and handles errors transparently.

Querying Decisions

Once decisions are recorded, you can query them by various criteria -- recent decisions, failures, successes, or by action name. The manager also supports finding similar past decisions and generating human-readable explanations.

List<DecisionTrace> recent = contextManager.getRecentDecisions(10);
List<DecisionTrace> failures = contextManager.findFailedDecisions();
List<DecisionTrace> successes = contextManager.findSuccessfulDecisions();
List<DecisionTrace> byAction = contextManager.findDecisionsByAction("approve");

Optional<String> explanation = contextManager.explainDecision(traceId);
List<DecisionTrace> precedents = contextManager.findPrecedents(referenceTrace, 0.7);
List<String> summaries = contextManager.summarizeRecentDecisions(5);

Entity Tracking

Register external entities (users, resources, tasks) so they are included in context snapshots. This helps the agent maintain awareness of what objects it is working with.

contextManager.trackEntity(customerEntity);
contextManager.untrackEntity("entity-123");
Collection<Entity> tracked = contextManager.getTrackedEntities();

Context Variables

Store arbitrary key-value metadata that gets included in every snapshot. Use this for session-level information like the current user, project, or priority level.

contextManager.setVariable("sessionType", "support");
contextManager.setVariable("priority", 5);
Optional<Integer> typed = contextManager.getVariable("priority", Integer.class);

Conversation Lifecycle

Group related decisions into a conversation so you can query all decisions that happened during a specific interaction.

String convId = contextManager.startConversation();
// ... process messages ...
List<DecisionTrace> inConv = contextManager.findDecisionsInCurrentConversation();
contextManager.endConversation();

Cleanup

Remove old traces and snapshots to prevent unbounded storage growth. Pass a cutoff time to delete everything older than that point.

Instant cutoff = Instant.now().minus(Duration.ofDays(30));
int deletedTraces = contextManager.cleanupOldTraces(cutoff);
int deletedSnapshots = contextManager.cleanupOldSnapshots(cutoff);

BDIContextMapper

An internal utility that converts the agent's BDI (Belief-Desire-Intention) state into the context graph structures used by AgentContextManager. You typically do not need to use this directly -- the context manager handles the mapping automatically.

AgentSession

A session records the full conversation between a user and an agent: every message, tool call, and result, along with metadata and outcome. Sessions are the building blocks for history search, knowledge extraction, and learning from past interactions.

AgentSession session = AgentSession.start("code-assistant", "/path/to/project");

session.addUserMessage("Fix the authentication bug");
session.addAssistantMessage("I'll analyze the auth module...");
session.addToolCall("readFile", Map.of("path", "auth.java"));
session.addToolResult("readFile", "public class Auth { ... }");

session.addMetadata("model", "gpt-4o");
session.addTags("security", "bug-fix");

session.success();
// or: session.fail("Compilation error");
// or: session.cancel();

session.getTurnCount();                           // 4
session.getDuration();                             // Duration since start
session.getTurnsByType(TurnType.TOOL_CALL);       // Tool call turns only
session.getFirstUserMessage();                     // "Fix the authentication bug"
session.isComplete();                              // true
session.isSuccess();                               // true

SessionOutcome

Tracks how the session ended. This is useful for filtering sessions during analysis -- for example, finding all failed sessions to understand common failure modes.

ValueDescription
IN_PROGRESSSession is active
SUCCESSCompleted successfully
FAILEDEnded with error
CANCELLEDUser cancelled

SessionStore / FileSessionStore

Persist agent sessions to disk so they survive application restarts. FileSessionStore saves each session as a JSON file and provides query methods to find sessions by agent, project, tag, outcome, or date range.

SessionStore store = SessionStore.file(".tnsai/history/");

store.save(session);
Optional<AgentSession> loaded = store.load("session-123");

store.findRecent(10);
store.findByAgent("code-assistant");
store.findByProject("/my/project");
store.findByTag("security");
store.findByOutcome(SessionOutcome.SUCCESS);
store.findByDateRange(LocalDate.now().minusDays(7), LocalDate.now());
store.count();

SearchableSessionStore / SessionQuery

When you need to search session contents (not just metadata), use SearchableSessionStore. It supports full-text search combined with filters for agent, project, tags, outcome, and date range.

SessionQuery query = SessionQuery.builder()
    .text("authentication bug")
    .agent("code-assistant")
    .project("/my/project")
    .tags("security", "urgent")
    .outcome(SessionOutcome.SUCCESS)
    .dateRange(LocalDate.now().minusDays(7), LocalDate.now())
    .searchMode(SessionQuery.SearchMode.ALL)
    .limit(20)
    .offset(0)
    .build();

List<SessionSearchResult> results = searchableStore.search(query);

SessionQuery Convenience

Shortcut factory methods for common query patterns so you do not need to use the builder for simple cases.

SessionQuery recent = SessionQuery.recent(10);
SessionQuery textSearch = SessionQuery.text("database migration");
SessionQuery lastWeek = SessionQuery.builder().lastDays(7).build();

SearchMode

Controls how the text search matches against session content.

ModeDescription
ANYMatch any word (default)
ALLMatch all words
EXACTMatch exact phrase
REGEXRegular expression match

AutoConsolidationManager

Monitors conversation state and triggers memory consolidation automatically based on configurable thresholds.

AutoConsolidationManager manager = AutoConsolidationManager.builder()
    .pipeline(consolidationPipeline)
    .triggers(Set.of(
        ConsolidationTrigger.MESSAGE_COUNT,
        ConsolidationTrigger.TOKEN_LIMIT,
        ConsolidationTrigger.ON_SESSION_END))
    .messageCountThreshold(50)
    .tokenBudget(8192)
    .tokenThresholdPercent(80)
    .build();

// Call on each message
ConsolidationResult result = manager.onMessageAdded(session, estimatedTokens);
if (result != null) {
    System.out.println("Consolidated: " + result.knowledgeCount() + " items");
}

// Call when session ends
manager.onSessionEnd(session);

ConsolidationTrigger

These triggers define when automatic consolidation runs. You can enable one or more triggers depending on your needs.

TriggerDefaultFires When
MESSAGE_COUNT50 messagesCounter reaches threshold
TOKEN_LIMIT80% of budgetEstimated tokens exceed threshold
ON_SESSION_ENDalwaysonSessionEnd() is called

Consolidation is thread-safe: concurrent triggers are skipped if one is already in progress.

LLMKnowledgeExtractor

Uses an LLM to analyze session conversations and extract reusable knowledge.

KnowledgeExtractor extractor = new LLMKnowledgeExtractor(llmClient, 0.5);

List<ExtractedKnowledge> knowledge = extractor.extract(session);

// Filter by type
knowledge.stream()
    .filter(k -> k.type() == ExtractionType.SOLUTION)
    .filter(k -> k.isHighConfidence())
    .forEach(k -> System.out.println(k.summary()));

// Extract specific types only
List<ExtractedKnowledge> decisions = extractor.extract(session,
    EnumSet.of(ExtractionType.DECISION, ExtractionType.LEARNING));

// Adjust confidence threshold
KnowledgeExtractor strict = extractor.withMinConfidence(0.8);

ExtractionType

Five categories of knowledge that can be extracted from session conversations. Each type has a different practical use -- solutions help with similar problems in the future, patterns provide reusable templates, and antipatterns warn about approaches to avoid.

TypeKeyDescription
SOLUTIONproblem-solutionA problem and how it was solved
PATTERNpatternA reusable code or design pattern
DECISIONdecisionAn architectural decision with rationale
LEARNINGlearningA lesson learned or insight
ANTIPATTERNantipatternAn approach to avoid

Each ExtractedKnowledge item includes content, optional problem description, code snippets, related files, tags, and a confidence score (0.0-1.0).

Knowledge Storage

Persist extracted knowledge for later retrieval. Two implementations are provided: in-memory for testing and file-based for production.

KnowledgeStore store = new InMemoryKnowledgeStore();
// Or persist to files:
KnowledgeStore store = new FileKnowledgeStore(".tnsai/knowledge/");

store.save(knowledge);
List<ExtractedKnowledge> solutions = store.findByType(ExtractionType.SOLUTION);

On this page