- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
200 lines
5.4 KiB
Python
200 lines
5.4 KiB
Python
"""
|
||
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
|
||
|