初步实现 LLM调用生成海报文案

This commit is contained in:
Yiwenjia 2025-05-28 21:42:26 +08:00
parent 45ecb5cb4b
commit f38e88d04b
9 changed files with 413 additions and 292 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
MOONSHOT_API_KEY=sk-Xzv3OqwLoM1RRsXIQuEhLMNATrMv2JsLwf7LbM97Ha6sFDun

View File

@ -20,4 +20,44 @@
python generate_layout.py
```
- 该脚本会自动调用 DeepSeek API 生成 React 组件代码,并将其保存到指定路径。
默认为``output/generated_code.jsx``,可以根据需要修改。
默认为``output/generated_code.jsx``,可以根据需要修改。
## 图层叠加和PSD导出部分
### 功能组件
- **图层合成**:根据生成的布局配置,将各个图层叠加在一起,生成完整的海报图像。
- **PSD导出**将合成的海报图像保存为标准的PSD格式同时保留各个图层的独立性方便后续编辑。
### 如何调用
1. **环境准备**
```bash
conda create -n ai_service python=3.8
conda activate ai_service
pip install -r requirements.txt
```
2. **运行脚本**
```bash
python export_psd.py #简单生成
python export_psd_from_json.py #从json文件加载布局并生成
python PSD_test.py #在run_pipline.py中的调用方法
```
该脚本会自动调用图层合成模块生成完整海报图像并将其保存为标准PSD格式。
## 文案生成
### 功能组件
使用 kimi API 提供的 LLM大型语言模型功能用于生成海报文案设计。当前实现的功能包括
- **标题生成**:根据海报主题生成适合的标题。
- **副标题生成**:提供与主题相关的副标题或说明文案。
- **字体与颜色建议**:为生成的文案提供适合的字体和颜色建议。
### 如何调用
1. **环境准备**
- 确保已安装 Python 环境和必要依赖(`openai`、`python-dotenv`等)。
- 在项目根目录的 `.env` 文件中配置 `MOONSHOT_API_KEY`
2. **运行脚本**
- 直接运行 `generate_text.py` 脚本:
```bash
python generate_text.py
```
- 该脚本会自动调用 kimi API 生成适合海报主题的标题、副标题和 Logo 文案,并提供字体和颜色建议,并将其保存到指定路径。
默认为``output/poster_content.json``,可以根据需要修改。

83
configs/font.yaml Normal file
View File

@ -0,0 +1,83 @@
# 颜色常量
NAMING_COLORS:
NANKAI_PURPLE: "#7E0C6E"
LOGO_WHITE: "#FFFFFF"
DARK_TEXT_ON_LIGHT_BG: "#000000" # 示例深色文字
LIGHT_TEXT_ON_DARK_BG: "#f4efd9" # 示例浅色文字
# 可用字体列表
# name: 字体文件在系统中的名称或前端能识别的名称
# tags: 用于规则匹配的标签
available_fonts:
- name: "FZLanTingHei-ExtraBold-GB" # 方正兰亭特黑简体 (假设这是实际的font-family名)
displayName: "方正兰亭特黑简体"
tags: ["现代", "力量感", "标题", "正式", "无衬线", "醒目"]
roles: ["title"]
- name: "Adobe Song Std L" # adobe宋体 (L 通常表示 Light weight)
displayName: "Adobe 宋体 Std L"
tags: ["传统", "正文", "经典", "宋体"]
roles: ["subtitle", "content"]
- name: "SimHei" # 黑体 简
displayName: "黑体 (简体)"
tags: ["通用", "简约", "正文", "无衬线", "清晰"]
roles: ["title", "subtitle", "content"] # 黑体用途广泛
- name: "Hiragino Sans GB W3" # 冬青黑体 (W3 通常表示字重)
displayName: "冬青黑体 W3"
tags: ["现代", "清晰", "正文", "优雅", "无衬线"]
roles: ["subtitle", "content"]
# 核心样式规则
# 主题 -> 图层ID -> 样式定义
STYLE_RULES:
"世界读书日": # 对应海报主题
"layer6_title": # 对应文字图层6大标题
font_prefs: # 字体选择偏好
- name: "ImpactfulModernSans" # 首选这个名字的字体
- tags: ["title", "bold", "modern_sans"] # 如果首选找不到,则找带这些标签的
font_size_category: "largest"
color_on_light_bg: DARK_TEXT_ON_LIGHT_BG # 使用上面定义的颜色名
color_on_dark_bg: LIGHT_TEXT_ON_DARK_BG
position_hint: "图片上方、居中、不遮挡图像"
"layer7_subtitle": # 对应文字图层7小字/文案
font_prefs:
- name: "ReadableBodySans"
- tags: ["body", "readable_sans"]
font_size_category: "small_below_title"
color_on_light_bg: DARK_TEXT_ON_LIGHT_BG
color_on_dark_bg: LIGHT_TEXT_ON_DARK_BG
position_hint: "标题下方"
"南开校庆":
"layer6_title":
font_prefs:
- name: "南开官方标准黑体"
- tags: ["formal", "title"]
font_size_category: "largest"
color_on_light_bg: NANKAI_PURPLE
color_on_dark_bg: LOGO_WHITE
position_hint: "图片上方、居中、庄重"
"通用默认": # 如果主题未匹配,则使用这里的规则
"default_title": # 给一个默认的标题图层ID
font_prefs:
- tags: ["title", "bold"]
font_size_category: "largest"
color_on_light_bg: "#1A1A1A"
color_on_dark_bg: "#E5E5E5"
"default_body":
font_prefs:
- tags: ["body", "readable_sans"]
font_size_category: "normal"
color_on_light_bg: "#000000"
color_on_dark_bg: "#ffffff"
# Logo的颜色规则 (独立于普通文字图层,像是对图像素材的选择)
LOGO_RULES:
"layer5_nankai_logo":
description: "南开大学官方logo位于四角预留空隙"
color_on_light_bg: NANKAI_PURPLE
color_on_dark_bg: LOGO_WHITE

View File

@ -1,27 +0,0 @@
python3: can't open file '/var/www/ai_service/your_server.py': [Errno 2] No such file or directory
python3: can't open file '/var/www/ai_service/aihelp.py': [Errno 2] No such file or directory
2025-04-10 10:12:52,702 - INFO - Starting server on port 9000
2025-04-10 10:13:17,949 - ERROR - Port 9000 is already in use
Traceback (most recent call last):
File "/var/www/ai_service/apihelp.py", line 4, in <module>
Traceback (most recent call last):
File "/var/www/ai_service/apihelp.py", line 4, in <module>
from c import HTTPServer, BaseHTTPRequestHandler
from c import HTTPServer, BaseHTTPRequestHandler
ModuleNotFoundError: No module named 'c'
ModuleNotFoundError: No module named 'c'
Error in sys.excepthook:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 228, in partial_apport_excepthook
return apport_excepthook(binary, exc_type, exc_obj, exc_tb)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 144, in apport_excepthook
os.open(pr_filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o640), "wb"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileExistsError: [Errno 17] File exists: '/var/crash/_var_www_ai_service_apihelp.py.0.crash'
Original exception was:
Traceback (most recent call last):
File "/var/www/ai_service/apihelp.py", line 4, in <module>
from c import HTTPServer, BaseHTTPRequestHandler
ModuleNotFoundError: No module named 'c'

View File

@ -1,51 +0,0 @@
import React from 'react';
const DragonBoatFestivalPoster = () => {
return (
<div style={{ position: 'relative', width: '1080px', height: '1920px' }}>
{/* 背景图层 */}
<div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }}></div>
{/* 主体图层 */}
<div style={{ position: 'absolute', top: '25%', left: '50%', transform: 'translate(-50%, -50%)' }}>
<div></div>
<div></div>
</div>
{/* 活动亮点 */}
<div style={{
position: 'absolute',
bottom: '25%',
left: '50%',
transform: 'translate(-50%, 0)',
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '20px'
}}>
<div>
<div></div>
<div></div>
<div></div>
</div>
<div>
<div></div>
<div></div>
<div></div>
</div>
<div>
<div></div>
<div></div>
<div></div>
</div>
</div>
{/* 页脚 */}
<div style={{ position: 'absolute', bottom: 0, left: '50%', transform: 'translateX(-50%)' }}>
<div></div>
<div></div>
</div>
</div>
);
};
export default DragonBoatFestivalPoster;

View File

@ -0,0 +1,16 @@
{
"layer5_logo_content": {
"text": "",
"color": "#000000"
},
"layer6_title_content": {
"content": "世界读书日",
"font_name": "FZLanTingHei-ExtraBold-GB",
"color": "#000000"
},
"layer7_subtitle_content": {
"content": "阅古今中外,启智慧之门",
"font_name": "Adobe Song Std L",
"color": "#4F4F4F"
}
}

View File

@ -1,140 +0,0 @@
import os
from openai import OpenAI
from dotenv import load_dotenv
import time
import logging
# === Config LLM call ===
load_dotenv()
deepseek_url = 'https://api.deepseek.com/v1' # set to be compatible with the OpenAI API
deepseek_api = os.getenv("DEEPSEEK_API_KEY")
if not deepseek_api:
raise ValueError("DEEPSEEK_API_KEY not set!")
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# === prompts and parameters ===
def call_deepseek(
messages=None,
system_prompt="你是一个擅长前端开发的AI专注于生成Vue.js代码。",
prompt=None,
model='deepseek-chat',
temperature=0.6,
max_tokens=1000,
stream=False,
max_retries=3,
):
"""
调用 DeepSeek API,支持多轮对话和流式/非流式响应
Args:
messages (list): 对话消息列表格式为[{"role": "user", "content": "..."},...]
如果提供则优先使用否则根据 system_prompt和 prompt 构造.
system_prompt (str): 系统提示词仅在 message 未提供时使用.
prompt (str): 用户提示词仅在 message 未提供时使用.
model (str): 模型名称默认为 'deepseek-chat'
temperature (float): 温度参数控制生成文本的随机性默认为 0.6
max_tokens (int): 最大生成 token 数量默认为 1000
stream (bool): 是否使用流式响应默认为 False
max_retries (int): 最大重试次数默认为 3
Returns:
tuple: (result, usage)
- result (str): 非流式返回字符串(回复内容),流式返回生成器(逐块内容).
- usage (dict): token使用统计(非流式返回 Usage 对象,流式返回 None).
Raises:
ValueError: 如果 message prompt 都为空或参数无效
Exception: 如果 API 调用失败且达到最大重试次数
"""
# 初始化 OpenAI 客户端
client = OpenAI(
api_key=deepseek_api,
base_url=deepseek_url,
)
# 参数验证
if messages is None:
if not prompt:
raise ValueError("必须提供 message 或 prompt 参数")
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
elif not isinstance(messages, list) or not messages:
raise ValueError("message 参数必须是非空列表")
# 模型验证
models = ["deepseek-chat", "deepseek-reasoner"]
if model not in models:
raise ValueError(f"无效的模型名称: {model},可用模型: {models}")
# 调用 API
for attempt in range(max_retries):
try:
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
stream=stream
)
# 流式响应
if stream:
def stream_generator():
usage = None
for chunk in response:
if chunk.choices[0].delta.content is not None:
yield chunk.choices[0].delta.content
return usage
return stream_generator(), None
else:
# 非流式响应
content = response.choices[0].message.content
return content, response.usage
except Exception as e:
if hasattr(e, 'status_code') and e.status_code == 429: # 限流
logger.warning(f"请求过于频繁,正在重试... (尝试 {attempt + 1}/{max_retries})")
time.sleep(2 ** attempt) # 指数退避
else:
logger.error(f"API 调用失败:{str(e)}")
raise
raise Exception("达到最大重试次数API 调用失败")
def generate_vue_code(prompt=None):
prompt = (
"生成一个React组件代码用于端午节活动海报包含以下部分并指定排版位置"
"1. 背景图层div占据整个组件区域。"
"2. 主体图层div位于顶部1/4处居中包含标题和副标题。"
"3. 活动亮点div位于底部1/4处居中使用网格布局展示三项活动每项包含图标、标题和描述"
"4. 页脚div位于底部居中包含主办单位信息和logo图片。"
"组件尺寸为1080x1920px布局使用absolute定位仅关注排版位置不包含任何样式描述如颜色、字体、阴影、动画等"
"仅生成React代码本身不包含说明性文字、注释或Markdown格式。"
)
system_prompt = (
"你是一个擅长前端开发的AI专注于生成React.js代码。"
"生成的代码仅关注排版位置使用absolute定位不包含任何样式描述如颜色、字体、阴影、动画等"
"确保代码符合React最佳实践仅生成代码本身。"
)
result,usage = call_deepseek(prompt=prompt, system_prompt=system_prompt,temperature=0.4)
# print(result)
# print(usage)
return result
def save_code(code,file_path="../outputs/generated_code.jsx"):
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
if __name__ == "__main__":
vue_code = generate_vue_code()
save_code(vue_code)
print("React组件代码已生成并保存到outputs/generated_code.jsx")

272
scripts/generate_text.py Normal file
View File

@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
import os
from openai import OpenAI
from dotenv import load_dotenv
import json
import yaml
# 加载 .env 文件
load_dotenv()
# 从环境变量中获取 API Key
MOONSHOT_API_KEY = os.getenv("MOONSHOT_API_KEY")
# --- 可配置的常量 ---
DEFAULT_LOGO_TEXT = ""
AVAILABLE_FONTS = []
NAMING_COLORS = {}
STYLE_RULES = {}
LOGO_RULES = {}
def load_config_from_file(file_path):
"""从 YAML 文件加载配置"""
global DEFAULT_LOGO_TEXT, AVAILABLE_FONTS, NAMING_COLORS, STYLE_RULES, LOGO_RULES
try:
with open(file_path, 'r', encoding='utf-8') as file:
config_data = yaml.safe_load(file)
DEFAULT_LOGO_TEXT = config_data.get("default_logo_text", DEFAULT_LOGO_TEXT)
AVAILABLE_FONTS = config_data.get("available_fonts", [])
NAMING_COLORS = config_data.get("NAMING_COLORS", {})
STYLE_RULES = config_data.get("STYLE_RULES", {})
LOGO_RULES = config_data.get("LOGO_RULES", {})
if not AVAILABLE_FONTS:
print("警告:未能从 YAML 配置中加载可用字体列表,或列表为空。")
AVAILABLE_FONTS = [{"name": "SimHei", "displayName": "黑体 (简体)", "tags": ["通用"], "roles": ["title", "subtitle", "content"]}]
print("YAML 配置文件加载成功。")
print(f"默认 Logo 文字: {DEFAULT_LOGO_TEXT if DEFAULT_LOGO_TEXT else '未设置'}")
except yaml.YAMLError as e:
print(f"解析 YAML 文件时发生错误: {e}")
AVAILABLE_FONTS = [{"name": "SimHei", "displayName": "黑体 (简体)", "tags": ["通用"], "roles": ["title", "subtitle", "content"]}]
print("已使用内部备用字体列表。")
except FileNotFoundError:
print(f"错误:未找到文件 {file_path}")
AVAILABLE_FONTS = [{"name": "SimHei", "displayName": "黑体 (简体)", "tags": ["通用"], "roles": ["title", "subtitle", "content"]}]
print("已使用内部备用字体列表。")
if not MOONSHOT_API_KEY:
print("错误:未能从环境变量中获取 MOONSHOT_API_KEY请检查 .env 文件。")
exit()
# 初始化 OpenAI 客户端
try:
client = OpenAI(api_key=MOONSHOT_API_KEY, base_url="https://api.moonshot.cn/v1")
print("Kimi客户端初始化成功。")
except Exception as e:
print(f"初始化OpenAI客户端失败: {e}")
exit()
def extract_parameters_from_input(user_input):
"""
使用模型从用户输入中提取主题风格元素背景颜色和自定义Logo文字等信息
"""
extraction_prompt = f"""
你是一个专业的自然语言理解助手请从以下用户输入中提取海报设计的关键信息并严格按照JSON格式返回
用户输入{user_input}
输出格式
{{
"poster_theme": "这里是海报的主题,例如:世界读书日、南开大学校庆",
"style_desc": "这里是海报的风格描述,例如:现代、简约、有文化气息",
"elements_to_include": ["这里是需要包含的元素,例如:南开大学教学楼", "书本", "阅读的人"],
"background_is_light": true,
"custom_logo_text": "这里是用户在输入中明确指定的、用作Logo的文字内容。例如如果用户说“Logo文字用技术创新研讨会则提取“技术创新研讨会”。如果用户没有明确指定Logo文字则返回null或空字符串。"
}}
注意对于 "background_is_light"如果用户输入明确提及背景色深浅则据此判断若未提及或无法判断默认为true (浅色)
对于 "custom_logo_text"仅当用户明确指定logo文字时才提取否则应为null或空字符串
"""
try:
completion = client.chat.completions.create(
model="moonshot-v1-8k",
messages=[
{"role": "system", "content": "你是一个专业的自然语言理解助手专门负责从用户输入中提取海报设计的关键参数并严格以JSON格式输出。"},
{"role": "user", "content": extraction_prompt}
],
temperature=0.2
)
response_content = completion.choices[0].message.content
print(f"--- 模型返回的参数提取结果 ---\n{response_content}\n--------------------------\n")
json_str = response_content.strip()
if "```json" in json_str:
json_str = json_str.split("```json")[1].split("```")[0].strip()
elif json_str.startswith("```") and json_str.endswith("```"):
json_str = json_str[3:-3].strip()
extracted_params = json.loads(json_str)
# 提供默认值
if "background_is_light" not in extracted_params:
extracted_params["background_is_light"] = True
if "custom_logo_text" not in extracted_params:
extracted_params["custom_logo_text"] = None
return extracted_params
except Exception as e:
print(f"调用模型提取参数或解析JSON时发生错误: {e}")
return {
"poster_theme": "默认主题",
"style_desc": "通用风格",
"elements_to_include": [],
"background_is_light": True,
"custom_logo_text": None,
"error": f"参数提取失败: {e}"
}
def generate_text_content_for_layers(poster_theme, style_desc, background_is_light, logo_text_to_display, elements_to_include=None):
"""
调用 Kimi API 生成文案内容和设计建议具体字体名称颜色包括指定的Logo文字
AI 将从 AVAILABLE_FONTS (来自YAML) 中选择字体
"""
global AVAILABLE_FONTS # 确保能访问到从YAML加载的字体列表
if elements_to_include is None: elements_to_include = []
if not client: return {"error": "Kimi客户端未初始化."}
background_color_desc = '浅色' if background_is_light else '深色'
# 构建字体列表供AI参考
font_list_for_prompt = "当前可用的字体列表如下 (请从中选择 'name' 作为 font_name 的值):\n"
if AVAILABLE_FONTS:
for font in AVAILABLE_FONTS:
font_list_for_prompt += (
f"- 名称(name): '{font['name']}' (显示名: {font.get('displayName', font['name'])}) - "
f"风格标签: [{', '.join(font.get('tags', []))}] - "
f"建议角色: [{', '.join(font.get('roles', []))}]\n"
)
else:
font_list_for_prompt += "- 名称(name): 'SimHei' (显示名: 黑体) - 风格标签: [通用] - 建议角色: [标题, 内容]\n"
font_list_for_prompt += "注意可用字体列表似乎未正确加载请基于通用字体如SimHei给出建议。\n"
# 提取背景颜色描述到变量中
background_color_desc = '浅色' if background_is_light else '深色'
prompt_parts = [
f"你是一个专业的海报文案及设计元素建议助手,请为关于“{poster_theme}”的主题海报创作宣传文案和设计建议。",
f"海报整体风格要求:{style_desc}",
f"设计背景是{background_color_desc}",
f"Logo文字已确定为{logo_text_to_display}”。",
"--------------------------------------------------",
"可用字体参考(请为下面的 'font_name' 字段从以下列表的 '名称(name)' 中选择一个最合适的):",
font_list_for_prompt, # 注入格式化后的字体列表
"--------------------------------------------------",
"请严格按照以下JSON格式返回结果仅包含针对指定图层的文本内容、从上述列表中选择的具体字体名称和颜色。所有建议需与海报主题、风格及背景色协调",
"{",
f' "layer5_logo_content": {{ "text": "{logo_text_to_display}", "color": "这里是为Logo文字“{logo_text_to_display}”在背景色 ({background_color_desc}) 上推荐的颜色。例如:浅色背景可使用主题色如南开紫(#7E0C6E或黑色#000000深色背景可使用白色#FFFFFF。请提供符合要求的具体十六进制颜色值。" }},',
f' "layer6_title_content": {{ "content": "这里是为“{poster_theme}”生成的主标题。如果用户输入中明确指定了主题例如世界读书日、端午、秋分则标题必须直接使用该主题且长度严格控制在2到8个汉字之间。", "font_name": "从上面提供的可用字体列表中为标题选择的具体字体\'名称(name)\' (例如: SimHei, FZLanTingHei-ExtraBold-GB)。", "color": "这里是建议的标题文字颜色,需确保在背景上高对比度且与整体风格和谐,可以根据背景选择白色(#FFFFFF、米色#f4efd9、黑色#000000等。请提供具体十六进制颜色值。" }},',
f''' "layer7_subtitle_content": {{ "content": "这里是为“{poster_theme}”生成的副标题或说明文案。请根据主题选择以下一种形式并创作,确保内容富有文化内涵或情感共鸣,避免空洞口号:\n 1. 点题短语或对偶句总计10-25字\n 2. 精美描述性或介绍性文字一段话20-35字\n 3. 若主题适合诗歌可为原创短诗例如绝句的前两句或类似形式总计10-20字", "font_name": "从上面提供的可用字体列表中为副标题选择的具体字体\'名称(name)\',应与主标题协调。", "color": "这里是建议的副标题文字颜色,确保清晰可读,并与整体色调和谐,避免过于鲜艳的颜色。请提供具体十六进制颜色值。" }}''',
"}",
"重要提示:",
"1. 为 'content' 字段生成的文本必须是直接的文字内容,不应包含诸如“这里是...”之类的描述性前缀。",
"2. 'font_name' 字段的值必须是从上面提供的可用字体列表中的一个确切的 '名称(name)'",
"3. 'color' 字段应提供明确的十六进制颜色代码(例如“#FFFFFF”",
"4. 确保最终输出是完整且严格符合上述格式的JSON对象不要在JSON内容之外添加任何解释、注释、Markdown标记如 ```json ```或其他任何非JSON字符。"
]
user_prompt = "\n".join(prompt_parts)
system_prompt_content = "你是一个专业的海报文案及设计元素建议助手。请严格按照用户要求的JSON格式输出确保内容符合设计原则、主题文化内涵并直接输出JSON内容不要包含任何修饰性或解释性的文字例如 '好的这是您要求的JSON''```json' 等标记。"
try:
completion = client.chat.completions.create(
model="moonshot-v1-8k",
messages=[
{"role": "system", "content": system_prompt_content},
{"role": "user", "content": user_prompt}
],
temperature=0.4
)
response_content = completion.choices[0].message.content
json_str = response_content.strip()
if "```json" in json_str:
json_str = json_str.split("```json")[1].split("```")[0].strip()
elif json_str.startswith("```") and json_str.endswith("```"):
json_str = json_str[3:-3].strip()
parsed_json = json.loads(json_str)
return parsed_json
except Exception as e:
print(f"调用Kimi或解析JSON时发生错误: {e}\n原始回复内容: {response_content if 'response_content' in locals() else 'N/A'}")
return {"error": f"LLM调用或解析失败: {e}"}
def get_poster_content_suggestions(user_input_string: str):
"""
主接口函数接收用户输入提取参数确定Logo文字并生成海报图层内容建议
返回包含建议内容的字典或包含错误信息的字典
"""
print(f"--- 开始处理用户输入: {user_input_string} ---")
# 1. 提取参数
extracted_params = extract_parameters_from_input(user_input_string)
if "error" in extracted_params:
print(f"参数提取过程中发生错误: {extracted_params['error']}")
return extracted_params # 返回包含错误信息的字典
poster_theme = extracted_params.get("poster_theme", "未知主题")
style_desc = extracted_params.get("style_desc", "默认风格")
elements_to_include = extracted_params.get("elements_to_include", [])
background_is_light = extracted_params.get("background_is_light", True)
custom_logo_text = extracted_params.get("custom_logo_text")
print(f"\n--- 提取到的参数用于生成文案 ---")
print(f"主题: {poster_theme}")
print(f"风格: {style_desc}")
print(f"背景是否浅色: {background_is_light}")
print(f"用户指定Logo文字: {custom_logo_text if custom_logo_text else '未指定'}")
# 2. Logo文字选择逻辑
final_logo_text = DEFAULT_LOGO_TEXT # 默认为 ""
if custom_logo_text and custom_logo_text.strip(): # 如果用户指定了且不为空
final_logo_text = custom_logo_text.strip()
print(f"--- 使用用户自定义Logo文字: “{final_logo_text}” ---")
else:
print(f"--- 使用默认Logo文字: “{final_logo_text}” ---")
print("--------------------------------\n")
# 3. 调用生成函数
generated_content = generate_text_content_for_layers(
poster_theme,
style_desc,
background_is_light,
final_logo_text, # 传递最终确定的Logo文字
elements_to_include
)
if "error" in generated_content:
print(f"内容生成过程中发生错误: {generated_content['error']}")
else:
print("\n--- 成功生成的文案及设计建议 ---")
# print(json.dumps(generated_content, indent=2, ensure_ascii=False))
return generated_content
if __name__ == "__main__":
# 用户自由输入示例
# user_input_example1 = "帮我设计一个关于'南开百年校庆'的海报风格要喜庆、现代Logo文字用'南开大学百年华诞',背景用浅红色。"
# user_input_example2 = "我要一个端午节的海报,中国风,深色背景。"
# user_input_example3 = "世界读书日海报,简约风格,元素包含打开的书和思考的人。"
load_config_from_file("./configs/font.yaml")
user_input = input("请输入您的海报需求:")
# 调用主接口函数
suggestions = get_poster_content_suggestions(user_input)
# 打印结果
if suggestions:
# print("\n--- 最终输出给调用者的内容 ---")
# print(json.dumps(suggestions, indent=2, ensure_ascii=False))
output_folder = "outputs"
output_file = os.path.join(output_folder, "poster_content.json")
try:
with open(output_file, "w", encoding="utf-8") as file:
json.dump(suggestions, file, indent=2, ensure_ascii=False)
print(f"\n--- 内容已成功保存到文件: {output_file} ---")
except Exception as e:
print(f"\n--- 保存到文件时发生错误: {e} ---")
else:
print("未生成任何内容,无法保存到文件。")

View File

@ -1,73 +0,0 @@
# server.py
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests # 用于发起HTTP请求(比如调用外部接口)
import json
app = Flask(__name__)
CORS(app) # 如果前后端分离需要跨域启用CORS
@app.route('/generate', methods=['POST'])
def generate():
try:
# 1. 从请求体中获取参数
data = request.json
prompt = data.get('prompt', '')
width = data.get('width', 512)
height = data.get('height', 512)
print(f'收到参数: prompt={prompt}, width={width}, height={height}')
# 2. 调用大模型API或其他逻辑生成图片
images = generate_images_coze(prompt, width, height)
# 返回 JSON 格式数据给前端
return jsonify({'images': images})
except Exception as e:
print('图片生成出错:', e)
return jsonify({'error': '图片生成出错'}), 500
def generate_images_coze(prompt, width, height):
print(f'正在根据 "{prompt}" 生成内容 (宽{width},高{height})...')
url = 'https://api.coze.cn/v3/chat'
headers = {
'Authorization': 'Bearer pat_7G46ritHNFFvDnbeIIzXWaoPabf8ocbHsGfnAl20te1dsdHX2aTiRKR0XTLbauHW',
'Content-Type': 'application/json'
}
data = {
"bot_id": "7486021323127980071",
"user_id": "123",
"stream": True,
"auto_save_history": True,
"additional_messages": [
{
"role": "user",
"content": prompt,
"content_type": "text"
}
]
}
try:
resp = requests.post(url, headers=headers, data=json.dumps(data))
resp.raise_for_status() # 如果响应非200会抛异常
result_text = resp.text
print('Coze API 返回结果:', result_text)
# 假设并没有真正返回图片链接,就先返回一个测试
return [f"大模型返回的内容:{result_text}"]
except Exception as e:
print('调用 Coze API 失败:', e)
# 出错的话就返回空列表
return []
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000, debug=False)