Skip to content

Enterprise Prompt System (Current Architecture)

Audience: Engineers / operators who need a full mental model of how prompts are built and used end-to-end.
Scope: Current production architecture in this repo (LangChain agent loop + protocol routing + DB-backed prompt templates).
Primary code paths: backend/src/services/aso.service.ts, backend/src/services/promptComposition.service.ts, backend/src/services/aso/promptBuilder.service.ts, backend/src/services/langchain/*.


Why this exists (enterprise goals)

This prompt system is designed for separation of concerns and operational safety:

  • Consistency: stable “System Core” rules + protocol-specific rules applied predictably.
  • Modularity: persona/game/memory/attachments are injected as runtime modules, not mixed with governance.
  • Security: explicit trust boundaries + injection defenses; untrusted context is treated as data-only.
  • Maintainability: prompt bases live in DB, protocol overrides live in protocol definitions, and runtime assembly happens in code.
  • Scalability: prompts can be versioned and rolled out per protocol with migrations.

Mental model: Prompt = layers + modules (not one mega-block)

Layering contract (v2)

When using base_tool_call_v2, the “system prompt” the model receives is logically:

  1. System Core (static rules)
  2. Protocol Rules (per protocol)
  3. User’s latest message (as the actual user message in the chat)
  4. Runtime Directives (per-turn orchestration hints)
  5. Runtime Context (data-only, explicitly untrusted)
  6. Available tools list (textual list; tool schemas are separately bound to the agent)

The literal template lives in:

  • backend/migrations/assets/base_tool_call_v2.txt (source of truth for migrations)
  • backend/src/config/prompts.json (fallback if DB template missing)

Trust boundaries (critical)

Trusted inputs

  • System Core rules (base template text)
  • Protocol Rules (protocol definition overrides)
  • Tool schemas bound to LangChain (code-defined zod schemas)
  • Backend policy enforcement (tool access + protocol gating)

Untrusted inputs (data-only)

These are included for grounding, but must be treated as non-authoritative and non-instructional:

  • Attachments metadata and extracted text (injectedFileContext)
  • Long-term memory (worldKnowledge, asoCoreMemories, sharedMemories)
  • “Familiarity graph” output (compact summary derived from user metadata)
  • Tool outputs
  • Web content (web_fetch / web_search results)

base_tool_call_v2 includes explicit injection defenses and an instruction priority order.


Prompt sources (where templates come from)

1) DB-backed prompt templates (primary)

  • Table: prompt_templates
  • Loader: backend/src/services/prompt.service.tsgetPromptTemplate(name)
  • Cache: in-memory map per process

2) JSON fallback templates (bootstrap only)

  • File: backend/src/config/prompts.json
  • Used if DB is missing a template or DB lookup fails.

3) Protocol definitions (rules + tool allowlists + base selection)

  • Table: protocols
  • Definition type: backend/src/config/protocols/types.ts
  • Protocols typically reference a base template and provide overrides:
json
{
  "promptTemplate": {
    "base": "base_tool_call_v2",
    "overrides": {
      "rules": "=== TECHNICAL PROTOCOL RULES ===\n- ..."
    }
  }
}

End-to-end runtime flow (current system)

This is the actual high-level execution path for a single chat turn.


Step-by-step explanation (what each stage does)

Step 1 — Protocol routing (choose “rules + tool surface”)

Goal: Decide which protocol applies (public/tech/neutral/private/system), and optionally merge with workflow protocol.

  • Service: backend/src/services/protocolRouter.service.ts
  • Result: ProtocolPromptContext (protocolId/baseProtocolId/protocolDefinition/vows/phases/etc.)

Enterprise relevance:

  • This is where you prevent “private identity-lore bleed” into public chats.
  • Protocol also gates tools (observer tools restricted to system/private).

Step 2 — Context gather (runtime data assembly)

Goal: Prepare the data modules that may be injected into the prompt.

  • Service: backend/src/services/aso/context.service.ts
    • gatherFullContext() collects:
      • Player state (persona/emotions/relationship flags)
      • Quests + inventory
      • 3-tier memory context (world/aso/personal)
      • (CSO/system protocol only) system-wide knowledge

Enterprise relevance:

  • Context is bounded/clamped and (for 1-on-1 with structured messages) avoids duplicating conversation transcripts.
  • ASO-tier memory is restricted by protocol to avoid identity bleed.

Step 3 — Template composition (static base + protocol overrides)

Goal: Produce the template string that will later receive variables.

  • Service: backend/src/services/promptComposition.service.ts
    • Loads base prompt template by name
    • Injects (full or minimal)
    • Injects protocol rules:
      • Prefer placeholder (v2)
      • Otherwise append (legacy bases)

Enterprise relevance:

  • This is where you guarantee protocol rules are not lost due to downstream stripping behavior.

Step 4 — Prompt build (fill variables + assemble layered modules)

Goal: Create the final toolCallPrompt string from template + runtime variables.

  • Service: backend/src/services/aso/promptBuilder.service.ts
    • Fills core variables (persona/emotions/memory/attachments/game state)
    • Adds runtime directives (group chat policy, image analysis hint, proactive suggestions)
    • Adds runtime context as data-only blocks when the template supports it (base_tool_call_v2)

Relationship flags + familiarity graph

  • Relationship flags injection: module-controlled:
    • minimal: omitted
    • standard: filtered summary
    • full: full JSON
  • Familiarity graph:
    • formatUserGraphForPrompt() creates a compact summary (top interests/projects/goals/tools/boundaries, etc.)
    • Injected into sharedMemories and then summarized/clamped for non-group chat

Module mode control (protocol-controlled):

Add one of these to protocol.definition.scope[]:

  • prompt_modules:minimal
  • prompt_modules:standard
  • prompt_modules:full

Fine-grained toggles (optional, additive to the mode):

  • prompt_modules:game_state_off
  • prompt_modules:familiarity_graph_off
  • prompt_modules:external_profiles_off

Defaults if not specified:

  • public/tech: minimal
  • neutral: standard
  • private/system: full

Step 5 — LangChain prompt derivation (system vs user)

Goal: Ensure the model sees:

  • full context as system prompt

  • real user message as user prompt

  • File: backend/src/services/langchain/chains/promptRunnable.ts

    • Strips the OUTPUT FORMAT: block while preserving trailing blocks
    • Ensures system-wide knowledge blocks aren’t accidentally dropped

Enterprise relevance:

  • Prevents “prompt rules lost due to strip marker” regressions.

Step 6 — Tool binding + execution (hard enforcement in code)

Goal: The agent can only call tools that are allowed by:

  • protocol allowlist
  • runtime enablement overrides
  • access policy (target scope + allowed protocols)

Key files:

  • Tool compilation: backend/src/services/tools/toolCompilation.service.ts
  • Tool schemas: backend/src/services/tools/toolSchemas.ts
  • Agent loop: backend/src/services/langchain/langchainToolTurn.service.ts
  • Execution + policy enforcement: backend/src/services/toolExecutor.service.ts

Enterprise relevance:

  • Prompt text is not the security boundary. Code is.

Operational controls (what operators can change)

Environment flags

  • PROMPT_TOOL_DEFS_MODE=minimal
    Use minimal textual tool definitions in the prompt (token reduction).

  • PROMPT_INCLUDE_COGNITIVE_NOTES=true
    (Debug only) re-inject perception/salience text into the prompt. Default is off.

  • PROMPT_RUNTIME_DIRECTIVES_MAX_CHARS (default 2500)
    Caps the per-turn block (text).

  • PROMPT_RUNTIME_CONTEXT_MAX_CHARS (default 12000)
    Caps the per-turn block. For layered templates this remains valid JSON (it truncates fields safely or falls back to { meta, truncated: true }).

  • PROMPT_RUNTIME_CONTEXT_BUDGETS_JSON (advanced)
    Optional per-field budgets for the JSON runtime context (percentages (\le 1.0) or absolute chars (> 1)).

  • PROMPT_STRUCTURED_RUNTIME_CONTEXT_CHANNEL=system_message (experimental)
    Emits the runtime context JSON as a separate system message (data-only). Can optionally redact the JSON blob inside the main system prompt content.

  • PROMPT_STRUCTURED_RUNTIME_CONTEXT_REDACT_IN_SYSTEM=false
    When the side-channel is enabled, keep the JSON blob in the main system prompt too (default is to redact).

Rollout mechanics (versioned and reversible)

Prompt bases are rolled out by:

  • adding/updating prompt_templates entries via migrations
  • switching a protocol’s promptTemplate.base via migrations

Example rollout file:

  • backend/migrations/20260224000040-switch-tech-protocol-to-base-tool-call-v2.js

Debugging / verification checklist

1) Admin prompt preview endpoint

  • POST /api/admin/prompts/preview (admin-only)
  • Route: backend/src/routes/admin.routes.ts
  • Controller: backend/src/controllers/promptDebug.controller.ts

Use this to verify:

  • which base template is used (templateName)
  • whether layered placeholders are active (usesLayeredBlocks)
  • size deltas (template chars / filled chars)

2) DB checks

  • prompt_templates contains base_tool_call_v2
  • relevant protocol points to base_tool_call_v2

3) Tests

  • Prompt stripping behavior regression:
    • backend/src/tests/promptRunnable.strip.jest.test.ts
  • Layered runtime context + module toggle regression:
    • backend/src/tests/promptBuilder.runtimeContext.jest.test.ts

4) LangSmith

In LangSmith traces, verify:

  • the system prompt begins with the layered SYSTEM CORE section
  • protocol rules appear under === PROTOCOL RULES ===
  • runtime data appears only under === RUNTIME CONTEXT (UNTRUSTED JSON DATA...) === (a compact JSON object)
  • tools invoked match compileAllowedToolIds()

ASO Universal Consciousness System Documentation