_utils.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. """内容质量评估工具函数 —— Prompt 构建 + LLM 响应解析"""
  2. import hashlib
  3. import re
  4. import logging
  5. from typing import Dict, List
  6. from src.infra.shared.tools import safe_json_parse
  7. from ._const import ContentQualityConst
  8. logger = logging.getLogger(__name__)
  9. def md5(text: str) -> str:
  10. return hashlib.md5(text.encode("utf-8")).hexdigest()
  11. # ═══════════════════════════════════════════════════════════════
  12. # Prompt 构建
  13. # ═══════════════════════════════════════════════════════════════
  14. def build_title_quality_prompt(titles: List[Dict]) -> str:
  15. """构造标题质量评分 prompt,每行一个标题"""
  16. lines = [t["title"] for t in titles]
  17. return ContentQualityConst.PROMPT_TITLE_QUALITY.format(
  18. title_list="\n".join(lines),
  19. )
  20. def build_category_prompt(titles: List[Dict]) -> str:
  21. """构造品类识别 prompt,格式: [(id, title), ...] 元组列表"""
  22. tuples = [(t["id"], t["title"]) for t in titles]
  23. return ContentQualityConst.PROMPT_TITLE_CATEGORY.format(
  24. title_list=str(tuples),
  25. )
  26. # ═══════════════════════════════════════════════════════════════
  27. # LLM 响应解析
  28. # ═══════════════════════════════════════════════════════════════
  29. def parse_title_quality_response(
  30. raw: str | None,
  31. batch_titles: List[Dict],
  32. ) -> List[Dict]:
  33. """解析 prompt1 返回值:LLM 按行输出纯数字分数,按顺序对应输入标题
  34. 返回: [{"id": 1, "score": 85}, ...]
  35. """
  36. result: List[Dict] = []
  37. if not raw:
  38. logger.warning("LLM 标题质量返回为空")
  39. return result
  40. # 从文本中提取所有数字
  41. scores = re.findall(r"\d+", raw)
  42. if not scores:
  43. logger.warning("LLM 标题质量未提取到分数: %s", raw[:200])
  44. return result
  45. for i, s in enumerate(scores):
  46. if i >= len(batch_titles):
  47. break
  48. score = int(s)
  49. if 0 <= score <= 100:
  50. result.append({"id": batch_titles[i]["id"], "score": score})
  51. return result
  52. def parse_category_response(
  53. raw: str | None,
  54. batch_titles: List[Dict],
  55. ) -> List[Dict]:
  56. """解析 prompt2 返回值:{id: category} JSON 对象
  57. 返回: [{"id": 1, "category": "知识科普"}, ...]
  58. """
  59. result: List[Dict] = []
  60. if not raw:
  61. logger.warning("LLM 品类识别返回为空")
  62. return result
  63. parsed = safe_json_parse(raw)
  64. if not isinstance(parsed, dict):
  65. logger.warning("LLM 品类识别返回非对象: %s", raw[:200])
  66. return result
  67. for t in batch_titles:
  68. category = parsed.get(str(t["id"]))
  69. if category:
  70. result.append({"id": t["id"], "category": str(category)[:32]})
  71. return result
  72. __all__ = [
  73. "md5",
  74. "build_title_quality_prompt",
  75. "build_category_prompt",
  76. "parse_title_quality_response",
  77. "parse_category_response",
  78. ]