feat: 租户级系统提示词 + 模块权限树
1. 系统提示词按租户配置: - 去掉所有硬编码的'奇瑞汽车'品牌名 - realtime_chat 的 _build_chat_prompt 按 tenant_id 动态获取提示词 - _generate_response 和 _generate_response_stream 都传递 tenant_id - 默认提示词为通用客服助手(不含品牌名) - 租户编辑弹窗新增系统提示词配置区 2. 模块权限树: - Tenant config 新增 modules 字段(14个模块的开关) - GET /api/tenants/my-modules 返回当前用户所属租户的模块权限 - 前端 applyModulePermissions() 初始化时隐藏无权限的侧边栏标签 - 租户编辑弹窗新增模块权限 checkbox 配置区 - 默认全部模块开启,取消勾选即隐藏 3. 其他清理: - llm_client.py 通用提示词去掉品牌名 - react_agent.py SYSTEM_PROMPT 去掉'车辆'限定
This commit is contained in:
Binary file not shown.
@@ -77,7 +77,7 @@ def _build_tools_prompt() -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
SYSTEM_PROMPT = f"""你是 TSP 智能客服助手,帮助用户解决车辆售后问题、查询知识库、管理客诉信息。
|
||||
SYSTEM_PROMPT = f"""你是 TSP 智能客服助手,帮助用户解决售后问题、查询知识库、管理客诉信息。
|
||||
|
||||
你可以使用以下工具来完成任务:
|
||||
{_build_tools_prompt()}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 = '<small class="text-muted">点击"刷新群列表"从飞书拉取机器人所在的群</small>';
|
||||
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 {
|
||||
|
||||
@@ -2273,6 +2273,32 @@
|
||||
</div>
|
||||
<input type="hidden" id="tenant-feishu-chatgroups">
|
||||
</div>
|
||||
<hr>
|
||||
<h6 class="text-muted">系统提示词</h6>
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" id="tenant-system-prompt" rows="3" placeholder="如:你是XX品牌的客服助手,专注于为用户提供XX相关的服务...(留空使用默认通用提示词)"></textarea>
|
||||
<div class="form-text">机器人回复时使用的角色设定,不同租户可以有不同的品牌定位</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h6 class="text-muted">模块权限 <small class="text-muted">(取消勾选可隐藏对应功能)</small></h6>
|
||||
<div class="mb-3" id="tenant-modules-checkboxes">
|
||||
<div class="row">
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="dashboard" id="mod-dashboard" checked><label class="form-check-label" for="mod-dashboard">仪表板</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="chat" id="mod-chat" checked><label class="form-check-label" for="mod-chat">智能对话</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="knowledge" id="mod-knowledge" checked><label class="form-check-label" for="mod-knowledge">知识库</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="workorders" id="mod-workorders" checked><label class="form-check-label" for="mod-workorders">工单管理</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="conversation-history" id="mod-conversation-history" checked><label class="form-check-label" for="mod-conversation-history">对话历史</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="alerts" id="mod-alerts" checked><label class="form-check-label" for="mod-alerts">预警管理</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="feishu-sync" id="mod-feishu-sync" checked><label class="form-check-label" for="mod-feishu-sync">飞书同步</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="agent" id="mod-agent" checked><label class="form-check-label" for="mod-agent">Agent管理</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="token-monitor" id="mod-token-monitor" checked><label class="form-check-label" for="mod-token-monitor">Token监控</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="ai-monitor" id="mod-ai-monitor" checked><label class="form-check-label" for="mod-ai-monitor">AI监控</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="analytics" id="mod-analytics" checked><label class="form-check-label" for="mod-analytics">数据分析</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="system-optimizer" id="mod-system-optimizer" checked><label class="form-check-label" for="mod-system-optimizer">系统优化</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="settings" id="mod-settings" checked><label class="form-check-label" for="mod-settings">系统设置</label></div></div>
|
||||
<div class="col-6"><div class="form-check"><input class="form-check-input tenant-module-cb" type="checkbox" value="tenant-management" id="mod-tenant-management" checked><label class="form-check-label" for="mod-tenant-management">租户管理</label></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
|
||||
Reference in New Issue
Block a user