""" 报告生成器 —— 单次分析报告 """ import json from typing import Any from core.config import LLM_CONFIG from core.utils import get_llm_client, llm_chat from layers.explorer import ExplorationStep from layers.insights import Insight REPORT_PROMPT = """你是一个数据分析报告撰写专家。基于以下信息撰写报告。 ## 用户问题 {question} ## 分析计划 {plan} ## 探索过程 {exploration} ## 主动洞察 {insights_text} ## 可用图表 {charts_text} ## 撰写要求 1. **开头**:一句话总结核心结论 2. **核心发现**:按重要性排列,带具体数字 3. **图表引用**:用 `![标题](路径)` 嵌入到相关段落 4. **深入洞察**:异常、趋势、关联 5. **建议**:基于数据的行动建议 6. **审计**:末尾附上所有 SQL 中文,专业简报风格。图表自然嵌入对应段落。""" class ReportGenerator: """报告生成器""" def __init__(self): self.client, self.model = get_llm_client(LLM_CONFIG) def generate(self, question: str, plan: dict, steps: list[ExplorationStep], insights: list[Insight], charts: list[dict] | None = None) -> str: exploration = self._build_exploration(steps) insights_text = "\n".join(str(i) for i in insights) if insights else "未检测到异常。" charts_text = "\n".join(f"{i}. 标题: {c['title']}, 路径: {c['path']}" for i, c in enumerate(charts or [], 1)) or "无图表。" prompt = REPORT_PROMPT.format( question=question, plan=json.dumps(plan, ensure_ascii=False, indent=2), exploration=exploration, insights_text=insights_text, charts_text=charts_text, ) return llm_chat( self.client, self.model, messages=[ {"role": "system", "content": "你是专业的数据分析师,撰写清晰、有洞察力的分析报告。"}, {"role": "user", "content": prompt}, ], temperature=0.3, max_tokens=4096, ) def _build_exploration(self, steps: list[ExplorationStep]) -> str: parts = [] for step in steps: if step.action == "done": parts.append(f"### 结束\n{step.reasoning}") elif step.success: parts.append( f"### 第 {step.round_num} 轮:{step.purpose}\n" f"SQL: `{step.sql}`\n结果 ({step.row_count} 行):\n" f"数据: {json.dumps(step.rows, ensure_ascii=False)}" ) else: parts.append(f"### 第 {step.round_num} 轮:{step.purpose}\nSQL: `{step.sql}`\n失败: {step.error}") return "\n\n".join(parts) if parts else "无探索步骤"