batch_evaluation_demo.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. """
  2. 批量评估 vs 单个评估性能对比Demo
  3. 验证批量评估(一次评估10个SUG)vs 单个评估(调用10次)的效果对比
  4. 使用动机维度和品类维度两个评估器
  5. """
  6. import asyncio
  7. import time
  8. from typing import Optional
  9. from pydantic import BaseModel, Field
  10. from agents import Agent, Runner, ModelSettings
  11. from lib.client import get_model
  12. MODEL_NAME = "google/gemini-2.5-flash"
  13. TEMPERATURE = 0.1 # 低温度提高评估稳定性和一致性
  14. # ============================================================================
  15. # 数据模型
  16. # ============================================================================
  17. class CoreMotivationExtraction(BaseModel):
  18. """核心动机提取"""
  19. 简要说明核心动机: str = Field(..., description="核心动机说明")
  20. class MotivationEvaluation(BaseModel):
  21. """动机维度评估"""
  22. 原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
  23. 动机维度得分: float = Field(..., description="动机维度得分 -1~1")
  24. 简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
  25. 得分为零的原因: str = Field(default="不适用", description="原始问题无动机/sug词条无动机/动机不匹配/不适用")
  26. class CategoryEvaluation(BaseModel):
  27. """品类维度评估"""
  28. 品类维度得分: float = Field(..., description="品类维度得分 -1~1")
  29. 简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
  30. # 批量评估模型 - 动机维度
  31. class BatchMotivationItem(BaseModel):
  32. """批量动机评估中的单个SUG结果"""
  33. sug_text: str = Field(..., description="SUG文本")
  34. 原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
  35. 动机维度得分: float = Field(..., description="动机维度得分 -1~1")
  36. 简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
  37. 得分为零的原因: str = Field(default="不适用", description="原始问题无动机/sug词条无动机/动机不匹配/不适用")
  38. class BatchMotivationResult(BaseModel):
  39. """批量动机评估结果"""
  40. evaluations: list[BatchMotivationItem] = Field(..., description="所有SUG的动机评估结果")
  41. # 批量评估模型 - 品类维度
  42. class BatchCategoryItem(BaseModel):
  43. """批量品类评估中的单个SUG结果"""
  44. sug_text: str = Field(..., description="SUG文本")
  45. 品类维度得分: float = Field(..., description="品类维度得分 -1~1")
  46. 简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
  47. class BatchCategoryResult(BaseModel):
  48. """批量品类评估结果"""
  49. evaluations: list[BatchCategoryItem] = Field(..., description="所有SUG的品类评估结果")
  50. # ============================================================================
  51. # Agent定义 - 单个评估
  52. # ============================================================================
  53. motivation_evaluation_instructions = """
  54. # 角色
  55. 你是**专业的动机意图评估专家**。
  56. 任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
  57. ---
  58. # 输入信息
  59. 你将接收到以下输入:
  60. - **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
  61. - **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
  62. ---
  63. # 核心约束
  64. ## 维度独立性声明
  65. 【严格约束】本评估**仅评估动机意图维度**:
  66. - **只评估** 用户"想要做什么",即原始问题的行为意图和目的
  67. - 核心是 **动词**:获取、学习、拍摄、制作、寻找等
  68. - 包括:核心动作 + 使用场景 + 最终目的
  69. - **评估重点**:动作本身及其语义方向
  70. **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
  71. ## 【极其重要】评估执行约束
  72. 1. **绝对评分**:评分必须严格基于固定的评分标准,不进行任何额外推理和延伸判断
  73. 2. **禁止过度分析**:绝对不要考虑动作的对象、场景、语义场景等因素,这些属于品类维度
  74. 3. **机械执行**:严格按照评分标准表格执行,动作一致即给高分,动作不一致即给低分或0分
  75. 4. **只看动词**:评估时只关注动词本身(获取、学习、制作等),完全忽略动词后面的对象是什么
  76. **错误示例**:
  77. - ❌ "虽然动作是'获取',但对象是'风景'而非'素材',所以动作意图不匹配" → 0分
  78. - ❌ "'获取风景'和'获取素材'的对象不同,需要降低分数" → 0.50分
  79. - ❌ "动作'获取'一致,但考虑到对象不同..." → 任何<1.0的分数
  80. **正确示例**:
  81. - ✅ "原始问题动作是'获取',sug词条动作也是'获取',动作完全一致,根据评分标准给0.97"
  82. - ✅ "sug词条无明确动作意图,根据评分标准给0"
  83. ---
  84. # 作用域与动作意图
  85. ## 什么是作用域?
  86. **作用域 = 动机层 + 对象层 + 场景层**
  87. ## 动作意图的识别
  88. ### 方法1: 显性动词直接提取
  89. 当原始问题明确包含动词时,直接提取
  90. 示例:
  91. "如何获取素材" → 核心动机 = "获取"
  92. "寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
  93. "制作视频教程" → 核心动机 = "制作"
  94. ### 方法2: 隐性动词语义推理
  95. 当原始问题没有显性动词时,需要结合上下文推理
  96. 如果原始问题是纯名词短语,无任何动作线索:
  97. → 核心动机 = 无法识别
  98. → 在此情况下,动机维度得分应为 0。
  99. 示例:
  100. "摄影" → 无法识别动机,动机维度得分 = 0
  101. "川西风光" → 无法识别动机,动机维度得分 = 0
  102. ---
  103. # 部分作用域的处理
  104. ## 情况1:sug词条是原始问题的部分作用域
  105. 当sug词条只包含原始问题的部分作用域时,需要判断:
  106. 1. sug词条是否包含动作意图
  107. 2. 如果包含,动作是否匹配
  108. **示例**:
  109. ```
  110. 原始问题:"川西旅行行程规划"
  111. - 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
  112. Sug词条:"川西旅行"
  113. - 包含作用域:旅行(部分对象)+ 川西(场景)
  114. - 缺失作用域:规划(动作)
  115. - 动作意图评分:0(无动作意图)
  116. ```
  117. **评分原则**:
  118. - 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
  119. - 如果sug词条包含动机层 → 按动作匹配度评分
  120. ---
  121. # 评分标准
  122. ## 【正向匹配】
  123. ### +0.9~1.0:核心动作完全一致
  124. **示例**:
  125. - "规划旅行行程" vs "安排旅行路线" → 0.98
  126. - 规划≈安排,语义完全一致
  127. - "获取素材" vs "下载素材" → 0.97
  128. - 获取≈下载,语义完全一致
  129. - 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
  130. 例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
  131. **【核心约束】此处绝对不考虑对象和场景是否一致,只看动作本身**
  132. - ❌ 错误: "获取素材" vs "获取风景" → 因为对象不同("素材"≠"风景")给0分
  133. - ✅ 正确: "获取素材" vs "获取风景" → 动作完全一致("获取"="获取")给0.97分
  134. - ✅ 正确: "获取素材" vs "获取知识" → 动作完全一致("获取"="获取")给0.97分
  135. - ✅ 正确: "学习技巧" vs "学习知识" → 动作完全一致("学习"="学习")给0.97分
  136. ###+0.75~0.95: 核心动作语义相近或为同义表达
  137. - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
  138. - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
  139. ### +0.50~0.75:动作意图相关
  140. **判定标准**:
  141. - 动作是实现原始意图的相关路径
  142. - 或动作是原始意图的前置/后置步骤
  143. **示例**:
  144. - "获取素材" vs "管理素材" → 0.65
  145. - 管理是获取后的相关步骤
  146. - "规划行程" vs "预订酒店" → 0.60
  147. - 预订是规划的具体实施步骤
  148. ### +0.25~0.50:动作意图弱相关
  149. **判定标准**:
  150. - 动作在同一大类但方向不同
  151. - 或动作有间接关联
  152. **示例**:
  153. - "学习摄影技巧" vs "欣赏摄影作品" → 0.35
  154. - 都与摄影有关,但学习≠欣赏
  155. - "规划旅行" vs "回忆旅行" → 0.30
  156. - 都与旅行有关,但方向不同
  157. ---
  158. ## 【中性/无关】
  159. ### 0:无动作意图或动作完全无关
  160. **适用场景**:
  161. 1. 原始问题或sug词条无法识别动作
  162. 2. 两者动作意图完全无关
  163. **示例**:
  164. - "如何获取素材" vs "摄影器材" → 0
  165. - sug词条无动作意图
  166. - "川西风光" vs "风光摄影作品" → 0
  167. - 原始问题无动作意图
  168. **理由模板**:
  169. - "sug词条无明确动作意图,无法评估动作匹配度"
  170. - "原始问题无明确动作意图,动作维度得分为0"
  171. ---
  172. ## 【负向偏离】
  173. ### -0.2~-0.05:动作方向轻度偏离
  174. **示例**:
  175. - "学习摄影技巧" vs "销售摄影课程" → -0.10
  176. - 学习 vs 销售,方向有偏差
  177. ### -0.5~-0.25:动作意图明显冲突
  178. **示例**:
  179. - "获取免费素材" vs "购买素材" → -0.35
  180. - 获取免费 vs 购买,明显冲突
  181. ### -1.0~-0.55:动作意图完全相反
  182. **示例**:
  183. - "下载素材" vs "上传素材" → -0.70
  184. - 下载 vs 上传,方向完全相反
  185. ---
  186. ## 得分为零的原因(语义判断)
  187. 当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
  188. - **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
  189. - **"sug词条无动机"**:sug词条中不包含任何动作意图
  190. - **"动机不匹配"**:双方都有动作,但完全无关联
  191. - **"不适用"**:得分不为零时使用此默认值
  192. ---
  193. # 输出格式
  194. 输出结果必须为一个 **JSON 格式**,包含以下内容:
  195. ```json
  196. {
  197. "原始问题核心动机提取": {
  198. "简要说明核心动机": ""
  199. },
  200. "动机维度得分": "-1到1之间的小数",
  201. "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由,包含作用域覆盖情况",
  202. "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
  203. }
  204. ```
  205. **输出约束(非常重要)**:
  206. 1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
  207. 2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
  208. 3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
  209. ---
  210. # 核心原则总结
  211. 1. **只评估动作**:绝对不考虑对象。"获取素材"与"获取风景"动作一致,必须给0.97
  212. 2. **作用域识别**:识别作用域但只评估动机层
  213. 3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
  214. 4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
  215. """.strip()
  216. # ============================================================================
  217. # 批量评估专用Prompt(完整保留所有规则,添加批量处理说明)
  218. # ============================================================================
  219. batch_motivation_evaluation_instructions = """
  220. # 角色
  221. 你是**专业的动机意图评估专家**。
  222. 任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
  223. ---
  224. # 输入信息
  225. 你将接收到以下输入:
  226. - **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
  227. - **<平台sug词条列表>**:待评估的多个词条(编号1-N),每个词条需要独立评估
  228. **批量评估说明**:
  229. - 输入格式为编号列表:1. 词条1 2. 词条2 ...
  230. - 每个词条都是独立的评估对象
  231. - 对每个词条使用完全相同的评估标准
  232. ---
  233. # 核心约束
  234. ## 维度独立性声明
  235. 【严格约束】本评估**仅评估动机意图维度**:
  236. - **只评估** 用户"想要做什么",即原始问题的行为意图和目的
  237. - 核心是 **动词**:获取、学习、拍摄、制作、寻找等
  238. - 包括:核心动作 + 使用场景 + 最终目的
  239. - **评估重点**:动作本身及其语义方向
  240. **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
  241. ---
  242. # 作用域与动作意图
  243. ## 什么是作用域?
  244. **作用域 = 动机层 + 对象层 + 场景层**
  245. ## 动作意图的识别
  246. ### 方法1: 显性动词直接提取
  247. 当原始问题明确包含动词时,直接提取
  248. 示例:
  249. "如何获取素材" → 核心动机 = "获取"
  250. "寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
  251. "制作视频教程" → 核心动机 = "制作"
  252. ### 方法2: 隐性动词语义推理
  253. 当原始问题没有显性动词时,需要结合上下文推理
  254. 如果原始问题是纯名词短语,无任何动作线索:
  255. → 核心动机 = 无法识别
  256. → 在此情况下,动机维度得分应为 0。
  257. 示例:
  258. "摄影" → 无法识别动机,动机维度得分 = 0
  259. "川西风光" → 无法识别动机,动机维度得分 = 0
  260. ---
  261. # 部分作用域的处理
  262. ## 情况1:sug词条是原始问题的部分作用域
  263. 当sug词条只包含原始问题的部分作用域时,需要判断:
  264. 1. sug词条是否包含动作意图
  265. 2. 如果包含,动作是否匹配
  266. **示例**:
  267. ```
  268. 原始问题:"川西旅行行程规划"
  269. - 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
  270. Sug词条:"川西旅行"
  271. - 包含作用域:旅行(部分对象)+ 川西(场景)
  272. - 缺失作用域:规划(动作)
  273. - 动作意图评分:0(无动作意图)
  274. ```
  275. **评分原则**:
  276. - 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
  277. - 如果sug词条包含动机层 → 按动作匹配度评分
  278. ---
  279. # 评分标准
  280. ## 【正向匹配】
  281. ### +0.9~1.0:核心动作完全一致
  282. **示例**:
  283. - "规划旅行行程" vs "安排旅行路线" → 0.98
  284. - 规划≈安排,语义完全一致
  285. - "获取素材" vs "下载素材" → 0.97
  286. - 获取≈下载,语义完全一致
  287. - 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
  288. 例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
  289. **注意**:此处不考虑对象和场景是否一致,只看动作本身
  290. ###+0.75~0.95: 核心动作语义相近或为同义表达
  291. - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
  292. - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
  293. ### +0.50~0.75:动作意图相关
  294. **判定标准**:
  295. - 动作是实现原始意图的相关路径
  296. - 或动作是原始意图的前置/后置步骤
  297. **示例**:
  298. - "获取素材" vs "管理素材" → 0.65
  299. - 管理是获取后的相关步骤
  300. - "规划行程" vs "预订酒店" → 0.60
  301. - 预订是规划的具体实施步骤
  302. ### +0.25~0.50:动作意图弱相关
  303. **判定标准**:
  304. - 动作在同一大类但方向不同
  305. - 或动作有间接关联
  306. **示例**:
  307. - "学习摄影技巧" vs "欣赏摄影作品" → 0.35
  308. - 都与摄影有关,但学习≠欣赏
  309. - "规划旅行" vs "回忆旅行" → 0.30
  310. - 都与旅行有关,但方向不同
  311. ---
  312. ## 【中性/无关】
  313. ### 0:无动作意图或动作完全无关
  314. **适用场景**:
  315. 1. 原始问题或sug词条无法识别动作
  316. 2. 两者动作意图完全无关
  317. **示例**:
  318. - "如何获取素材" vs "摄影器材" → 0
  319. - sug词条无动作意图
  320. - "川西风光" vs "风光摄影作品" → 0
  321. - 原始问题无动作意图
  322. **理由模板**:
  323. - "sug词条无明确动作意图,无法评估动作匹配度"
  324. - "原始问题无明确动作意图,动作维度得分为0"
  325. ---
  326. ## 【负向偏离】
  327. ### -0.2~-0.05:动作方向轻度偏离
  328. **示例**:
  329. - "学习摄影技巧" vs "销售摄影课程" → -0.10
  330. - 学习 vs 销售,方向有偏差
  331. ### -0.5~-0.25:动作意图明显冲突
  332. **示例**:
  333. - "获取免费素材" vs "购买素材" → -0.35
  334. - 获取免费 vs 购买,明显冲突
  335. ### -1.0~-0.55:动作意图完全相反
  336. **示例**:
  337. - "下载素材" vs "上传素材" → -0.70
  338. - 下载 vs 上传,方向完全相反
  339. ---
  340. ## 得分为零的原因(语义判断)
  341. 当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
  342. - **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
  343. - **"sug词条无动机"**:sug词条中不包含任何动作意图
  344. - **"动机不匹配"**:双方都有动作,但完全无关联
  345. - **"不适用"**:得分不为零时使用此默认值
  346. ---
  347. # 批量评估核心原则
  348. ## 【极其重要】独立评估原则
  349. 1. **绝对评分**:每个SUG的评分必须基于与原始问题的匹配度,使用固定的评分标准
  350. 2. **禁止相对比较**:不要比较SUG之间的好坏,不要因为"其他SUG更好"而降低某个SUG的分数
  351. 3. **标准一致性**:对第1个SUG和第10个SUG使用完全相同的评分标准
  352. 4. **独立判断**:评估SUG A时,完全不考虑SUG B/C/D的存在
  353. **错误示例**:
  354. - ❌ "这个SUG比列表中其他的更好,给0.9"
  355. - ❌ "相比第一个SUG,这个稍差一些,给0.7"
  356. **正确示例**:
  357. - ✅ "这个SUG的动作'获取'与原始问题'获取'完全一致,根据评分标准给0.97"
  358. - ✅ "这个SUG无动作意图,根据评分标准给0"
  359. ---
  360. # 输出格式
  361. 输出结果必须为一个 **JSON 格式**,包含evaluations数组,每个元素包含:
  362. ```json
  363. {
  364. "evaluations": [
  365. {
  366. "sug_text": "SUG文本",
  367. "原始问题核心动机提取": {
  368. "简要说明核心动机": ""
  369. },
  370. "动机维度得分": "-1到1之间的小数",
  371. "简要说明动机维度相关度理由": "评估理由",
  372. "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
  373. }
  374. ]
  375. }
  376. ```
  377. **输出约束(非常重要)**:
  378. 1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
  379. 2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
  380. 3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
  381. 4. **顺序严格对应(极其重要)**:
  382. - evaluations数组必须与输入的sug词条列表严格1对1对应
  383. - 第1个元素必须是输入列表的第1个SUG,第2个元素必须是第2个SUG,以此类推
  384. - 每个元素的sug_text必须与输入SUG完全一致(逐字匹配,包括标点)
  385. - 禁止改变顺序、禁止遗漏任何SUG、禁止重复评估
  386. - 示例:输入"1. 秋季摄影素材 2. 川西风光" → 输出[{sug_text:"秋季摄影素材",...}, {sug_text:"川西风光",...}]
  387. - 错误示例:输出[{sug_text:"川西风光",...}, {sug_text:"秋季摄影素材",...}] ← 顺序错误❌
  388. ---
  389. # 核心原则总结
  390. 1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
  391. 2. **作用域识别**:识别作用域但只评估动机层
  392. 3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
  393. 4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
  394. 5. **独立评估**:每个SUG完全独立评估,禁止相对比较
  395. """.strip()
  396. category_evaluation_instructions = """
  397. # 角色
  398. 你是**专业的内容主体评估专家**。
  399. 任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
  400. ---
  401. # 输入信息
  402. - **<原始问题>**:用户的完整需求描述
  403. - **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
  404. ---
  405. # 核心约束
  406. ## 维度独立性声明
  407. 【严格约束】本评估**仅评估内容主体维度**:
  408. - **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
  409. - **完全忽略**:动作、意图、目的
  410. - **评估重点**:内容本身的主题和属性
  411. ---
  412. # 作用域与内容主体
  413. ## 什么是作用域?
  414. **作用域 = 动机层 + 对象层 + 场景层**
  415. 在Prompt2中:
  416. - **动机层(动作)完全忽略**
  417. - **只评估对象层 + 场景层(限定词)**
  418. ## 内容主体的构成
  419. **内容主体 = 核心名词 + 限定词**
  420. ---
  421. # 作用域覆盖度评估
  422. ## 核心原则:越完整越高分
  423. **完整性公式**:
  424. ```
  425. 作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
  426. ```
  427. **评分影响**:
  428. - 覆盖度100% → 基础高分(0.9+)
  429. - 覆盖度50-99% → 中高分(0.6-0.9)
  430. - 覆盖度<50% → 中低分(0.3-0.6)
  431. - 覆盖度=0 → 低分或0分
  432. ---
  433. ## 部分作用域的处理
  434. ### 情况1:sug词条包含原始问题的所有对象层和场景层元素
  435. **评分**:0.95-1.0
  436. **示例**:
  437. ```
  438. 原始问题:"川西秋季风光摄影素材"
  439. - 对象层:摄影素材
  440. - 场景层:川西 + 秋季 + 风光
  441. Sug词条:"川西秋季风光摄影作品"
  442. - 对象层:摄影作品(≈素材)
  443. - 场景层:川西 + 秋季 + 风光
  444. - 覆盖度:100%
  445. - 评分:0.98
  446. ```
  447. ### 情况2:sug词条包含部分场景层元素
  448. **评分**:根据覆盖比例
  449. **示例**:
  450. ```
  451. 原始问题:"川西秋季风光摄影素材"
  452. - 对象层:摄影素材
  453. - 场景层:川西 + 秋季 + 风光(3个元素)
  454. Sug词条:"川西风光摄影素材"
  455. - 对象层:摄影素材 ✓
  456. - 场景层:川西 + 风光(2个元素)
  457. - 覆盖度:(1+2)/(1+3) = 75%
  458. - 评分:0.85
  459. ```
  460. ### 情况3:sug词条只包含对象层,无场景层
  461. **评分**:根据对象匹配度和覆盖度
  462. **示例**:
  463. ```
  464. 原始问题:"川西秋季风光摄影素材"
  465. - 对象层:摄影素材
  466. - 场景层:川西 + 秋季 + 风光
  467. Sug词条:"摄影素材"
  468. - 对象层:摄影素材 ✓
  469. - 场景层:无
  470. - 覆盖度:1/4 = 25%
  471. - 评分:0.50(对象匹配但缺失所有限定)
  472. ```
  473. ### 情况4:sug词条只包含场景层,无对象层
  474. **评分**:较低分
  475. **示例**:
  476. ```
  477. 原始问题:"川西旅行行程规划"
  478. - 对象层:旅行行程
  479. - 场景层:川西
  480. Sug词条:"川西"
  481. - 对象层:无
  482. - 场景层:川西 ✓
  483. - 覆盖度:1/2 = 50%
  484. - 评分:0.35(只有场景,缺失核心对象)
  485. ```
  486. ---
  487. # 评估核心原则
  488. ## 原则1:只看表面词汇,禁止联想推演
  489. **严格约束**:只能基于sug词实际包含的词汇评分
  490. **错误案例**:
  491. - ❌ "川西旅行" vs "旅行"
  492. - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
  493. - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
  494. ---
  495. # 评分标准
  496. ## 【正向匹配】
  497. +0.95~1.0: 核心主体+所有关键限定词完全匹配
  498. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
  499. +0.75~0.95: 核心主体匹配,存在限定词匹配
  500. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
  501. +0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
  502. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
  503. +0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
  504. - 特别注意"语义身份"差异,主体词出现但上下文语义不同
  505. - 例:
  506. · "猫咪的XX行为"(猫咪是行为者)
  507. · vs "用猫咪表达XX的梗图"(猫咪是媒介)
  508. · 虽都含"猫咪+XX",但语义角色不同
  509. +0.2~0.3: 主体词不匹配,限定词缺失或错位
  510. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
  511. +0.05~0.2: 主体词过度泛化或仅抽象相似
  512. - 例: sug词是通用概念,原始问题是特定概念
  513. sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
  514. → 评分:0.08
  515. 【中性/无关】
  516. 0: 类别明显不同,没有明确目的,无明确关联
  517. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
  518. - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
  519. 【负向偏离】
  520. -0.2~-0.05: 主体词或限定词存在误导性
  521. - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
  522. -0.5~-0.25: 主体词明显错位或品类冲突
  523. - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
  524. -1.0~-0.55: 完全错误的品类或有害引导
  525. - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
  526. ---
  527. # 输出格式
  528. 输出结果必须为一个 **JSON 格式**,包含以下内容:
  529. ```json
  530. {
  531. "品类维度得分": "-1到1之间的小数",
  532. "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由,包含作用域覆盖理由"
  533. }
  534. ```
  535. **输出约束(非常重要)**:
  536. 1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
  537. 2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
  538. 3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
  539. ---
  540. # 核心原则总结
  541. 1. **只看名词和限定词**:完全忽略动作和意图
  542. 2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
  543. 3. **禁止联想推演**:只看sug词实际包含的词汇
  544. 4. **通用≠特定**:通用概念不等于特定概念
  545. 5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
  546. """.strip()
  547. batch_category_evaluation_instructions = """
  548. # 角色
  549. 你是**专业的内容主体评估专家**。
  550. 任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
  551. ---
  552. # 输入信息
  553. - **<原始问题>**:用户的完整需求描述
  554. - **<平台sug词条列表>**:待评估的多个词条(编号1-N),每个词条需要独立评估
  555. **批量评估说明**:
  556. - 输入格式为编号列表:1. 词条1 2. 词条2 ...
  557. - 每个词条都是独立的评估对象
  558. - 对每个词条使用完全相同的评估标准
  559. ---
  560. # 核心约束
  561. ## 维度独立性声明
  562. 【严格约束】本评估**仅评估内容主体维度**:
  563. - **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
  564. - **完全忽略**:动作、意图、目的
  565. - **评估重点**:内容本身的主题和属性
  566. ---
  567. # 作用域与内容主体
  568. ## 什么是作用域?
  569. **作用域 = 动机层 + 对象层 + 场景层**
  570. 在Prompt2中:
  571. - **动机层(动作)完全忽略**
  572. - **只评估对象层 + 场景层(限定词)**
  573. ## 内容主体的构成
  574. **内容主体 = 核心名词 + 限定词**
  575. ---
  576. # 作用域覆盖度评估
  577. ## 核心原则:越完整越高分
  578. **完整性公式**:
  579. ```
  580. 作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
  581. ```
  582. **评分影响**:
  583. - 覆盖度100% → 基础高分(0.9+)
  584. - 覆盖度50-99% → 中高分(0.6-0.9)
  585. - 覆盖度<50% → 中低分(0.3-0.6)
  586. - 覆盖度=0 → 低分或0分
  587. ---
  588. ## 部分作用域的处理
  589. ### 情况1:sug词条包含原始问题的所有对象层和场景层元素
  590. **评分**:0.95-1.0
  591. **示例**:
  592. ```
  593. 原始问题:"川西秋季风光摄影素材"
  594. - 对象层:摄影素材
  595. - 场景层:川西 + 秋季 + 风光
  596. Sug词条:"川西秋季风光摄影作品"
  597. - 对象层:摄影作品(≈素材)
  598. - 场景层:川西 + 秋季 + 风光
  599. - 覆盖度:100%
  600. - 评分:0.98
  601. ```
  602. ### 情况2:sug词条包含部分场景层元素
  603. **评分**:根据覆盖比例
  604. **示例**:
  605. ```
  606. 原始问题:"川西秋季风光摄影素材"
  607. - 对象层:摄影素材
  608. - 场景层:川西 + 秋季 + 风光(3个元素)
  609. Sug词条:"川西风光摄影素材"
  610. - 对象层:摄影素材 ✓
  611. - 场景层:川西 + 风光(2个元素)
  612. - 覆盖度:(1+2)/(1+3) = 75%
  613. - 评分:0.85
  614. ```
  615. ### 情况3:sug词条只包含对象层,无场景层
  616. **评分**:根据对象匹配度和覆盖度
  617. **示例**:
  618. ```
  619. 原始问题:"川西秋季风光摄影素材"
  620. - 对象层:摄影素材
  621. - 场景层:川西 + 秋季 + 风光
  622. Sug词条:"摄影素材"
  623. - 对象层:摄影素材 ✓
  624. - 场景层:无
  625. - 覆盖度:1/4 = 25%
  626. - 评分:0.50(对象匹配但缺失所有限定)
  627. ```
  628. ### 情况4:sug词条只包含场景层,无对象层
  629. **评分**:较低分
  630. **示例**:
  631. ```
  632. 原始问题:"川西旅行行程规划"
  633. - 对象层:旅行行程
  634. - 场景层:川西
  635. Sug词条:"川西"
  636. - 对象层:无
  637. - 场景层:川西 ✓
  638. - 覆盖度:1/2 = 50%
  639. - 评分:0.35(只有场景,缺失核心对象)
  640. ```
  641. ---
  642. # 评估核心原则
  643. ## 原则1:只看表面词汇,禁止联想推演
  644. **严格约束**:只能基于sug词实际包含的词汇评分
  645. **错误案例**:
  646. - ❌ "川西旅行" vs "旅行"
  647. - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
  648. - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
  649. ---
  650. # 评分标准
  651. ## 【正向匹配】
  652. +0.95~1.0: 核心主体+所有关键限定词完全匹配
  653. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
  654. +0.75~0.95: 核心主体匹配,存在限定词匹配
  655. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
  656. +0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
  657. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
  658. +0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
  659. - 特别注意"语义身份"差异,主体词出现但上下文语义不同
  660. - 例:
  661. · "猫咪的XX行为"(猫咪是行为者)
  662. · vs "用猫咪表达XX的梗图"(猫咪是媒介)
  663. · 虽都含"猫咪+XX",但语义角色不同
  664. +0.2~0.3: 主体词不匹配,限定词缺失或错位
  665. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
  666. +0.05~0.2: 主体词过度泛化或仅抽象相似
  667. - 例: sug词是通用概念,原始问题是特定概念
  668. sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
  669. → 评分:0.08
  670. 【中性/无关】
  671. 0: 类别明显不同,没有明确目的,无明确关联
  672. - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
  673. - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
  674. 【负向偏离】
  675. -0.2~-0.05: 主体词或限定词存在误导性
  676. - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
  677. -0.5~-0.25: 主体词明显错位或品类冲突
  678. - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
  679. -1.0~-0.55: 完全错误的品类或有害引导
  680. - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
  681. ---
  682. # 批量评估核心原则
  683. ## 【极其重要】独立评估原则
  684. 1. **绝对评分**:每个SUG的评分必须基于与原始问题的匹配度,使用固定的评分标准
  685. 2. **禁止相对比较**:不要比较SUG之间的好坏,不要因为"其他SUG更好"而降低某个SUG的分数
  686. 3. **标准一致性**:对第1个SUG和第10个SUG使用完全相同的评分标准
  687. 4. **独立判断**:评估SUG A时,完全不考虑SUG B/C/D的存在
  688. **错误示例**:
  689. - ❌ "这个SUG比列表中其他的更完整,给0.95"
  690. - ❌ "相比第一个SUG,这个覆盖度较低,给0.6"
  691. **正确示例**:
  692. - ✅ "这个SUG包含对象层'摄影素材'和场景层'川西+秋季',覆盖度75%,根据评分标准给0.85"
  693. - ✅ "这个SUG只有场景'川西',无对象层,覆盖度50%,根据评分标准给0.35"
  694. ---
  695. # 输出格式
  696. 输出结果必须为一个 **JSON 格式**,包含evaluations数组,每个元素包含:
  697. ```json
  698. {
  699. "evaluations": [
  700. {
  701. "sug_text": "SUG文本",
  702. "品类维度得分": "-1到1之间的小数",
  703. "简要说明品类维度相关度理由": "评估理由"
  704. }
  705. ]
  706. }
  707. ```
  708. **输出约束(非常重要)**:
  709. 1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
  710. 2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
  711. 3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
  712. 4. **顺序严格对应(极其重要)**:
  713. - evaluations数组必须与输入的sug词条列表严格1对1对应
  714. - 第1个元素必须是输入列表的第1个SUG,第2个元素必须是第2个SUG,以此类推
  715. - 每个元素的sug_text必须与输入SUG完全一致(逐字匹配,包括标点)
  716. - 禁止改变顺序、禁止遗漏任何SUG、禁止重复评估
  717. - 示例:输入"1. 秋季摄影素材 2. 川西风光" → 输出[{sug_text:"秋季摄影素材",...}, {sug_text:"川西风光",...}]
  718. - 错误示例:输出[{sug_text:"川西风光",...}, {sug_text:"秋季摄影素材",...}] ← 顺序错误❌
  719. ---
  720. # 核心原则总结
  721. 1. **只看名词和限定词**:完全忽略动作和意图
  722. 2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
  723. 3. **禁止联想推演**:只看sug词实际包含的词汇
  724. 4. **通用≠特定**:通用概念不等于特定概念
  725. 5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
  726. 6. **独立评估**:每个SUG完全独立评估,禁止相对比较
  727. """.strip()
  728. motivation_evaluator = Agent[None](
  729. name="动机维度评估专家",
  730. instructions=motivation_evaluation_instructions,
  731. model=get_model(MODEL_NAME),
  732. model_settings=ModelSettings(temperature=TEMPERATURE),
  733. output_type=MotivationEvaluation,
  734. )
  735. category_evaluator = Agent[None](
  736. name="品类维度评估专家",
  737. instructions=category_evaluation_instructions,
  738. model=get_model(MODEL_NAME),
  739. model_settings=ModelSettings(temperature=TEMPERATURE),
  740. output_type=CategoryEvaluation,
  741. )
  742. # ============================================================================
  743. # Agent定义 - 批量评估
  744. # ============================================================================
  745. # 批量动机evaluator - 使用批量专用的prompt
  746. batch_motivation_evaluator = Agent[None](
  747. name="批量动机维度评估专家",
  748. instructions=batch_motivation_evaluation_instructions,
  749. model=get_model(MODEL_NAME),
  750. model_settings=ModelSettings(temperature=TEMPERATURE),
  751. output_type=BatchMotivationResult,
  752. )
  753. # 批量品类evaluator - 使用批量专用的prompt
  754. batch_category_evaluator = Agent[None](
  755. name="批量品类维度评估专家",
  756. instructions=batch_category_evaluation_instructions,
  757. model=get_model(MODEL_NAME),
  758. model_settings=ModelSettings(temperature=TEMPERATURE),
  759. output_type=BatchCategoryResult,
  760. )
  761. # ============================================================================
  762. # 评估函数
  763. # ============================================================================
  764. async def evaluate_single(sug: str, original_question: str) -> dict:
  765. """单个评估:对一个SUG进行动机+品类评估"""
  766. eval_input = f"""
  767. <原始问题>
  768. {original_question}
  769. </原始问题>
  770. <平台sug词条>
  771. {sug}
  772. </平台sug词条>
  773. 请评估平台sug词条与原始问题的匹配度。
  774. """
  775. # 并发调用两个评估器
  776. motivation_task = Runner.run(motivation_evaluator, eval_input)
  777. category_task = Runner.run(category_evaluator, eval_input)
  778. motivation_result, category_result = await asyncio.gather(
  779. motivation_task,
  780. category_task
  781. )
  782. motivation_eval: MotivationEvaluation = motivation_result.final_output
  783. category_eval: CategoryEvaluation = category_result.final_output
  784. return {
  785. "sug": sug,
  786. "motivation_score": motivation_eval.动机维度得分,
  787. "category_score": category_eval.品类维度得分,
  788. "motivation_reason": motivation_eval.简要说明动机维度相关度理由,
  789. "category_reason": category_eval.简要说明品类维度相关度理由,
  790. }
  791. async def evaluate_single_mode(sugs: list[str], original_question: str) -> tuple[list[dict], float]:
  792. """单个评估模式:逐个调用"""
  793. print(f"\n{'='*60}")
  794. print(f"模式1: 单个评估(调用{len(sugs)}次)")
  795. print(f"{'='*60}")
  796. start_time = time.time()
  797. results = []
  798. for i, sug in enumerate(sugs, 1):
  799. print(f" [{i}/{len(sugs)}] 评估: {sug}")
  800. result = await evaluate_single(sug, original_question)
  801. results.append(result)
  802. elapsed = time.time() - start_time
  803. print(f"\n✅ 单个评估模式完成")
  804. print(f" 耗时: {elapsed:.2f}秒")
  805. print(f" 平均每个SUG: {elapsed/len(sugs):.2f}秒")
  806. return results, elapsed
  807. async def evaluate_batch_mode(sugs: list[str], original_question: str) -> tuple[list[dict], float]:
  808. """批量评估模式:分别批量评估动机和品类维度"""
  809. print(f"\n{'='*60}")
  810. print(f"模式2: 批量评估(批量动机+批量品类,各评估{len(sugs)}个)")
  811. print(f"{'='*60}")
  812. start_time = time.time()
  813. # 构建批量评估输入
  814. sug_list_str = "\n".join([f"{i}. {sug}" for i, sug in enumerate(sugs, 1)])
  815. batch_input = f"""
  816. <原始问题>
  817. {original_question}
  818. </原始问题>
  819. <平台sug词条列表>
  820. {sug_list_str}
  821. </平台sug词条列表>
  822. 请对以上所有SUG每一个进行完全独立评估。
  823. """
  824. print(f" [1/2] 发送批量动机评估请求...")
  825. motivation_task = Runner.run(batch_motivation_evaluator, batch_input)
  826. print(f" [2/2] 发送批量品类评估请求...")
  827. category_task = Runner.run(batch_category_evaluator, batch_input)
  828. # 并发执行两个批量评估
  829. motivation_result, category_result = await asyncio.gather(
  830. motivation_task,
  831. category_task
  832. )
  833. batch_motivation: BatchMotivationResult = motivation_result.final_output
  834. batch_category: BatchCategoryResult = category_result.final_output
  835. elapsed = time.time() - start_time
  836. # ========== 顺序验证 ==========
  837. print(f"\n [验证] 检查批量评估结果顺序...")
  838. # 验证数量
  839. mot_count = len(batch_motivation.evaluations)
  840. cat_count = len(batch_category.evaluations)
  841. expected_count = len(sugs)
  842. if mot_count != expected_count:
  843. print(f" ⚠️ 警告: 动机评估数量不匹配! 期望{expected_count}个,实际{mot_count}个")
  844. if cat_count != expected_count:
  845. print(f" ⚠️ 警告: 品类评估数量不匹配! 期望{expected_count}个,实际{cat_count}个")
  846. # 验证顺序和文本匹配
  847. order_errors = []
  848. for i, (expected_sug, mot_item, cat_item) in enumerate(zip(sugs, batch_motivation.evaluations, batch_category.evaluations), 1):
  849. if mot_item.sug_text != expected_sug:
  850. order_errors.append(f" 位置{i}: 动机维度sug_text='{mot_item.sug_text}' != 期望'{expected_sug}'")
  851. if cat_item.sug_text != expected_sug:
  852. order_errors.append(f" 位置{i}: 品类维度sug_text='{cat_item.sug_text}' != 期望'{expected_sug}'")
  853. if order_errors:
  854. print(f" ❌ 发现顺序错误:")
  855. for error in order_errors[:5]: # 最多显示前5个错误
  856. print(error)
  857. if len(order_errors) > 5:
  858. print(f" ... 还有{len(order_errors)-5}个错误未显示")
  859. else:
  860. print(f" ✅ 顺序验证通过: 所有SUG文本与输入完全匹配")
  861. # 合并结果
  862. results = []
  863. for mot_item, cat_item in zip(batch_motivation.evaluations, batch_category.evaluations):
  864. results.append({
  865. "sug": mot_item.sug_text,
  866. "motivation_score": mot_item.动机维度得分,
  867. "category_score": cat_item.品类维度得分,
  868. "motivation_reason": mot_item.简要说明动机维度相关度理由,
  869. "category_reason": cat_item.简要说明品类维度相关度理由,
  870. })
  871. print(f"\n✅ 批量评估模式完成")
  872. print(f" 耗时: {elapsed:.2f}秒")
  873. print(f" 平均每个SUG: {elapsed/len(sugs):.2f}秒")
  874. print(f" (包含: 1次批量动机评估 + 1次批量品类评估)")
  875. return results, elapsed
  876. def compare_results(single_results: list[dict], batch_results: list[dict]):
  877. """对比两种模式的评估结果质量"""
  878. print(f"\n{'='*60}")
  879. print(f"结果质量对比")
  880. print(f"{'='*60}")
  881. # ========== 得分对比表格 ==========
  882. print(f"\n【得分对比】")
  883. print(f"\n{'SUG':<30} {'单个动机':<10} {'批量动机':<10} {'差异':<8} {'单个品类':<10} {'批量品类':<10} {'差异':<8}")
  884. print(f"{'-'*30} {'-'*10} {'-'*10} {'-'*8} {'-'*10} {'-'*10} {'-'*8}")
  885. total_motivation_diff = 0
  886. total_category_diff = 0
  887. for single, batch in zip(single_results, batch_results):
  888. sug = single['sug'][:28]
  889. single_mot = single['motivation_score']
  890. batch_mot = batch['motivation_score']
  891. mot_diff = abs(single_mot - batch_mot)
  892. single_cat = single['category_score']
  893. batch_cat = batch['category_score']
  894. cat_diff = abs(single_cat - batch_cat)
  895. total_motivation_diff += mot_diff
  896. total_category_diff += cat_diff
  897. print(f"{sug:<30} {single_mot:<10.2f} {batch_mot:<10.2f} {mot_diff:<8.3f} {single_cat:<10.2f} {batch_cat:<10.2f} {cat_diff:<8.3f}")
  898. avg_mot_diff = total_motivation_diff / len(single_results)
  899. avg_cat_diff = total_category_diff / len(single_results)
  900. print(f"\n平均得分差异:")
  901. print(f" 动机维度: {avg_mot_diff:.3f}")
  902. print(f" 品类维度: {avg_cat_diff:.3f}")
  903. print(f" 综合差异: {(avg_mot_diff + avg_cat_diff) / 2:.3f}")
  904. # ========== 评估理由对比 ==========
  905. print(f"\n{'='*60}")
  906. print(f"【评估理由对比】")
  907. print(f"{'='*60}")
  908. for i, (single, batch) in enumerate(zip(single_results, batch_results), 1):
  909. sug = single['sug']
  910. print(f"\n{i}. {sug}")
  911. print(f"{'-'*60}")
  912. # 动机维度理由对比
  913. print(f"\n 【动机维度】 (单个: {single['motivation_score']:.2f} | 批量: {batch['motivation_score']:.2f} | 差异: {abs(single['motivation_score'] - batch['motivation_score']):.3f})")
  914. print(f" 单个评估理由: {single['motivation_reason']}")
  915. print(f" 批量评估理由: {batch['motivation_reason']}")
  916. # 品类维度理由对比
  917. print(f"\n 【品类维度】 (单个: {single['category_score']:.2f} | 批量: {batch['category_score']:.2f} | 差异: {abs(single['category_score'] - batch['category_score']):.3f})")
  918. print(f" 单个评估理由: {single['category_reason']}")
  919. print(f" 批量评估理由: {batch['category_reason']}")
  920. # ============================================================================
  921. # 主函数
  922. # ============================================================================
  923. async def main():
  924. """主测试函数"""
  925. # 测试数据
  926. original_question = "如何获取能体现川西秋季特色的高质量风光摄影素材?"
  927. test_sugs = [
  928. "川西风光摄影",
  929. "秋季摄影素材",
  930. "高质量风光素材",
  931. "川西秋季旅游攻略",
  932. "风光摄影技巧",
  933. "川西风光",
  934. "摄影素材下载",
  935. "秋季旅行",
  936. "风光摄影作品",
  937. "获取川西秋季风景",
  938. ]
  939. print(f"{'='*60}")
  940. print(f"批量评估 vs 单个评估性能对比Demo")
  941. print(f"{'='*60}")
  942. print(f"\n原始问题: {original_question}")
  943. print(f"测试SUG数量: {len(test_sugs)}")
  944. print(f"\nSUG列表:")
  945. for i, sug in enumerate(test_sugs, 1):
  946. print(f" {i}. {sug}")
  947. # 模式1: 单个评估
  948. single_results, single_time = await evaluate_single_mode(test_sugs, original_question)
  949. # 模式2: 批量评估
  950. batch_results, batch_time = await evaluate_batch_mode(test_sugs, original_question)
  951. # 对比结果质量
  952. compare_results(single_results, batch_results)
  953. # 性能总结
  954. print(f"\n{'='*60}")
  955. print(f"性能总结")
  956. print(f"{'='*60}")
  957. print(f"单个评估模式: {single_time:.2f}秒")
  958. print(f"批量评估模式: {batch_time:.2f}秒")
  959. speedup = single_time / batch_time
  960. print(f"\n性能提升: {speedup:.2f}x")
  961. if speedup > 1:
  962. print(f"✅ 批量评估更快 {speedup:.2f}倍")
  963. else:
  964. print(f"❌ 批量评估更慢 {1/speedup:.2f}倍")
  965. print(f"\n{'='*60}")
  966. print(f"结论:")
  967. print(f"{'='*60}")
  968. if speedup > 1.5:
  969. print(f"✅ 批量评估显著更快,建议采用")
  970. elif speedup > 1.1:
  971. print(f"⚠️ 批量评估略快,但优势不明显")
  972. else:
  973. print(f"❌ 批量评估无性能优势,不建议采用")
  974. if __name__ == "__main__":
  975. asyncio.run(main())