Back to Blog
Jan 5, 20264 min readOnuzulike Anthony

Grounding LLM Responses in Application State

The CognitiveBridge pattern: injecting arousal, conflict, surprise, and memory state into system prompts so LLM responses are shaped by what the application knows.

AI/MLLLMSystem PromptsCognitive ArchitectureAI/ML

By default, LLMs respond based purely on the conversation history and their training. They don't know whether your application is under load, whether the user seems confused, or what the system's memory banks contain. The CognitiveBridge pattern bridges this gap — it extracts application state and injects it into every system prompt as structured context.

The Problem

Suppose you're building a conversational interface over a knowledge base. The LLM can answer questions, but it doesn't know:

  • Whether it just retrieved weak or strong evidence
  • Whether the current query is semantically similar to recent ones (low surprise) or completely novel (high surprise)
  • Whether internal signals suggest the user is confused or on track
  • What facts have been explicitly taught to the system

All of this information exists in your application — but without explicit injection, the LLM is blind to it.

CognitiveBridge Architecture

The bridge sits between the user message and the LLM call:

python
class CognitiveBridge:
    def __init__(self, model, mechanisms, memories, encoder, llm, base_system_prompt):
        self.model = model          # cognitive neural network
        self.memories = memories    # dict of memory blocks
        self.encoder = encoder      # text → vector
        self.llm = llm              # LLM client
        self.base_prompt = base_system_prompt
        self._history = []
        self._knowledge = []        # durable facts
 
    def turn(self, user_message: str) -> str:
        # 1. Encode
        embedding = self.encoder.encode(user_message)
 
        # 2. Run cognitive model
        output = self.model.forward(embedding)
 
        # 3. Extract state
        state = StateExtractor.extract(self.model, self.memories)
 
        # 4. Update neuromodulators from state
        self._update_neuromodulators(state)
 
        # 5. Build enriched system prompt
        system = self._build_system_prompt(state)
 
        # 6. Call LLM
        self._history.append({"role": "user", "content": user_message})
        response = self.llm.send(system, self._history)
        self._history.append({"role": "assistant", "content": response})
 
        return response

Extracting Cognitive State

python
@dataclass
class CognitiveState:
    arousal: float       # 0-1: how activated/engaged the system is
    conflict: float      # 0-1: disagreement between internal signals
    surprise: float      # 0-1: how unexpected the current input is
    uncertainty: float   # 0-1: confidence in the retrieved evidence
    memory_load: float   # 0-1: how full working memory is
 
class StateExtractor:
    @staticmethod
    def extract(model, memories) -> CognitiveState:
        # Arousal: mean activation magnitude across blocks
        activations = [block.out for block in model.blocks if hasattr(block, 'out')]
        arousal = float(np.mean([np.abs(a).mean() for a in activations if a is not None]))
 
        # Conflict: variance across parallel processing streams
        if len(activations) > 1:
            stacked = np.stack([a.mean(0) for a in activations], axis=0)
            conflict = float(np.var(stacked, axis=0).mean())
        else:
            conflict = 0.0
 
        # Surprise: distance from working memory baseline
        wm = memories.get("working")
        if wm is not None:
            baseline = wm.buffer.mean(0)
            current = activations[-1].mean(0) if activations else baseline
            surprise = float(np.mean((current - baseline) ** 2))
        else:
            surprise = 0.0
 
        return CognitiveState(
            arousal=np.clip(arousal, 0, 1),
            conflict=np.clip(conflict, 0, 1),
            surprise=np.clip(surprise, 0, 1),
            uncertainty=1.0 - np.clip(arousal, 0, 1),
            memory_load=wm.ptr / wm.capacity if wm else 0.0,
        )

Building the Enriched System Prompt

python
def _build_system_prompt(self, state: CognitiveState) -> str:
    directive = StateInterpreter.interpret(state)
 
    knowledge_section = ""
    if self._knowledge:
        facts = "\n".join(f"- {fact}" for fact in self._knowledge)
        knowledge_section = f"\n\n[Known Facts]\n{facts}"
 
    state_section = f"""
[Internal Cognitive State]
- Arousal: {state.arousal:.2f} (system engagement level)
- Conflict: {state.conflict:.2f} (internal signal disagreement)
- Surprise: {state.surprise:.2f} (input novelty)
- Memory load: {state.memory_load:.0%} capacity used
- Directive: {directive}"""
 
    return self.base_prompt + knowledge_section + state_section

StateInterpreter: Translating Numbers to Language

python
class StateInterpreter:
    @staticmethod
    def interpret(state: CognitiveState) -> str:
        parts = []
 
        if state.arousal > 0.7:
            parts.append("System is highly engaged — prioritize depth over breadth.")
        elif state.arousal < 0.3:
            parts.append("System is in low-arousal mode — be concise and direct.")
 
        if state.conflict > 0.6:
            parts.append("High internal conflict — acknowledge uncertainty in your response.")
 
        if state.surprise > 0.7:
            parts.append("Novel input detected — do not assume prior context applies.")
 
        if state.memory_load > 0.8:
            parts.append("Memory near capacity — prioritize most recent context.")
 
        return " ".join(parts) if parts else "Normal operating state."

Teaching Durable Facts

The bridge supports injecting facts that persist across every subsequent call:

python
bridge.teach("The user's project uses Next.js 15 App Router")
bridge.teach("Prefer TypeScript over JavaScript in all code examples")
bridge.teach("The database is Neon PostgreSQL with Drizzle ORM")
 
# Every system prompt now includes these facts verbatim
response = bridge.turn("How do I add a new API route?")

This is distinct from retrieval — taught facts are always included, not ranked. Use it for ground truths and preferences that should never be filtered out.

Why This Matters

Without this pattern, the LLM is an oracle consulted in isolation. With it, the LLM becomes an output layer for a stateful application — one that knows its own arousal, uncertainty, and memory state, and can communicate that to the model through the system prompt.

The practical result: responses that hedge appropriately when internal confidence is low, that stay focused when arousal is high, and that leverage explicitly taught knowledge without requiring retrieval.