GH/utils/ai_copywriter.py

438 lines
18 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
AI文案生成服务集成
使用AI大模型为照片生成创意文案
支持多种文案风格和用途
支持DeepSeek和DashScope两种大模型
"""
import os
import json
import requests
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
class AICopywriter:
"""AI文案生成服务类"""
def __init__(self, provider='deepseek'):
"""初始化AI文案生成客户端"""
self.provider = provider
if provider == 'deepseek':
self.api_key = os.getenv('DEEPSEEK_API_KEY')
if not self.api_key:
raise Exception("DeepSeek API密钥未配置请在.env文件中设置DEEPSEEK_API_KEY")
self.base_url = "https://api.deepseek.com/v1/chat/completions"
elif provider == 'dashscope':
self.api_key = os.getenv('DASHSCOPE_API_KEY')
if not self.api_key:
raise Exception("DashScope API密钥未配置请在.env文件中设置DASHSCOPE_API_KEY")
else:
raise Exception(f"不支持的AI提供商: {provider}")
def generate_photo_caption(self, image_description, style='creative', length='medium'):
"""为照片生成文案"""
try:
if self.provider == 'deepseek':
return self._generate_with_deepseek(image_description, style, length)
elif self.provider == 'dashscope':
return self._generate_with_dashscope(image_description, style, length)
else:
raise Exception(f"不支持的AI提供商: {self.provider}")
except Exception as e:
raise Exception(f"AI文案生成失败: {str(e)}")
def _generate_with_deepseek(self, image_description, style, length):
"""使用DeepSeek生成文案"""
try:
prompt = self._build_prompt(image_description, style, length)
headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
data = {
'model': 'deepseek-chat',
'messages': [
{
'role': 'system',
'content': '你是一个专业的创意文案创作助手,擅长为照片生成各种风格的创意文案。你具有丰富的文学素养和营销知识,能够根据照片内容创作出富有创意和感染力的文案。'
},
{
'role': 'user',
'content': prompt
}
],
'max_tokens': 500,
'temperature': 0.8,
'top_p': 0.9
}
response = requests.post(self.base_url, headers=headers, json=data)
result = response.json()
if 'choices' in result and len(result['choices']) > 0:
caption = result['choices'][0]['message']['content'].strip()
# 清理可能的格式标记
caption = caption.replace('"', '').replace('\n', ' ').strip()
return caption
else:
# 如果API调用失败使用备用文案生成
return self._generate_fallback_caption(image_description, style, length)
except Exception as e:
# API调用失败时使用备用方案
return self._generate_fallback_caption(image_description, style, length)
def _generate_with_dashscope(self, image_description, style, length):
"""使用DashScope生成文案"""
try:
url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
# 根据风格和长度构建提示词
prompt = self._build_prompt(image_description, style, length)
data = {
'model': 'qwen-turbo',
'input': {
'messages': [
{
'role': 'system',
'content': '你是一个专业的文案创作助手,擅长为照片生成各种风格的创意文案。'
},
{
'role': 'user',
'content': prompt
}
]
},
'parameters': {
'max_tokens': 500,
'temperature': 0.8
}
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if 'output' in result and 'text' in result['output']:
return result['output']['text']
else:
# 如果API调用失败使用备用文案生成
return self._generate_fallback_caption(image_description, style, length)
except Exception as e:
# API调用失败时使用备用方案
return self._generate_fallback_caption(image_description, style, length)
def _build_prompt(self, image_description, style, length):
"""构建AI提示词"""
style_descriptions = {
'creative': '创意文艺风格,富有诗意和想象力',
'professional': '专业正式风格,简洁明了',
'social': '社交媒体风格,活泼有趣,适合朋友圈',
'marketing': '营销推广风格,吸引眼球,促进转化',
'simple': '简单描述风格,直接明了',
'emotional': '情感表达风格,温暖感人'
}
length_descriptions = {
'short': '10-20字简洁精炼',
'medium': '30-50字适中长度',
'long': '80-120字详细描述'
}
prompt = f"""
请为以下照片内容生成{style_descriptions.get(style, '创意')}的文案要求{length_descriptions.get(length, '适中长度')}
照片内容描述{image_description}
文案要求
1. 符合{style}风格
2. 长度{length}
3. 有创意吸引人
4. 适合社交媒体分享
请直接输出文案内容不要添加其他说明
"""
return prompt.strip()
def _generate_fallback_caption(self, image_description, style, length):
"""备用文案生成当AI服务不可用时"""
# 基于照片描述的简单文案生成
keywords = image_description.lower().split()
# 提取关键信息
objects = []
scenes = []
# 简单的关键词分类实际应用中可以使用更复杂的NLP处理
object_keywords = ['', '建筑', '天空', '', '', '动物', '', '食物', '', '']
scene_keywords = ['户外', '室内', '自然', '城市', '夜景', '日出', '日落', '海滩', '森林']
for word in keywords:
if any(obj in word for obj in object_keywords):
objects.append(word)
if any(scene in word for scene in scene_keywords):
scenes.append(word)
# 根据风格生成文案
if style == 'creative':
if scenes:
caption = f"{scenes[0]}的怀抱中,时光静静流淌"
elif objects:
caption = f"{objects[0]}的美丽瞬间,定格永恒"
else:
caption = "捕捉生活中的美好,让每一刻都值得珍藏"
elif style == 'social':
if objects:
caption = f"今天遇到的{objects[0]}太可爱了!分享给大家~"
else:
caption = "分享一张美照,希望大家喜欢!"
elif style == 'professional':
if scenes and objects:
caption = f"专业拍摄:{scenes[0]}场景中的{objects[0]}特写"
else:
caption = "专业摄影作品展示"
elif style == 'marketing':
if objects:
caption = f"惊艳!这个{objects[0]}你一定要看看!"
else:
caption = "不容错过的精彩瞬间,点击了解更多!"
else: # simple or emotional
if objects:
caption = f"美丽的{objects[0]}照片"
else:
caption = "一张值得分享的照片"
# 根据长度调整
if length == 'long' and len(caption) < 50:
caption += "。这张照片记录了珍贵的瞬间,展现了生活的美好,值得细细品味和珍藏。"
elif length == 'short' and len(caption) > 20:
# 简化长文案
caption = caption[:20] + "..."
return caption
def generate_multiple_captions(self, image_description, count=3, style='creative'):
"""生成多个文案选项"""
try:
if self.provider == 'deepseek':
return self._generate_multiple_with_deepseek(image_description, count, style)
elif self.provider == 'dashscope':
return self._generate_multiple_with_dashscope(image_description, count, style)
else:
raise Exception(f"不支持的AI提供商: {self.provider}")
except Exception as e:
raise Exception(f"生成多个文案失败: {str(e)}")
def _generate_multiple_with_deepseek(self, image_description, count=3, style='creative'):
"""使用DeepSeek生成多个文案选项"""
try:
captions = []
# 使用不同的提示词变体生成多个文案
prompt_variants = [
f"请为'{image_description}'照片创作一个{style}风格的文案,要求新颖独特",
f"基于照片内容'{image_description}',写一个{style}风格的创意文案",
f"为这张'{image_description}'的照片设计一个{style}风格的吸引人文案"
]
for i in range(min(count, len(prompt_variants))):
prompt = prompt_variants[i]
headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
data = {
'model': 'deepseek-chat',
'messages': [
{
'role': 'system',
'content': '你是专业的创意文案专家,擅长为照片创作多种风格的文案。'
},
{
'role': 'user',
'content': prompt
}
],
'max_tokens': 200,
'temperature': 0.9, # 提高温度增加多样性
'top_p': 0.95
}
response = requests.post(self.base_url, headers=headers, json=data)
result = response.json()
if 'choices' in result and len(result['choices']) > 0:
caption = result['choices'][0]['message']['content'].strip()
caption = caption.replace('"', '').replace('\n', ' ').strip()
captions.append({
'option': i + 1,
'caption': caption,
'style': style,
'char_count': len(caption)
})
return captions
except Exception as e:
raise Exception(f"DeepSeek多文案生成失败: {str(e)}")
def _generate_multiple_with_dashscope(self, image_description, count=3, style='creative'):
"""使用DashScope生成多个文案选项"""
try:
captions = []
# 尝试使用不同的长度和微调风格
lengths = ['short', 'medium', 'long']
for i in range(min(count, len(lengths))):
caption = self.generate_photo_caption(image_description, style, lengths[i])
captions.append({
'option': i + 1,
'caption': caption,
'length': lengths[i],
'char_count': len(caption)
})
# 如果数量不足,使用不同风格补充
if len(captions) < count:
additional_styles = ['social', 'professional', 'emotional']
for i, add_style in enumerate(additional_styles):
if len(captions) >= count:
break
caption = self.generate_photo_caption(image_description, add_style, 'medium')
captions.append({
'option': len(captions) + 1,
'caption': caption,
'style': add_style,
'char_count': len(caption)
})
return captions
except Exception as e:
raise Exception(f"DashScope多文案生成失败: {str(e)}")
def analyze_photo_suitability(self, image_description):
"""分析照片适合的文案风格"""
try:
# 简单的风格适合性分析
keywords = image_description.lower()
suitability = {
'creative': 0,
'professional': 0,
'social': 0,
'marketing': 0,
'emotional': 0
}
# 关键词匹配实际应用中可以使用更复杂的NLP分析
creative_words = ['美丽', '艺术', '创意', '独特', '梦幻']
professional_words = ['专业', '商业', '产品', '展示', '特写']
social_words = ['朋友', '聚会', '日常', '分享', '生活']
marketing_words = ['促销', '优惠', '新品', '限时', '推荐']
emotional_words = ['情感', '感动', '回忆', '温暖', '幸福']
for word in creative_words:
if word in keywords:
suitability['creative'] += 1
for word in professional_words:
if word in keywords:
suitability['professional'] += 1
for word in social_words:
if word in keywords:
suitability['social'] += 1
for word in marketing_words:
if word in keywords:
suitability['marketing'] += 1
for word in emotional_words:
if word in keywords:
suitability['emotional'] += 1
# 排序并返回推荐
recommended = sorted(suitability.items(), key=lambda x: x[1], reverse=True)
return {
'suitability_scores': suitability,
'recommended_styles': [style for style, score in recommended if score > 0],
'most_suitable': recommended[0][0] if recommended[0][1] > 0 else 'creative'
}
except Exception as e:
raise Exception(f"照片适合性分析失败: {str(e)}")
def generate_photo_caption(image_description, style='creative', length='medium', provider='dashscope'):
"""为照片生成文案"""
try:
copywriter = AICopywriter(provider)
return copywriter.generate_photo_caption(image_description, style, length)
except Exception as e:
raise Exception(f"照片文案生成失败: {str(e)}")
def generate_multiple_captions(image_description, count=3, style='creative', provider='dashscope'):
"""生成多个文案选项"""
try:
copywriter = AICopywriter(provider)
return copywriter.generate_multiple_captions(image_description, count, style)
except Exception as e:
raise Exception(f"多文案生成失败: {str(e)}")
def analyze_photo_suitability(image_description, provider='dashscope'):
"""分析照片适合的文案风格"""
try:
copywriter = AICopywriter(provider)
return copywriter.analyze_photo_suitability(image_description)
except Exception as e:
raise Exception(f"照片适合性分析失败: {str(e)}")
def check_copywriter_config(provider='deepseek'):
"""检查AI文案生成配置是否完整"""
try:
if provider == 'deepseek':
api_key = os.getenv('DEEPSEEK_API_KEY')
if not api_key:
return False, "DeepSeek API密钥未配置"
# 测试连接
copywriter = AICopywriter(provider)
return True, "AI文案生成配置正确DeepSeek大模型"
elif provider == 'dashscope':
api_key = os.getenv('DASHSCOPE_API_KEY')
if not api_key:
return False, "DashScope API密钥未配置"
# 测试连接
copywriter = AICopywriter(provider)
return True, "AI文案生成配置正确DashScope"
else:
return False, f"不支持的AI提供商: {provider}"
except Exception as e:
return False, f"AI文案生成配置错误: {str(e)}"