Skip to main content

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

sentiment_analysis.axon
// 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

Example Input

"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
  • Types — Epistemic type system
  • Flow — Cognitive pipelines
  • Persona — Agent identities
  • Anchor — Hard constraints

Build docs developers (and LLMs) love