""" Coze AI对话服务 """ import logging from typing import Optional from cozepy import Coze, COZE_CN_BASE_URL, Message from cozepy.exception import CozeError, CozeAPIError from app.core.config import settings from app.services.ai.coze.client import get_auth_manager # 注意:不再直接使用 TokenAuth,统一通过 get_auth_manager() 管理认证 logger = logging.getLogger(__name__) class CozeService: """Coze对话服务""" def __init__(self): """初始化Coze客户端""" if not settings.COZE_PRACTICE_BOT_ID: raise ValueError("COZE_PRACTICE_BOT_ID 未配置") self.bot_id = settings.COZE_PRACTICE_BOT_ID self._auth_manager = get_auth_manager() logger.info( f"CozeService初始化成功,Bot ID={self.bot_id}, " f"Base URL={COZE_CN_BASE_URL}" ) @property def client(self) -> Coze: """获取Coze客户端(每次获取确保OAuth token有效)""" return self._auth_manager.get_client(force_new=True) def build_scene_prompt( self, scene_name: str, scene_background: str, scene_ai_role: str, scene_objectives: list, scene_keywords: Optional[list] = None, scene_description: Optional[str] = None, user_message: str = "" ) -> str: """ 构建场景提示词(Markdown格式) 参数: scene_name: 场景名称 scene_background: 场景背景 scene_ai_role: AI角色描述 scene_objectives: 练习目标列表 scene_keywords: 关键词列表 scene_description: 场景描述(可选) user_message: 用户第一句话 返回: 完整的场景提示词(Markdown格式) """ # 构建练习目标 objectives_text = "\n".join( f"{i+1}. {obj}" for i, obj in enumerate(scene_objectives) ) # 构建关键词 keywords_text = ", ".join(scene_keywords) if scene_keywords else "" # 构建完整提示词 prompt = f"""# 陪练场景设定 ## 场景名称 {scene_name} """ # 添加场景描述(如果有) if scene_description: prompt += f""" ## 场景描述 {scene_description} """ prompt += f""" ## 场景背景 {scene_background} ## AI角色要求 {scene_ai_role} ## 练习目标 {objectives_text} """ # 添加关键词(如果有) if keywords_text: prompt += f""" ## 关键词 {keywords_text} """ prompt += f""" --- 现在开始陪练对话。请你严格按照上述场景设定扮演角色,与学员进行实战对话练习。 不要提及"场景设定"或"角色扮演"等元信息,直接进入角色开始对话。 学员的第一句话:{user_message} """ return prompt def create_stream_chat( self, user_id: str, message: str, conversation_id: Optional[str] = None ): """ 创建流式对话 参数: user_id: 用户ID message: 消息内容 conversation_id: 对话ID(续接对话时使用) 返回: Coze流式对话迭代器 """ try: logger.info( f"创建Coze流式对话,user_id={user_id}, " f"conversation_id={conversation_id}, " f"message_length={len(message)}" ) stream = self.client.chat.stream( bot_id=self.bot_id, user_id=user_id, additional_messages=[Message.build_user_question_text(message)], conversation_id=conversation_id ) # 记录LogID用于排查问题 if hasattr(stream, 'response') and hasattr(stream.response, 'logid'): logger.info(f"Coze对话创建成功,logid={stream.response.logid}") return stream except (CozeError, CozeAPIError) as e: logger.error(f"Coze API调用失败: {e}") raise except Exception as e: logger.error(f"创建Coze对话失败: {e}") raise def cancel_chat(self, conversation_id: str, chat_id: str): """ 中断对话 参数: conversation_id: 对话ID chat_id: 聊天ID """ try: logger.info(f"中断Coze对话,conversation_id={conversation_id}, chat_id={chat_id}") result = self.client.chat.cancel( conversation_id=conversation_id, chat_id=chat_id ) logger.info(f"对话中断成功") return result except (CozeError, CozeAPIError) as e: logger.error(f"中断对话失败: {e}") raise except Exception as e: logger.error(f"中断对话异常: {e}") raise # 单例模式 _coze_service: Optional[CozeService] = None def get_coze_service() -> CozeService: """ 获取CozeService单例 用于FastAPI依赖注入 """ global _coze_service if _coze_service is None: _coze_service = CozeService() return _coze_service