You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
11 KiB
294 lines
11 KiB
# Analyst Report Design
|
|
|
|
## Problem
|
|
|
|
The current report shows raw metric floats (0.4, 0.5) that are meaningless to users doing market research or customer sentiment analysis. A user seeing "brand_sentiment: 0.72" cannot act on that. The report needs to tell users what to do, why, and back it with evidence — not just show numbers.
|
|
|
|
## Goals
|
|
|
|
1. **Decision support**: Lead with actionable recommendations (go/no-go, segment targeting, risk mitigation)
|
|
2. **Evidence gathering**: Back every claim with traceable data (agent quotes, action counts, timeline events)
|
|
3. **Emergent insights**: Surface things the user didn't ask about — unexpected patterns, coalition formation, cascade effects
|
|
4. **Background generation**: Report starts building immediately when simulation ends, ready when user clicks "View Report"
|
|
|
|
## Non-Goals
|
|
|
|
- Interactive post-report chat (future)
|
|
- Agent survey/interview (future)
|
|
- PDF/PowerPoint export (future, covered in enterprise strategy)
|
|
- Real-time streaming of report sections (future)
|
|
|
|
## Report Structure (Top to Bottom)
|
|
|
|
### 1. Executive Brief
|
|
|
|
Colored banner at the top. Green (go), amber (caution), red (rethink).
|
|
|
|
- **Headline**: One sentence verdict — "This product launch shows strong potential in 2 of 4 segments, with a critical churn risk in price-sensitive demographics"
|
|
- **Summary**: One paragraph expanding on the headline
|
|
- **Confidence**: High / Medium / Low — derived from simulation length, agent count, and metric volatility
|
|
- **Meta**: Agent count, total days, total events in a subtle row
|
|
|
|
### 2. Scorecard
|
|
|
|
Replaces the current raw metric bar charts. Grid of metric cards (2-3 columns).
|
|
|
|
Each card shows:
|
|
- **Metric name** (e.g., "Brand Sentiment")
|
|
- **Rating pill**: Critical / Weak / Moderate / Strong / Excellent (color-coded)
|
|
- **Value**: Scaled to 0-100 (not 0.0-1.0)
|
|
- **Trend**: Arrow + delta from start value (e.g., "↑ from 50")
|
|
- **Explanation**: One sentence from the LLM explaining *why* it's at this level
|
|
|
|
Rating thresholds (on 0-100 scale):
|
|
- 0-20: Critical (red)
|
|
- 21-40: Weak (orange)
|
|
- 41-60: Moderate (amber)
|
|
- 61-80: Strong (green)
|
|
- 81-100: Excellent (emerald)
|
|
|
|
For inverse metrics (conflict, churn_risk) the scale is reversed — high values = Critical, low = Excellent.
|
|
|
|
Market metrics (brand_sentiment, purchase_intent, word_of_mouth, churn_risk, adoption_rate) only shown for market simulations. Social metrics (stability, prosperity, trust, freedom, conflict) always shown.
|
|
|
|
### 3. Key Insights — "Things You Should Know"
|
|
|
|
3-5 emergent behaviors the user didn't anticipate. Each insight is a card with:
|
|
- **Type badge**: Opportunity (blue) / Risk (red) / Surprise (amber)
|
|
- **Title**: Short hook
|
|
- **Description**: 2-3 sentences explaining what happened and why it matters
|
|
- **Evidence tags**: Collapsible — lists agent names, days, action types that support the claim
|
|
|
|
Examples:
|
|
- "An informal boycott coalition formed on Day 12 when 3 price-sensitive agents independently started discouraging purchases"
|
|
- "Your product's strongest advocates are not the target demographic — they're from an adjacent segment you didn't design for"
|
|
|
|
### 4. Segment Deep-Dive
|
|
|
|
One card per segment (agents grouped by role/faction). Each card contains:
|
|
- **Segment name + adoption percentage**
|
|
- **Mini funnel**: Horizontal stacked bar showing Aware → Interested → Tried → Adopted → Churned with counts
|
|
- **Reaction summary**: 1-2 sentences on how this segment responded
|
|
- **Top objection**: Most common reason for rejection/abandonment (pulled from agent internal_thought and speech)
|
|
- **Champion profile**: Who in this segment advocated, and a short quote why
|
|
- **Representative quote**: A real agent quote from the simulation
|
|
|
|
### 5. Action Items
|
|
|
|
Numbered, prioritized list. Each item is a structured card:
|
|
- **Priority badge**: High (red) / Medium (amber) / Low (gray)
|
|
- **What to do**: Specific, concrete action
|
|
- **Why**: Evidence-backed reasoning
|
|
- **Expected impact**: What changes if this action is taken
|
|
|
|
### 6. Risks & Second-Order Effects
|
|
|
|
Split into two sub-sections:
|
|
- **Risks**: Each with severity badge (High/Medium/Low) and description
|
|
- **Second-order effects**: Bullet list of downstream consequences
|
|
|
|
### 7. Evidence Trail (Collapsible)
|
|
|
|
Initially collapsed behind a "Show Evidence Trail" toggle. Contains the existing report data restructured:
|
|
- **Narrative summary** (current "What Happened")
|
|
- **Key moments timeline** (current timeline with dot indicators)
|
|
- **Factions** (current faction cards)
|
|
- **Metric sparklines** (improved from current bar blocks — still simple div-based, but with better visual treatment)
|
|
- **The Surprise** (current surprise callout)
|
|
|
|
## Backend: Multi-Pass Analysis Pipeline
|
|
|
|
### Current State
|
|
|
|
`narrator.generate_report()` does:
|
|
1. Load world state, metrics history, narratives
|
|
2. Build 30-day epochs, summarize each with LLM (N calls)
|
|
3. One big LLM call with `REPORT_TRENDS_SYSTEM` prompt to produce everything
|
|
4. Attach metrics_history and meta
|
|
|
|
Problems: One massive LLM call tries to do too much. Raw metric floats passed to frontend. No structured data analysis before LLM.
|
|
|
|
### New Pipeline
|
|
|
|
#### Pass 0: Epoch Summarization (existing, unchanged)
|
|
|
|
Already happens. Epoch summaries are available when report generation starts.
|
|
|
|
#### Pass 1: Data Aggregation (no LLM — pure computation)
|
|
|
|
Runs before any LLM calls. Builds structured stats from raw simulation data.
|
|
|
|
**Per-metric stats:**
|
|
- start_value, end_value, min, max
|
|
- trend direction (up/down/flat — based on start vs end with > 5% threshold)
|
|
- volatility (standard deviation across the history)
|
|
- human_label (Critical/Weak/Moderate/Strong/Excellent based on thresholds)
|
|
- value_100 (scaled to 0-100)
|
|
|
|
**Per-agent stats:**
|
|
- Action counts by type (PURCHASE: 3, ABANDON: 1, RECOMMEND: 5, etc.)
|
|
- Speeches and internal thoughts (for quote extraction)
|
|
- Final emotional state, faction membership
|
|
- Personality traits (for segment correlation)
|
|
|
|
**Per-segment stats (agents grouped by role):**
|
|
- Aggregate action counts
|
|
- Adoption funnel: count of agents who did PURCHASE (adopted), ABANDON (churned), RECOMMEND (advocates), neither (neutral)
|
|
- Top speeches/thoughts from this segment
|
|
|
|
**Inflection points:**
|
|
- Rounds where any metric delta exceeded a threshold (e.g., > 0.05 change in a single round)
|
|
- Mapped to day numbers for the timeline
|
|
|
|
#### Pass 2: Metric Interpretation + Insights (1 LLM call)
|
|
|
|
**Input:** Structured stats from Pass 1 + epoch summaries
|
|
|
|
**Prompt asks for:**
|
|
- Per-metric: one-sentence explanation of why it reached its final value
|
|
- 3-5 emergent insights with evidence references (agent names, days, action types)
|
|
- Executive verdict: go/caution/rethink + confidence + headline + summary paragraph
|
|
|
|
**Output format:** Structured JSON matching the `executive_brief`, `scorecard[].explanation`, and `insights[]` fields.
|
|
|
|
#### Pass 3: Segment Analysis (1 LLM call)
|
|
|
|
**Input:** Per-segment agent data (actions, quotes, emotional states, personality traits) + structured stats
|
|
|
|
**Prompt asks for:**
|
|
- Per-segment: reaction summary, top objection, champion profile with quote, representative quote
|
|
- Cross-segment dynamics (which segments influenced which)
|
|
|
|
**Output format:** Structured JSON matching `segments[]` fields. Funnel numbers come from Pass 1 computation, not LLM.
|
|
|
|
#### Pass 4: Action Synthesis (1 LLM call)
|
|
|
|
**Input:** Everything from Passes 1-3
|
|
|
|
**Prompt asks for:**
|
|
- Prioritized action items: what to do, why, expected impact, priority
|
|
- Key risks with severity
|
|
- Second-order effects
|
|
|
|
**Output format:** Structured JSON matching `action_items[]`, `risks[]`, `second_order_effects[]`.
|
|
|
|
### LLM Cost
|
|
|
|
Current: N epoch calls + 1 big report call
|
|
New: N epoch calls (unchanged) + 3 focused calls
|
|
Net change: +2 LLM calls, but each is smaller and more focused → better quality per call.
|
|
|
|
## Report Data Structure
|
|
|
|
```python
|
|
{
|
|
"executive_brief": {
|
|
"verdict": "go" | "caution" | "rethink",
|
|
"confidence": "high" | "medium" | "low",
|
|
"headline": "str",
|
|
"summary": "str"
|
|
},
|
|
"scorecard": [{
|
|
"metric": "brand_sentiment",
|
|
"label": "Brand Sentiment",
|
|
"rating": "strong",
|
|
"value": 72,
|
|
"start_value": 50,
|
|
"trend": "up" | "down" | "flat",
|
|
"explanation": "str"
|
|
}],
|
|
"insights": [{
|
|
"title": "str",
|
|
"description": "str",
|
|
"evidence": {
|
|
"agents": ["name1", "name2"],
|
|
"days": [8, 12],
|
|
"actions": ["ABANDON", "RECOMMEND"]
|
|
},
|
|
"type": "opportunity" | "risk" | "surprise"
|
|
}],
|
|
"segments": [{
|
|
"name": "str",
|
|
"adoption_pct": 65,
|
|
"funnel": {
|
|
"aware": 20,
|
|
"interested": 15,
|
|
"tried": 12,
|
|
"adopted": 8,
|
|
"churned": 3
|
|
},
|
|
"reaction": "str",
|
|
"top_objection": "str",
|
|
"champion": {"name": "str", "why": "str"},
|
|
"representative_quote": "str"
|
|
}],
|
|
"action_items": [{
|
|
"action": "str",
|
|
"reasoning": "str",
|
|
"expected_impact": "str",
|
|
"priority": "high" | "medium" | "low"
|
|
}],
|
|
"risks": [{"risk": "str", "severity": "high" | "medium" | "low"}],
|
|
"second_order_effects": ["str"],
|
|
"narrative": {
|
|
"summary": "str",
|
|
"key_moments": [{"day": "int", "title": "str", "description": "str"}],
|
|
"surprise": "str",
|
|
"factions": [{"name": "str", "description": "str", "peak_members": "int"}]
|
|
},
|
|
"metrics_history": [{"round": "int", "stability": "float", "...": "..."}],
|
|
"meta": {
|
|
"agent_count": "int",
|
|
"total_days": "int",
|
|
"total_actions": "int",
|
|
"rules": ["str"],
|
|
"world_name": "str"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Async Report Generation
|
|
|
|
### Trigger
|
|
|
|
When the simulation engine finishes its last round and status transitions to `completed`, immediately spawn a background task to generate the report.
|
|
|
|
### New Status Field
|
|
|
|
`report_status` on the simulation record:
|
|
- `pending` — simulation not done yet
|
|
- `generating` — report pipeline is running
|
|
- `ready` — report is stored and available
|
|
- `failed` — report generation errored
|
|
|
|
### Storage
|
|
|
|
Report JSON stored in the simulation's SQLite database in a new `report` table (single row, `report_json TEXT`).
|
|
|
|
### API
|
|
|
|
`GET /simulation/{sim_id}/report`:
|
|
- If `report_status == ready`: return the full report JSON
|
|
- If `report_status == generating`: return `{"status": "generating"}`
|
|
- If `report_status == failed`: return `{"status": "failed", "error": "..."}`
|
|
- If `report_status == pending`: return `{"status": "pending"}`
|
|
|
|
### Frontend Polling
|
|
|
|
1. User clicks "View Report" → navigates to `/report/:id`
|
|
2. Fetch report endpoint
|
|
3. If `ready` → render report
|
|
4. If `generating` → show skeleton loader with "Analyzing simulation results..." and poll every 3 seconds
|
|
5. On transition to `ready` → fade in the report
|
|
|
|
## Files Changed
|
|
|
|
### Backend
|
|
- `backend/app/services/narrator.py` — Replace `generate_report()` with multi-pass pipeline. Add `ReportAnalyzer` class with Pass 1 computation. Add new LLM prompt templates for Passes 2-4.
|
|
- `backend/app/services/engine.py` — After simulation completes, spawn background report generation task.
|
|
- `backend/app/db/store.py` — Add `report` table. Add `save_report()`, `get_report()`, `get_report_status()`, `set_report_status()` methods.
|
|
- `backend/app/api/routes.py` (or equivalent) — Update `/simulation/{sim_id}/report` endpoint to check status and return accordingly.
|
|
|
|
### Frontend
|
|
- `frontend/src/views/ReportView.vue` — Complete rewrite with new section structure, polling logic, skeleton loader.
|
|
- `frontend/src/api/client.js` (or equivalent) — Update `getReport()` to handle status responses and polling.
|
|
|