""" AI分析服务 处理Gemini API集成和AI分析功能 """ from typing import Dict, Any, Optional, List import httpx import asyncio import json from datetime import datetime from ..core.exceptions import ( AIAnalysisError, APIError, AuthenticationError, RateLimitError ) from ..schemas.report import AIAnalysisRequest, AIAnalysisResponse class GeminiAnalyzer: """Gemini AI分析器""" def __init__(self, api_key: str, config: Optional[Dict[str, Any]] = None): if not api_key: raise AuthenticationError("gemini", {"message": "Gemini API密钥未配置"}) self.api_key = api_key self.config = config or {} self.base_url = self.config.get("base_url", "https://generativelanguage.googleapis.com/v1beta") self.model = self.config.get("model", "gemini-pro") self.timeout = self.config.get("timeout", 60) self.max_retries = self.config.get("max_retries", 3) self.retry_delay = self.config.get("retry_delay", 2) # 生成配置 self.generation_config = { "temperature": self.config.get("temperature", 0.7), "top_p": self.config.get("top_p", 0.8), "top_k": self.config.get("top_k", 40), "max_output_tokens": self.config.get("max_output_tokens", 8192), } async def analyze_business_info(self, symbol: str, market: str, financial_data: Dict[str, Any]) -> AIAnalysisResponse: """分析公司业务信息""" prompt = self._build_business_info_prompt(symbol, market, financial_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="business_info", content=self._parse_business_info_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"业务信息分析失败: {str(e)}", self.model) async def analyze_fundamental(self, symbol: str, market: str, financial_data: Dict[str, Any], business_info: Dict[str, Any]) -> AIAnalysisResponse: """基本面分析(景林模型)""" prompt = self._build_fundamental_analysis_prompt(symbol, market, financial_data, business_info) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="fundamental_analysis", content=self._parse_fundamental_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"基本面分析失败: {str(e)}", self.model) async def analyze_bullish_case(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """看涨分析(隐藏资产、护城河分析)""" prompt = self._build_bullish_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="bullish_analysis", content=self._parse_bullish_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"看涨分析失败: {str(e)}", self.model) async def analyze_bearish_case(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """看跌分析(价值底线、最坏情况分析)""" prompt = self._build_bearish_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="bearish_analysis", content=self._parse_bearish_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"看跌分析失败: {str(e)}", self.model) async def analyze_market_sentiment(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """市场情绪分析""" prompt = self._build_market_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="market_analysis", content=self._parse_market_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"市场分析失败: {str(e)}", self.model) async def analyze_news_catalysts(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """新闻催化剂分析""" prompt = self._build_news_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="news_analysis", content=self._parse_news_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"新闻分析失败: {str(e)}", self.model) async def analyze_trading_dynamics(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """交易动态分析""" prompt = self._build_trading_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="trading_analysis", content=self._parse_trading_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"交易分析失败: {str(e)}", self.model) async def analyze_insider_institutional(self, symbol: str, market: str, context_data: Dict[str, Any]) -> AIAnalysisResponse: """内部人与机构动向分析""" prompt = self._build_insider_analysis_prompt(symbol, market, context_data) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="insider_analysis", content=self._parse_insider_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"内部人分析失败: {str(e)}", self.model) async def generate_final_conclusion(self, symbol: str, market: str, all_analyses: List[Dict[str, Any]]) -> AIAnalysisResponse: """生成最终结论""" prompt = self._build_conclusion_prompt(symbol, market, all_analyses) try: result = await self._retry_request(self._call_gemini_api, prompt) return AIAnalysisResponse( symbol=symbol, market=market, analysis_type="final_conclusion", content=self._parse_conclusion_response(result), model_used=self.model, generated_at=datetime.now() ) except Exception as e: if isinstance(e, (AIAnalysisError, APIError)): raise raise AIAnalysisError(f"最终结论生成失败: {str(e)}", self.model) async def _call_gemini_api(self, prompt: str) -> str: """调用Gemini API""" url = f"{self.base_url}/models/{self.model}:generateContent" headers = { "Content-Type": "application/json", } data = { "contents": [ { "parts": [ { "text": prompt } ] } ], "generationConfig": self.generation_config } params = { "key": self.api_key } try: async with httpx.AsyncClient(timeout=self.timeout) as client: response = await client.post(url, json=data, headers=headers, params=params) response.raise_for_status() result = response.json() if "error" in result: error = result["error"] error_code = error.get("code", 0) error_message = error.get("message", "Unknown error") if error_code == 401 or "API key" in error_message: raise AuthenticationError("gemini", {"message": error_message}) elif error_code == 429 or "quota" in error_message.lower(): raise RateLimitError("gemini") else: raise APIError(f"Gemini API错误: {error_message}", error_code, "gemini") candidates = result.get("candidates", []) if not candidates: raise AIAnalysisError("Gemini API返回空结果", self.model) content = candidates[0].get("content", {}) parts = content.get("parts", []) if not parts: raise AIAnalysisError("Gemini API返回内容为空", self.model) return parts[0].get("text", "") except httpx.HTTPStatusError as e: if e.response.status_code == 401: raise AuthenticationError("gemini", {"status_code": e.response.status_code}) elif e.response.status_code == 429: raise RateLimitError("gemini") else: raise APIError(f"HTTP错误: {e.response.status_code}", e.response.status_code, "gemini") except httpx.RequestError as e: raise AIAnalysisError(f"网络请求失败: {str(e)}", self.model) async def _retry_request(self, func, *args, **kwargs): """重试机制""" last_exception = None for attempt in range(self.max_retries): try: return await func(*args, **kwargs) except (httpx.TimeoutException, httpx.ConnectError, AIAnalysisError) as e: last_exception = e if attempt < self.max_retries - 1: await asyncio.sleep(self.retry_delay * (2 ** attempt)) # 指数退避 continue except (AuthenticationError, RateLimitError) as e: # 认证错误和频率限制错误不重试 raise e except Exception as e: # 其他异常不重试 raise e # 所有重试都失败了 raise AIAnalysisError( f"Gemini API请求失败,已重试 {self.max_retries} 次: {str(last_exception)}", self.model ) def _build_business_info_prompt(self, symbol: str, market: str, financial_data: Dict[str, Any]) -> str: """构建业务信息分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行全面的业务信息分析。 基于以下财务数据: {json.dumps(financial_data, ensure_ascii=False, indent=2)} 请提供以下内容的详细分析: 1. 公司概览 - 公司基本信息和历史背景 - 主要业务领域和市场地位 2. 主营业务分析 - 核心产品和服务 - 业务模式和盈利模式 - 主要收入来源构成 3. 发展历程 - 重要发展里程碑 - 业务转型和扩张历史 4. 核心团队 - 管理层背景和经验 - 关键人员变动情况 5. 供应链分析 - 主要供应商和客户 - 供应链风险和优势 6. 销售模式 - 销售渠道和策略 - 市场覆盖和客户群体 7. 未来展望 - 发展战略和规划 - 市场机遇和挑战 请用中文回答,内容要详实、客观,基于可获得的公开信息进行分析。 """ def _build_fundamental_analysis_prompt(self, symbol: str, market: str, financial_data: Dict[str, Any], business_info: Dict[str, Any]) -> str: """构建基本面分析提示词(景林模型)""" return f""" 请使用景林投资的基本面分析框架,对股票代码 {symbol}({market}市场)进行深度分析。 财务数据: {json.dumps(financial_data, ensure_ascii=False, indent=2)} 业务信息: {json.dumps(business_info, ensure_ascii=False, indent=2)} 请按照以下景林模型问题集进行分析: 1. 商业模式分析 - 这是一门什么样的生意? - 商业模式的核心竞争力是什么? - 盈利模式是否可持续? 2. 行业地位分析 - 公司在行业中的地位如何? - 市场份额和竞争优势? - 行业发展趋势对公司的影响? 3. 财务质量分析 - 收入增长的质量如何? - 盈利能力和现金流状况? - 资产负债结构是否健康? 4. 管理层评估 - 管理层的能力和诚信度? - 公司治理结构是否完善? - 股东利益是否得到保护? 5. 估值分析 - 当前估值水平是否合理? - 与同行业公司比较如何? - 未来增长预期是否支撑估值? 请用中文提供详细、专业的分析,每个方面都要有具体的数据支撑和逻辑推理。 """ def _build_bullish_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建看涨分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行看涨分析,重点关注隐藏资产和护城河竞争优势。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行看涨分析: 1. 隐藏资产发现 - 账面价值被低估的资产 - 无形资产的真实价值 - 潜在的资产重估机会 2. 护城河分析 - 品牌价值和客户忠诚度 - 技术壁垒和专利保护 - 规模经济和网络效应 - 转换成本和客户粘性 3. 成长潜力 - 新业务和新市场机会 - 产品创新和技术升级 - 市场扩张的可能性 4. 催化剂识别 - 短期可能的积极因素 - 政策支持和行业利好 - 公司内部改革和优化 5. 最佳情况假设 - 如果一切顺利,公司价值可能达到什么水平? - 关键假设和实现路径 请用中文提供乐观但理性的分析,要有具体的逻辑支撑。 """ def _build_bearish_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建看跌分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行看跌分析,重点关注价值底线和最坏情况。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行看跌分析: 1. 价值底线分析 - 清算价值估算 - 资产的最低合理价值 - 下行风险的底线在哪里 2. 主要风险因素 - 行业周期性风险 - 竞争加剧的威胁 - 技术替代的可能性 - 监管政策变化风险 3. 财务脆弱性 - 债务压力和流动性风险 - 现金流恶化的可能性 - 盈利能力下降的风险 4. 管理层风险 - 决策失误的历史 - 治理结构的缺陷 - 利益冲突的可能性 5. 最坏情况假设 - 如果一切都出错,公司价值可能跌到什么水平? - 关键风险因素和触发条件 请用中文提供谨慎但客观的分析,要有具体的风险量化。 """ def _build_market_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建市场分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行市场情绪分析,重点关注分歧点与变化驱动。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行市场分析: 1. 市场情绪评估 - 当前市场对该股票的主流观点 - 机构投资者的持仓变化 - 散户投资者的情绪指标 2. 分歧点识别 - 市场存在哪些主要分歧? - 乐观派和悲观派的核心观点 - 分歧的根本原因是什么? 3. 变化驱动因素 - 什么因素可能改变市场共识? - 关键数据点和时间节点 - 外部环境变化的影响 4. 资金流向分析 - 主力资金的进出情况 - 不同类型投资者的行为模式 - 流动性状况评估 5. 市场预期vs现实 - 市场预期是否过于乐观或悲观? - 预期差的投资机会在哪里? 请用中文提供专业的市场分析,要有数据支撑和逻辑推理。 """ def _build_news_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建新闻分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行新闻催化剂分析,重点关注股价拐点预判。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行新闻分析: 1. 近期重要新闻梳理 - 公司公告和重大事件 - 行业政策和监管变化 - 宏观经济相关新闻 2. 催化剂识别 - 正面催化剂(业绩超预期、政策利好等) - 负面催化剂(风险事件、竞争加剧等) - 中性但重要的信息 3. 拐点预判 - 基本面拐点的可能时间 - 市场情绪拐点的信号 - 技术面拐点的确认 4. 新闻影响评估 - 短期影响vs长期影响 - 市场反应是否充分 - 后续发展的可能路径 5. 关注要点 - 未来需要重点关注的事件 - 可能的风险点和机会点 - 时间窗口和操作建议 请用中文提供前瞻性的分析,要有时间维度和影响程度的判断。 """ def _build_trading_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建交易分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行交易分析,重点关注市场体量与增长路径。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行交易分析: 1. 市场体量分析 - 总市值和流通市值 - 日均成交量和换手率 - 市场容量和流动性评估 2. 增长路径分析 - 历史增长轨迹和驱动因素 - 未来增长的可能路径 - 增长的可持续性评估 3. 交易特征分析 - 股价波动特征和规律 - 主要交易时段和模式 - 大宗交易和异常交易情况 4. 技术面分析 - 关键技术位和支撑阻力 - 趋势线和形态分析 - 技术指标的信号 5. 交易策略建议 - 适合的交易时机和方式 - 风险控制和仓位管理 - 进出场点位的选择 请用中文提供实用的交易分析,要有具体的数据和操作建议。 """ def _build_insider_analysis_prompt(self, symbol: str, market: str, context_data: Dict[str, Any]) -> str: """构建内部人分析提示词""" return f""" 请对股票代码 {symbol}({market}市场)进行内部人与机构动向分析。 基础数据: {json.dumps(context_data, ensure_ascii=False, indent=2)} 请从以下角度进行分析: 1. 内部人交易分析 - 高管和大股东的买卖行为 - 内部人交易的时机和规模 - 内部人交易的信号意义 2. 机构持仓分析 - 主要机构投资者的持仓变化 - 新进和退出的机构情况 - 机构持仓集中度分析 3. 股东结构变化 - 股权结构的演变趋势 - 重要股东的进出情况 - 股权激励和员工持股情况 4. 资金流向追踪 - 大资金的进出时机 - 不同类型资金的偏好 - 资金成本和收益预期 5. 动向信号解读 - 内部人和机构行为的一致性 - 与股价走势的相关性 - 对未来走势的指示意义 请用中文提供专业的分析,要有数据支撑和逻辑推理。 """ def _build_conclusion_prompt(self, symbol: str, market: str, all_analyses: List[Dict[str, Any]]) -> str: """构建最终结论提示词""" analyses_text = "\n\n".join([ f"{analysis.get('analysis_type', '未知分析')}:\n{json.dumps(analysis.get('content', {}), ensure_ascii=False, indent=2)}" for analysis in all_analyses ]) return f""" 基于以下所有分析结果,请对股票代码 {symbol}({market}市场)给出最终投资结论。 所有分析结果: {analyses_text} 请从以下角度进行综合分析: 1. 关键矛盾识别 - 当前最核心的投资矛盾是什么? - 不同分析维度的结论是否一致? - 主要的不确定性因素有哪些? 2. 预期差分析 - 市场预期与实际情况的差异 - 可能被忽视或误解的关键信息 - 预期差带来的投资机会 3. 拐点临近性判断 - 基本面拐点的时间和概率 - 市场情绪拐点的信号 - 催化剂的时效性分析 4. 风险收益评估 - 上行空间和下行风险的量化 - 风险调整后的收益预期 - 投资的风险收益比 5. 最终投资建议 - 明确的投资观点(看多/看空/中性) - 建议的投资时间框架 - 关键的跟踪指标和退出条件 请用中文提供清晰、明确的投资结论,要有逻辑性和可操作性。 """ def _parse_business_info_response(self, response: str) -> Dict[str, Any]: """解析业务信息分析响应""" return { "company_overview": self._extract_section(response, "公司概览"), "main_business": self._extract_section(response, "主营业务"), "development_history": self._extract_section(response, "发展历程"), "core_team": self._extract_section(response, "核心团队"), "supply_chain": self._extract_section(response, "供应链"), "sales_model": self._extract_section(response, "销售模式"), "future_outlook": self._extract_section(response, "未来展望"), "full_analysis": response } def _parse_fundamental_response(self, response: str) -> Dict[str, Any]: """解析基本面分析响应""" return { "business_model": self._extract_section(response, "商业模式"), "industry_position": self._extract_section(response, "行业地位"), "financial_quality": self._extract_section(response, "财务质量"), "management_assessment": self._extract_section(response, "管理层"), "valuation_analysis": self._extract_section(response, "估值分析"), "full_analysis": response } def _parse_bullish_response(self, response: str) -> Dict[str, Any]: """解析看涨分析响应""" return { "hidden_assets": self._extract_section(response, "隐藏资产"), "moat_analysis": self._extract_section(response, "护城河"), "growth_potential": self._extract_section(response, "成长潜力"), "catalysts": self._extract_section(response, "催化剂"), "best_case": self._extract_section(response, "最佳情况"), "full_analysis": response } def _parse_bearish_response(self, response: str) -> Dict[str, Any]: """解析看跌分析响应""" return { "value_floor": self._extract_section(response, "价值底线"), "risk_factors": self._extract_section(response, "风险因素"), "financial_vulnerability": self._extract_section(response, "财务脆弱性"), "management_risks": self._extract_section(response, "管理层风险"), "worst_case": self._extract_section(response, "最坏情况"), "full_analysis": response } def _parse_market_response(self, response: str) -> Dict[str, Any]: """解析市场分析响应""" return { "market_sentiment": self._extract_section(response, "市场情绪"), "disagreement_points": self._extract_section(response, "分歧点"), "change_drivers": self._extract_section(response, "变化驱动"), "capital_flow": self._extract_section(response, "资金流向"), "expectation_vs_reality": self._extract_section(response, "预期vs现实"), "full_analysis": response } def _parse_news_response(self, response: str) -> Dict[str, Any]: """解析新闻分析响应""" return { "recent_news": self._extract_section(response, "重要新闻"), "catalysts": self._extract_section(response, "催化剂"), "inflection_points": self._extract_section(response, "拐点预判"), "news_impact": self._extract_section(response, "影响评估"), "focus_points": self._extract_section(response, "关注要点"), "full_analysis": response } def _parse_trading_response(self, response: str) -> Dict[str, Any]: """解析交易分析响应""" return { "market_size": self._extract_section(response, "市场体量"), "growth_path": self._extract_section(response, "增长路径"), "trading_characteristics": self._extract_section(response, "交易特征"), "technical_analysis": self._extract_section(response, "技术面"), "trading_strategy": self._extract_section(response, "交易策略"), "full_analysis": response } def _parse_insider_response(self, response: str) -> Dict[str, Any]: """解析内部人分析响应""" return { "insider_trading": self._extract_section(response, "内部人交易"), "institutional_holdings": self._extract_section(response, "机构持仓"), "ownership_changes": self._extract_section(response, "股东结构"), "capital_flow": self._extract_section(response, "资金流向"), "signal_interpretation": self._extract_section(response, "信号解读"), "full_analysis": response } def _parse_conclusion_response(self, response: str) -> Dict[str, Any]: """解析最终结论响应""" return { "key_contradictions": self._extract_section(response, "关键矛盾"), "expectation_gap": self._extract_section(response, "预期差"), "inflection_timing": self._extract_section(response, "拐点临近性"), "risk_return": self._extract_section(response, "风险收益"), "investment_recommendation": self._extract_section(response, "投资建议"), "full_analysis": response } def _extract_section(self, text: str, section_name: str) -> str: """从文本中提取特定章节内容""" lines = text.split('\n') section_content = [] in_section = False for line in lines: if section_name in line and ('.' in line or ':' in line or ':' in line): in_section = True section_content.append(line) continue if in_section: if line.strip() and any(keyword in line for keyword in ['1.', '2.', '3.', '4.', '5.']) and section_name not in line: # 遇到下一个主要章节,停止 break section_content.append(line) return '\n'.join(section_content).strip() class AIAnalyzerFactory: """AI分析器工厂""" @classmethod def create_gemini_analyzer(cls, api_key: str, config: Optional[Dict[str, Any]] = None) -> GeminiAnalyzer: """创建Gemini分析器""" return GeminiAnalyzer(api_key, config) @classmethod def create_analyzer(cls, analyzer_type: str, **kwargs) -> GeminiAnalyzer: """创建分析器(可扩展支持其他AI服务)""" if analyzer_type.lower() == "gemini": return cls.create_gemini_analyzer(kwargs.get("api_key"), kwargs.get("config")) else: raise AIAnalysisError(f"不支持的AI分析器类型: {analyzer_type}")