debug_select_creative_patterns.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """调试单个视频的创意模式选择。
  2. 除启用 ODPS 查询时可能填充视频特征缓存外,本脚本只读;不会生成图片、上传
  3. OSS 文件或创建腾讯广告。
  4. """
  5. from __future__ import annotations
  6. import argparse
  7. import json
  8. import sys
  9. from pathlib import Path
  10. from dotenv import load_dotenv
  11. _HERE = Path(__file__).parent
  12. load_dotenv(_HERE / ".env")
  13. sys.path.insert(0, str(_HERE))
  14. from tools.material_strategy_learning import select_creative_patterns # noqa: E402
  15. from tools.video_feature_query import ( # noqa: E402
  16. fetch_video_element_features,
  17. read_cached_video_element_features,
  18. )
  19. def parse_args() -> argparse.Namespace:
  20. parser = argparse.ArgumentParser(description="调试单个视频的创意 pattern 选择")
  21. parser.add_argument("--video-id", type=int, required=True)
  22. parser.add_argument("--crowd-package", default="", help="保留展示字段,pattern选择不按人群包区分")
  23. parser.add_argument("--placement", default="")
  24. parser.add_argument("--top-k", type=int, default=3)
  25. parser.add_argument(
  26. "--include-draft",
  27. action="store_true",
  28. default=True,
  29. help="包含 DRAFT pattern,用于上线前调试评估",
  30. )
  31. parser.add_argument(
  32. "--approved-only",
  33. action="store_true",
  34. help="只看 APPROVED/enabled=1 的生产可用 pattern",
  35. )
  36. parser.add_argument(
  37. "--cached-only",
  38. action="store_true",
  39. help="只读本地视频特征缓存,不查 ODPS",
  40. )
  41. parser.add_argument(
  42. "--no-model",
  43. action="store_true",
  44. help="关闭模型选择,只看非语义稳定兜底排序",
  45. )
  46. parser.add_argument(
  47. "--model",
  48. default=None,
  49. help="覆盖 OPENROUTER_TEXT_MODEL,例如 google/gemini-2.5-flash",
  50. )
  51. return parser.parse_args()
  52. def _feature_to_dict(feature) -> dict:
  53. return {
  54. "video_id": feature.video_id,
  55. "dt": feature.dt,
  56. "element_dimension": feature.element_dimension,
  57. "point_type": feature.point_type,
  58. "standard_element": feature.standard_element,
  59. "contribution_score": feature.contribution_score,
  60. }
  61. def main() -> int:
  62. args = parse_args()
  63. if args.cached_only:
  64. feature_map = read_cached_video_element_features([args.video_id])
  65. else:
  66. feature_map = fetch_video_element_features([args.video_id])
  67. features = feature_map.get(args.video_id) or []
  68. selections = select_creative_patterns(
  69. video_features=features,
  70. crowd_package=args.crowd_package,
  71. placement=args.placement,
  72. include_draft=not args.approved_only and args.include_draft,
  73. top_k=args.top_k,
  74. use_model=not args.no_model,
  75. model=args.model,
  76. )
  77. payload = {
  78. "video_id": args.video_id,
  79. "crowd_package": args.crowd_package,
  80. "placement": args.placement,
  81. "selection_mode": "model_select" if not args.no_model else "stable_fallback",
  82. "feature_count": len(features),
  83. "features": [_feature_to_dict(feature) for feature in features],
  84. "selected_patterns": [
  85. {
  86. "pattern_version": item.pattern.pattern_version,
  87. "pattern_key": item.pattern.pattern_key,
  88. "pattern_name": item.pattern.pattern_name,
  89. "status": item.pattern.status,
  90. "enabled": item.pattern.enabled,
  91. "score": item.score,
  92. "reasons": item.reasons,
  93. "penalties": item.penalties,
  94. "matched_features": item.matched_features,
  95. "title_hook_rule": item.pattern.title_hook_rule,
  96. "visual_rule": item.pattern.visual_rule,
  97. "relevance_rule": item.pattern.relevance_rule,
  98. "compliance_rule": item.pattern.compliance_rule,
  99. "positive_examples": item.pattern.positive_examples or [],
  100. "negative_examples": item.pattern.negative_examples or [],
  101. }
  102. for item in selections
  103. ],
  104. }
  105. print(json.dumps(payload, ensure_ascii=False, indent=2))
  106. return 0
  107. if __name__ == "__main__":
  108. raise SystemExit(main())