- name
- strategic-communication-analysis
- description
- Narrative framing, media analysis, discourse analysis, influence assessment, information warfare detection for Swedish political intelligence
- license
- Apache-2.0
# Strategic Communication Analysis Skill
## 🔴 AI FIRST Quality Principle
> **Apply the AI FIRST principle: never accept first-pass quality. Minimum 2 iterations. Read all output, improve every section. No shortcuts.**
## Purpose
This skill provides comprehensive methodologies for analyzing strategic political communication in Swedish parliament and media. It enables intelligence operatives to identify narrative framing techniques, detect coordinated messaging campaigns, assess media bias, analyze parliamentary discourse patterns, and identify information warfare tactics through evidence-based analysis of speeches, documents, and media coverage.
## When to Use This Skill
Apply this skill when:
- ✅ Analyzing parliamentary debate rhetoric and framing strategies
- ✅ Detecting coordinated messaging campaigns across party members
- ✅ Identifying media bias in political coverage
- ✅ Assessing influence operations and disinformation patterns
- ✅ Analyzing discourse shifts over time (framing evolution)
- ✅ Evaluating communication effectiveness of political figures
- ✅ Detecting astroturfing and inauthentic amplification
- ✅ Mapping information ecosystems and echo chambers
- ✅ Identifying counter-narrative opportunities
Do NOT use for:
- ❌ Content censorship or suppression of legitimate political speech
- ❌ Manipulating public discourse through disinformation
- ❌ Violating freedom of expression protections
- ❌ Personal attacks on communication style without policy substance
## Strategic Communication Framework
### Five Analytical Dimensions
The Riksdagsmonitor platform analyzes political communication across five critical dimensions to understand messaging strategies and influence dynamics:
```mermaid
graph TB
subgraph "Communication Intelligence Collection"
A1[📝 Parliamentary Documents<br/>89K+ documents<br/>Motions, interpellations, bills]
A2[🎤 Debate Transcripts<br/>Parliamentary speeches<br/>Committee testimony]
A3[📰 Media Coverage<br/>News articles, press releases<br/>Social media posts]
A4[🗳️ Voting Records<br/>Action-rhetoric alignment<br/>Behavior vs. statements]
A5[🤝 Coalition Messaging<br/>Party coordination<br/>Message discipline]
end
subgraph "Communication Analysis"
A1 --> B1[Narrative Framing Analysis]
A2 --> B2[Discourse Pattern Recognition]
A3 --> B3[Media Bias Detection]
A4 --> B4[Rhetoric-Action Gap Analysis]
A5 --> B5[Coordination Assessment]
end
subgraph "Intelligence Products"
B1 --> C1[Frame Identification]
B2 --> C2[Discourse Networks]
B3 --> C3[Bias Profiles]
B4 --> C4[Credibility Assessment]
B5 --> C5[Campaign Detection]
end
subgraph "Strategic Intelligence"
C1 & C2 & C3 & C4 & C5 --> D[📊 Communication Profile]
D --> E[Influence Assessment]
D --> F[Disinformation Detection]
D --> G[Counter-Narrative Strategy]
end
style A1 fill:#e1f5ff
style A2 fill:#e1f5ff
style A3 fill:#e1f5ff
style A4 fill:#e1f5ff
style A5 fill:#e1f5ff
style D fill:#ffe6cc
style E fill:#ccffcc
style F fill:#ffcccc
style G fill:#fff9cc
```
## 1. Narrative Framing Analysis
### Framing Theory Application
Political actors use framing to define issues in ways that advantage their positions. The Riksdagsmonitor platform identifies dominant frames through document and speech analysis.
**Database Views:**
- `view_riksdagen_politician_document` - All politician-authored documents
- `view_riksdagen_document_content` - Full document text for NLP analysis
- `view_riksdagen_document_element` - Document structure and metadata
### Frame Identification System
```python
from typing import Dict, List, Tuple
import re
from collections import Counter
from dataclasses import dataclass
@dataclass
class Frame:
"""Political framing structure."""
frame_type: str
keywords: List[str]
metaphors: List[str]
values: List[str]
emotional_tone: str
class NarrativeFramingAnalyzer:
"""
Identifies narrative frames in political communication.
Based on Entman (1993) framing theory:
1. Problem definition
2. Causal interpretation
3. Moral evaluation
4. Treatment recommendation
"""
# Swedish political frame templates
FRAME_TEMPLATES = {
'ECONOMIC_COMPETITIVENESS': Frame(
frame_type='ECONOMIC_COMPETITIVENESS',
keywords=['konkurrenskraft', 'tillväxt', 'företagande', 'innovation', 'export'],
metaphors=['motor', 'drivkraft', 'framtid'],
values=['prosperity', 'efficiency', 'growth'],
emotional_tone='aspirational'
),
'SOCIAL_JUSTICE': Frame(
frame_type='SOCIAL_JUSTICE',
keywords=['rättvisa', 'jämlikhet', 'solidaritet', 'välfärd', 'trygghet'],
metaphors=['broar', 'skyddsnät', 'gemenskap'],
values=['equality', 'fairness', 'solidarity'],
emotional_tone='empathetic'
),
'NATIONAL_SECURITY': Frame(
frame_type='NATIONAL_SECURITY',
keywords=['säkerhet', 'försvar', 'gränser', 'suveränitet', 'beredskap'],
metaphors=['skydd', 'fort', 'försvar'],
values=['safety', 'sovereignty', 'protection'],
emotional_tone='protective'
),
'ENVIRONMENTAL_CRISIS': Frame(
frame_type='ENVIRONMENTAL_CRISIS',
keywords=['klimat', 'miljö', 'hållbarhet', 'förnybart', 'utsläpp'],
metaphors=['kris', 'hotet', 'framtiden'],
values=['sustainability', 'responsibility', 'legacy'],
emotional_tone='urgent'
),
'PERSONAL_FREEDOM': Frame(
frame_type='PERSONAL_FREEDOM',
keywords=['frihet', 'valfrihet', 'självständighet', 'ansvar', 'rättigheter'],
metaphors=['kedjor', 'byråkrati', 'frihet'],
values=['liberty', 'autonomy', 'choice'],
emotional_tone='libertarian'
),
'IMMIGRATION_CONTROL': Frame(
frame_type='IMMIGRATION_CONTROL',
keywords=['integration', 'migration', 'ordning', 'kontroll', 'ansvar'],
metaphors=['ordning', 'gränser', 'system'],
values=['order', 'fairness', 'sustainability'],
emotional_tone='controlled'
)
}
def analyze_document_framing(self, document_id: str) -> Dict:
"""
Analyzes narrative framing in a parliamentary document.
Returns:
- Dominant frame(s)
- Frame intensity score
- Supporting evidence (keywords, metaphors)
- Frame consistency over time
"""
query = """
SELECT
d.document_id,
d.document_type,
d.title,
d.subtitle,
dc.text_content,
p.person_id,
p.first_name || ' ' || p.last_name as author,
p.party,
d.created_date,
d.issue_category,
d.issue_subcategory
FROM document d
JOIN document_content dc ON d.document_id = dc.document_id
JOIN document_person dp ON d.document_id = dp.document_id
JOIN person p ON dp.person_id = p.person_id
WHERE d.document_id = %s
"""
doc = pd.read_sql(query, self.connection, params=[document_id]).iloc[0]
# Analyze text for frame indicators
text_content = doc['text_content'].lower()
title_content = f"{doc['title']} {doc['subtitle']}".lower()
frame_scores = {}
frame_evidence = {}
for frame_name, frame in self.FRAME_TEMPLATES.items():
# Score based on keyword presence
keyword_score = sum(
text_content.count(keyword) for keyword in frame.keywords
) / len(frame.keywords)
# Score based on metaphor usage
metaphor_score = sum(
text_content.count(metaphor) * 2 for metaphor in frame.metaphors
) / len(frame.metaphors)
# Bonus for title/subtitle framing (higher salience)
title_score = sum(
title_content.count(keyword) * 3 for keyword in frame.keywords
) / len(frame.keywords)
total_score = keyword_score + metaphor_score + title_score
frame_scores[frame_name] = round(total_score, 2)
frame_evidence[frame_name] = {
'keywords_found': [kw for kw in frame.keywords if kw in text_content],
'metaphors_found': [m for m in frame.metaphors if m in text_content],
'title_framing': any(kw in title_content for kw in frame.keywords)
}
# Identify dominant frame(s)
sorted_frames = sorted(frame_scores.items(), key=lambda x: x[1], reverse=True)
dominant_frame = sorted_frames[0][0] if sorted_frames[0][1] > 3 else 'MIXED_FRAMING'
return {
'document_id': document_id,
'author': doc['author'],
'party': doc['party'],
'document_type': doc['document_type'],
'dominant_frame': dominant_frame,
'frame_scores': frame_scores,
'frame_evidence': frame_evidence[dominant_frame],
'frame_intensity': sorted_frames[0][1],
'mixed_framing': len([s for s in sorted_frames if s[1] > 2]) > 1,
'created_date': doc['created_date']
}
def analyze_party_framing_strategy(self, party_id: str, date_from: str, date_to: str) -> Dict:
"""
Analyzes a party's framing strategy over time.
Identifies:
- Consistent frames vs. tactical frame shifts
- Frame coordination across party members
- Frame evolution in response to events
"""
query = """
SELECT
d.document_id,
d.created_date,
d.issue_category,
p.person_id,
p.first_name || ' ' || p.last_name as author
FROM document d
JOIN document_person dp ON d.document_id = dp.document_id
JOIN person p ON dp.person_id = p.person_id
WHERE p.party = %s
AND d.created_date BETWEEN %s AND %s
AND d.document_type IN ('motion', 'interpellation', 'bill')
ORDER BY d.created_date
"""
documents = pd.read_sql(query, self.connection,
params=[party_id, date_from, date_to])
# Analyze each document
party_frames = []
for _, doc in documents.iterrows():
frame_analysis = self.analyze_document_framing(doc['document_id'])
frame_analysis['author'] = doc['author']
frame_analysis['created_date'] = doc['created_date']
party_frames.append(frame_analysis)
# Calculate frame consistency
frame_distribution = Counter([f['dominant_frame'] for f in party_frames])
total_docs = len(party_frames)
# Calculate coordination score
authors = list(set([f['author'] for f in party_frames]))
author_frames = {author: Counter() for author in authors}
for frame_analysis in party_frames:
author_frames[frame_analysis['author']][frame_analysis['dominant_frame']] += 1
# Coordination: Do different authors use similar frames?
coordination_score = 0
if len(authors) > 1:
top_frame = frame_distribution.most_common(1)[0][0]
authors_using_top_frame = sum(
1 for author, frames in author_frames.items()
if frames[top_frame] > 0
)
coordination_score = authors_using_top_frame / len(authors)
return {
'party': party_id,
'analysis_period': f"{date_from} to {date_to}",
'total_documents': total_docs,
'frame_distribution': dict(frame_distribution),
'dominant_party_frame': frame_distribution.most_common(1)[0][0],
'frame_consistency': frame_distribution.most_common(1)[0][1] / total_docs,
'coordination_score': round(coordination_score, 3),
'frame_diversity': len(frame_distribution),
'classification': self._classify_framing_strategy(
frame_distribution.most_common(1)[0][1] / total_docs,
coordination_score
)
}
def _classify_framing_strategy(self, consistency: float, coordination: float) -> str:
"""Classify party framing strategy."""
if consistency > 0.7 and coordination > 0.7:
return "DISCIPLINED_MESSAGING"
elif consistency > 0.5 and coordination > 0.5:
return "COORDINATED_STRATEGY"
elif consistency < 0.3 or coordination < 0.3:
return "FRAGMENTED_MESSAGING"
else:
return "ADAPTIVE_POSITIONING"
```
## 2. Media Bias Detection
### Systematic Bias Analysis
Media bias affects public perception of political actors. The Riksdagsmonitor platform systematically measures bias through coverage patterns and linguistic analysis.
```java
@Service
public class MediaBiasDetector {
/**
* Detects systematic media bias in political coverage.
*
* Bias indicators:
* 1. Coverage frequency disparities
* 2. Sentiment bias (positive vs. negative framing)
* 3. Source selection bias (which politicians quoted)
* 4. Issue framing bias (which frames amplified)
* 5. Omission bias (what's not covered)
*/
public MediaBiasProfile analyzeBias(
String mediaOutletId,
String politicalEntity,
LocalDate startDate,
LocalDate endDate
) {
String sql = """
WITH coverage_baseline AS (
-- Calculate expected coverage based on political relevance
SELECT
political_entity_id,
entity_type,
political_relevance_score,
CASE entity_type
WHEN 'PARTY_LEADER' THEN political_relevance_score * 3
WHEN 'MINISTER' THEN political_relevance_score * 2.5
WHEN 'COMMITTEE_CHAIR' THEN political_relevance_score * 1.5
ELSE political_relevance_score
END as expected_coverage_weight
FROM political_entity_relevance
),
actual_coverage AS (
-- Measure actual coverage from media outlet
SELECT
mc.political_entity_id,
COUNT(DISTINCT mc.article_id) as article_count,
AVG(mc.sentiment_score) as avg_sentiment,
SUM(mc.prominence_score) as total_prominence,
-- Linguistic indicators
AVG(LENGTH(mc.quote_text)) as avg_quote_length,
COUNT(CASE WHEN mc.is_primary_source THEN 1 END) as primary_source_count,
-- Framing indicators
mc.dominant_frame,
mc.frame_valence
FROM media_coverage mc
WHERE mc.media_outlet_id = :mediaOutletId
AND mc.coverage_date BETWEEN :startDate AND :endDate
GROUP BY mc.political_entity_id, mc.dominant_frame, mc.frame_valence
),
bias_analysis AS (
SELECT
cb.political_entity_id,
在 GitHub 查看