Browse Source

feat: 调整女性的提示词

zhangwl 3 weeks ago
parent
commit
f52c50b874
2 changed files with 356 additions and 1 deletions
  1. 349 1
      app/routers/chat_tools.py
  2. 7 0
      app/schemas/chat.py

+ 349 - 1
app/routers/chat_tools.py

@@ -4,7 +4,7 @@ from datetime import datetime
 from ..core.ark_client import get_client
 from ..config.config import Config
 from ..schemas.chat import ChatMessage, ChatResponse, CommentRequest, CirclePromptConfig, HistoricalFigure, \
-    RephraseRequest, FigureUpsert, GroupChatRequest, PostCommentBotRequest
+    RephraseRequest, FigureUpsert, GroupChatRequest, PostCommentBotRequest, RoleBasedChatRequest
 from ..db.souyue_mongo import get_mblog_by_id
 from ..db.mongo import save_chat_log,get_circle_prompt, upsert_circle_prompt, get_all_figures, get_figure_by_id, insert_figure, update_figure, delete_figure
 from ..utils.chat_utils import  get_web_search_tools, get_knowledge_search_tools
@@ -27,6 +27,47 @@ def _strip_markdown(text: str) -> str:
     return text.strip()
 
 
+def _should_block_role_message(message: str) -> bool:
+    """角色对话接口的高风险/不处理话题拦截。
+
+    这些话题不交给模型生成回复,统一由接口返回相同的错误详情,
+    避免模型偶尔绕过系统提示词输出内容。
+    """
+    text = re.sub(r"\s+", "", message or "").lower()
+
+    suicide_or_self_harm = (
+        "不想活", "不想活了", "想死", "去死", "自杀", "自残", "割腕",
+        "结束生命", "结束自己", "活不下去", "活着没意义", "跳楼", "安眠药",
+    )
+    extreme_emotion = (
+        "彻底崩溃", "情绪崩溃", "绝望至极", "极度绝望", "快撑不住",
+        "撑不下去", "崩溃了", "要疯了",
+    )
+    intimate_relationship = (
+        "男朋友", "女朋友", "老公", "老婆", "丈夫", "妻子", "伴侣",
+        "前任", "分手", "复合", "出轨", "婚姻", "恋爱", "暧昧",
+        "婆媳", "相亲", "结婚", "离婚", "对象",
+    )
+    scam_or_promotion = (
+        "诈骗", "被骗", "杀猪盘", "传销", "资金盘", "刷单", "高收益",
+        "稳赚", "保本", "拉人入群", "加群", "推广", "代理加盟",
+        "扫码", "返利", "投资理财",
+    )
+    extreme_content = (
+        "仇恨", "恐怖袭击", "恐怖主义", "极端主义", "暴力血腥", "屠杀",
+        "炸弹", "爆炸", "枪击", "政治敏感", "反政府", "邪教",
+    )
+
+    groups = (
+        suicide_or_self_harm,
+        extreme_emotion,
+        intimate_relationship,
+        scam_or_promotion,
+        extreme_content,
+    )
+    return any(keyword in text for group in groups for keyword in group)
+
+
 def _build_prompt(product_text: str, prompt_config: dict) -> str:
     name = prompt_config.get("name", "兴趣圈")
     role = prompt_config.get("role", "活跃用户")
@@ -462,3 +503,310 @@ async def post_comment_bot(request: PostCommentBotRequest):
     )
 
     return {"data": result}
+
+
+# 企业家角色对话
+@router.post("/entrepreneur")
+async def entrepreneur_chat(request: RoleBasedChatRequest):
+    """企业家社群群主角色对话接口"""
+    try:
+        # 与女性情感接口保持一致:命中高风险/不处理话题时直接返回,不调用模型。
+        if _should_block_role_message(request.message):
+            raise HTTPException(status_code=500, detail="AI未能生成回复")
+
+        # 获取 AI 配置和客户端
+        ai_config = get_config_by_app_name(request.app_name)
+        if not ai_config:
+            raise HTTPException(status_code=404, detail=f"未找到appName '{request.app_name}' 的配置")
+
+        client = get_client(request.app_name)
+        knowledge_id = ai_config.get("knowledgeId")
+
+        # 企业家角色系统提示词
+        system_prompt = """
+## 角色设定
+
+你是"云悦企业家社群"的成员,也是一位企业家。你在群里的身份是参与者,不是主持人。
+背景设定:搜索引擎领域从业多年,关注企业经营、技术创新和创业实践。
+以上背景仅作角色参考。不得声称自己就是某位现实人物,不得编造具体的公司数据、客户案例或亲身见闻。不确定的事直接说不确定。
+
+## 群聊场景
+
+社群约500人,多为企业家、创业者、管理者和技术负责人。讨论涉及经营、管理、市场、产品、技术、融资及创业经历。
+
+## 不回复的情况(最高优先级)
+
+遇到以下内容保持沉默,不输出任何文字:
+
+1. **自杀/自伤倾向**
+   - “我不想活了””想死””想结束自己”等明确表达
+   - 这超出群聊能力范围,回复反而可能延误专业救助
+
+2. **诈骗/违规推广**
+   - 承诺高收益、保本翻倍、拉人入群
+   - 传销话术、资金盘、未经允许的广告
+
+3. **极端内容**
+   - 煽动仇恨、极端政治、宗教争议
+   - 明显的机器人测试、灌水刷屏
+
+遇到以上内容直接保持沉默,不解释为什么不回复。
+
+## 回应原则:先判断,再回复
+
+**提问:** 给1-2个具体建议,不罗列现象。可以分享你自己的经验("我之前遇到过类似情况"),不要总说"我接触的不少企业"。
+
+**分享成果:** 肯定具体亮点,追问一个关键细节。例如:"15%这个数字挺实在,你在小程序里主要做了哪些设计?"
+
+**吐槽/挫败:** 先接住情绪,不要马上讲道理。可以说"今年这情况确实普遍",然后问"最难的是哪块?"或"想聊聊具体怎么破局吗?"
+
+**邀请讨论:** 给出你的看法(一两句话),不必每次都邀请其他人补充。
+
+**闲聊/无关消息:** 不必强行回复。
+
+## 互动风格
+
+1. **不是每条都要回**:只在被@、被提问、话题值得补充或群聊冷场时发言。
+2. **不是每次都邀请其他人**:不要机械收尾"群里有...的朋友可以聊聊"。只有确实需要群体经验时才这么说,且要具体(例如"群里有做过东南亚市场的吗?物流成本这块我不太熟")。
+3. **像群友一样说话**:
+   - 用"我觉得""我之前""我建议",不用"不少企业""很多团队"
+   - 数字可以模糊:"涨得厉害"而不是"同比涨30%-50%"
+   - 简单问题简短回复,不要每次都分三段
+4. **避免AI味重的套路**:
+   - 不要总是"总结现象+分析原因+邀请补充"
+   - 不要用"X确实是一个值得关注的话题"这类开头
+   - 不要说"这个问题的本质是..."
+5. **适度口语化**:可以说"这事儿""挺好""确实",但不刻意卖萌或滥用网络梗。
+
+## 专业边界
+
+- 经营、技术、管理问题:给有条件的建议,不把猜测说成结论。
+- 法律、税务、投资、用工等高风险问题:提醒咨询专业人士,不做保证性判断。
+- 保护隐私和商业机密,不传播未经同意的信息。
+
+## 输出格式
+
+只输出可以直接发到群里的消息,不加解释、分析过程、角色标签或引号。
+一次只发一条消息。
+如果不需要回应,保持沉默(不输出任何内容)。
+"""
+
+        # 构造请求消息
+        system_msg = {"role": "system", "content": [{"type": "input_text", "text": system_prompt}]}
+        api_messages = [system_msg, {"role": "user", "content": request.message}]
+
+        # 配置工具:联网搜索 + 知识库
+        tools = get_web_search_tools()
+        if knowledge_id:
+            tools += get_knowledge_search_tools(knowledge_id)
+
+        # 调用 AI 客户端
+        response = client.responses.create(
+            model=Config.MODEL_NAME,
+            input=api_messages,
+            stream=False,
+            store=False,
+            text={"format": {"type": "text"}},
+            tools=tools,
+        )
+
+        # 提取回复内容
+        result = ""
+        for item in response.output:
+            if hasattr(item, 'type') and item.type == 'message' and hasattr(item, 'content'):
+                if isinstance(item.content, list):
+                    for content_item in item.content:
+                        if hasattr(content_item, 'text'):
+                            result += content_item.text
+                else:
+                    result += str(item.content)
+
+        result = _strip_markdown(result)
+
+        if not result:
+            save_chat_log(
+                user_id=request.user_id or "anonymous",
+                question=request.message,
+                stream_mode=False,
+                raw_response=repr(response),
+                status="error",
+            )
+            raise HTTPException(status_code=500, detail="AI未能生成回复")
+
+        # 记录日志
+        save_chat_log(
+            user_id=request.user_id or "anonymous",
+            question=request.message,
+            stream_mode=False,
+            raw_response=repr(response),
+            status="success",
+        )
+
+        return {"data": result}
+
+    except HTTPException:
+        raise
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=f"Entrepreneur chat error: {str(e)}")
+
+
+# 女性情感角色对话
+@router.post("/femininity")
+async def femininity_chat(request: RoleBasedChatRequest):
+    """知心姐姐情感支持角色对话接口"""
+    try:
+        # 命中不处理的话题时直接返回统一错误,不调用模型。
+        if _should_block_role_message(request.message):
+            raise HTTPException(status_code=500, detail="AI未能生成回复")
+
+        # 获取 AI 配置和客户端
+        ai_config = get_config_by_app_name(request.app_name)
+        if not ai_config:
+            raise HTTPException(status_code=404, detail=f"未找到appName '{request.app_name}' 的配置")
+
+        client = get_client(request.app_name)
+        knowledge_id = ai_config.get("knowledgeId")
+
+        # 女性情感角色系统提示词
+        system_prompt = """
+你是群里的一位女性朋友,温和、靠谱、会倾听。
+用自然、真诚的口语回应,像真人朋友聊天,不要像客服、教材或鸡汤号。
+
+## 不回复的情况(最高优先级)
+
+遇到以下内容,什么都不要输出,连一个字都不要有:
+
+1. **自杀/自伤/极端情绪**
+   - “我不想活了””想死””想结束自己””想自残”
+   - 示例:”我真的不想活了” → 不输出任何内容(连空格都不要)
+
+2. **亲密关系问题**
+   - 男朋友/老公/伴侣/恋爱相关的情感困惑
+   - 示例:”为什么我男朋友最近突然对我很冷淡” → 不输出任何内容
+   - 示例:”分手后我总忍不住看他的朋友圈” → 不输出任何内容
+
+3. **诈骗/违规推广**
+   - 承诺高收益、拉人入群、传销话术
+
+4. **极端内容**
+   - 煽动仇恨、政治、宗教争议
+
+重要:遇到上述情况,绝对不要输出”(保持沉默,不输出任何内容)”或任何类似文字,而是真的不生成任何字符。留空白。什么都不说。
+
+## 回应原则
+
+如果不属于”不回复的情况”,再根据对方状态选一个:
+
+1. **倾诉情绪**:简短共情,1-2句就够
+2. **问”为什么”**:给一个可能原因,用”可能””也许”
+3. **问”怎么办”**:给一两个具体建议,简短说
+4. **闲聊**:轻松直接回
+
+## 语言风格
+
+**真人朋友怎么说,就怎么说:**
+- “这种感觉我懂” 不是 “我理解你的感受”
+- “你手里存款够撑几个月不” 不是 “建议评估财务状况”
+- “他可能最近工作忙” 不是 “可能存在以下几种情况”
+- “先想想你最受不了哪几件事” 不是 “列个清单评估一下”
+
+**不要用的句式:**
+- “你可以先...然后...最后...” — 太有步骤
+- “要是...就...不然...” — 太长
+- “如果...那么...否则...” — 太结构化
+- “建议你...另外...同时...” — 太理性
+- “首先...其次...” — 太分条
+
+**多用简短自然的:**
+- “我懂”
+- “太正常了”
+- “确实难受”
+- “可能他最近忙”
+- “你存款够撑几个月不”
+- “先想想你最受不了啥”
+
+## 回复要求
+
+- 1-3句话为主,最多5句
+- 不用【】标题
+- 不固定拆成几段
+- 不反复用同一套安慰句
+- 不叫”宝贝””亲爱的”
+- 不夸张”你太棒了”
+- 不堆感叹号
+- 不假定对方性格
+- 不追问细节
+- 不命令式
+- 不制造性别对立
+
+## 专业边界
+
+- 不远程诊断心理疾病
+- 严重情况(持续失眠/抑郁/惊恐)建议找专业人士
+- 涉及症状/用药提醒咨询医生
+- 不编造案例
+- 不确定的事说不确定
+
+## 输出格式
+
+如果属于”不回复的情况”:什么都不输出,完全空白。
+如果不属于”不回复的情况”:只输出可以直接发到群里的话。
+"""
+
+        # 构造请求消息
+        system_msg = {"role": "system", "content": [{"type": "input_text", "text": system_prompt}]}
+        api_messages = [system_msg, {"role": "user", "content": request.message}]
+
+        # 配置工具:联网搜索 + 知识库
+        tools = get_web_search_tools()
+        if knowledge_id:
+            tools += get_knowledge_search_tools(knowledge_id)
+
+        # 调用 AI 客户端
+        response = client.responses.create(
+            model=Config.MODEL_NAME,
+            input=api_messages,
+            stream=False,
+            store=False,
+            text={"format": {"type": "text"}},
+            tools=tools,
+        )
+
+        # 提取回复内容
+        result = ""
+        for item in response.output:
+            if hasattr(item, 'type') and item.type == 'message' and hasattr(item, 'content'):
+                if isinstance(item.content, list):
+                    for content_item in item.content:
+                        if hasattr(content_item, 'text'):
+                            result += content_item.text
+                else:
+                    result += str(item.content)
+
+        result = _strip_markdown(result)
+
+        if not result:
+            save_chat_log(
+                user_id=request.user_id or "anonymous",
+                question=request.message,
+                stream_mode=False,
+                raw_response=repr(response),
+                status="error",
+            )
+            raise HTTPException(status_code=500, detail="AI未能生成回复")
+
+        # 记录日志
+        save_chat_log(
+            user_id=request.user_id or "anonymous",
+            question=request.message,
+            stream_mode=False,
+            raw_response=repr(response),
+            status="success",
+        )
+
+        return {"data": result}
+
+    except HTTPException:
+        raise
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=f"Femininity chat error: {str(e)}")

+ 7 - 0
app/schemas/chat.py

@@ -125,3 +125,10 @@ class PostCommentBotRequest(BaseModel):
     app_name: str                             # app 包名
     message: str                              # @机器人的消息内容
     parent_comment: Optional[str] = None      # 被回复的评论原文(场景2)
+
+
+# 角色对话请求(企业家/女性情感等)
+class RoleBasedChatRequest(BaseModel):
+    message: str = Field(..., description="用户消息内容")
+    app_name: str = Field(..., description="应用包名")
+    user_id: Optional[str] = Field(None, description="用户编号(可选)")