diff --git a/README.md b/README.md
index aeeec4c..2b2a346 100644
--- a/README.md
+++ b/README.md
@@ -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``,可以根据需要修改。
\ No newline at end of file
diff --git a/outputs/generated_code.jsx b/outputs/generated_code.jsx
new file mode 100644
index 0000000..5970be7
--- /dev/null
+++ b/outputs/generated_code.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+
+const DragonBoatFestivalPoster = () => {
+ return (
+
+ {/* 背景图层 */}
+
+
+ {/* 主体图层 */}
+
+
+ {/* 活动亮点 */}
+
+
+ {/* 页脚 */}
+
+
+ );
+};
+
+export default DragonBoatFestivalPoster;
\ No newline at end of file
diff --git a/scripts/generate_layout.py b/scripts/generate_layout.py
new file mode 100644
index 0000000..56906c4
--- /dev/null
+++ b/scripts/generate_layout.py
@@ -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")
\ No newline at end of file