Advanced Intelligence Patterns
Advanced cognitive capabilities in TnsAI.Intelligence for reasoning, memory consolidation, output validation, and iterative refinement.
Reasoning Strategies
TnsAI.Intelligence provides three advanced reasoning executors that go beyond simple prompt-response patterns. Each implements a different strategy for exploring solution spaces.
ReActExecutor
The ReActExecutor implements the ReAct (Reason + Act + Observe) pattern where an LLM explicitly reasons about what to do, takes an action, observes the result, and repeats until the goal is achieved. This produces structured Thought/Action/Observation traces for interpretability and decision auditing.
Based on "ReAct: Synergizing Reasoning and Acting in Language Models" (Yao et al., 2022).
ReActExecutor react = ReActExecutor.builder()
.llm(client)
.actionHandler((name, input) -> {
if ("search".equals(name)) return Optional.of(searchService.search(input));
if ("calculate".equals(name)) return Optional.of(calculator.eval(input));
return Optional.empty();
})
.maxSteps(10)
.thoughtFormat(ThoughtFormat.STRUCTURED)
.stopCondition(StopCondition.finalAnswerDetected())
.availableActions("search, calculate")
.timeout(Duration.ofMinutes(5))
.build();
ReActResult result = react.execute("Find the population of Tokyo and compare it to NYC");
System.out.println("Answer: " + result.getFinalAnswer());
System.out.println("Status: " + result.getStatus()); // SUCCESS, TIMEOUT, ERROR, MAX_STEPS_REACHED, STOPPED
System.out.println("Steps: " + result.getSteps().size());
System.out.println("LLM calls: " + result.getTotalLLMCalls());
System.out.println("Duration: " + result.getDuration());
// Inspect individual reasoning steps
for (ReActStep step : result.getSteps()) {
System.out.println("Step " + step.getStepNumber());
System.out.println(" Thought: " + step.getThought());
System.out.println(" Action: " + step.getAction());
System.out.println(" Observation: " + step.getObservation());
}ThoughtFormat options: STRUCTURED (formal Thought/Action/Observation format) or FREE_FORM (open-ended reasoning). The ActionHandler is a @FunctionalInterface that takes an action name and input, returning Optional<String> (empty means action not available).
ReActStatus values: SUCCESS, TIMEOUT, ERROR, MAX_STEPS_REACHED, STOPPED.
TreeOfThoughtsExecutor
The TreeOfThoughtsExecutor explores multiple reasoning paths by generating candidate thoughts at each step, evaluating them for promise, pruning low-quality branches, and continuing on promising paths.
Based on "Tree of Thoughts: Deliberate Problem Solving with Large Language Models" (Yao et al., 2023).
TreeOfThoughtsExecutor tot = TreeOfThoughtsExecutor.builder()
.llm(client)
.evaluator(BranchEvaluator.llm(evalClient))
.pruning(PruningStrategy.BEAM_SEARCH)
.beamWidth(3)
.maxDepth(5)
.branchingFactor(3)
.pruneThreshold(0.3)
.timeout(Duration.ofMinutes(10))
.build();
ToTResult result = tot.explore("Design a REST API for a todo app");
System.out.println("Best path: " + result.getBestPath());
System.out.println("Total nodes: " + result.getTotalNodes());
System.out.println("Pruned: " + result.getPrunedNodes());
System.out.println("Max depth reached: " + result.getMaxDepthReached());PruningStrategy options:
| Strategy | Behavior |
|---|---|
BEAM_SEARCH | Keep top-K nodes by score at each level (default) |
BEST_FIRST | Sort by cumulative score, keep top-K |
GREEDY | Keep only the single best node |
DEPTH_LIMITED | No score-based pruning, only depth limit |
EXHAUSTIVE | No pruning at all |
GraphOfThoughtsExecutor
The GraphOfThoughtsExecutor extends Tree of Thoughts by allowing cycles and merging of thought branches. Better for problems where partial solutions can be combined.
Based on "Graph of Thoughts" (Besta et al., 2023).
GraphOfThoughtsExecutor got = GraphOfThoughtsExecutor.builder()
.llm(client)
.evaluator(BranchEvaluator.llm(client))
.operations(List.of(GoTOperation.GENERATE, GoTOperation.AGGREGATE, GoTOperation.REFINE))
.maxNodes(20)
.branchingFactor(3)
.timeout(Duration.ofMinutes(10))
.build();
GoTResult result = got.explore("Design a database schema for e-commerce");
System.out.println("Best thought: " + result.getBestThought());
System.out.println("Aggregated insight: " + result.aggregatedInsight());
System.out.println("Total nodes: " + result.totalNodes());
System.out.println("Merges performed: " + result.mergeCount());
System.out.println("Duration: " + result.duration());GoTOperation types: GENERATE (create child thoughts), AGGREGATE (merge top-scoring nodes into a unified solution), REFINE (improve existing thoughts), SCORE (evaluate nodes).
The executor uses a frontier-based exploration: each iteration generates children, refines promising nodes (score \> 0.3), and aggregates top nodes from different branches. The frontier is pruned to the top branchingFactor nodes each round.
Cross-reference: For basic reasoning configuration, see Reasoning.
Atom of Thought (AoT)
The AotProcessor implements the Atom of Thought reasoning technique that decomposes complex problems into independent "atoms" solved in parallel. Unlike Chain-of-Thought where errors in early steps propagate, AoT isolates atoms so one bad result does not ruin the answer.
Based on "Atom of Thoughts for Markov LLM Test-Time Scaling" (arXiv, Feb 2025).
Benefits and Trade-offs
- +30-40% accuracy improvement on complex reasoning tasks
- Error isolation between atoms
- Parallel execution of independent atoms
- Per-atom confidence tracking
- Trade-off: +20-30% token usage due to multiple LLM calls
Usage
// Simple usage
AotProcessor aot = new AotProcessor(llmClient);
AotResult result = aot.process("Calculate compound interest for $1000 at 5% for 3 years");
System.out.println("Answer: " + result.getAnswer());
System.out.println("Confidence: " + result.getConfidence() + "%");
// With configuration
AotProcessor aot = AotProcessor.builder()
.llmClient(llmClient)
.maxAtoms(8)
.parallelExecution(true)
.minConfidence(40f)
.complexityThreshold(50)
.synthesisStrategy(AtomSynthesizer.SynthesisStrategy.WEIGHTED_COMBINATION)
.build();
AotResult result = aot.process(complexQuestion);
System.out.println(result.getDetailedTrace());
// Force AoT even for simple questions
AotResult forced = aot.processForced("What is 2+2?");
// Check question complexity before processing
int score = aot.getComplexityScore("Compare Python and Java for web development");How It Works
- Complexity check: The processor scores the question for complexity (multi-step indicators, calculation keywords, comparison keywords). Below the threshold, it processes as a single atom.
- AtomGenerator: Decomposes the problem into atoms. Tries heuristic patterns first (calculation, comparison, list), then falls back to LLM-based decomposition. Each atom has an
id,description,prompt,type(REASONING, RETRIEVAL, COMPUTATION, VALIDATION, SYNTHESIS),dependencies, andpriority. - AtomSolver: Solves atoms in parallel with dependency resolution. Groups atoms by level (independent atoms first, then dependent ones). Includes dependency context in prompts and extracts confidence scores from LLM responses.
- AtomSynthesizer: Combines atom solutions into a final answer using the configured synthesis strategy, weighting by confidence.
Call aot.shutdown() to release the executor thread pool when done.
Memory Consolidation
The MemoryConsolidationPipeline automates the transition from short-term conversation memory to long-term knowledge storage.
Pipeline Steps
- Extract knowledge from the session (via
KnowledgeExtractor) - Filter by confidence threshold
- Apply forgetting curve to older knowledge
- Store high-value knowledge (via
KnowledgeStore) - Compact conversation context if compactor provided
MemoryConsolidationPipeline pipeline = MemoryConsolidationPipeline.builder()
.knowledgeExtractor(extractor)
.contextCompactor(compactor)
.knowledgeStore(store)
.forgettingCurve(ForgettingCurve.EXPONENTIAL)
.compactionConfig(compactionConfig)
.minKnowledgeConfidence(0.5)
.build();
ConsolidationResult result = pipeline.consolidate(session);
System.out.println("Extracted: " + result.knowledgeCount() + " knowledge items");
System.out.println("Original turns: " + result.originalCount());
System.out.println("Consolidated turns: " + result.consolidatedCount());
System.out.println("Summary: " + result.summary());
System.out.println("Duration: " + result.duration());ForgettingCurve
Controls how memory importance decays over time when not accessed. Knowledge that decays below 0.1 is automatically deleted from the store.
| Curve | Formula | Behavior |
|---|---|---|
NONE | No decay | All memories retain full importance forever |
LINEAR | score * max(0, 1 - days * 0.01) | Gradual linear decline, reaches zero after ~100 days |
EXPONENTIAL | score * e^(-0.05 * days) | Ebbinghaus-inspired rapid initial decay that slows over time |
double decayed = ForgettingCurve.EXPONENTIAL.decay(0.9, Duration.ofDays(7));Cross-reference: For context management fundamentals, see Context.
Context Compaction
The ContextCompactor interface provides strategies for reducing conversation context size when approaching the token limit.
ContextCompactor Interface
public interface ContextCompactor {
CompactionResult compact(List<Map<String, Object>> messages, CompactionConfig config);
boolean shouldCompact(int currentTokens, int maxTokens, CompactionConfig config);
int estimateTokens(List<Map<String, Object>> messages);
}TwoPhaseCompactor
Combines truncation and LLM summarization in two phases for optimal context reduction.
- Phase 1 (
TruncatingCompactor): Truncates large tool-call arguments and tool-result content in older messages. Fast, requires no LLM call. - Phase 2 (
LLMContextCompactor): Summarizes older messages using an LLM. Only runs if phase 1 alone is insufficient.
LLMClient summarizer = LLMClientFactory.create("openai", "gpt-4o-mini");
TwoPhaseCompactor compactor = new TwoPhaseCompactor(
new TruncatingCompactor(200), // max 200 chars per truncated field
new LLMContextCompactor(summarizer)
);
CompactionConfig config = CompactionConfig.builder()
.thresholdRatio(0.85) // trigger compaction at 85% capacity
.preserveLastN(5) // keep last 5 messages intact
.build();
if (compactor.shouldCompact(currentTokens, maxTokens, config)) {
CompactionResult result = compactor.compact(messages, config);
String summary = result.summary();
int savedTokens = result.originalTokenCount() - result.compactedTokenCount();
}Structured Output Validation
The StructuredOutputExecutor ensures LLM outputs conform to an expected schema with automatic retry on validation failure.
StructuredOutputExecutor<OrderSummary> executor = StructuredOutputExecutor.<OrderSummary>builder()
.llm(client)
.targetType(OrderSummary.class)
.outputFormat(OutputFormat.JSON)
.rules(List.of(
ValidationRule.notNull("orderId"),
ValidationRule.range("total", 0, 100000),
ValidationRule.pattern("email", "^[\\w.-]+@[\\w.-]+$")
))
.maxRetries(3)
.systemPrompt("You are an order processing assistant.")
.build();
StructuredOutputExecutor.StructuredOutputResult<OrderSummary> result =
executor.generate("Summarize this order: ...");
if (result.success()) {
OrderSummary order = result.value();
System.out.println("Parsed in " + result.attempts() + " attempts");
} else {
System.out.println("Failed after " + result.attempts() + " attempts");
System.out.println("Errors: " + result.errors());
}ValidationRule
Built-in validation rules for common checks:
| Factory Method | Behavior |
|---|---|
ValidationRule.notNull(fieldName) | Field must exist and be non-null |
ValidationRule.range(fieldName, min, max) | Numeric field must be within range |
ValidationRule.pattern(fieldName, regex) | String field must match regex |
ValidationRule.custom(description, predicate) | Custom predicate on the entire data map |
On validation failure, the executor sends a correction prompt containing the specific errors and re-requests a valid response, up to maxRetries times.
NormEngine
The NormEngine evaluates and enforces behavioral norms (obligations, prohibitions, permissions) on agent actions at runtime. Norms can be defined via @Norm annotations on role classes or programmatically.
Annotation-Based Norms
@Norms({
@Norm(type = NormType.OBLIGATION, action = "logActivity",
description = "Must log all activities", priority = 10),
@Norm(type = NormType.PROHIBITION, action = "shareData",
condition = "isConfidential",
description = "Must not share confidential data", priority = 20),
@Norm(type = NormType.PERMISSION, action = "readPublicData",
description = "May read public data")
})
public class AnalystRole { }
NormEngine engine = NormEngine.fromAnnotations(AnalystRole.class);Programmatic Norms
NormEngine engine = NormEngine.of(
new NormEntry(NormType.PROHIBITION, "isConfidential", "shareData",
"Must not share confidential data", 20),
new NormEntry(NormType.OBLIGATION, "", "logActivity",
"Must log all activities", 10)
);
// Add norms dynamically at runtime
engine.addNorm(new NormEntry(NormType.PERMISSION, "", "readData", "May read data", 5));Checking Actions
// Check if an action is permitted (evaluates prohibitions)
NormEngine.CheckResult result = engine.checkAction("shareData",
condition -> condition.equals("isConfidential") && context.isConfidential());
if (result.isViolation()) {
for (NormViolation v : result.violations()) {
System.out.println("Violated: " + v.norm().description());
}
}
// Check for unfulfilled obligations
NormEngine.CheckResult obligations = engine.checkObligations(
Set.of("readData"), // actions already performed
condition -> true // condition evaluator
);
// Query active norms for current context
List<NormEntry> activeObligations = engine.getActiveObligations(cond -> true);
List<NormEntry> activeProhibitions = engine.getActiveProhibitions(cond -> true);
List<NormEntry> activePermissions = engine.getActivePermissions(cond -> true);
List<NormEntry> allNorms = engine.getAllNorms();NormEntry Record
Fields: type() (NormType), condition(), action(), description(), priority(). The hasCondition() method returns true if the norm has a non-blank condition expression.
RefinementLoop
The RefinementLoop iteratively refines LLM outputs until they meet predefined quality standards. Inspired by Claude's Ralph plugin, it repeatedly evaluates output against completion criteria and re-prompts for corrections.
RefinementLoop loop = RefinementLoop.builder()
.task("Convert Python to TypeScript")
.completionCriteria(CompletionCriteria.builder()
.compilerCheck("tsc --noEmit")
.testCommand("npm test")
.mustNotContain("def ", "import ")
.mustContain("interface", "type")
.customCheck("no-any", code -> !code.contains(": any"))
.build())
.maxIterations(10)
.timeout(Duration.ofMinutes(30))
.stopHook(StopHook.onAllTestsPass())
.onIteration(iter -> log.info("Iteration {} score: {}",
iter.iterationNumber(), iter.evaluation().overallScore()))
.build();
RefinementResult result = loop.execute(agent, pythonCode); // or loop.execute(llmClient, input)
System.out.println("Final output: " + result.getFinalOutput());
System.out.println("Iterations: " + result.getIterationCount());
System.out.println("Status: " + result.getStatus()); // SUCCESS, TIMEOUT, MAX_ITERATIONS, STOPPED, ERROR
System.out.println("Duration: " + result.getDuration());
System.out.println("Metrics: " + result.getMetrics()); // totalIterations, totalDurationMs, totalTokens, scoreProgression, finalScoreCompletionCriteria
Defines when the refinement loop should stop. Combines multiple check types:
CompletionCriteria criteria = CompletionCriteria.builder()
.withLLM(llmClient) // required for llmCheck
.compilerCheck("tsc --noEmit") // shell command (exit 0 = pass)
.testCommand("npm test") // shell command
.lintCheck("eslint .") // shell command
.mustContain("interface", "type") // required content
.mustNotContain("def ", "import ") // forbidden content
.matchesPattern("export default .*") // regex match
.wordCount(50, 5000) // word count range
.minLines(10) // minimum line count
.validJson() // valid JSON check
.customCheck("no-any", code -> !code.contains(": any"))
.llmCheck("Is this idiomatic TypeScript?", 0.8) // LLM-based quality check
.build();
EvaluationResult eval = criteria.evaluate(output);
boolean done = eval.allCriteriaMet();
double score = eval.overallScore(); // 0.0 - 1.0
List<CheckResult> passed = eval.passed();
List<CheckResult> failed = eval.failed();
List<String> reasons = eval.getFailureReasons();RefinementStatus
Possible terminal states: SUCCESS (all criteria met), TIMEOUT, MAX_ITERATIONS, STOPPED (stop hook triggered), ERROR.
The refinement prompt includes failed checks as issues to fix and passed checks as elements to preserve, guiding the LLM toward incremental improvement.
Learning and Refinement
Feedback-driven learning, normative constraint enforcement, iterative refinement loops, prompt optimization, and structured output validation. These components enable agents to improve over time and produce higher-quality outputs.
RAG
Retrieval-Augmented Generation — from knowledge base setup to production pipelines.