初步实现 LLM调用生成排版 react 代码

1. 创建scripts,outputs文件夹,生成 layout 代码位于 scripts 下的generate_layout.py当中,生成结果保存在 outputs 下的 generated_code.jsx 当中
    2. 实现了根据 Prompt 生成指定的 react 代码
    3. 需要根据前端传递给后端的相关prompt来完善功能,故暂时未暴露给后端接口
This commit is contained in:
Wang Xiuqiang 2025-05-19 23:28:21 +08:00
parent 574650f3f0
commit a8a2c0c48d
4 changed files with 210 additions and 0 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
venv/
.idea/

View File

@ -1 +1,23 @@
# ai_service
## LLM 调用部分
### 功能组件
使用 DeepSeek API 提供的 LLM大型语言模型功能用于生成 React 组件代码,主要服务于端午节活动海报的排版设计。当前实现的功能包括:
- **代码生成**根据提示prompt生成 React 组件代码,专注于排版位置(不包含样式描述)。
- **分层排版**:生成包含背景图层、主体图层、活动亮点和页脚的 React 组件
- **文件保存**:将生成的 React 代码保存到指定路径。
### 如何调用
1. **环境准备**
- 确保已安装 Python 环境和必要依赖(`openai`、`python-dotenv`等)。
- 在项目根目录的 `.env` 文件中配置 `DEEPSEEK_API_KEY`
2. **运行脚本**
- 直接运行 `generate_layout.py` 脚本:
```bash
python generate_layout.py
```
- 该脚本会自动调用 DeepSeek API 生成 React 组件代码,并将其保存到指定路径。
默认为``output/generated_code.jsx``,可以根据需要修改。

View File

@ -0,0 +1,47 @@
```jsx
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%)', textAlign: 'center' }}>
<h1></h1>
<h2></h2>
</div>
{/* 活动亮点 */}
<div style={{ position: 'absolute', bottom: '25%', left: '50%', transform: 'translateX(-50%)', width: '80%' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
<div style={{ textAlign: 'center' }}>
<div></div>
<h3></h3>
<p></p>
</div>
<div style={{ textAlign: 'center' }}>
<div></div>
<h3></h3>
<p></p>
</div>
<div style={{ textAlign: 'center' }}>
<div></div>
<h3></h3>
<p></p>
</div>
</div>
</div>
{/* 页脚 */}
<div style={{ position: 'absolute', bottom: '5%', left: '50%', transform: 'translateX(-50%)', textAlign: 'center' }}>
<p></p>
<div></div>
</div>
</div>
);
};
export default DragonBoatFestivalPoster;
```

140
scripts/generate_layout.py Normal file
View File

@ -0,0 +1,140 @@
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")