diff --git a/data/tsp_assistant.db b/data/tsp_assistant.db index 89ee44e..3ed182e 100644 Binary files a/data/tsp_assistant.db and b/data/tsp_assistant.db differ diff --git a/src/agent/react_agent.py b/src/agent/react_agent.py index 23c0969..042add9 100644 --- a/src/agent/react_agent.py +++ b/src/agent/react_agent.py @@ -77,7 +77,7 @@ def _build_tools_prompt() -> str: return "\n".join(lines) -SYSTEM_PROMPT = f"""你是 TSP 智能客服助手,帮助用户解决车辆售后问题、查询知识库、管理客诉信息。 +SYSTEM_PROMPT = f"""你是 TSP 智能客服助手,帮助用户解决售后问题、查询知识库、管理客诉信息。 你可以使用以下工具来完成任务: {_build_tools_prompt()} diff --git a/src/core/llm_client.py b/src/core/llm_client.py index 80713c1..15c554d 100644 --- a/src/core/llm_client.py +++ b/src/core/llm_client.py @@ -140,7 +140,7 @@ class LLMClient: knowledge_base: Optional[List[str]] = None, ) -> Dict[str, Any]: """快捷生成回复""" - system_prompt = "你是一个专业的客服助手,请根据用户问题提供准确、有帮助的回复。" + system_prompt = "你是一个专业的智能客服助手,请根据用户问题提供准确、有帮助的回复。" if context: system_prompt += f"\n\n上下文信息: {context}" if knowledge_base: diff --git a/src/dialogue/realtime_chat.py b/src/dialogue/realtime_chat.py index 724bc05..899dd0d 100644 --- a/src/dialogue/realtime_chat.py +++ b/src/dialogue/realtime_chat.py @@ -164,7 +164,8 @@ class RealtimeChatManager: user_message, knowledge_results, session["context"], - session["work_order_id"] + session["work_order_id"], + tenant_id=session_tenant ) # 创建助手消息 @@ -228,14 +229,11 @@ class RealtimeChatManager: logger.error(f"搜索知识库失败: {e}") return [] - def _generate_response(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], work_order_id: Optional[int] = None) -> Dict[str, Any]: + def _generate_response(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], work_order_id: Optional[int] = None, tenant_id: str = None) -> Dict[str, Any]: """生成回复""" try: - # 检查是否有相关的工单AI建议 ai_suggestions = self._get_workorder_ai_suggestions(work_order_id) - - # 构建提示词 - prompt = self._build_chat_prompt(user_message, knowledge_results, context, ai_suggestions) + prompt = self._build_chat_prompt(user_message, knowledge_results, context, ai_suggestions, tenant_id=tenant_id) # 调用大模型 response = self.llm_client.chat_completion( @@ -272,11 +270,11 @@ class RealtimeChatManager: "ai_suggestions": [] } - def _generate_response_stream(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], work_order_id: Optional[int] = None): + def _generate_response_stream(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], work_order_id: Optional[int] = None, tenant_id: str = None): """流式生成回复,yield 每个 token 片段""" try: ai_suggestions = self._get_workorder_ai_suggestions(work_order_id) - prompt = self._build_chat_prompt(user_message, knowledge_results, context, ai_suggestions) + prompt = self._build_chat_prompt(user_message, knowledge_results, context, ai_suggestions, tenant_id=tenant_id) for chunk in self.llm_client.chat_completion_stream( messages=[{"role": "user", "content": prompt}], @@ -364,10 +362,16 @@ class RealtimeChatManager: # 发送完成事件 yield f"data: {json.dumps({'done': True, 'confidence_score': confidence, 'message_id': assistant_msg.message_id}, ensure_ascii=False)}\n\n" - def _build_chat_prompt(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], ai_suggestions: List[str] = None) -> str: - """构建聊天提示词""" - prompt = f""" -你是一个专业的奇瑞汽车客服助手。请根据用户的问题和提供的知识库信息,给出专业、友好的回复。 + def _build_chat_prompt(self, user_message: str, knowledge_results: List[Dict], context: List[Dict], ai_suggestions: List[str] = None, tenant_id: str = None) -> str: + """构建聊天提示词(按租户使用不同的系统提示词)""" + # 获取租户级系统提示词 + try: + from src.web.blueprints.tenants import get_tenant_system_prompt + system_prompt = get_tenant_system_prompt(tenant_id) if tenant_id else get_tenant_system_prompt('default') + except Exception: + system_prompt = "你是一个专业的智能客服助手。请根据用户的问题和提供的知识库信息,给出专业、友好的回复。" + + prompt = f"""{system_prompt} 用户问题:{user_message} diff --git a/src/web/blueprints/tenants.py b/src/web/blueprints/tenants.py index 9e3b989..008de53 100644 --- a/src/web/blueprints/tenants.py +++ b/src/web/blueprints/tenants.py @@ -43,6 +43,15 @@ def list_tenants(): return jsonify({"error": str(e)}), 500 +@tenants_bp.route('/my-modules', methods=['GET']) +def get_my_modules(): + """获取当前登录用户所属租户的模块权限""" + from flask import session as flask_session + tenant_id = flask_session.get('tenant_id', DEFAULT_TENANT) + modules = get_tenant_modules(tenant_id) + return jsonify({"success": True, "tenant_id": tenant_id, "modules": modules}) + + @tenants_bp.route('/feishu-groups', methods=['GET']) def list_feishu_groups(): """获取机器人所在的所有飞书群,并标注每个群当前绑定的租户""" @@ -205,3 +214,48 @@ def get_tenant_feishu_config(tenant_id: str) -> dict: except Exception as e: logger.error(f"获取租户飞书配置失败: {e}") return {} + + +# 默认系统提示词(不含品牌名) +DEFAULT_SYSTEM_PROMPT = "你是一个专业的智能客服助手。请根据用户的问题和提供的知识库信息,给出专业、友好的回复。" + +# 默认模块权限(全部开启) +DEFAULT_MODULES = { + "dashboard": True, "chat": True, "agent": True, + "alerts": True, "knowledge": True, "workorders": True, + "feishu-sync": True, "conversation-history": True, + "token-monitor": True, "ai-monitor": True, + "system-optimizer": True, "analytics": True, + "settings": True, "tenant-management": True +} + + +def get_tenant_system_prompt(tenant_id: str) -> str: + """获取租户的系统提示词,未配置则返回默认提示词""" + try: + with db_manager.get_session() as session: + tenant = session.query(Tenant).filter(Tenant.tenant_id == tenant_id).first() + if tenant and tenant.config: + cfg = json.loads(tenant.config) + prompt = cfg.get('system_prompt') + if prompt: + return prompt + except Exception as e: + logger.warning(f"获取租户提示词失败: {e}") + return DEFAULT_SYSTEM_PROMPT + + +def get_tenant_modules(tenant_id: str) -> dict: + """获取租户的模块权限配置""" + try: + with db_manager.get_session() as session: + tenant = session.query(Tenant).filter(Tenant.tenant_id == tenant_id).first() + if tenant and tenant.config: + cfg = json.loads(tenant.config) + modules = cfg.get('modules') + if modules: + # 合并默认值(新增模块自动开启) + return {**DEFAULT_MODULES, **modules} + except Exception as e: + logger.warning(f"获取租户模块权限失败: {e}") + return DEFAULT_MODULES.copy() diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index 04bbcd0..28f83cb 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -105,6 +105,7 @@ class TSPDashboard { this.restorePageState(); this.initLanguage(); this.initSmartUpdate(); + this.applyModulePermissions(); window.addEventListener('beforeunload', () => { this.destroyAllCharts(); this.cleanupConnections(); }); } @@ -115,6 +116,24 @@ class TSPDashboard { updatePageLanguage(this.currentLanguage); } + async applyModulePermissions() { + try { + const resp = await fetch('/api/tenants/my-modules'); + const data = await resp.json(); + if (!data.success) return; + const modules = data.modules || {}; + // 隐藏无权限的侧边栏标签和对应内容区 + document.querySelectorAll('[data-tab]').forEach(link => { + const tabName = link.dataset.tab; + if (modules[tabName] === false) { + link.style.display = 'none'; + const tabContent = document.getElementById(`${tabName}-tab`); + if (tabContent) tabContent.style.display = 'none'; + } + }); + } catch (e) { /* 权限加载失败不影响使用 */ } + } + init() { this.bindEvents(); this.populateTenantSelectors(); diff --git a/src/web/static/js/modules/tenants.js b/src/web/static/js/modules/tenants.js index 9126ced..5e6614a 100644 --- a/src/web/static/js/modules/tenants.js +++ b/src/web/static/js/modules/tenants.js @@ -44,6 +44,8 @@ Object.assign(TSPDashboard.prototype, { document.getElementById('tenant-name-input').value = ''; document.getElementById('tenant-desc-input').value = ''; document.getElementById('tenant-feishu-chatgroups').value = ''; + document.getElementById('tenant-system-prompt').value = ''; + document.querySelectorAll('.tenant-module-cb').forEach(cb => cb.checked = true); document.getElementById('feishu-groups-list').innerHTML = '点击"刷新群列表"从飞书拉取机器人所在的群'; new bootstrap.Modal(document.getElementById('tenantModal')).show(); }, @@ -62,6 +64,13 @@ Object.assign(TSPDashboard.prototype, { document.getElementById('tenant-name-input').value = tenant.name || ''; document.getElementById('tenant-desc-input').value = tenant.description || ''; document.getElementById('tenant-feishu-chatgroups').value = (tenant.config?.feishu?.chat_groups || []).join('\n'); + // 系统提示词 + document.getElementById('tenant-system-prompt').value = tenant.config?.system_prompt || ''; + // 模块权限 + document.querySelectorAll('.tenant-module-cb').forEach(cb => { + const mod = cb.value; + cb.checked = tenant.config?.modules?.[mod] !== false; // 默认开启 + }); } } catch (e) { console.warn('加载租户数据失败:', e); } // 自动加载飞书群列表 @@ -152,6 +161,21 @@ Object.assign(TSPDashboard.prototype, { if (Object.keys(config.feishu).length === 0) delete config.feishu; } + // 系统提示词 + const systemPrompt = document.getElementById('tenant-system-prompt').value.trim(); + if (systemPrompt) { + config.system_prompt = systemPrompt; + } else { + delete config.system_prompt; + } + + // 模块权限 + const modules = {}; + document.querySelectorAll('.tenant-module-cb').forEach(cb => { + modules[cb.value] = cb.checked; + }); + config.modules = modules; + if (!name) { this.showNotification('租户名称不能为空', 'error'); return; } try { diff --git a/src/web/templates/dashboard.html b/src/web/templates/dashboard.html index 11e36ab..6dd8b0f 100644 --- a/src/web/templates/dashboard.html +++ b/src/web/templates/dashboard.html @@ -2273,6 +2273,32 @@ +