Overview
This example demonstrates a sentiment analysis agent that goes beyond simple positive/negative classification. It tracks epistemic confidence, separates factual observations from subjective interpretations, and provides multi-dimensional sentiment scores.
Use Case
Analyze text sentiment to:
Classify overall sentiment (positive, negative, neutral)
Provide quantified sentiment scores (-1.0 to 1.0)
Identify emotional tone and intensity
Separate factual content from opinion
Track confidence in sentiment assessment
Handle ambiguous or mixed sentiment
Complete Code
// AXON Example — Sentiment Analysis
// Epistemic sentiment analysis with confidence tracking
persona SentimentAnalyst {
domain: ["NLP", "psychology", "linguistics"]
tone: analytical
confidence_threshold: 0.75
}
context AnalysisMode {
memory: none
language: "en"
depth: moderate
max_tokens: 2048
temperature: 0.2
}
anchor FactualGrounding {
require: text_evidence
confidence_floor: 0.70
unknown_response: "Unable to determine sentiment with sufficient confidence"
on_violation: raise AnchorBreachError
}
type SentimentScore(-1.0..1.0)
type EmotionalTone {
primary: FactualClaim,
intensity: Float,
confidence: ConfidenceScore
}
type SentimentAnalysis {
overall_score: SentimentScore,
classification: FactualClaim,
emotional_tone: EmotionalTone,
factual_content: List<FactualClaim>,
opinions: List<Opinion>,
ambiguity_score: Float,
confidence: ConfidenceScore
}
flow AnalyzeSentiment(text: Document) -> SentimentAnalysis {
step ExtractContent {
given: text
ask: "Separate factual statements from opinions and emotional language"
output: ContentMap
}
step ClassifySentiment {
given: ContentMap
ask: "Determine overall sentiment and emotional tone"
output: SentimentClassification
}
reason EmotionalAssessment {
given: [ContentMap, SentimentClassification]
about: "emotional tone and intensity"
ask: "What is the primary emotional tone and how intense is it?"
depth: 2
output: EmotionalTone
}
validate EmotionalAssessment against ToneSchema {
if confidence < 0.75 -> refine(max_attempts: 2)
if ambiguous -> warn "Mixed or unclear sentiment detected"
}
weave [
ContentMap,
SentimentClassification,
EmotionalAssessment
] into SentimentAnalysis {
format: StructuredReport
}
}
run AnalyzeSentiment(inputText)
as SentimentAnalyst
within AnalysisMode
constrained_by [FactualGrounding]
on_failure: retry(backoff: linear)
output_to: "sentiment.json"
effort: medium
Key Components
Persona: SentimentAnalyst
persona SentimentAnalyst {
domain: ["NLP", "psychology", "linguistics"]
tone: analytical
confidence_threshold: 0.75
}
Defines an expert in:
Natural Language Processing : Technical understanding of text analysis
Psychology : Understanding of emotional expression
Linguistics : Language structure and meaning
Analytical tone : Objective, systematic approach
Context: AnalysisMode
context AnalysisMode {
memory: none
language: "en"
depth: moderate
max_tokens: 2048
temperature: 0.2
}
Configures for consistent analysis:
Memory : No memory (stateless analysis)
Depth : Moderate analysis (not shallow, not exhaustive)
Temperature : Low (0.2) for consistent classifications
Custom Types
type SentimentScore(-1.0..1.0)
Range-constrained sentiment score:
-1.0 = Very negative
0.0 = Neutral
1.0 = Very positive
type EmotionalTone {
primary: FactualClaim,
intensity: Float,
confidence: ConfidenceScore
}
Captures emotional characteristics:
primary: Primary emotion (joy, anger, sadness, etc.)
intensity: How strong the emotion is
confidence: How confident we are in the assessment
type SentimentAnalysis {
overall_score: SentimentScore,
classification: FactualClaim,
emotional_tone: EmotionalTone,
factual_content: List<FactualClaim>,
opinions: List<Opinion>,
ambiguity_score: Float,
confidence: ConfidenceScore
}
Comprehensive analysis output:
overall_score : Quantified sentiment
classification : Category (positive, negative, neutral)
emotional_tone : Detailed emotional assessment
factual_content : Factual statements found
opinions : Opinion statements found
ambiguity_score : How unclear the sentiment is
confidence : Overall confidence in the analysis
Separating factual_content and opinions prevents conflating objective statements with subjective sentiment.
Flow: AnalyzeSentiment
Three-step cognitive pipeline with validation:
Step 1: ExtractContent
step ExtractContent {
given: text
ask: "Separate factual statements from opinions and emotional language"
output: ContentMap
}
Separates:
Factual statements (objective)
Opinions (subjective)
Emotional language (sentiment-bearing)
Step 2: ClassifySentiment
step ClassifySentiment {
given: ContentMap
ask: "Determine overall sentiment and emotional tone"
output: SentimentClassification
}
Determines:
Overall sentiment polarity
Primary sentiment category
Sentiment score
Step 3: EmotionalAssessment (Reasoning)
reason EmotionalAssessment {
given: [ContentMap, SentimentClassification]
about: "emotional tone and intensity"
ask: "What is the primary emotional tone and how intense is it?"
depth: 2
output: EmotionalTone
}
Uses explicit reasoning to:
Identify primary emotion
Assess intensity
Consider context
Validation
validate EmotionalAssessment against ToneSchema {
if confidence < 0.75 -> refine(max_attempts: 2)
if ambiguous -> warn "Mixed or unclear sentiment detected"
}
Ensures quality:
Refines if confidence too low
Warns on ambiguous sentiment
Synthesis
weave [
ContentMap,
SentimentClassification,
EmotionalAssessment
] into SentimentAnalysis {
format: StructuredReport
}
Combines all analyses into comprehensive output.
Usage
Validate and Run
# Check syntax
axon check sentiment_analysis.axon
# Compile to IR
axon compile sentiment_analysis.axon
# Run with tracing
axon run sentiment_analysis.axon --backend openai --trace
"The new restaurant opened last week, and while the location is convenient
and the decor is modern, the service was disappointingly slow. The food
quality was acceptable but nothing special. I might give them another
chance in a few months."
Example Output
{
"type" : "SentimentAnalysis" ,
"overall_score" : -0.15 ,
"classification" : "mixed (slightly negative)" ,
"emotional_tone" : {
"primary" : "disappointment" ,
"intensity" : 0.6 ,
"confidence" : 0.82
},
"factual_content" : [
"The restaurant opened last week" ,
"The location is convenient" ,
"The decor is modern"
],
"opinions" : [
"Service was disappointingly slow" ,
"Food quality was acceptable but nothing special" ,
"Might give them another chance in a few months"
],
"ambiguity_score" : 0.45 ,
"confidence" : 0.82 ,
"explanation" : "Mixed sentiment with both positive (location, decor) and negative (service, food) elements. Primary negative emotion is disappointment, moderated by openness to trying again."
}
Advanced Patterns
Aspect-Based Sentiment
Analyze sentiment for specific aspects:
type AspectSentiment {
aspect: FactualClaim,
sentiment: SentimentScore,
confidence: ConfidenceScore
}
flow AnalyzeAspects(text: Document) -> List<AspectSentiment> {
step IdentifyAspects {
given: text
ask: "Identify all aspects mentioned (service, quality, price, etc.)"
output: AspectList
}
step ScoreAspects {
given: AspectList
ask: "Rate sentiment for each aspect individually"
output: List<AspectSentiment>
}
}
Comparative Sentiment
Compare sentiment across multiple texts:
flow CompareSentiment(texts: List<Document>) -> ComparisonReport {
step AnalyzeAll {
given: texts
ask: "Analyze sentiment for each text"
output: List<SentimentAnalysis>
}
reason Comparison {
given: AnalyzeAll.output
about: "sentiment trends and differences"
ask: "How do sentiments compare across texts?"
depth: 3
output: ComparisonReport
}
}
Temporal Sentiment Tracking
Track sentiment changes over time:
context TimeSeriesAnalysis {
memory: persistent
language: "en"
depth: moderate
}
flow TrackSentimentTrend(texts: List<Document>) -> TrendAnalysis {
step AnalyzeEach {
given: texts
ask: "Analyze sentiment for each text with timestamp"
output: TimestampedSentiments
}
step IdentifyTrends {
given: AnalyzeEach.output
ask: "Identify patterns and trends in sentiment over time"
output: TrendAnalysis
}
remember(TrendAnalysis) -> SentimentHistory
}
Multi-Language Sentiment
Handle multiple languages:
flow MultilingualSentiment(text: Document, lang: String) -> SentimentAnalysis {
step DetectLanguage {
given: text
ask: "What language is this text?"
output: LanguageCode
}
if LanguageCode != "en" -> step Translate {
given: text
ask: "Translate to English"
output: TranslatedText
}
step AnalyzeSentiment {
given: TranslatedText
ask: "Analyze sentiment"
output: SentimentAnalysis
}
}
Best Practices
1. Separate Facts from Opinions
// ✅ Good: Clear separation
type Analysis {
facts: List<FactualClaim>,
opinions: List<Opinion>
}
// ❌ Bad: Mixed
type Analysis {
content: List<String>
}
2. Use Range-Constrained Scores
// ✅ Good: Guaranteed valid
type SentimentScore(-1.0..1.0)
// ❌ Bad: Unbounded
type SentimentScore(Float)
3. Track Confidence
type SentimentAnalysis {
score: SentimentScore,
confidence: ConfidenceScore // Always include
}
4. Handle Ambiguity
validate Result against Schema {
if confidence < threshold -> refine(max_attempts: 2)
if ambiguous -> warn "Mixed sentiment"
}
5. Use Low Temperature
context AnalysisMode {
temperature: 0.2 // Consistent classifications
}
Contract Analyzer Legal contract analysis with risk assessment
Data Extraction Extract structured data from unstructured text
Multi-Step Reasoning Complex reasoning with chain-of-thought