瀏覽代碼

增加报告节点

jiaqiang 1 天之前
父節點
當前提交
409dfad8aa
共有 6 個文件被更改,包括 104 次插入15 次删除
  1. 4 2
      llmops/agents/outline_agent.py
  2. 2 2
      llmops/agents/planning_agent.py
  3. 93 5
      llmops/complete_agent_flow_rule.py
  4. 3 3
      llmops/config.py
  5. 1 2
      llmops/main.py
  6. 1 1
      llmops/workflow_state.py

+ 4 - 2
llmops/agents/outline_agent.py

@@ -652,7 +652,8 @@ class OutlineGeneratorAgent:
                     示例:
                     sec_1:metric-total_income,metric-expense_trend,metric-profit_margin
                     sec_2:metric-customer_analysis,metric-market_share"""),
-                                    ("human", """报告标题:{report_title}
+                ("human", """
+                    报告标题:{report_title}
                     
                     章节列表:
                     {sections}
@@ -660,7 +661,8 @@ class OutlineGeneratorAgent:
                     可用指标:
                     {available_metrics}
                     
-                    请为每个章节分配最合适的指标ID。""")
+                    请为每个章节分配最合适的指标ID。
+                """)
             ])
 
             # 格式化输入

+ 2 - 2
llmops/agents/planning_agent.py

@@ -104,7 +104,7 @@ class PlanningAgent:
             3. 检查 outline_draft 是否为空 → 空则选择 generate_outline
             4. 检查 metrics_requirements 是否为空 → 空则选择 generate_outline
             5. 检查是否有待计算指标 → 有则选择 compute_metrics
-            6. 所有指标都已计算完成 → 选择 finalize_report
+            6. 所有指标都已计算完成 → 选择 report_generator
             7. 如果无法理解需求 → 选择 clarify_requirements
 
             ### 重要原则
@@ -382,7 +382,7 @@ async def plan_next_action(question: str, industry: str, current_state: Dict[str
 
         # 直接返回最基本的默认决策,避免复杂的默认决策逻辑
         return PlanningDecision(
-            decision="finalize_report",
+            decision="report_generator",
             reasoning="规划决策失败,使用默认的报告生成决策",
             next_actions=["生成最终报告"],
             metrics_to_compute=[],

+ 93 - 5
llmops/complete_agent_flow_rule.py

@@ -40,9 +40,10 @@ from llmops.workflow_state import (
     update_state_with_planning_decision,
     update_state_with_data_classified,
     convert_numpy_types,
-    update_state_with_data_standardize
+    update_state_with_data_standardize,
+    update_state_with_report
 )
-from llmops.agents.outline_agent import  generate_report_outline
+from llmops.agents.outline_agent import generate_report_outline
 from llmops.agents.planning_agent import  plan_next_action
 from llmops.agents.rules_engine_metric_calculation_agent import RulesEngineMetricCalculationAgent
 from llmops.agents.data_manager import DataManager
@@ -83,6 +84,7 @@ class CompleteAgentFlow:
         workflow.add_node("metric_calculator", self._metric_calculator_node)
         workflow.add_node("data_classify", self._data_classify_node)
         workflow.add_node("data_standardize", self._data_standardize_node)
+        workflow.add_node("report_generator", self._report_generator_node)
 
         # 设置入口点
         workflow.set_entry_point("planning_node")
@@ -96,6 +98,7 @@ class CompleteAgentFlow:
                 "metric_calculator": "metric_calculator",
                 "data_classify": "data_classify",
                 "data_standardize": "data_standardize",
+                "report_generator": "report_generator",
                 END: END
             }
         )
@@ -105,6 +108,7 @@ class CompleteAgentFlow:
         workflow.add_edge("data_classify", "planning_node")
         workflow.add_edge("outline_generator", "planning_node")
         workflow.add_edge("metric_calculator", "planning_node")
+        workflow.add_edge("report_generator", END)
 
         return workflow
 
@@ -170,11 +174,11 @@ class CompleteAgentFlow:
             if failed_attempts.get(mid, 0) < max_retries
         ]
 
-        # 如果覆盖率 >= 80%,或者没有可重试的指标 → 结束流程
+        # 如果覆盖率 >= 80%,或者没有可重试的指标 → 生成报告
         if coverage >= 0.8 or not retryable_metrics:
             reason = "覆盖率达到80%" if coverage >= 0.8 else "没有可重试指标"
-            print(f"→ 结束流程(覆盖率={coverage:.2%},原因:{reason})")
-            return END
+            print(f"→ 指标计算完成,进入生成报告(覆盖率={coverage:.2%},原因:{reason})")
+            return "report_generator"
 
         # 默认返回规划节点
         return "planning_node"
@@ -319,7 +323,90 @@ class CompleteAgentFlow:
             new_state["errors"].append(f"数据标准化错误: {str(e)}")
             return convert_numpy_types(new_state)
 
+    async def _report_generator_node(self, state: IntegratedWorkflowState) -> IntegratedWorkflowState:
+        """报告完成节点:生成最终报告"""
+        try:
+            print("📋 正在生成最终报告...")
+
+            # 获取大纲和计算结果
+            outline = state.get("outline_draft")
+            computed_metrics = state.get("computed_metrics", {})
+            print(f"已经完成的计算指标:{computed_metrics}")
+
+            if not outline:
+                raise ValueError("没有可用的报告大纲")
+
+            # 生成最终报告
+            final_report = {
+                "title": outline.report_title,
+                "generated_at": datetime.now().isoformat(),
+                "summary": {
+                    "total_sections": len(outline.sections),
+                    "total_metrics_required": len(outline.global_metrics),
+                    "total_metrics_computed": len(computed_metrics),
+                    "planning_steps": state.get("planning_step", 0),
+                    "completion_rate": len(computed_metrics) / len(
+                        outline.global_metrics) if outline.global_metrics else 0
+                },
+                "sections": [],
+                "metrics_detail": {}
+            }
+
+            # 构建章节内容
+            for section in outline.sections:
+                section_content = {
+                    "section_id": section.section_id,
+                    "title": section.title,
+                    "description": section.description,
+                    "metrics": {}
+                }
+
+                # 添加该章节的指标数据
+                for metric_id in section.metrics_needed:
+                    if metric_id in computed_metrics:
+                        section_content["metrics"][metric_id] = computed_metrics[metric_id]
+                    else:
+                        if not metric_id.startswith("metric-"):
+                            # 指标缺少metric前缀,进行补充
+                            section_content["metrics"][metric_id] = computed_metrics["metric-"+metric_id]
+                        else:
+                            section_content["metrics"][metric_id] = "数据缺失"
+
+                final_report["sections"].append(section_content)
+
+            # 添加详细的指标信息
+            for metric_req in outline.global_metrics:
+                metric_id = metric_req.metric_id
+                final_report["metrics_detail"][metric_id] = {
+                    "name": metric_req.metric_name,
+                    "logic": metric_req.calculation_logic,
+                    "required_fields": metric_req.required_fields,
+                    "computed": metric_id in computed_metrics,
+                    "value": computed_metrics.get(metric_id, {}).get("value", "N/A")
+                }
+
+            # 更新状态
+            new_state = update_state_with_report(state, final_report)
+
+            # 添加完成消息
+            new_state["messages"].append({
+                "role": "assistant",
+                "content": f"🎉 完整报告生成流程完成:{outline.report_title}",
+                "timestamp": datetime.now().isoformat()
+            })
+
+            print(f"✅ 最终报告生成完成:{outline.report_title}")
+            print(f"   章节数:{len(final_report['sections'])}")
+            print(f"   计算指标:{len(computed_metrics)}/{len(outline.global_metrics)}")
+            print(".2%")
 
+            return convert_numpy_types(new_state)
+
+        except Exception as e:
+            print(f"❌ 报告完成失败: {e}")
+            new_state = state.copy()
+            new_state["errors"].append(f"报告完成错误: {str(e)}")
+            return convert_numpy_types(new_state)
 
     def _print_ai_selection_analysis(self, outline):
         """打印AI指标选择的推理过程分析 - 完全通用版本"""
@@ -740,6 +827,7 @@ async def main():
     )
 
     print(f"📋 结果: {'✅ 成功' if result.get('success') else '❌ 失败'}")
+    print(f"{result}")
 
     if result.get('success'):
         summary = result.get('execution_summary', {})

+ 3 - 3
llmops/config.py

@@ -119,9 +119,9 @@ deepseek_v3_model = {
 }
 
 
-LLM_API_KEY=qwen3_32B_model["api_key"]
-LLM_BASE_URL=qwen3_32B_model["base_url"]
-LLM_MODEL_NAME=qwen3_32B_model["name"]
+LLM_API_KEY=deepseek_v3_model["api_key"]
+LLM_BASE_URL=deepseek_v3_model["base_url"]
+LLM_MODEL_NAME=deepseek_v3_model["name"]
 
 # 多模型接口地址
 multimodal_api_url = "http://103.154.31.78:20012/api/file/read"

+ 1 - 2
llmops/main.py

@@ -139,8 +139,7 @@ async def gen_report(file: UploadFile = File(...), question: str = Form(...), in
         return {
             "status": 0,
             "message": "success",
-            "outline_draft": result["result"]["outline_draft"],
-            "computed_metrics": result["result"]["computed_metrics"]
+            "report_draft": result["result"]["report_draft"]
         }
     except Exception as e:
         print(f"生成流水分析报告异常: {e}")

+ 1 - 1
llmops/workflow_state.py

@@ -314,7 +314,7 @@ def update_state_with_planning_decision(state: IntegratedWorkflowState, decision
     return new_state
 
 
-def finalize_state_with_report(state: IntegratedWorkflowState, final_report: Dict[str, Any]) -> IntegratedWorkflowState:
+def update_state_with_report(state: IntegratedWorkflowState, final_report: Dict[str, Any]) -> IntegratedWorkflowState:
     """
     使用最终报告完成状态