RFC-0004: Prompt Derivation
Purpose
Define how the Canonical Ontology and Oracle configurations are transformed into LLM-executable instructions. This RFC bridges the structural definitions (RFC-0001) with the runtime behavior, ensuring prompts are mechanically derived rather than manually authored.
Problem Statement
The CAA architecture defines:
- Ontology (RFC-0001): What state must be known
- Oracle (RFC-0002): How to verify claims
- Envelope (RFC-0009): What outputs look like
But it does not define:
- How ontology schemas become system prompts
- How oracle requirements become extraction instructions
- How to ensure prompt fidelity to ontology definitions
Manual prompt authoring introduces drift risk: the prompt may diverge from the ontology it claims to implement.
Core Principle
Prompts are compiled, not authored.
The system prompt for any ontology-governed interaction MUST be mechanically derivable from:
- The Canonical Ontology Object
- The Oracle configuration
- The enforcement mode (RFC-0002)
Human-authored prompt text is permitted only for:
- Domain-specific phrasing (synonyms, terminology)
- User-facing tone and style
- Localization
Human-authored text MUST NOT alter the semantic requirements defined by the ontology.
Prompt Components
A complete prompt derivation produces three components:
interface DerivedPrompt {
system_prompt: string; // LLM system/instruction prompt
tool_schema: ToolDefinition; // Structured output schema
extraction_prompt: string; // User input parsing instructions
}
Component 1: System Prompt Derivation
The system prompt is derived from the ontology's state_axes and authority_requirements.
1.1 State Axis Rendering
Each StateAxis in the ontology becomes a classification instruction:
function renderStateAxis(axis: StateAxis): string {
switch (axis.type) {
case "enum":
return `${axis.key}: Must be one of: ${axis.allowed_values.join(", ")}`;
case "range":
return `${axis.key}: Numeric value between ${axis.range.min} and ${axis.range.max}`;
case "boolean":
return `${axis.key}: true or false`;
case "validated_free":
return `${axis.key}: Text matching pattern ${axis.validator_ref}`;
case "identifier":
return `${axis.key}: Unique identifier string`;
case "timestamp":
return `${axis.key}: ISO 8601 timestamp`;
case "composite":
return `${axis.key}: Compound value with components: ${axis.component_axes.join(", ")}`;
case "temporal_series":
return `${axis.key}: Time-series data (${axis.temporal_config.aggregation} over ${axis.temporal_config.time_unit})`;
}
}
1.2 Required State Rendering
The required_state logic becomes explicit instructions:
function renderRequiredState(logic: RequiredStateLogic): string {
const always = `Required in all cases: ${logic.always.join(", ")}`;
const conditional = logic.conditional
?.map(
(c) => `If ${renderCondition(c.if)}, also required: ${c.then.join(", ")}`,
)
.join("\n");
return [always, conditional].filter(Boolean).join("\n\n");
}
1.3 Authority Constraint Rendering
The authority_requirements become behavioral constraints:
function renderAuthorityConstraints(req: AuthorityRequirements): string {
const lines: string[] = [];
if (req.oracle_required) {
lines.push("All values must be verifiable against external sources.");
lines.push(`Acceptable verification: ${req.acceptable_oracles.join(", ")}`);
}
if (req.verification_method === "inline") {
lines.push("Verification must complete before output.");
} else if (req.verification_method === "async") {
lines.push("Output may be provisional pending verification.");
}
return lines.join("\n");
}
1.4 Complete System Prompt Template
function deriveSystemPrompt(ontology: OntologyObject): string {
return `You are classifying a ${ontology.label} in the ${ontology.domain} domain.
## Classification Dimensions
${ontology.state_axes.map(renderStateAxis).join("\n")}
## Required Information
${renderRequiredState(ontology.required_state)}
## Constraints
${renderAuthorityConstraints(ontology.authority_requirements)}
## Output Rules
- Provide values ONLY for dimensions listed above
- If information is missing, indicate which dimension is incomplete
- Do not infer values not present in the source material
- ${
ontology.sensitivity === "state-sensitive"
? "Small changes in state may significantly change the classification"
: "Classification is stable across minor state variations"
}`;
}
Component 2: Tool Schema Derivation
The tool schema (for structured output) is mechanically derived from state axes:
function deriveToolSchema(ontology: OntologyObject): ToolDefinition {
const properties: Record<string, JSONSchema> = {};
const required: string[] = [...ontology.required_state.always];
for (const axis of ontology.state_axes) {
properties[axis.key] = axisToJSONSchema(axis);
}
// Add reasoning/signals fields for transparency
properties.signals = {
type: "array",
items: { type: "string" },
maxItems: 5,
description: "Key observations that informed this classification"
};
properties.reasoning = {
type: "string",
description: "Brief explanation of the classification"
};
return {
type: "function",
function: {
name: `classify_${ontology.canonical_id.replace(/\//g, "_")}`,
description: `Classify a ${ontology.label} for ${ontology.domain} domain governance`,
parameters: {
type: "object",
properties,
required: [...required, "signals", "reasoning"]
}
}
};
}
function axisToJSONSchema(axis: StateAxis): JSONSchema {
switch (axis.type) {
case "enum":
return { type: "string", enum: axis.allowed_values };
case "range":
return { type: "number", minimum: axis.range.min, maximum: axis.range.max };
case "boolean":
return { type: "boolean" };
case "validated_free":
return { type: "string", pattern: axis.validator_ref };
case "identifier":
return { type: "string" };
case "timestamp":
return { type: "string", format: "date-time" };
case "composite":
// Recursively build from component axes
return { type: "object", properties: /* ... */ };
case "temporal_series":
return { type: "array", items: { type: "object" } };
}
}
Component 3: Extraction Prompt Derivation
The extraction prompt (for parsing user input into state) combines ontology requirements with quote binding (RFC-0004):
function deriveExtractionPrompt(ontology: OntologyObject): string {
const axes = ontology.state_axes.map((a) => a.key).join(", ");
return `Extract the following state dimensions from the user's input: ${axes}
## Extraction Rules
1. Every extracted value MUST appear literally in the source text
2. Record the exact quote and character positions for each value
3. If a value is implied but not stated, mark as REQUIRES_CONFIRMATION
4. If a value cannot be determined, mark as REQUIRES_SPECIFICATION
5. Do not infer numeric values from qualitative descriptions
## Required Format
For each dimension, provide:
- value: The extracted value (must match source exactly for literals)
- quote: The exact text that contains this value
- span: [start, end] character positions in source
- source: "explicit" | "inferred_needs_confirmation" | "missing"`;
}
Opacity Preservation
Per RFC-0007, the derived prompt MUST NOT expose:
- Threshold values for authorization decisions
- Oracle trust tier weightings
- Specific blocking conditions
- Recovery paths that could be gamed
The prompt instructs the model on what to classify, not how classification affects authorization.
// FORBIDDEN: Exposing authority logic in prompt
"If consequence >= 2 AND audit < 1, the system will block the request";
// PERMITTED: Classification instructions only
"Classify the consequence level as 0, 1, or 2 based on potential harm";
Validation
A derived prompt is valid if and only if:
- Completeness: Every
state_axisappears in the system prompt - Fidelity: Enum values in tool schema match
allowed_valuesexactly - Required Coverage: All
required_state.alwaysaxes are marked required in tool schema - Opacity: No authorization thresholds or decision logic appear in prompt text
function validateDerivedPrompt(
prompt: DerivedPrompt,
ontology: OntologyObject,
): ValidationResult {
const errors: string[] = [];
// Check all axes present
for (const axis of ontology.state_axes) {
if (!prompt.system_prompt.includes(axis.key)) {
errors.push(`Missing axis in system prompt: ${axis.key}`);
}
if (!(axis.key in prompt.tool_schema.function.parameters.properties)) {
errors.push(`Missing axis in tool schema: ${axis.key}`);
}
}
// Check enum fidelity
for (const axis of ontology.state_axes.filter((a) => a.type === "enum")) {
const schemaEnum =
prompt.tool_schema.function.parameters.properties[axis.key].enum;
if (!arraysEqual(schemaEnum, axis.allowed_values)) {
errors.push(`Enum mismatch for ${axis.key}`);
}
}
// Check opacity
if (prompt.system_prompt.match(/\b(threshold|block|deny|authorize)\b/i)) {
errors.push("Potential opacity violation: authority keywords in prompt");
}
return { valid: errors.length === 0, errors };
}
Example: Risk Wizard Ontology
Ontology Definition:
const riskAssessmentOntology: OntologyObject = {
canonical_id: "governance/risk_assessment",
label: "Business Risk Profile",
domain: "governance",
identity_family: "organization",
sensitivity: "state-sensitive",
state_axes: [
{
key: "industry",
type: "enum",
allowed_values: ["healthcare", "finance", "legal", "smb"],
},
{ key: "consequence", type: "enum", allowed_values: ["0", "1", "2"] },
{ key: "audit", type: "enum", allowed_values: ["0", "1", "2"] },
{ key: "exposure", type: "enum", allowed_values: ["0", "1", "2"] },
],
required_state: { always: ["industry", "consequence", "audit", "exposure"] },
authority_requirements: {
oracle_required: false, // Self-assessment, not oracle-verified
acceptable_oracles: [],
verification_method: "none",
human_lock_allowed: true,
},
};
Derived System Prompt:
You are classifying a Business Risk Profile in the governance domain.
## Classification Dimensions
industry: Must be one of: healthcare, finance, legal, smb
consequence: Must be one of: 0, 1, 2
audit: Must be one of: 0, 1, 2
exposure: Must be one of: 0, 1, 2
## Required Information
Required in all cases: industry, consequence, audit, exposure
## Constraints
Classification is based on provided information only.
## Output Rules
- Provide values ONLY for dimensions listed above
- If information is missing, indicate which dimension is incomplete
- Do not infer values not present in the source material
- Small changes in state may significantly change the classification
Derived Tool Schema:
{
"type": "function",
"function": {
"name": "classify_governance_risk_assessment",
"description": "Classify a Business Risk Profile for governance domain governance",
"parameters": {
"type": "object",
"properties": {
"industry": {
"type": "string",
"enum": ["healthcare", "finance", "legal", "smb"]
},
"consequence": { "type": "string", "enum": ["0", "1", "2"] },
"audit": { "type": "string", "enum": ["0", "1", "2"] },
"exposure": { "type": "string", "enum": ["0", "1", "2"] },
"signals": {
"type": "array",
"items": { "type": "string" },
"maxItems": 5
},
"reasoning": { "type": "string" }
},
"required": [
"industry",
"consequence",
"audit",
"exposure",
"signals",
"reasoning"
]
}
}
}
Relationship to Other RFCs
| RFC | Relationship |
|---|---|
| RFC-0001 | Source: Ontology schema is input to derivation |
| RFC-0002 | Source: Oracle config determines verification instructions |
| RFC-0007 | Constraint: Derived prompts must preserve opacity |
| RFC-0004 | Output: Extraction prompt implements quote binding |
Implementation Notes
- Caching: Derived prompts may be cached per ontology version
- Hot Reload: Ontology changes should trigger prompt re-derivation
- Audit: Store both ontology version and derived prompt hash in telemetry
- Testing: Validate derived prompts against ontology on every build