diff --git a/Controller/YltAnalyticsController.py b/Controller/YltAnalyticsController.py index ff15a88..7b4c1f2 100644 --- a/Controller/YltAnalyticsController.py +++ b/Controller/YltAnalyticsController.py @@ -214,29 +214,97 @@ async def export_ai_report_docx(req: AiReportRequest): @router.get("/api/ai/pricing/strategy-summary") async def ai_pricing_strategy_summary(): async def generate_stream(): - resp = await get_operators_hourly_prices() - data = resp.get("operators", []) - text_data = [] - for item in data: - text_data.append({"operator": item.get("operator"), "series": item.get("series")}) - - prompt = ( - "下面是四家供应商(新电途、特来电、驿来特、艾特吉易充)基于最新爬取数据计算出的平均24小时分时电价:\n" - f"{text_data}\n" - "请根据这些数据,综合分析各司的定价策略差异,重点对比我司(驿来特)与其他供应商的分时电价水平," - "指出我司在不同时段可能存在的潜在问题和风险(例如明显偏贵、价格结构不合理等),并给出2-3条可执行的优化建议。" - "请使用Markdown格式输出,重点可以使用加粗,如有公式可使用LaTeX格式。" - "回答控制在800字以内。" - ) - - async for chunk in get_llm_response( - prompt, - stream=True, - system_prompt="你是熟悉中国充电桩行业的电价策略分析顾问。", - ): - yield chunk + try: + # 发送初始信息并增加一些空白填充,防止某些代理缓存 + yield "正在收集各供应商价格数据,请稍候...\n\n" + (" " * 512) + "\n" + print("AI分析开始: 获取运营商价格数据...") + + # 使用 asyncio.wait_for 防止数据库查询无限挂起 + try: + # 1. 获取当前最新 24 小时平均价格 + resp = await asyncio.wait_for(get_operators_hourly_prices(), timeout=30.0) + + # 2. 获取最近 3 天的价格变动趋势 + trend_resp = await asyncio.wait_for(get_operators_price_trends(days=3), timeout=30.0) + except asyncio.TimeoutError: + print("获取价格数据超时") + yield "\n\n**错误**: 获取价格数据超时,数据库响应过慢,请稍后重试。" + return - return StreamingResponse(generate_stream(), media_type="text/event-stream") + # 处理当前价格数据 + data = resp.get("operators", []) + text_data = [] + for item in data: + text_data.append({"operator": item.get("operator"), "series": item.get("series")}) + + # 处理 3 天趋势数据 + trend_dates = trend_resp.get("dates", []) + trend_series = trend_resp.get("series", []) + trend_text = [] + for s in trend_series: + trend_text.append({"operator": s.get("name"), "daily_avg_prices": s.get("data")}) + + print(f"数据获取完成,准备请求LLM. 数据条数: {len(text_data)}, 趋势天数: {len(trend_dates)}") + yield "数据收集完成,正在分析最近 3 天的价格波动趋势并生成深度建议...\n\n" + + # 增加一个心跳,确保连接不断开 + yield " " * 128 + "\n" + + prompt = ( + "你是一位专业的充电桩调价策略分析顾问。下面是四家供应商(新电途、特来电、驿来特、艾特吉易充)的电价分析数据:\n\n" + "### 1. 当前最新 24 小时平均分时电价 (元/kWh)\n" + f"{json.dumps(text_data, ensure_ascii=False)}\n\n" + "### 2. 最近 3 天的价格变动趋势 (每日平均电价)\n" + f"日期序列: {trend_dates}\n" + f"各司趋势: {json.dumps(trend_text, ensure_ascii=False)}\n\n" + "请根据以上数据进行深度分析:\n" + "1. **现状对比**:对比我司(驿来特)与竞对在不同时段的电价水平,找出我司偏高或偏低的关键时段。\n" + "2. **趋势洞察**:分析最近 3 天各供应商的价格调整动态,判断市场整体是在涨价、降价还是保持稳定,我司的反应是否及时。\n" + "3. **问题诊断**:指出我司目前定价中存在的潜在风险(如价格倒挂、错失高峰收益、低谷缺乏竞争力等)。\n" + "4. **优化方案**:给出 2-3 条具体的、可落地的调价建议,并说明理由。\n\n" + "要求:\n" + "- 使用专业、客观的语气。\n" + "- 采用 Markdown 格式,适当使用加粗和表格。\n" + "- 回答控制在 800-1000 字以内。" + ) + + # 清空之前的提示信息,开始正式输出 AI 内容 + yield "---CLEAR_PREVIOUS_HINTS---\n" + + chunk_count = 0 + # 使用 asyncio.wait_for 防止 LLM 请求完全死掉 + try: + # 某些时候 LLM 可能会卡住,设置一个合理的整体超时 + async for chunk in get_llm_response( + prompt, + stream=True, + system_prompt="你是熟悉中国充电桩行业的电价策略分析顾问。", + ): + chunk_count += 1 + if chunk_count == 1: + print("收到LLM首个chunk") + yield chunk + except Exception as llm_e: + print(f"LLM请求异常: {str(llm_e)}") + yield f"\n\n**AI 分析服务异常**: {str(llm_e)}。这可能是由于大模型服务商(如 DeepSeek)响应过慢或连接中断导致的。" + return + + print(f"AI分析完成,共发送 {chunk_count} 个chunks") + except Exception as e: + error_msg = f"\n\n**分析过程出现严重错误**: {str(e)}" + print(error_msg) + yield error_msg + + return StreamingResponse( + generate_stream(), + media_type="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "X-Accel-Buffering": "no", + "Content-Type": "text/event-stream; charset=utf-8" + } + ) @router.get("/api/ylt/stations", response_model=List[StationBase]) diff --git a/Controller/__pycache__/YltAnalyticsController.cpython-310.pyc b/Controller/__pycache__/YltAnalyticsController.cpython-310.pyc index bcfba8a..e94bf76 100644 Binary files a/Controller/__pycache__/YltAnalyticsController.cpython-310.pyc and b/Controller/__pycache__/YltAnalyticsController.cpython-310.pyc differ diff --git a/Util/LlmUtil.py b/Util/LlmUtil.py index 2d2f6df..46bbf8a 100644 --- a/Util/LlmUtil.py +++ b/Util/LlmUtil.py @@ -45,7 +45,7 @@ async def get_llm_response(query_text: str, stream: bool = True, system_prompt: request_params['temperature'] = temperature # 创建请求 - completion = await client.chat.completions.create(**request_params) + completion = await asyncio.wait_for(client.chat.completions.create(**request_params), timeout=60.0) if stream: # 流式输出模式,返回生成器 diff --git a/Util/__pycache__/LlmUtil.cpython-310.pyc b/Util/__pycache__/LlmUtil.cpython-310.pyc index 69b5a82..36a4f51 100644 Binary files a/Util/__pycache__/LlmUtil.cpython-310.pyc and b/Util/__pycache__/LlmUtil.cpython-310.pyc differ diff --git a/static/dashboard.html b/static/dashboard.html index 673f2c0..1d118fa 100644 --- a/static/dashboard.html +++ b/static/dashboard.html @@ -5,6 +5,7 @@