fix simulation reporting and research resilience
This commit is contained in:
@@ -8,8 +8,12 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<MoodDot :state="agent.emotional_state" size="md" />
|
||||
<h3 class="font-semibold text-sm">{{ agent.name }}</h3>
|
||||
</div>
|
||||
<p class="text-[11px] text-slate-500 mt-0.5">{{ agent.role }}, {{ agent.age }}</p>
|
||||
</div>
|
||||
<p class="text-[11px] text-slate-500 mt-0.5">{{ agent.role }}, {{ agent.age }}</p>
|
||||
<span v-if="agent.generation_source && agent.generation_source !== 'llm'"
|
||||
class="inline-flex mt-1 px-1.5 py-0.5 rounded bg-amber-100 text-amber-700 text-[9px]">
|
||||
{{ $t('agent.profile.generationFallback') }}
|
||||
</span>
|
||||
</div>
|
||||
<button @click="$emit('close')" class="text-slate-400 hover:text-slate-700 text-xs p-1 rounded hover:bg-gray-100 transition-colors">✕</button>
|
||||
</div>
|
||||
|
||||
@@ -72,10 +72,13 @@ const { t, tm } = useI18n()
|
||||
// canonical English enum strings while we render localized labels.
|
||||
function _metricLabel(key) {
|
||||
const keyMap = { stability: 'report.metricLabels.stability', prosperity: 'report.metricLabels.prosperity',
|
||||
trust: 'report.metricLabels.trust', freedom: 'report.metricLabels.freedom',
|
||||
conflict: 'report.metricLabels.conflict', brand_sentiment: 'report.metricLabels.brandSentiment',
|
||||
purchase_intent: 'report.metricLabels.purchaseIntent', word_of_mouth: 'report.metricLabels.wordOfMouth',
|
||||
churn_risk: 'report.metricLabels.churnRisk', adoption_rate: 'report.metricLabels.adoptionRate' }
|
||||
trust: 'report.metricLabels.trust', freedom: 'report.metricLabels.freedom',
|
||||
conflict: 'report.metricLabels.conflict', brand_sentiment: 'report.metricLabels.brand_sentiment',
|
||||
purchase_intent: 'report.metricLabels.purchase_intent', word_of_mouth: 'report.metricLabels.word_of_mouth',
|
||||
churn_risk: 'report.metricLabels.churn_risk', adoption_rate: 'report.metricLabels.adoption_rate',
|
||||
information_spread: 'report.metricLabels.information_spread',
|
||||
echo_chamber_index: 'report.metricLabels.echo_chamber_index',
|
||||
rumor_distortion: 'report.metricLabels.rumor_distortion' }
|
||||
const i18nKey = keyMap[key]
|
||||
return i18nKey ? t(i18nKey) : key
|
||||
}
|
||||
|
||||
@@ -203,8 +203,9 @@
|
||||
"noMemories": "No memories yet...",
|
||||
"beliefs": "Beliefs",
|
||||
"remembers": "Remembers",
|
||||
"goals": "Goals",
|
||||
"askPrefix": "Ask",
|
||||
"goals": "Goals",
|
||||
"generationFallback": "Degraded generation",
|
||||
"askPrefix": "Ask",
|
||||
"thinking": "Thinking...",
|
||||
"chatPlaceholder": "Ask something...",
|
||||
"ageAtEvent": "Age {age}"
|
||||
@@ -317,8 +318,16 @@
|
||||
"medium": "medium",
|
||||
"low": "low"
|
||||
},
|
||||
"confidenceSuffix": "confidence",
|
||||
"metricLabels": {
|
||||
"confidenceSuffix": "confidence",
|
||||
"systematic": {
|
||||
"title": "Systematic Summary",
|
||||
"evidenceDays": "Evidence days"
|
||||
},
|
||||
"dataGaps": {
|
||||
"title": "Data Gaps & Limitations",
|
||||
"subtitle": "These limitations were detected in this run; they are not necessarily universal simulator limitations."
|
||||
},
|
||||
"metricLabels": {
|
||||
"stability": "Stability",
|
||||
"prosperity": "Prosperity",
|
||||
"trust": "Trust",
|
||||
@@ -328,7 +337,10 @@
|
||||
"purchase_intent": "Purchase Intent",
|
||||
"word_of_mouth": "Word of Mouth",
|
||||
"churn_risk": "Churn Risk",
|
||||
"adoption_rate": "Adoption Rate"
|
||||
"adoption_rate": "Adoption Rate",
|
||||
"information_spread": "Information Spread",
|
||||
"echo_chamber_index": "Echo Chamber Index",
|
||||
"rumor_distortion": "Rumor Distortion"
|
||||
},
|
||||
"ratings": {
|
||||
"excellent": "Excellent",
|
||||
|
||||
@@ -203,8 +203,9 @@
|
||||
"noMemories": "暂无记忆...",
|
||||
"beliefs": "信念",
|
||||
"remembers": "铭记",
|
||||
"goals": "目标",
|
||||
"askPrefix": "询问",
|
||||
"goals": "目标",
|
||||
"generationFallback": "降级生成",
|
||||
"askPrefix": "询问",
|
||||
"thinking": "思考中...",
|
||||
"chatPlaceholder": "问点什么...",
|
||||
"ageAtEvent": "{age} 岁时"
|
||||
@@ -317,8 +318,16 @@
|
||||
"medium": "中",
|
||||
"low": "低"
|
||||
},
|
||||
"confidenceSuffix": "置信度",
|
||||
"metricLabels": {
|
||||
"confidenceSuffix": "置信度",
|
||||
"systematic": {
|
||||
"title": "系统性总结",
|
||||
"evidenceDays": "证据日期"
|
||||
},
|
||||
"dataGaps": {
|
||||
"title": "数据盲区与限制",
|
||||
"subtitle": "以下内容是本次模拟中可确定检测到的限制,不代表所有模拟都存在相同问题。"
|
||||
},
|
||||
"metricLabels": {
|
||||
"stability": "稳定性",
|
||||
"prosperity": "繁荣度",
|
||||
"trust": "信任度",
|
||||
@@ -328,7 +337,10 @@
|
||||
"purchase_intent": "购买意向",
|
||||
"word_of_mouth": "口碑传播",
|
||||
"churn_risk": "流失风险",
|
||||
"adoption_rate": "采纳率"
|
||||
"adoption_rate": "采纳率",
|
||||
"information_spread": "信息传播",
|
||||
"echo_chamber_index": "回音室指数",
|
||||
"rumor_distortion": "谣言失真"
|
||||
},
|
||||
"ratings": {
|
||||
"excellent": "极佳",
|
||||
|
||||
@@ -98,10 +98,40 @@
|
||||
<span>{{ (meta.rules || []).length }} {{ $t('report.rules') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Scorecard (extracted component with sparklines) -->
|
||||
<ScorecardGrid :scorecard="report.scorecard" :metrics-history="report.metrics_history" :forecasts="report.forecasts || {}" />
|
||||
</div>
|
||||
|
||||
<!-- 2. Systematic narrative and deterministic limitations -->
|
||||
<div v-if="report.systematic_summary?.summary || report.systematic_summary?.chapters?.length" class="space-y-4">
|
||||
<h2 class="section-title">{{ $t('report.systematic.title') }}</h2>
|
||||
<div v-if="report.systematic_summary?.summary" class="bg-white border border-gray-200 rounded-xl p-5">
|
||||
<p class="text-slate-700 leading-relaxed whitespace-pre-line">{{ report.systematic_summary.summary }}</p>
|
||||
</div>
|
||||
<div v-if="report.systematic_summary?.chapters?.length" class="space-y-3">
|
||||
<div v-for="(chapter, idx) in report.systematic_summary.chapters" :key="idx"
|
||||
class="bg-white border border-gray-200 rounded-lg p-4">
|
||||
<h3 class="font-semibold text-slate-700">{{ chapter.title }}</h3>
|
||||
<p class="text-sm text-slate-600 leading-relaxed mt-2 whitespace-pre-line">{{ chapter.body }}</p>
|
||||
<div v-if="chapter.evidence_days?.length" class="text-xs text-emerald-600 mt-2">
|
||||
{{ $t('report.systematic.evidenceDays') }}: {{ chapter.evidence_days.join(', ') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="report.data_gaps?.length" class="bg-amber-50 border border-amber-200 rounded-xl p-5">
|
||||
<h2 class="section-title !mt-0">{{ $t('report.dataGaps.title') }}</h2>
|
||||
<p class="text-xs text-amber-800/70 mb-3">{{ $t('report.dataGaps.subtitle') }}</p>
|
||||
<div class="space-y-3">
|
||||
<div v-for="gap in report.data_gaps" :key="gap.key" class="border-l-2 border-amber-400 pl-3">
|
||||
<div class="text-sm font-medium text-amber-900">{{ gap.gap }}</div>
|
||||
<div class="text-xs text-amber-800/80 mt-1">{{ gap.why_it_matters }}</div>
|
||||
<div v-if="gap.suggested_fix" class="text-xs text-amber-700 mt-1">{{ gap.suggested_fix }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Scorecard (extracted component with sparklines) -->
|
||||
<ScorecardGrid :scorecard="report.scorecard" :metrics-history="report.metrics_history" :forecasts="report.forecasts || {}" />
|
||||
|
||||
<!-- Causal Map -->
|
||||
<div v-if="report.causal_map?.length">
|
||||
@@ -432,11 +462,15 @@ const confidenceText = computed(() => {
|
||||
})
|
||||
const meta = computed(() => report.value?.meta || {})
|
||||
|
||||
const METRIC_CONFIG = [
|
||||
{ key: 'stability', label: 'Stability', color: 'bg-emerald-500' },
|
||||
{ key: 'trust', label: 'Trust', color: 'bg-blue-500' },
|
||||
{ key: 'conflict', label: 'Conflict', color: 'bg-red-500' },
|
||||
{ key: 'freedom', label: 'Freedom', color: 'bg-amber-500' },
|
||||
const METRIC_CONFIG = [
|
||||
{ key: 'stability', label: 'Stability', color: 'bg-emerald-500' },
|
||||
{ key: 'prosperity', label: 'Prosperity', color: 'bg-sky-400' },
|
||||
{ key: 'trust', label: 'Trust', color: 'bg-blue-500' },
|
||||
{ key: 'conflict', label: 'Conflict', color: 'bg-red-500' },
|
||||
{ key: 'freedom', label: 'Freedom', color: 'bg-amber-500' },
|
||||
{ key: 'information_spread', label: 'Information Spread', color: 'bg-cyan-400' },
|
||||
{ key: 'echo_chamber_index', label: 'Echo Chamber Index', color: 'bg-orange-400' },
|
||||
{ key: 'rumor_distortion', label: 'Rumor Distortion', color: 'bg-rose-400' },
|
||||
{ key: 'brand_sentiment', label: 'Brand Sentiment', color: 'bg-emerald-400', market: true },
|
||||
{ key: 'purchase_intent', label: 'Purchase Intent', color: 'bg-blue-400', market: true },
|
||||
{ key: 'word_of_mouth', label: 'Word of Mouth', color: 'bg-violet-400', market: true },
|
||||
@@ -571,7 +605,11 @@ function copyMarkdown() {
|
||||
let md = `# ${b.headline || m.world_name || 'Report'}\n\n`
|
||||
md += `**Verdict:** ${b.verdict || 'N/A'} | **Confidence:** ${b.confidence || 'N/A'}\n\n`
|
||||
md += `${b.summary || ''}\n\n`
|
||||
md += `**Stats:** ${m.agent_count} agents, ${m.total_days} days, ${m.total_actions} actions\n\n`
|
||||
md += `**Stats:** ${m.agent_count} agents, ${m.total_days} days, ${m.total_actions} actions\n\n`
|
||||
|
||||
if (report.value.systematic_summary?.summary) {
|
||||
md += `## Systematic Summary\n\n${report.value.systematic_summary.summary}\n\n`
|
||||
}
|
||||
|
||||
if (report.value.insights?.length) {
|
||||
md += `## Key Insights\n\n`
|
||||
@@ -581,13 +619,21 @@ function copyMarkdown() {
|
||||
md += '\n'
|
||||
}
|
||||
|
||||
if (report.value.action_items?.length) {
|
||||
if (report.value.action_items?.length) {
|
||||
md += `## Action Items\n\n`
|
||||
for (const a of report.value.action_items) {
|
||||
md += `${a.priority === 'high' ? '!' : '-'} ${a.action}\n`
|
||||
}
|
||||
md += '\n'
|
||||
}
|
||||
md += '\n'
|
||||
}
|
||||
|
||||
if (report.value.data_gaps?.length) {
|
||||
md += `## Data Gaps & Limitations\n\n`
|
||||
for (const gap of report.value.data_gaps) {
|
||||
md += `- **${gap.gap}** ${gap.why_it_matters || ''} ${gap.suggested_fix || ''}\n`
|
||||
}
|
||||
md += '\n'
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(md)
|
||||
copiedMd.value = true
|
||||
|
||||
Reference in New Issue
Block a user