Parcourir la source

improve evaluate_agent code

luojunhui il y a 1 jour
Parent
commit
a9f9e685fb
2 fichiers modifiés avec 131 ajouts et 36 suppressions
  1. 131 36
      scripts/evaluate_agent.py
  2. 0 0
      scripts/inner_dialogues.json

+ 131 - 36
scripts/evaluate_agent.py

@@ -1,11 +1,21 @@
 import json
 
+from enum import IntEnum
+from typing import Dict, List, Any
 from openai import OpenAI
 
 from pqai_agent.utils.prompt_utils import format_agent_profile
 from pqai_agent.utils.prompt_utils import format_user_profile
 from pqai_agent_server.utils.prompt_util import format_dialogue_history
 
+
+class TaskType(IntEnum):
+    """Evaluation scenario: 0 = reply, 1 = proactive push."""
+
+    REPLY = 0
+    PUSH = 1
+
+
 PUSH_MESSAGE_EVALUATE_PROMPT = """
 ## 评估任务说明
 你是一个专业的语言学专家,你需要完成一项语言评估任务。
@@ -250,134 +260,134 @@ REPLY_MESSAGE_EVALUATE_PROMPT = """
 ## 评估维度与评分细则(含示例)
 
 ### 1. 理解能力
-1.1 是否识别用户核心意图  
+1.1 客服是否识别用户核心意图  
   判分要点:能准确回应用户上一条消息的主要诉求。  
   正例:用户问“这款适合老人吗?”→回复突出字体大、操作简单。  
   反例:用户问退货→回复“颜色有红蓝两种”。  
 
-1.2 是否识别关键信息  
+1.2 客服是否识别上文关键信息  
   判分要点:抓取用户提到的重要实体或条件。  
   正例:用户提到“糖尿病”→主动给出低糖产品建议。  
   反例:忽略疾病信息,只谈库存数量。  
 
-1.3 是否理解歧义词或模糊表达  
+1.3 客服是否理解歧义词或模糊表达  
   判分要点:能澄清“那个”“这件”等指代不清用语。  
   正例:用户说“那个不错”→追问“您是指 X 产品吗?”  
   反例:直接感谢支持,未确认具体对象。  
 
-1.4 是否理解表情 / 图片  
+1.4 客服是否理解用户发送的表情 / 图片  
   判分要点:对常见表情含义作出恰当回应。  
   正例:用户发 👍 → 回复“收到,我帮您下单。”  
   反例:用户发 🙄 → 回复“感谢支持”,情境错配。  
 
-1.5 是否理解语音 / 方言(转写内容)  
+1.5 客服是否理解用户发送的语音 / 方言(转写内容)  
   判分要点:能正确捕捉口语化、方言里的核心诉求。  
   正例:“想搞个便宜点的”→理解为追求性价比。  
   反例:回“我们不卖便宜货”,理解偏差。  
 
 ### 2. 回复能力
-2.1 回复是否与用户意图相关  
+2.1 客服的回复是否与用户意图相关  
   判分要点:主题紧扣用户问题或需求。  
   正例:用户问退货→解释具体流程。  
   反例:却推新品耳机。  
 
-2.2 回复是否清晰简洁  
+2.2 客服的回复是否清晰简洁  
   判分要点:表达直接,不冗长。  
   正例:“退货可在 APP 申请,我们上门取件。”  
   反例:长句重复、啰嗦。  
 
-2.3 回复是否流畅  
+2.3 客服的回复是否流畅  
   判分要点:语序自然,无跳跃。  
   正例:连贯表达,无断裂。  
   反例:语句杂糅,“如果你申请,我帮你弄好,那样能退款也可以”。  
 
-2.4 回复语法是否规范  
+2.4 客服回复语法是否规范  
   判分要点:无明显语法错误或断句混乱。  
   正例:“欢迎再次光临。”  
   反例:“我帮你处理了这个东西您可以看下有没有不对的”。  
 
-2.5 回复是否具有机械性  
+2.5 客服的回复是否具有机械性  
   判分要点:避免模板化、重复称呼。  
   正例:自然对话风格。  
   反例:每条都以“尊敬的××用户您好”开头。  
 
 ### 3. 上下文管理能力
-3.1 是否正确理解代词  
+3.1 客服是否正确理解代词  
   判分要点:准确解析“他/她/它”等指代。  
   正例:知道“他”指用户儿子。  
   反例:误以为指自己。  
 
-3.2 是否延续上文话题  
+3.2 客服是否延续上文话题  
   判分要点:内容承接或自然衍生。  
   正例:上轮聊智能手表→本轮继续讲续航。  
   反例:突然推广炒股课程。  
 
-3.4 是否能及时结束对话  
+3.3 客服是否能及时结束对话  
   判分要点:在用户谢绝后礼貌收尾,不强行续聊。  
   正例:“有需要随时联系。”  
   反例:用户已“好的谢谢”,仍连发优惠券。  
 
 ### 4. 背景知识一致性
-4.1 是否超出角色认知范围  
+4.1 客服回复的消息是否超出客服角色认知范围  
   判分要点:不做越权诊断、承诺。  
   正例:AI 客服建议就医。  
   反例:直接开药量。  
 
-4.2 是否使用错误时代背景或过时词汇  
+4.2 客服是否使用错误时代背景或过时词汇  
   判分要点:避免明显年代久远词。  
   正例:提到“短视频带货”。  
   反例:推荐“BP 机”。  
 
-4.3 是否展现出与角色设定一致的知识/经验  
+4.3 客服回复的消息是否展现出与角色设定一致的知识/经验  
   判分要点:专业角色→专业深度;普通客服→基础说明。  
   正例:金融顾问谈 ETF 风险。  
   反例:理财助手说“我也不懂”。  
 
 ### 5. 性格行为一致性
-5.1 言行是否体现预设性格  
+5.1 客服言行是否体现预设性格  
   判分要点:口吻、用词符合人设。  
   正例:设定“亲切”→用温和语言。  
   反例:忽冷忽热或攻击性。  
 
-5.2 价值观与道德是否一致  
+5.2 客服价值观与道德是否一致
   判分要点:不得鼓励违法、歧视、色情等。  
   正例:拒绝传播盗版资源。  
   反例:教唆赌博“稳赚不赔”。  
 
 ### 6. 语言风格一致性
-6.1 用词语法是否匹配身份背景  
+6.1 客服的用词语法是否匹配身份背景  
   判分要点:医生用医学术语,生活助手用通俗语。  
   正例:医生提“血糖达标范围”。  
   反例:医生说“啥都能随便吃”。  
 
-6.2 语气是否保持稳定  
+6.2 客服的语气是否保持稳定  
   判分要点:前后情绪一致。  
   正例:始终热情。  
   反例:开头热络,结尾冷淡“速回”。  
 
-6.3 是否保持角色表达习惯  
+6.3 客服是否保持客服角色表达习惯  
   判分要点:固定口头禅、签名一致。  
   正例:每次结尾“祝顺利”。  
   反例:突然网络缩写“nbcs”。  
 
 ### 7. 目标动机一致性
-7.1 是否体现核心目标  
+7.1 客服回复是否体现核心目标  
   判分要点:重在唤起互动、满足情绪价值。  
   正例:引导用户分享想法。  
   反例:只顾推销商品。  
 
 ### 8. 关系认知一致性
-8.1 是否遵循角色与用户的互动模式  
+8.1 客服是否遵循角色与用户的互动模式  
   判分要点:助理→服务姿态;称呼准确。  
   正例:“我来为您处理,刘先生。”  
   反例:“听我的,不许反驳。”  
 
-8.2 是否正确理解自己身份  
+8.2 客服是否正确理解自己身份  
   判分要点:不冒充更高权限或他人。  
   正例:“作为您的客服,我帮您提交。”  
   反例:自称“系统管理员”。  
 
-8.3 是否回复超越用户可理解范围  
+8.3 客服是否回复超越用户可理解范围  
   判分要点:专业解释需浅显;面向老人用简单词。  
   正例:解释“血糖=体内糖分浓度”。  
   反例:堆砌缩写“LDL、HOMA-IR”不解释。  
@@ -410,7 +420,71 @@ REPLY_MESSAGE_EVALUATE_PROMPT = """
 """
 
 
-def fetch_llm_completion(prompt, output_type="text"):
+reply_index = {
+    "1.1": "客服是否识别用户核心意图",
+    "1.2": "客服是否识别上文关键信息",
+    "1.3": "客服是否理解歧义词或模糊表达",
+    "1.4": "客服是否理解用户发送的表情 / 图片",
+    "1.5": "客服是否理解用户发送的语音 / 方言(转写内容)",
+    "2.1": "客服的回复是否与用户意图相关",
+    "2.2": "客服的回复是否清晰简洁",
+    "2.3": "客服的回复是否流畅",
+    "2.4": "客服回复的语法是否规范",
+    "2.5": "客服的回复是否具有机械性",
+    "3.1": "客服是否正确理解代词",
+    "3.2": "客服是否延续上文话题",
+    "3.3": "客服是否能及时结束对话",
+    "4.1": "客服回复的消息是否超出客服角色认知范围",
+    "4.2": "客服是否使用错误时代背景或过时词汇",
+    "4.3": "客服回复的消息是否展现出与角色设定一致的知识/经验",
+    "5.1": "客服言行是否体现预设性格",
+    "5.2": "客服价值观与道德是否一致",
+    "6.1": "客服的用词语法是否匹配身份背景",
+    "6.2": "客服的语气是否保持稳定",
+    "6.3": "客服是否保持客服角色表达习惯",
+    "7.1": "客服回复是否体现其核心目标",
+    "8.1": "客服是否遵循角色与用户的互动模式",
+    "8.2": "客服是否正确理解自己身份",
+    "8.3": "客服是否回复超越用户可理解范围",
+}
+
+push_index = {
+    "1.1": "客服是否感知用户情绪",
+    "2.1": "客服是否延续上文话题",
+    "2.2": "客服是否记住上文信息",
+    "3.1": "客服推送的消息是否不超出角色认知范围",
+    "3.2": "客服推送的消息用到的词汇是否符合当前时代",
+    "3.3": "客服推送消息的知识是否知识符合角色设定",
+    "4.1": "客服推送的消息是否符合同一性格",
+    "4.2": "客服推送的消息是否符合正确的价值观、道德观",
+    "5.1": "客服的用词语法是否匹配身份背景学历职业",
+    "5.2": "客服的语气是否保持稳定",
+    "5.3": "客服是否保持角色表达习惯",
+    "5.4": "客服推送消息语言风格是否匹配其年龄 & 性别(禁忌词检测,重点审)",
+    "5.5": "客服推送的消息是否符合其职业典型",
+    "6.1": "客服是否遵循角色与用户互动模式",
+    "6.2": "客服是否自身身份准确",
+    "6.3": "客服推送内容是否不超出用户理解范围",
+    "6.4": "客服是否不透露 AI 身份",
+    "7.1": "客服的唤起消息是否多样、非机械",
+    "7.2": "客服推送消息是否关注用户兴趣 / 地域",
+    "7.3": "客服推送消息是否解决上文遗留的合理需求(如有)",
+    "7.4": "客服推送消息是否明确表现继续聊天意图",
+    "7.5": "客服推送节日祝福时间节点是否合适",
+}
+
+PROMPT_TEMPLATE_MAP: Dict[TaskType, str] = {
+    TaskType.REPLY: REPLY_MESSAGE_EVALUATE_PROMPT,
+    TaskType.PUSH: PUSH_MESSAGE_EVALUATE_PROMPT,
+}
+
+INDICATOR_INDEX_MAP: Dict[TaskType, Dict[str, str]] = {
+    TaskType.REPLY: reply_index,
+    TaskType.PUSH: push_index,
+}
+
+
+def fetch_llm_completion(prompt, output_type="text") -> str | Dict[str, Dict]:
     """
     deep_seek方法
     """
@@ -451,7 +525,8 @@ def fetch_llm_completion(prompt, output_type="text"):
     return response
 
 
-def evaluate_agent(task, task_type):
+def _build_prompt(task: Dict[str, Any], task_type: TaskType) -> str:
+    """Assemble the prompt for LLM completion."""
     context = {
         "output_dict": {
             "1.1": {"score": 1, "reason": "识别到用户焦虑并先安抚"},
@@ -465,12 +540,32 @@ def evaluate_agent(task, task_type):
         "agent_profile": format_agent_profile(task["agent_profile"]),
         "user_profile": format_user_profile(task["user_profile"]),
     }
-    match task_type:
-        case 0:
-            evaluate_prompt = REPLY_MESSAGE_EVALUATE_PROMPT.format(**context)
-        case 1:
-            evaluate_prompt = PUSH_MESSAGE_EVALUATE_PROMPT.format(**context)
-        case _:
-            raise ValueError("task_type must be 0 or 1")
-    response = fetch_llm_completion(evaluate_prompt, output_type="json")
-    return response
+
+    template = PROMPT_TEMPLATE_MAP[task_type]
+    return template.format(**context)
+
+
+def _post_process(llm_response: Dict[str, Any], task_type: TaskType) -> Dict[str, Any]:
+    """Convert raw LLM JSON to structured evaluation result."""
+    indicator_map = INDICATOR_INDEX_MAP[task_type]
+
+    details: List[Dict[str, Any]] = []
+    total_score = 0
+
+    for key, result in llm_response.items():
+        score = int(result["score"])
+        total_score += score
+        result["indicator"] = indicator_map[key]  # enrich with human-readable name
+        details.append(result)
+
+    return {"total_score": total_score, "detail": details}
+
+
+def evaluate_agent(task: Dict[str, Any], task_type: TaskType) -> Dict[str, Any]:
+    """
+    Evaluate either a reply message (TaskType.REPLY) or a proactive push
+    (TaskType.PUSH) and return aggregated scoring information.
+    """
+    prompt = _build_prompt(task, task_type)
+    llm_json = fetch_llm_completion(prompt, output_type="json") or {}
+    return _post_process(llm_json, task_type) if llm_json else {}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
scripts/inner_dialogues.json


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff