ai_service/scripts/generate_layout.py
Wang Xiuqiang a8a2c0c48d 初步实现 LLM调用生成排版 react 代码
1. 创建scripts,outputs文件夹,生成 layout 代码位于 scripts 下的generate_layout.py当中,生成结果保存在 outputs 下的 generated_code.jsx 当中
    2. 实现了根据 Prompt 生成指定的 react 代码
    3. 需要根据前端传递给后端的相关prompt来完善功能,故暂时未暴露给后端接口
2025-05-19 23:28:21 +08:00

140 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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")