eractions with narrative elements to refine the contract and improve model alignment over time.
Code Examples
1. Narrative Configuration Schema
Define a strict configuration interface to ensure consistency across the product.
// src/config/narrative-config.ts
export interface NarrativeConfig {
persona: {
role: string;
tone: 'authoritative' | 'collaborative' | 'inquisitive';
limitations: string[];
};
transparency: {
level: 'low' | 'medium' | 'high';
showConfidence: boolean;
showReasoning: boolean;
};
fallbacks: {
lowConfidence: string;
error: string;
refusal: string;
};
ui: {
streamingEnabled: boolean;
progressiveDisclosure: boolean;
};
}
export const defaultNarrativeConfig: NarrativeConfig = {
persona: {
role: 'AI Assistant',
tone: 'collaborative',
limitations: [
'May produce inaccurate information for niche topics.',
'Does not have access to real-time data beyond training cutoff.',
'Cannot perform actions outside the provided tools.'
]
},
transparency: {
level: 'medium',
showConfidence: true,
showReasoning: true
},
fallbacks: {
lowConfidence: 'I found potential matches, but I am not fully confident. Please verify the details.',
error: 'I encountered an issue processing your request. Please try rephrasing or check your input.',
refusal: 'I cannot fulfill this request due to safety guidelines. Here are alternative ways to achieve your goal...'
},
ui: {
streamingEnabled: true,
progressiveDisclosure: true
}
};
2. Narrative Middleware
Middleware enriches responses with narrative metadata and applies fallback logic.
// src/middleware/narrative-enricher.ts
import { NarrativeConfig } from '../config/narrative-config';
export interface EnrichedResponse {
content: string;
metadata: {
confidence: number;
reasoning?: string;
narrativeType: 'success' | 'low-confidence' | 'error' | 'refusal';
};
}
export class NarrativeEnricher {
constructor(private config: NarrativeConfig) {}
async enrich(
rawResponse: string,
modelMetadata: { confidence: number; reasoning?: string },
context: Record<string, any>
): Promise<EnrichedResponse> {
const { confidence, reasoning } = modelMetadata;
let content = rawResponse;
let narrativeType: EnrichedResponse['metadata']['narrativeType'] = 'success';
// Apply narrative logic based on confidence and config
if (confidence < 0.6) {
content = `${this.config.fallbacks.lowConfidence} ${content}`;
narrativeType = 'low-confidence';
}
if (this.config.transparency.showReasoning && reasoning) {
// Inject reasoning into content or attach to metadata for UI rendering
// Depending on transparency level
if (this.config.transparency.level === 'high') {
content = `**Reasoning:** ${reasoning}\n\n${content}`;
}
}
return {
content,
metadata: {
confidence,
reasoning: this.config.transparency.level === 'high' ? undefined : reasoning,
narrativeType
}
};
}
}
3. React Component for Narrative Rendering
A component that renders the response with appropriate UI cues based on narrative metadata.
// src/components/AIResponseRenderer.tsx
import React from 'react';
import { EnrichedResponse } from '../middleware/narrative-enricher';
import { ConfidenceBadge } from './ConfidenceBadge';
import { ReasoningDrawer } from './ReasoningDrawer';
interface Props {
response: EnrichedResponse;
}
export const AIResponseRenderer: React.FC<Props> = ({ response }) => {
const { content, metadata } = response;
return (
<div className="ai-response-container">
<div className="response-header">
<span className="ai-label">AI Generated</span>
{metadata.narrativeType === 'low-confidence' && (
<span className="warning-badge">Verify Output</span>
)}
</div>
{metadata.confidence !== undefined && (
<ConfidenceBadge score={metadata.confidence} />
)}
{metadata.reasoning && <ReasoningDrawer reasoning={metadata.reasoning} />}
<div className="response-content">
<p>{content}</p>
</div>
<div className="response-footer">
<button>Copy</button>
<button>Regenerate</button>
<button>Feedback</button>
</div>
</div>
);
};
Architecture Decisions
- Decoupled Narrative Config: Narrative settings are externalized from the model invocation logic. This allows product teams to adjust tone, transparency, and fallbacks without code deployments or model retraining.
- Metadata-Rich Responses: The middleware attaches structured metadata to responses. This enables the UI to render dynamic components (confidence badges, reasoning drawers) rather than relying on static text parsing.
- Progressive Disclosure: For high-complexity tasks, reasoning is hidden by default and revealed on demand. This reduces cognitive load while maintaining access to explainability for power users.
- Streaming Support: Narrative elements are designed to stream alongside model output. Confidence indicators may update dynamically as the response completes, providing real-time feedback.
Pitfall Guide
Common Mistakes
-
Anthropomorphization Overload:
- Mistake: Using language that implies human consciousness or emotions (e.g., "I feel," "I think," "I'm sorry").
- Impact: Users develop false mental models, leading to misplaced trust and emotional manipulation concerns.
- Fix: Use functional language. "Based on the data, the recommendation is..." instead of "I believe..."
-
Static Storytelling:
- Mistake: Applying the same narrative tone and transparency level to all users and contexts.
- Impact: Novice users are overwhelmed by technical details; experts are frustrated by excessive hand-holding.
- Fix: Implement adaptive narratives based on user role, history, and interaction patterns.
-
Ignoring the "Negative Story":
- Mistake: Focusing only on success paths and providing generic errors for failures.
- Impact: Users abandon the product when things go wrong because they don't know how to recover.
- Fix: Design narrative fallbacks that explain the error, suggest actionable fixes, and maintain trust.
-
Confidence Hallucination:
- Mistake: Displaying confidence scores that do not accurately reflect model uncertainty.
- Impact: Erodes trust when the model is wrong but displays high confidence.
- Fix: Calibrate confidence metrics using ensemble methods or calibration layers. Never fabricate confidence scores.
-
Narrative-Model Disconnect:
- Mistake: UI copy promises capabilities the model cannot deliver.
- Impact: Immediate user frustration and support spikes.
- Fix: Align narrative config with model capabilities. Use tool-use constraints to enforce boundaries.
-
Performance Degradation:
- Mistake: Generating excessive narrative text or reasoning traces that increase latency and token costs.
- Impact: Higher costs and slower response times.
- Fix: Optimize narrative generation. Use lightweight models for reasoning summaries or generate them asynchronously.
-
Lack of Feedback Integration:
- Mistake: Treating storytelling as a one-way broadcast without capturing user reactions.
- Impact: Missed opportunities to refine narrative effectiveness.
- Fix: Implement feedback mechanisms (thumbs up/down, "Was this helpful?") linked to narrative variants.
Best Practices
- Probabilistic Language: Train the narrative to use calibrated uncertainty markers ("likely," "possible," "based on current data").
- Actionable Explanations: Explanations should guide user action, not just describe model internals.
- A/B Test Narratives: Treat narrative variants like feature flags. Test different tones, transparency levels, and fallbacks to optimize for engagement and trust.
- Consistency Across Channels: Ensure the narrative is consistent across UI, documentation, and error messages.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High-Risk Domain (Finance/Health) | High Transparency, Rigorous Fallbacks | Compliance, safety, and liability require explicit explainability and error handling. | High (More tokens, complex UI) |
| Creative Tool (Writing/Design) | Low Transparency, Inspiring Tone | Users prioritize flow and creativity over technical details. | Low |
| Internal Dev Tool | Medium Transparency, Technical Tone | Developers need context and reasoning but prefer concise, technical language. | Medium |
| Consumer Chatbot | Adaptive Narrative, Progressive Disclosure | Balances engagement with safety; adapts to user skill level dynamically. | Medium-High |
Configuration Template
Copy this template to initialize narrative configuration for a new AI feature.
# ai-story-config.yaml
feature_id: "code-assistant-v1"
persona:
role: "Senior Code Reviewer"
tone: "professional"
style: "concise"
limitations:
- "Cannot execute code."
- "May miss context from files outside the current workspace."
- "Suggestions should be reviewed by a human."
transparency:
level: "medium"
show_confidence: true
show_reasoning: true
reasoning_depth: "summary" # summary | detailed
fallbacks:
low_confidence: |
"I'm not entirely sure about this suggestion. Please verify the logic against your codebase."
error: |
"I ran into an issue analyzing the code. Check the syntax or try a smaller snippet."
refusal: |
"I cannot review code that contains sensitive credentials. Please redact secrets before submitting."
ui:
streaming: true
progressive_disclosure: true
feedback_enabled: true
Quick Start Guide
-
Initialize Configuration:
Create ai-story-config.yaml in your project root using the template above. Adjust persona, transparency, and fallbacks to match your feature requirements.
-
Install Narrative Middleware:
If using a custom implementation, add the NarrativeEnricher class to your backend service. If using a framework, install the narrative plugin:
npm install @codcompass/ai-story-middleware
-
Wrap Model Invocation:
Integrate the middleware into your AI pipeline. Pass the raw response and model metadata to the enricher before sending data to the client.
const enriched = await enricher.enrich(rawText, metadata, config);
res.json(enriched);
-
Render Narrative UI:
Use the AIResponseRenderer component in your frontend. Pass the enriched response to dynamically render confidence, reasoning, and fallback narratives.
<AIResponseRenderer response={apiResponse} />
-
Validate and Iterate:
Test the feature with representative users. Monitor narrative metrics and adjust the configuration to optimize trust and adoption. Deploy narrative changes via config updates without redeploying code.