debug_generate_ai_material.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. """手动调试 AI 图片生成和 OSS 上传。
  2. 默认只生成图片并上传 OSS,打印 JSON 输出;不会创建广告/创意。
  3. 示例:
  4. python debug_generate_ai_material.py --video-id 71187017
  5. 需要环境变量:
  6. OPENROUTER_API_KEY
  7. ALIYUN_OSS_ENDPOINT
  8. ALIYUN_OSS_BUCKET
  9. ALIYUN_OSS_ACCESS_KEY_ID
  10. ALIYUN_OSS_ACCESS_KEY_SECRET
  11. AI_IMAGE_PUBLIC_BASE_URL
  12. """
  13. from __future__ import annotations
  14. import argparse
  15. import json
  16. import sys
  17. from pathlib import Path
  18. from dotenv import load_dotenv
  19. _HERE = Path(__file__).parent
  20. load_dotenv(_HERE / ".env")
  21. sys.path.insert(0, str(_HERE.parent.parent))
  22. sys.path.insert(0, str(_HERE))
  23. from tools.ai_generated_material import ( # noqa: E402
  24. AI_IMAGE_PATTERN_PLACEMENT,
  25. AI_IMAGE_PATTERN_TOP_K,
  26. OPENROUTER_TEXT_MODEL,
  27. OPENROUTER_IMAGE_MODEL,
  28. build_ai_image_object_key,
  29. build_generation_prompts,
  30. build_pattern_generation_prompts,
  31. generate_image_bytes,
  32. insert_and_review_generated_material,
  33. sanitize_video_description,
  34. upload_image_to_oss,
  35. )
  36. from tools.video_recall import LandingVideo # noqa: E402
  37. from tools.video_feature_query import read_cached_video_element_features # noqa: E402
  38. def main() -> int:
  39. parser = argparse.ArgumentParser()
  40. parser.add_argument("--video-id", type=int, default=0)
  41. parser.add_argument("--title", default="")
  42. parser.add_argument("--category", default="")
  43. parser.add_argument("--model", default=OPENROUTER_IMAGE_MODEL)
  44. parser.add_argument("--text-model", default=OPENROUTER_TEXT_MODEL)
  45. parser.add_argument("--limit", type=int, default=1)
  46. parser.add_argument("--account-id", type=int, default=0)
  47. parser.add_argument("--adgroup-id", type=int, default=0)
  48. parser.add_argument("--crowd-package", default="")
  49. parser.add_argument("--placement", default=AI_IMAGE_PATTERN_PLACEMENT)
  50. parser.add_argument("--pattern-top-k", type=int, default=AI_IMAGE_PATTERN_TOP_K)
  51. parser.add_argument("--use-pattern-selector", action="store_true")
  52. parser.add_argument("--write-db", action="store_true")
  53. args = parser.parse_args()
  54. if not args.video_id:
  55. raise SystemExit("必须提供 --video-id")
  56. features_by_vid = read_cached_video_element_features([args.video_id])
  57. features = features_by_vid.get(args.video_id) or []
  58. topic = next((f.standard_element for f in features if f.element_dimension == "解构选题"), "")
  59. sanitized_description = sanitize_video_description(topic) if topic else ""
  60. if args.use_pattern_selector:
  61. built_prompts = build_pattern_generation_prompts(
  62. video_id=args.video_id,
  63. title=args.title,
  64. category=args.category,
  65. features=features,
  66. sanitized_description=sanitized_description,
  67. crowd_package=args.crowd_package,
  68. placement=args.placement,
  69. top_k=args.pattern_top_k,
  70. text_model=args.text_model,
  71. )
  72. else:
  73. built_prompts = build_generation_prompts(
  74. video_id=args.video_id,
  75. title=args.title,
  76. category=args.category,
  77. features=features,
  78. sanitized_description=sanitized_description,
  79. )
  80. prompts = built_prompts[: max(1, args.limit)]
  81. outputs = []
  82. landing = LandingVideo(
  83. video_id=args.video_id,
  84. title=args.title,
  85. cover_url="",
  86. video_url="",
  87. score=0,
  88. rov=0,
  89. sim=0,
  90. visit_uv=0,
  91. category=args.category,
  92. standard_element="",
  93. category_name="",
  94. demand_content_title="",
  95. demand_content_topic="",
  96. demand_content_id="",
  97. demand_type="",
  98. point_type="",
  99. dimension="",
  100. experiment_id="",
  101. raw={},
  102. )
  103. for prompt in prompts:
  104. image_bytes, content_type, raw = generate_image_bytes(prompt.prompt_text, model=args.model)
  105. ext = ".png" if content_type == "image/png" else ".jpg"
  106. object_key = build_ai_image_object_key(
  107. account_id=args.account_id or "debug",
  108. landing_video_id=args.video_id,
  109. prompt_type=prompt.prompt_type,
  110. extension=ext,
  111. debug=not args.write_db,
  112. )
  113. oss_url = upload_image_to_oss(image_bytes, content_type, object_key)
  114. asset_id = None
  115. review_output = None
  116. if args.write_db:
  117. asset, review = insert_and_review_generated_material(
  118. account_id=args.account_id,
  119. adgroup_id=args.adgroup_id,
  120. crowd_package=args.crowd_package or "debug",
  121. landing=landing,
  122. prompt=prompt,
  123. model=args.model,
  124. object_key=object_key,
  125. oss_url=oss_url,
  126. raw_response=raw,
  127. )
  128. asset_id = asset.id
  129. review_output = {
  130. "status": review.status,
  131. "score": review.score,
  132. "reason": review.reason,
  133. "risk_tags": review.risk_tags,
  134. "ocr_text": review.ocr_text,
  135. }
  136. outputs.append({
  137. "asset_id": asset_id,
  138. "ai_review": review_output,
  139. "prompt_type": prompt.prompt_type,
  140. "model": args.model,
  141. "text_model": args.text_model,
  142. "content_type": content_type,
  143. "oss_url": oss_url,
  144. "object_key": object_key,
  145. "prompt_text": prompt.prompt_text,
  146. "feature_hits": prompt.feature_hits,
  147. "raw_response_id": raw.get("id"),
  148. })
  149. print(json.dumps(outputs, ensure_ascii=False, indent=2))
  150. return 0
  151. if __name__ == "__main__":
  152. raise SystemExit(main())