- 添加MDF文件导出功能 - 集成阿里云OCR大模型识别 - 添加百度智能云AI照片评分 - 集成DeepSeek大模型创意文案生成 - 完善文档和配置管理 - 使用uv进行现代化依赖管理 - 添加完整的.gitignore配置
438 lines
18 KiB
Python
438 lines
18 KiB
Python
#!/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)}" |