增加后台管理项目;
This commit is contained in:
@@ -24,8 +24,27 @@ async def lifespan(app: FastAPI):
|
||||
) # 批量注册模型
|
||||
print(f"🗄️ 数据库连接成功: {database_name}")
|
||||
|
||||
# 初始化GLM服务,确保默认角色存在
|
||||
try:
|
||||
from app.services.glm_service import get_glm_service
|
||||
|
||||
glm_service = get_glm_service()
|
||||
await glm_service.ensure_default_role()
|
||||
print(f"✅ GLM角色配置初始化完成")
|
||||
except Exception as e:
|
||||
print(f"⚠️ GLM角色配置初始化失败: {e}")
|
||||
|
||||
yield # 应用在此处进入正常运行状态,接收请求
|
||||
|
||||
# 关闭GLM服务
|
||||
try:
|
||||
from app.services.glm_service import close_glm_service
|
||||
|
||||
close_glm_service()
|
||||
print(f"GLM服务已关闭")
|
||||
except Exception as e:
|
||||
print(f"关闭GLM服务时出错: {e}")
|
||||
|
||||
# 关闭数据库连接
|
||||
await client.close()
|
||||
print(f"数据库连接关闭")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from .base import BaseOut
|
||||
from .message import Message
|
||||
from .glm_role import GLMRole
|
||||
|
||||
models = [Message]
|
||||
models = [Message, GLMRole]
|
||||
|
||||
# 在所有模型导入后调用 model_rebuild 来解决循环导入问题
|
||||
# Message.model_rebuild()
|
||||
|
||||
56
backend/app/models/glm_role.py
Normal file
56
backend/app/models/glm_role.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from pydantic import Field
|
||||
from beanie import Document
|
||||
import arrow
|
||||
|
||||
|
||||
class GLMRole(Document):
|
||||
"""GLM角色配置模型"""
|
||||
|
||||
# 基本信息
|
||||
name: str = Field(..., description="角色名称")
|
||||
description: str = Field(..., description="角色描述")
|
||||
skills: str = Field(default="", description="技能列表(多行文本字符串)")
|
||||
restrictions: str = Field(default="", description="限制列表(多行文本字符串)")
|
||||
|
||||
# 状态管理
|
||||
is_active: bool = Field(default=True, description="是否激活")
|
||||
is_default: bool = Field(default=False, description="是否为默认角色")
|
||||
|
||||
# 时间戳
|
||||
created_at: datetime = Field(default_factory=lambda: arrow.now().datetime)
|
||||
updated_at: datetime = Field(default_factory=lambda: arrow.now().datetime)
|
||||
|
||||
class Settings:
|
||||
name = "glm_roles" # MongoDB集合名称
|
||||
indexes = [
|
||||
[("is_active", 1)], # 按激活状态查询
|
||||
[("is_default", 1)], # 按默认状态查询
|
||||
[("created_at", -1)], # 按创建时间排序
|
||||
]
|
||||
|
||||
def update_timestamp(self):
|
||||
"""更新更新时间戳"""
|
||||
self.updated_at = arrow.now().datetime
|
||||
|
||||
@classmethod
|
||||
def from_role_config(cls, role_config: dict) -> "GLMRole":
|
||||
"""从角色配置字典创建GLMRole实例"""
|
||||
return cls(
|
||||
name=role_config.get("name", ""),
|
||||
description=role_config.get("description", ""),
|
||||
skills=role_config.get("skills", ""),
|
||||
restrictions=role_config.get("restrictions", ""),
|
||||
is_active=True,
|
||||
is_default=True,
|
||||
)
|
||||
|
||||
def to_role_config_dict(self) -> dict:
|
||||
"""转换为角色配置字典"""
|
||||
return {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"skills": self.skills,
|
||||
"restrictions": self.restrictions,
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import httpx
|
||||
import os
|
||||
import logging
|
||||
|
||||
from app.services.glm_service import get_glm_service, GLMService
|
||||
from app.models.base import BaseOut
|
||||
|
||||
# 创建API路由,使用/glm前缀
|
||||
router = APIRouter(prefix="/glm", tags=["GLM聊天"])
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# 数据模型
|
||||
class Message(BaseModel):
|
||||
@@ -27,73 +33,79 @@ class ChatResponse(BaseModel):
|
||||
class RoleConfig(BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
skills: List[str]
|
||||
restrictions: List[str]
|
||||
skills: str # 多行文本字符串
|
||||
restrictions: str # 多行文本字符串
|
||||
|
||||
|
||||
# 默认角色配置
|
||||
# 默认角色配置(使用字符串格式)
|
||||
DEFAULT_ROLE_CONFIG = RoleConfig(
|
||||
name="兜兜",
|
||||
description="你是兜兜,1989年出生,有一个5岁的女儿(2018年出生)。你是一位对用户负责、选品严格的宝妈客服。当用户给你发来疑问时,你总能第一时间回复,并以亲切温暖的风格与用户交流。",
|
||||
skills=[
|
||||
"亲切温暖的沟通",
|
||||
"轻松友好的语气",
|
||||
"简洁明了的描述",
|
||||
"亲昵且口语化的用词",
|
||||
"乱码话术重复问询",
|
||||
],
|
||||
restrictions=[
|
||||
"回复必须基于提供的知识库中的内容",
|
||||
"每条回复应保持在80字以内,使用口语化语言",
|
||||
"回复中可以适当加入表情符号,但不要过度使用",
|
||||
"只讨论与宝妈相关的主题,如育儿、选品、团购等",
|
||||
"始终以用户的需求和满意度为优先",
|
||||
],
|
||||
skills="亲切温暖的沟通\n轻松友好的语气\n简洁明了的描述\n亲昵且口语化的用词\n乱码话术重复问询",
|
||||
restrictions="回复必须基于提供的知识库中的内容\n每条回复应保持在80字以内,使用口语化语言\n回复中可以适当加入表情符号,但不要过度使用\n只讨论与宝妈相关的主题,如育儿、选品、团购等\n始终以用户的需求和满意度为优先",
|
||||
)
|
||||
|
||||
# 全局变量存储角色配置和对话历史
|
||||
current_role_config = DEFAULT_ROLE_CONFIG
|
||||
# 全局变量存储对话历史(暂时保持内存存储,根据用户要求不持久化)
|
||||
conversations = {}
|
||||
|
||||
|
||||
def build_system_prompt(role_config: RoleConfig) -> str:
|
||||
"""构建系统提示词"""
|
||||
def build_system_prompt(role_config: dict) -> str:
|
||||
"""构建系统提示词
|
||||
|
||||
Args:
|
||||
role_config: 角色配置字典,包含name, description, skills, restrictions字段
|
||||
|
||||
Returns:
|
||||
系统提示词字符串
|
||||
"""
|
||||
# 解析技能字符串为列表
|
||||
skills_list = [
|
||||
skill.strip() for skill in role_config["skills"].split("\n") if skill.strip()
|
||||
]
|
||||
restrictions_list = [
|
||||
restriction.strip()
|
||||
for restriction in role_config["restrictions"].split("\n")
|
||||
if restriction.strip()
|
||||
]
|
||||
|
||||
# 构建技能部分
|
||||
skills_section = ""
|
||||
for i, skill in enumerate(skills_list, 1):
|
||||
skills_section += f"\n### 技能 {i}: {skill}"
|
||||
# 添加技能描述(这里可以根据技能名称添加不同的描述)
|
||||
if "亲切温暖" in skill:
|
||||
skills_section += '\n- **风格**:你的风格非常亲切,像是一个知心朋友在和大家聊天,给人一种温暖的感觉。你经常使用"宝"、"美妈"等亲昵的称呼,拉近了与用户的距离。'
|
||||
skills_section += "\n- **耐心细致**:你总是耐心解答用户的问题,尤其是对于新用户或不熟悉流程的用户,你会反复提醒他们查看群公告,保持安静等待开团。"
|
||||
skills_section += "\n- **负责靠谱**:你给人一种非常靠谱的感觉,尤其是在售后问题上,你会主动承担责任,承诺包处理满意,给用户很强的信任感。"
|
||||
elif "轻松友好" in skill:
|
||||
skills_section += '\n- **轻松友好**:你的语气非常轻松,像是在和朋友聊天,不会给人压力。比如"不着急哦"、"爱你!"等表达方式,让人感到轻松愉快。'
|
||||
skills_section += '\n- **安抚性**:当用户有疑问或遇到问题时,你的语气总是充满安抚性,比如"美妈不用担心"、"兜兜来处理哈",给人一种安心感。'
|
||||
skills_section += '\n- **鼓励性**:你经常用"不着急"、"等开团的时候会有统一说明"等话语,鼓励用户耐心等待,保持积极的心态。'
|
||||
elif "简洁明了" in skill:
|
||||
skills_section += '\n- **简洁明了**:你的描述非常简洁,直接回答用户的问题,不会绕弯子。比如"贝亲是咱们深度合作品牌,经常上团的",直接给出了明确的答案。'
|
||||
skills_section += '\n- **细节丰富**:在回答问题时,你会提供一些细节信息,比如"一般2天左右就会来"、"奶粉类的,贝亲,世喜,赫根,等常见品牌都有",让用户对情况有更清晰的了解。'
|
||||
skills_section += '\n- **引导性**:你经常引导用户查看群公告,保持安静等待开团,比如"不熟悉的可以看一下群公告哦"、"现在安静的等开团就行啦",帮助用户更好地参与活动。'
|
||||
elif "亲昵且口语化" in skill:
|
||||
skills_section += '\n- **亲昵称呼**:你经常使用"宝"、"美妈"、"大家"等亲昵的称呼,拉近与用户的距离,给人一种亲切感。'
|
||||
skills_section += '\n- **口语化**:你的用词非常口语化,像是日常聊天,比如"不着急哦"、"爱你!"、"兜兜来处理哈",给人一种轻松自然的感觉,称呼自己为"兜兜"或者"我"。'
|
||||
skills_section += '\n- **积极正面**:你的用词总是积极正面的,比如"不用担心"、"包处理满意"、"很快"、"常见品牌都有",给用户传递了积极的信息。'
|
||||
elif "乱码话术" in skill:
|
||||
skills_section += '\n- **确认问题**:用户说的看不懂的情况来进行再次确认,比如 "45615465" "分隔符规划规范的" "8分隔符公司的"等跟常规问题没关联的话术来重复确认,例如:"宝看不懂你说的是什么啊,能在具体点嘛"'
|
||||
else:
|
||||
skills_section += f"\n- **{skill}**:请根据这个技能特点来回答问题。"
|
||||
|
||||
# 构建限制部分
|
||||
restrictions_section = "\n".join(
|
||||
[f"- {restriction}" for restriction in restrictions_list]
|
||||
)
|
||||
|
||||
prompt = f"""# 角色
|
||||
{role_config.description}
|
||||
{role_config["description"]}
|
||||
|
||||
## 技能
|
||||
|
||||
### 技能 1: 亲切温暖的沟通
|
||||
- **风格**:你的风格非常亲切,像是一个知心朋友在和大家聊天,给人一种温暖的感觉。你经常使用"宝"、"美妈"等亲昵的称呼,拉近了与用户的距离。
|
||||
- **耐心细致**:你总是耐心解答用户的问题,尤其是对于新用户或不熟悉流程的用户,你会反复提醒他们查看群公告,保持安静等待开团。
|
||||
- **负责靠谱**:你给人一种非常靠谱的感觉,尤其是在售后问题上,你会主动承担责任,承诺包处理满意,给用户很强的信任感。
|
||||
|
||||
### 技能 2: 轻松友好的语气
|
||||
- **轻松友好**:你的语气非常轻松,像是在和朋友聊天,不会给人压力。比如"不着急哦"、"爱你!"等表达方式,让人感到轻松愉快。
|
||||
- **安抚性**:当用户有疑问或遇到问题时,你的语气总是充满安抚性,比如"美妈不用担心"、"兜兜来处理哈",给人一种安心感。
|
||||
- **鼓励性**:你经常用"不着急"、"等开团的时候会有统一说明"等话语,鼓励用户耐心等待,保持积极的心态。
|
||||
|
||||
### 技能 3: 简洁明了的描述
|
||||
- **简洁明了**:你的描述非常简洁,直接回答用户的问题,不会绕弯子。比如"贝亲是咱们深度合作品牌,经常上团的",直接给出了明确的答案。
|
||||
- **细节丰富**:在回答问题时,你会提供一些细节信息,比如"一般2天左右就会来"、"奶粉类的,贝亲,世喜,赫根,等常见品牌都有",让用户对情况有更清晰的了解。
|
||||
- **引导性**:你经常引导用户查看群公告,保持安静等待开团,比如"不熟悉的可以看一下群公告哦"、"现在安静的等开团就行啦",帮助用户更好地参与活动。
|
||||
|
||||
### 技能 4: 亲昵且口语化的用词
|
||||
- **亲昵称呼**:你经常使用"宝"、"美妈"、"大家"等亲昵的称呼,拉近与用户的距离,给人一种亲切感。
|
||||
- **口语化**:你的用词非常口语化,像是日常聊天,比如"不着急哦"、"爱你!"、"兜兜来处理哈",给人一种轻松自然的感觉,称呼自己为"兜兜"或者"我"。
|
||||
- **积极正面**:你的用词总是积极正面的,比如"不用担心"、"包处理满意"、"很快"、"常见品牌都有",给用户传递了积极的信息。
|
||||
|
||||
### 技能 5: 乱码话术重复问询
|
||||
- **确认问题**:用户说的看不懂的情况来进行再次确认,比如 "45615465" "分隔符规划规范的" "8分隔符公司的"等跟常规问题没关联的话术来重复确认,例如:"宝看不懂你说的是什么啊,能在具体点嘛"
|
||||
## 技能{skills_section}
|
||||
|
||||
## 限制
|
||||
- 回复必须基于提供的知识库中的内容。
|
||||
- 每条回复应保持在80字以内,使用口语化语言。
|
||||
- 回复中可以适当加入表情符号,但不要过度使用。
|
||||
- 只讨论与宝妈相关的主题,如育儿、选品、团购等。
|
||||
- 始终以用户的需求和满意度为优先。
|
||||
- 在回答问题时,确保信息准确无误,避免误导用户。
|
||||
- 保持沟通的亲切性和温暖感,但不要过度使用亲昵称呼,以免显得过于随意。
|
||||
{restrictions_section}
|
||||
|
||||
请按照以上角色设定和限制来回答用户的问题。"""
|
||||
return prompt
|
||||
@@ -145,22 +157,42 @@ async def root():
|
||||
return {"message": "DeepSeek AI Chat Assistant API"}
|
||||
|
||||
|
||||
@router.get("/role")
|
||||
async def get_role_config():
|
||||
@router.get("/role", response_model=BaseOut)
|
||||
async def get_role_config(service: GLMService = Depends(get_glm_service)):
|
||||
"""获取当前角色配置"""
|
||||
return current_role_config
|
||||
try:
|
||||
role = await service.get_current_role()
|
||||
return BaseOut(code=200, message="ok", result=role.to_role_config_dict())
|
||||
except Exception as e:
|
||||
logger.error(f"获取角色配置失败: {e}")
|
||||
return BaseOut(code=500, message=f"获取角色配置失败: {str(e)}", result=None)
|
||||
|
||||
|
||||
@router.put("/role")
|
||||
async def update_role_config(role_config: RoleConfig):
|
||||
@router.put("/role", response_model=BaseOut)
|
||||
async def update_role_config(
|
||||
role_config: RoleConfig, service: GLMService = Depends(get_glm_service)
|
||||
):
|
||||
"""更新角色配置"""
|
||||
global current_role_config
|
||||
current_role_config = role_config
|
||||
return {"message": "角色配置已更新", "role_config": current_role_config}
|
||||
try:
|
||||
# 将Pydantic模型转换为字典
|
||||
role_data = role_config.dict()
|
||||
updated_role = await service.update_role(role_data)
|
||||
|
||||
return BaseOut(
|
||||
code=200,
|
||||
message="角色配置已更新",
|
||||
result={
|
||||
"message": "角色配置已更新",
|
||||
"role_config": updated_role.to_role_config_dict(),
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新角色配置失败: {e}")
|
||||
return BaseOut(code=500, message=f"更新角色配置失败: {str(e)}", result=None)
|
||||
|
||||
|
||||
@router.post("/chat", response_model=ChatResponse)
|
||||
async def chat(request: ChatRequest):
|
||||
async def chat(request: ChatRequest, service: GLMService = Depends(get_glm_service)):
|
||||
"""处理聊天请求"""
|
||||
# 创建或获取对话历史
|
||||
if request.conversation_id and request.conversation_id in conversations:
|
||||
@@ -170,8 +202,16 @@ async def chat(request: ChatRequest):
|
||||
request.conversation_id = f"conv_{len(conversations) + 1}"
|
||||
conversations[request.conversation_id] = conversation_history
|
||||
|
||||
# 获取当前角色配置
|
||||
try:
|
||||
role = await service.get_current_role()
|
||||
system_prompt = build_system_prompt(role.to_role_config_dict())
|
||||
except Exception as e:
|
||||
logger.error(f"获取角色配置失败: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"获取角色配置失败: {str(e)}")
|
||||
|
||||
# 构建消息列表
|
||||
messages = [{"role": "system", "content": build_system_prompt(current_role_config)}]
|
||||
messages = [{"role": "system", "content": system_prompt}]
|
||||
|
||||
# 添加历史消息
|
||||
for msg in conversation_history[-10:]: # 保留最近10条历史
|
||||
@@ -204,3 +244,24 @@ async def get_conversation(conversation_id: str):
|
||||
"conversation_id": conversation_id,
|
||||
"messages": conversations[conversation_id],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/role/reset", response_model=BaseOut)
|
||||
async def reset_role_config(service: GLMService = Depends(get_glm_service)):
|
||||
"""重置角色配置为默认值"""
|
||||
try:
|
||||
# 使用 DEFAULT_ROLE_CONFIG 更新角色
|
||||
role_data = DEFAULT_ROLE_CONFIG.dict()
|
||||
updated_role = await service.update_role(role_data)
|
||||
|
||||
return BaseOut(
|
||||
code=200,
|
||||
message="角色配置已重置为默认值",
|
||||
result={
|
||||
"message": "角色配置已重置为默认值",
|
||||
"role_config": updated_role.to_role_config_dict(),
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"重置角色配置失败: {e}")
|
||||
return BaseOut(code=500, message=f"重置角色配置失败: {str(e)}", result=None)
|
||||
|
||||
125
backend/app/services/glm_service.py
Normal file
125
backend/app/services/glm_service.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
GLM聊天服务,包含角色配置管理
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from app.models.glm_role import GLMRole
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GLMService:
|
||||
"""GLM聊天服务,包含角色配置管理"""
|
||||
|
||||
def __init__(self):
|
||||
self._current_role: Optional[GLMRole] = None
|
||||
|
||||
async def get_current_role(self) -> GLMRole:
|
||||
"""获取当前激活的角色配置"""
|
||||
if self._current_role is None:
|
||||
self._current_role = await self._load_or_create_role()
|
||||
return self._current_role
|
||||
|
||||
async def update_role(self, role_data: dict) -> GLMRole:
|
||||
"""更新角色配置
|
||||
|
||||
Args:
|
||||
role_data: 角色配置数据,包含name, description, skills, restrictions字段
|
||||
|
||||
Returns:
|
||||
更新后的GLMRole实例
|
||||
"""
|
||||
role = await self.get_current_role()
|
||||
|
||||
# 更新字段
|
||||
if "name" in role_data:
|
||||
role.name = role_data["name"]
|
||||
if "description" in role_data:
|
||||
role.description = role_data["description"]
|
||||
if "skills" in role_data:
|
||||
role.skills = role_data["skills"]
|
||||
if "restrictions" in role_data:
|
||||
role.restrictions = role_data["restrictions"]
|
||||
|
||||
role.update_timestamp()
|
||||
await role.save()
|
||||
|
||||
# 更新缓存
|
||||
self._current_role = role
|
||||
|
||||
logger.info(f"角色配置已更新: {role.name}")
|
||||
return role
|
||||
|
||||
async def _load_or_create_role(self) -> GLMRole:
|
||||
"""加载或创建默认角色"""
|
||||
# 1. 尝试获取激活的角色
|
||||
active_role = await GLMRole.find_one(GLMRole.is_active == True)
|
||||
if active_role:
|
||||
logger.info(f"加载激活角色: {active_role.name}")
|
||||
return active_role
|
||||
|
||||
# 2. 尝试获取默认角色
|
||||
default_role = await GLMRole.find_one(GLMRole.is_default == True)
|
||||
if default_role:
|
||||
default_role.is_active = True
|
||||
await default_role.save()
|
||||
logger.info(f"加载默认角色并激活: {default_role.name}")
|
||||
return default_role
|
||||
|
||||
# 3. 创建默认角色
|
||||
logger.info("创建默认角色配置")
|
||||
return await self._create_default_role()
|
||||
|
||||
async def _create_default_role(self) -> GLMRole:
|
||||
"""创建默认角色配置"""
|
||||
# 导入DEFAULT_ROLE_CONFIG
|
||||
from app.routes.glm_routes import DEFAULT_ROLE_CONFIG
|
||||
|
||||
role = GLMRole(
|
||||
name=DEFAULT_ROLE_CONFIG.name,
|
||||
description=DEFAULT_ROLE_CONFIG.description,
|
||||
skills=DEFAULT_ROLE_CONFIG.skills,
|
||||
restrictions=DEFAULT_ROLE_CONFIG.restrictions,
|
||||
is_active=True,
|
||||
is_default=True,
|
||||
)
|
||||
await role.save()
|
||||
|
||||
logger.info(f"默认角色创建成功: {role.name}")
|
||||
return role
|
||||
|
||||
async def ensure_default_role(self):
|
||||
"""确保存在默认角色配置(应用启动时调用)"""
|
||||
try:
|
||||
role = await self._load_or_create_role()
|
||||
logger.info(f"默认角色已确保存在: {role.name}")
|
||||
return role
|
||||
except Exception as e:
|
||||
logger.error(f"确保默认角色存在时出错: {e}")
|
||||
raise
|
||||
|
||||
async def close(self):
|
||||
"""关闭服务,清理资源"""
|
||||
self._current_role = None
|
||||
logger.info("GLM服务已关闭")
|
||||
|
||||
|
||||
# 全局服务实例(单例模式)
|
||||
_glm_service: Optional[GLMService] = None
|
||||
|
||||
|
||||
def get_glm_service() -> GLMService:
|
||||
"""获取GLM服务实例(单例)"""
|
||||
global _glm_service
|
||||
if _glm_service is None:
|
||||
_glm_service = GLMService()
|
||||
return _glm_service
|
||||
|
||||
|
||||
def close_glm_service():
|
||||
"""关闭GLM服务"""
|
||||
global _glm_service
|
||||
if _glm_service:
|
||||
_glm_service.close()
|
||||
_glm_service = None
|
||||
Reference in New Issue
Block a user