├── local_img_storage_en.json ├── local_img_storage_zh.json ├── demo ├── demo.mp4 ├── demo.webm ├── index.png ├── panel.png ├── projects.png ├── skills.png ├── publications.png ├── skills.html ├── pages.json ├── index.html ├── publications.html └── projects.html ├── requirements.txt ├── prompts ├── __init__.py ├── process_img_prompts.py ├── write_original_website_prompts.py ├── write_original_website_prompts_zh.py ├── refine_prompts.py ├── refine_prompts_zh.py ├── write_original_website_prompts_en.py ├── plan_prompts_zh.py ├── refine_prompts_en.py ├── plan_prompts_en.py └── plan_prompts.py ├── config.yaml ├── logs └── token.json ├── LICENSE ├── base_agent.py ├── README.md ├── llm.py ├── utils.py ├── .gitignore ├── webserver.py ├── llm_api.py └── HomepageDesign.py /local_img_storage_en.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /local_img_storage_zh.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /demo/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/demo.mp4 -------------------------------------------------------------------------------- /demo/demo.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/demo.webm -------------------------------------------------------------------------------- /demo/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/index.png -------------------------------------------------------------------------------- /demo/panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/panel.png -------------------------------------------------------------------------------- /demo/projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/projects.png -------------------------------------------------------------------------------- /demo/skills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/skills.png -------------------------------------------------------------------------------- /demo/publications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bj-Liu/resumeagent/HEAD/demo/publications.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | openai 2 | pillow 3 | pyyaml 4 | Requests 5 | selenium 6 | tenacity 7 | webdriver_manager 8 | yaspin 9 | bs4 10 | anthropic 11 | zhipuai -------------------------------------------------------------------------------- /prompts/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .process_img_prompts import * 3 | from .plan_prompts import * 4 | from .refine_prompts import * 5 | from .write_original_website_prompts import * 6 | 7 | 8 | ''' 9 | 从当前包(或模块所在的目录)中的process_img_prompts模块或包中导入 10 | 所有可用的公开(非以下划线_开头的)名称(比如函数、类、变量等)。 11 | 这里的.表示当前目录,即这行代码所在的Python包或模块所在的目录。 12 | ''' -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | web_type : "firefox" # "chrome" or "firefox" or "edge" 2 | LLM_TYPE : 'qwen' #openai , "qwen" 3 | 4 | # LLM settings 5 | # openai settings 6 | is_azure : True 7 | AZURE_OPENAI_ENDPOINT : 8 | AZURE_OPENAI_KEY : 9 | AZURE_OPENAI_API_VERSION : 10 | 11 | OPENAI_API_KEY : 12 | OPENAI_PROXY_URL : 13 | OPENAI_BASE_URL : 14 | 15 | # qwen settings 16 | DASHSCOPE_API_KEY : 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /logs/token.json: -------------------------------------------------------------------------------- 1 | {"gpt-35-turbo-16k": [0, 0], "gpt4o-0513": [2260206, 1299497], "dalle3": 877, "gpt-4o": [14, 56], "gpt-4o-2024-05-13": [13, 413], "glm-4": [56904, 48878], "glm-4v": [19427, 3631], "glm-4-0520": [143036, 119176], "qwen-max-longcontext": [186314, 115802], "cogview-3": 16, "qwen2-1.5b-instruct": [17100, 41630], "qwen2-7b-instruct": [15535, 12766], "wanx": 4, "qwen2-72b-instruct": [776318, 451950], "qwen-vl-max": [15123, 2544], "qwen1.5-110b-chat": [113942, 33039], "qwen-max-0428": [97244, 50092], "qwen-max": [23943, 10716], "qwen-vl-max-0809": [0, 0], "qwen2.5-72b-instruct": [23031, 8165], "qwen3-max": [77155, 83764]} -------------------------------------------------------------------------------- /prompts/process_img_prompts.py: -------------------------------------------------------------------------------- 1 | process_img_en = """ 2 | Your task is to give a brief description of the above picture. Please keep it as short as possible and do not output other content, please output both Chinese and English descriptions, the output format is as follows: 3 | {Your Chinese description of the picture} 4 | {Your English description of the picture} 5 | """ 6 | 7 | process_img_zh = """ 8 | 您的任务是对上图进行简要描述。请尽量简短,不要输出其他内容,请输出中文跟英文两种描述,输出格式如下: 9 | {你对图片的中文描述} 10 | {你对图片的英文描述} 11 | """ 12 | 13 | def get_process_img_prompt(language="en"): 14 | if language == "en": 15 | return process_img_en 16 | else: 17 | return process_img_zh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 BJ - L 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /base_agent.py: -------------------------------------------------------------------------------- 1 | 2 | from LLM import get_llm 3 | 4 | Openai_Models = ["gpt-35-turbo-16k","gpt-4-turbo-2024-04-09"] 5 | Qwen_Models = ["qwen3-max","qwen2-72b-instruct"] 6 | 7 | class BaseAgent: 8 | def __init__(self,model = "qwen3-max") -> None: 9 | self.model = model 10 | self.llm = get_llm() 11 | 12 | def act(self, instruction): 13 | raise NotImplementedError 14 | 15 | def get_LLM_response(self, messages, **kwargs): 16 | if kwargs.get("model") is None: 17 | kwargs["model"] = self.model 18 | if hasattr(self.llm, "response"): 19 | return self.llm.response(messages, **kwargs) 20 | raise RuntimeError("LLM does not support synchronous response()") 21 | 22 | async def get_LLM_response_async(self, messages, **kwargs): 23 | if kwargs.get("model") is None: 24 | kwargs["model"] = self.model 25 | if hasattr(self.llm, "response_async"): 26 | return await self.llm.response_async(messages, **kwargs) 27 | if hasattr(self.llm, "response"): 28 | return self.llm.response(messages, **kwargs) 29 | raise RuntimeError("LLM does not support response_async() or response()") 30 | 31 | def get_answer(self, messages, **kwargs): 32 | return self.get_LLM_response(messages, **kwargs) 33 | 34 | async def get_answer_async(self, messages, **kwargs): 35 | return await self.get_LLM_response_async(messages, **kwargs) 36 | 37 | 38 | -------------------------------------------------------------------------------- /prompts/write_original_website_prompts.py: -------------------------------------------------------------------------------- 1 | def get_write_original_website_prompt(basic_information = None, academic_achievements = None, experience=None, professional_skills=None,img = None,page_info = None,css_frame = None,feedback = "",language = "en"): 2 | if language == "en": 3 | from .write_original_website_prompts_en import text_img_task,text_task,img_task,write_original_prompt,Tailwind_role,Boostrap_role,Materialize_role,Bulma_role,original_role,Tailwind_output_format,Boostrap_output_format,Materialize_output_format,Bulma_output_format,original_output_format 4 | feedback = f"The user's requirements is as follows(very important, please pay attention to it!):{feedback}" if feedback else "" 5 | 6 | elif language == "zh": 7 | from .write_original_website_prompts_zh import text_img_task,text_task,img_task,write_original_prompt,Tailwind_role,Boostrap_role,Materialize_role,Bulma_role,original_role,Tailwind_output_format,Boostrap_output_format,Materialize_output_format,Bulma_output_format,original_output_format 8 | feedback = f"用户的需求如下(非常重要,请注意!):{feedback}" if feedback else "" 9 | 10 | 11 | if css_frame == "Tailwind": 12 | role = Tailwind_role 13 | output_format = Tailwind_output_format.format(feedback=feedback) 14 | elif css_frame == "Boostrap": 15 | role = Boostrap_role 16 | output_format = Boostrap_output_format.format(feedback=feedback) 17 | elif css_frame == "Materialize": 18 | role = Materialize_role 19 | output_format = Materialize_output_format.format(feedback=feedback) 20 | elif css_frame == "Bulma": 21 | role = Bulma_role 22 | output_format = Bulma_output_format.format(feedback=feedback) 23 | else: 24 | role = original_role 25 | output_format = original_output_format.format(feedback=feedback) 26 | if img and (basic_information or academic_achievements or experience or professional_skills): 27 | task = text_img_task.format(page_info=page_info) 28 | prompt = write_original_prompt.format(role=role,task=task,page_info=page_info,output_format=output_format) 29 | elif img: 30 | task = img_task.format(page_info =page_info) 31 | prompt = write_original_prompt.format(role=role,task=task,output_format=output_format) 32 | else: 33 | task = text_task.format(page_info =page_info) 34 | prompt = write_original_prompt.format(role=role,task=task,output_format=output_format) 35 | return prompt 36 | 37 | ''' 38 | 这个函数主要用于指导如何根据提供的文本、图片、页面信息、CSS框架以及用户反馈来编写个人主页代码 39 | 40 | 参数说明: 41 | basic_information: 可选,提供网站的基本信息内容,包括教育背景、个人简介、成就、现任职务和背景介绍。 42 | academic_achievements: 可选,提供学术成果的文本内容,如发表论文,专利等。 43 | experience: 可选,提供工作和项目经历的文本内容,如工作成果、项目描述等。 44 | professional_skills: 可选,提供专业技能的文本内容,如技术栈、语言能力、相关证书等。 45 | img:可选,提供用于网站的图片内容。 46 | page_info:可选,提供关于页面的额外信息。 47 | css_frame:可选,指定使用的CSS框架,如Tailwind、Bootstrap等。 48 | feedback:可选,提供用户反馈或额外要求。 49 | language:指定使用哪种语言的提示,默认为英文("en")。 50 | 51 | 返回值: 52 | 函数返回一个字符串,该字符串包含了根据输入参数生成的用于创建原创网站的详细指令或提示。 53 | ''' -------------------------------------------------------------------------------- /prompts/write_original_website_prompts_zh.py: -------------------------------------------------------------------------------- 1 | original_role = "您是一位专家级的Html开发者,擅长使用HTML、CSS和JavaScript编写网页。(无需输出js文件,将js文件嵌入到html文件中)" 2 | 3 | Tailwind_role = "您是一位专家级的Tailwind开发者,擅长使用Tailwind CSS框架编写网页。" 4 | 5 | Boostrap_role = "您是一位专家级的Bootstrap开发者,擅长使用Bootstrap CSS框架编写网页。" 6 | 7 | Materialize_role = "您是一位专家级的Materialize开发者,擅长使用Materialize CSS框架编写网页。" 8 | 9 | Bulma_role = "您是一位专家级的Bulma开发者,擅长使用Bulma CSS框架编写网页。" 10 | 11 | img_task = """ 12 | 上图是我们提供给您的网页的截图。 13 | 页面信息如下:{page_info}(button和link的跳转页面的文件名为其链接地址); 14 | 您的任务是根据截图和页面关系构建一个单页面应用。 15 | - 确保您创建的页面与截图完全相同。 16 | - 注意页面的布局,相关文本,按钮,链接等要与截图一致。 17 | - 注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。完全匹配颜色和大小要与截图一致。 18 | - 使用截图中的确切文本。 19 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 20 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 21 | - 避免使用图像作为背景。例如: background: url('https://placehold.co/1600x900')。 你可以使用渐变的颜色作为背景或者本地图片作为背景。 22 | - 您必须确保您生成的页面与我们提供的页面完全一致(布局、格式、文本、内容)! 23 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 24 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 25 | """ 26 | 27 | text_img_task = """ 28 | 上图是我们提供给您的参考网页的截图。 29 | 页面信息如下:{page_info}(button和link的跳转页面的文件名为其链接地址); 30 | 您的任务是根据参考网页的结构和布局以及提供的页面信息构建一个新的网页。 31 | - 请根据已知信息进行修改,避免虚构内容! 32 | - 不要匹配参考网页上的文本! 33 | - 不需要与参考网页一致,只需学习其优点。 34 | - 请注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。 35 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 36 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 37 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 38 | - 尽量使页面看起来丰富,注意协调,个人主页应设计简洁。 39 | - 避免使用图像作为背景。例如: background: url('https://placehold.co/1600x900')。 你可以使用渐变的颜色作为背景或者本地图片作为背景。 40 | - 鼓励您使用更多颜色、更多按钮、更精致的布局,并尝试添加更多特效,例如波浪效果、渐变效果、滚动效果等。 41 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 42 | """ 43 | 44 | text_task = """ 45 | 页面信息如下:{page_info}(button和link的跳转页面的文件名为其链接地址); 46 | 您的任务是根据页面信息构建一个单页面应用。 47 | - 请注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。 48 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 49 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 50 | - 尽量使页面看起来丰富,注意协调,个人主页应设计简洁。 51 | - 鼓励您使用更多颜色、更多按钮、更精致的布局,并尝试添加更多特效,例如波浪效果、渐变效果、滚动效果等。 52 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 53 | - 避免使用图像作为背景。例如: background: url('https://placehold.co/1600x900')。 你可以使用渐变的颜色作为背景或者本地图片作为背景。 54 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 55 | """ 56 | 57 | original_output_format = """ 58 | {feedback} 59 | 60 | 请输出html(包含js代码),css代码。 61 | """ 62 | 63 | Tailwind_output_format = """ 64 | {feedback} 65 | 66 | 现在输出带有Tailwind CSS框架的HTML代码。(务必添加: 到你的html文件中。并且网站内容需要是中文) 67 | """ 68 | 69 | Boostrap_output_format = """ 70 | {feedback} 71 | 72 | 现在输出带有Bootstrap CSS框架的HTML代码。(务必添加: 到你的html文件中。并且网站内容需要是中文) 73 | """ 74 | 75 | Materialize_output_format = """ 76 | {feedback} 77 | 78 | 现在输出带有Materialize CSS框架的HTML代码。(务必添加: 到你的html文件中。并且网站内容需要是中文) 79 | """ 80 | 81 | Bulma_output_format = """ 82 | {feedback} 83 | 84 | 现在输出带有Bulma CSS框架的HTML代码。(务必添加: 到你的html文件中。并且网站内容需要是中文) 85 | """ 86 | 87 | 88 | 89 | write_original_prompt = """ 90 | {role} 91 | {task} 92 | {output_format} 93 | """ 94 | 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ResumeAgent 2 | 3 | > 🤖 An autonomous agent for generating elegant personal resume websites using large language models (LLMs). 4 | 5 | --- 6 | 7 | ## 🌟 Overview 8 | 9 | **ResumeAgent** is a lightweight and extendable framework, focusing on generating **personal homepage / resume websites** through LLM-driven planning and web code generation. 10 | ResumeAgent focuses on simplicity and usability — after running the setup, anyone can easily build a personalized website via a simple GUI panel, without requiring design or coding experience. 11 | It supports both text-based descriptions and optional image templates for layout inspiration. 12 | 13 | ### 🎥 Demo Video 14 | 15 | You can view a demo video here: 16 | 17 |

18 | 21 |

22 | 23 | --- 24 | 25 | ## 🔑 Key Features 26 | 27 | 🧩 Resume Website Generation – Automatically design personal websites (About, Projects, Publications, Contact, etc.) based on your CV or written description. 28 | 29 | ✨ Simple GUI Workflow – Once set up, anyone can build their own website with just a few clicks in the GUI panel. 30 | 31 | 🖼️ Optional Image Templates – Load local images or layout templates to guide visual structure. 32 | 33 | 🛠️ Editable & Extendable – You can manually refine generated HTML/CSS or let the agent iteratively improve the results. 34 | 35 | 🔗 Multi-Page Output – Automatically generate connected pages with consistent layout and internal links. 36 | 37 | 🤖 Qwen & OpenAI Compatible – Supports both Alibaba DashScope and OpenAI APIs. 38 | 39 | --- 40 | 41 | ## 🛠️ Requirements and Installation 42 | 43 | ```bash 44 | git clone https://github.com/bj-Liu/resumeagent.git 45 | cd resumeagent 46 | pip install -r requirements.txt 47 | ``` 48 | 49 | ## ⚙️ Configuration Example (config.yaml) 50 | 51 | ```bash 52 | # Browser type for visualization (choose one) 53 | web_type: "firefox" # Options: "chrome", "firefox", "edge" 54 | 55 | # LLM backend selection 56 | LLM_TYPE: "qwen" # Options: "openai", "qwen" 57 | 58 | # --- OpenAI / Azure configuration --- 59 | is_azure: false # Set true if using Azure OpenAI API 60 | 61 | # Azure settings (only needed if is_azure: true) 62 | AZURE_OPENAI_ENDPOINT: "" 63 | AZURE_OPENAI_KEY: "" 64 | AZURE_OPENAI_API_VERSION: "" 65 | 66 | # OpenAI settings (for direct OpenAI API usage) 67 | OPENAI_API_KEY: "" 68 | OPENAI_PROXY_URL: "" 69 | OPENAI_BASE_URL: "" 70 | 71 | # --- Qwen configuration (for DashScope) --- 72 | DASHSCOPE_API_KEY: "" 73 | ``` 74 | 75 | ## 🚀 Quick Start 76 | 77 | GUI Mode 78 | 79 | python gui.py 80 | Then you will enter the GUI as follows: 81 | ![Homepage Screenshot](demo/panel.png) 82 | 83 | Choose Web Design Mode 84 | 85 | Select Model (e.g. qwen3-7b, gpt-4o, etc.) 86 | Set your language and CSS framework 87 | Fill in your personal profile, e.g. "Zhang San, M.S. in Computer Science, Tsinghua University. Research focus: Multimodal LLMs, Embodied AI, Explainable Vision. Academic email: zhangsan@tsinghua.edu.cn" 88 | Click Plan → Auto Generate 89 | 90 | Generated HTML files and screenshots will be saved in your configured save_file path. 91 | 92 | 93 | ## 🙏 Acknowledgements 94 | 95 | This project is inspired by [WebDesignAgent](https://github.com/DAMO-NLP-SG/WebDesignAgent), 96 | ResumeAgent reuses and modifies certain components under the terms of that license. 97 | 98 | © 2025 bj-Liu 99 | Licensed under the MIT License. -------------------------------------------------------------------------------- /prompts/refine_prompts.py: -------------------------------------------------------------------------------- 1 | # ["Tailwind","Boostrap","Materialize","Bulma",None] 2 | def get_refine_prompt(basic_information = None, academic_achievements = None, experience=None, professional_skills=None,img = None,html_code = None,css_code = None,page_info = None,feedback = "",css_frame = None,language = "en",local_img_storage = [],vision = True): 3 | if language == "en": 4 | from .refine_prompts_en import refine_Tailwind_output_format,refine_Boostrap_output_format,refine_Materialize_output_format,refine_Bulma_output_format,refine_original_output_format,refine_feedback_task,refine_img_text_task,refine_img_task,refine_text_task,refine_prompt,Tailwind_role,Boostrap_role,Materialize_role,Bulma_role,original_role 5 | feedback = f"The user's feedback is as follows(very important, please pay attention to it!):{feedback}" if feedback else "" 6 | local_img_storage = f"The img you maybe use to design the page(It is not required to use it, you can choose it according to the situation, please pay close attention to the size of the picture):{local_img_storage}" if local_img_storage else "" 7 | pre_task = "The image above is a screenshot of the website you have already designed." 8 | 9 | elif language == "zh": 10 | from .refine_prompts_zh import refine_Tailwind_output_format,refine_Boostrap_output_format,refine_Materialize_output_format,refine_Bulma_output_format,refine_original_output_format,refine_feedback_task,refine_img_text_task,refine_img_task,refine_text_task,refine_prompt,Tailwind_role,Boostrap_role,Materialize_role,Bulma_role,original_role 11 | feedback = f"用户的反馈如下(非常重要,请注意!):{feedback}" if feedback else "" 12 | local_img_storage = f"您可能在你的网页中可能可以用到的图片(并非要求一定使用,你看情况使用挑选,请严格注意图片的大小尺寸):{local_img_storage}" if local_img_storage else "" 13 | pre_task = "上面的图片是您已经设计的网站的截图。" 14 | 15 | if css_frame == "Tailwind": 16 | role = Tailwind_role 17 | output_format = refine_Tailwind_output_format.format(html_code = html_code,feedback = feedback) 18 | elif css_frame == "Boostrap": 19 | role = Boostrap_role 20 | output_format = refine_Boostrap_output_format.format(html_code = html_code,feedback = feedback) 21 | elif css_frame == "Materialize": 22 | role = Materialize_role 23 | output_format = refine_Materialize_output_format.format(html_code = html_code,feedback = feedback) 24 | elif css_frame == "Bulma": 25 | role = Bulma_role 26 | output_format = refine_Bulma_output_format.format(html_code = html_code,feedback = feedback) 27 | else: 28 | role = original_role 29 | output_format = refine_original_output_format.format(html_code = html_code,css_code=css_code,feedback = feedback) 30 | if feedback: 31 | task = refine_feedback_task.format(page_info = page_info,local_img_storage = local_img_storage) 32 | task = pre_task + task if vision else task 33 | elif img and (basic_information or academic_achievements or experience or professional_skills): 34 | task = refine_img_text_task.format(page_info = page_info,local_img_storage = local_img_storage) 35 | elif img: 36 | task = refine_img_task.format(page_info = page_info,local_img_storage = local_img_storage) 37 | else: 38 | task = refine_text_task.format(page_info = page_info,local_img_storage = local_img_storage) 39 | task = pre_task + task if vision else task 40 | 41 | prompt = refine_prompt.format(role = role,task = task,output_format = output_format) 42 | return prompt 43 | 44 | #根据输入的CSS框架、反馈、图片和文本等信息,动态地构建出一个包含任务、角色和输出格式的prompt字符串,用于指导或自动化网页设计或修改的流程。 -------------------------------------------------------------------------------- /llm.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("..") 3 | import openai 4 | import retry 5 | import requests 6 | from vl_prompt.p_manager import extract_integer_answer, extract_scores, extract_objects, \ 7 | get_frontier_prompt, get_candidate_prompt, get_grouping_prompt, get_discover_prompt, get_decisionTiming_prompt, extract_interval_steps 8 | 9 | from openai import AzureOpenAI 10 | 11 | import os 12 | 13 | 14 | class LLM(): 15 | def __init__(self, goal_name, prompt_type): 16 | self.goal_name = goal_name 17 | self.prompt_type = prompt_type 18 | self.history = [] 19 | self.max_tokens = 2000 20 | self.headers = { 21 | "Content-Type": "application/json", 22 | "api-key": GPT4V_KEY, 23 | } 24 | 25 | 26 | def inference_once(self, system_prompt, message): 27 | if message: 28 | msg = { 29 | "role": "user", 30 | "content": message 31 | } 32 | self.history = system_prompt + [msg] 33 | try: 34 | payload = {'messages': self.history} 35 | response = requests.post(GPT4V_ENDPOINT, headers=self.headers, json=payload) 36 | response.raise_for_status() 37 | reply = response.json()["choices"][0]["message"]["content"] 38 | except Exception as e: 39 | print(f"=====> llm inference error: {e}") 40 | 41 | return reply 42 | 43 | 44 | def discover_objects(self, img, objects): 45 | payload = get_discover_prompt(img, objects) 46 | response = requests.post(GPT4V_ENDPOINT, headers=self.headers, json=payload) 47 | reply = response.json()["choices"][0]["message"]["content"] 48 | c = extract_objects(reply) 49 | return c 50 | 51 | def choose_decision_step(self, img): 52 | payload = get_decisionTiming_prompt(img) 53 | response = requests.post(GPT4V_ENDPOINT, headers=self.headers, json=payload) 54 | reply = response.json()["choices"][0]["message"]["content"] 55 | c = extract_interval_steps(reply) 56 | return c 57 | 58 | def inference_accumulate(self, message): 59 | if message: 60 | self.history.append( 61 | {"role": "user", "content": message}, 62 | ) 63 | try: 64 | payload = {'messages': self.history} 65 | response = requests.post(GPT4V_ENDPOINT, headers=self.headers, json=payload) 66 | response.raise_for_status() 67 | reply = response.json()["choices"][0]["message"]["content"] 68 | except Exception as e: 69 | print(f"=====> llm inference error: {e}") 70 | 71 | self.history.append({"role": "assistant", "content": reply}) 72 | return reply 73 | 74 | 75 | def choose_frontier(self, message): 76 | system_prompt = get_frontier_prompt(self.prompt_type) 77 | reply = self.inference_once(system_prompt, message) 78 | if self.prompt_type == "deterministic": 79 | answer = extract_integer_answer(reply) 80 | elif self.prompt_type == "scoring": 81 | answer, scores = extract_scores(reply) 82 | 83 | return answer, reply 84 | 85 | def imagine_candidate(self, instr): 86 | system_prompt = get_candidate_prompt(candidate_type="open") 87 | reply = self.inference_once(system_prompt, instr) 88 | c = extract_objects(reply) 89 | return c 90 | 91 | def group_candidate(self, clist, nlist): 92 | system_prompt = get_grouping_prompt() 93 | message = f"Current object list: {clist}\n\nNew object list: {nlist}" 94 | reply = self.inference_once(system_prompt, message) 95 | c = extract_objects(reply) 96 | new_c = [] 97 | for ob in c: 98 | if ("room" in ob) or ("wall" in ob) or ("floor" in ob) or ("ceiling" in ob): 99 | continue 100 | new_c.append(ob) 101 | return new_c -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import re 3 | import os 4 | import httpx 5 | from yaspin import yaspin 6 | 7 | 8 | def cal_cost(prompt_tokens,completion_tokens,model = "gpt-4"): 9 | if "gpt-4o" in model: 10 | return prompt_tokens/1000000 * 5 + completion_tokens/1000000 * 15 11 | elif "Sonnet" in model: 12 | return prompt_tokens/1000000 * 3 + completion_tokens/1000000 * 15 13 | elif model == "qwen3-max": 14 | return prompt_tokens / 1000 * 0.0012 + completion_tokens / 1000 * 0.006 15 | else: 16 | return prompt_tokens/1000 *0.005 + completion_tokens/1000 *0.015 17 | 18 | def write_file(file_path, content): 19 | if content: 20 | with open(file_path, "w",encoding='utf-8') as f: 21 | f.write(content) 22 | 23 | def get_all_files_in_dir(directory): 24 | file_list = [] 25 | for root, dirs, files in os.walk(directory): 26 | for file in files: 27 | file_list.append(os.path.join(root, file)) 28 | return file_list 29 | 30 | def get_content_between_a_b(start_tag, end_tag, text): 31 | extracted_text = "" 32 | start_index = text.find(start_tag) 33 | while start_index != -1: 34 | end_index = text.find(end_tag, start_index + len(start_tag)) 35 | if end_index != -1: 36 | extracted_text += text[start_index + len(start_tag) : end_index] + " " 37 | start_index = text.find(start_tag, end_index + len(end_tag)) 38 | else: 39 | break 40 | 41 | return extracted_text.strip() 42 | 43 | 44 | def extract(text, type): 45 | target_str = get_content_between_a_b(f"<{type}>", f"", text) 46 | return target_str 47 | 48 | 49 | def encode_image(image_path): 50 | with open(image_path, "rb") as image_file: 51 | return base64.b64encode(image_file.read()).decode('utf-8') 52 | 53 | def get_openai_url(img_pth): 54 | end = img_pth.split(".")[-1] 55 | if end == "jpg": 56 | end = "jpeg" 57 | base64_image = encode_image(img_pth) 58 | return f"data:image/{end};base64,{base64_image}" 59 | 60 | 61 | def wrap_func(func,**args): 62 | text = "🤖💭 " + args.get("wrap_text","Thinking now...") 63 | if "wrap_text" in args: 64 | del args["wrap_text"] 65 | with yaspin(text = text, color="cyan") as spinner: 66 | result = func(**args) 67 | spinner.ok("✔") 68 | return result 69 | 70 | 71 | def extract_dimensions(url): 72 | 73 | matches = re.findall(r"(\d+)x(\d+)", url) 74 | 75 | if matches: 76 | width, height = matches[0] 77 | width = int(width) 78 | height = int(height) 79 | return (width, height) 80 | else: 81 | return (100, 100) 82 | 83 | 84 | def extract_img_from_html(html_code): 85 | pattern = r']+)>' 86 | img_tags = re.findall(pattern, html_code) 87 | result = [] 88 | for tag in img_tags: 89 | img_info = {"original": tag} 90 | src_match = re.search(r'src="([^"]*)"', tag) 91 | if src_match: 92 | img_info["src"] = src_match.group(1) 93 | alt_match = re.search(r'alt="([^"]*)"', tag) 94 | if alt_match: 95 | img_info["alt"] = alt_match.group(1) 96 | 97 | class_match = re.search(r'class="([^"]*)"', tag) 98 | if class_match: 99 | img_info["class"] = class_match.group(1) 100 | if "src" in img_info and "alt" in img_info: 101 | result.append(img_info) 102 | return result 103 | 104 | def create_file(path): 105 | if not os.path.exists(path): 106 | directory = os.path.dirname(os.path.abspath(path)) 107 | if not os.path.exists(directory): 108 | os.makedirs(directory) 109 | with open(path, 'w') as f: 110 | f.write("{}") 111 | 112 | 113 | def modify_input_dict(s): 114 | s = s.replace("\'","\"") 115 | s = s.replace("False","false") 116 | s = s.replace("True","true") 117 | s = s.replace("None","null") 118 | s = s.replace("\'","\"") 119 | s = s.replace("\"s","\'s") 120 | s = s.replace(" \'"," \"") 121 | return s 122 | 123 | def get_html_css_from_response(response): 124 | html = get_content_between_a_b("```html", "```", response) if "```html" in response else None 125 | css = get_content_between_a_b("```css", "```", response) if "```css" in response else None 126 | return html,css 127 | 128 | 129 | async def fetch_image(url): 130 | async with httpx.AsyncClient() as client: 131 | response = await client.get(url) 132 | response.raise_for_status() 133 | return response.content 134 | 135 | if __name__ == "__main__": 136 | img_url = get_openai_url("/home/evelyn/resumeagent/ex.png") 137 | print(img_url) -------------------------------------------------------------------------------- /prompts/refine_prompts_zh.py: -------------------------------------------------------------------------------- 1 | original_role = "您是一位专家级的Html开发者,擅长使用HTML、CSS、JavaScript编写网页。(无需输出js文件,将js文件嵌入到html文件中即可)" 2 | 3 | Tailwind_role = "您是一位专家级的Tailwind开发者,擅长使用Tailwind CSS框架编写网页。" 4 | 5 | Boostrap_role = "您是一位专家级的Bootstrap开发者,擅长使用Bootstrap CSS框架编写网页。" 6 | 7 | Materialize_role = "您是一位专家级的Materialize开发者,擅长使用Materialize CSS框架编写网页。" 8 | 9 | Bulma_role = "您是一位专家级的Bulma开发者,擅长使用Bulma CSS框架编写网页。" 10 | 11 | refine_img_task = """ 12 | 第一张图片是我们的目标网页的截图,第二张图片是您已经构建的网页截图。 13 | {local_img_storage} 14 | 页面信息如下: 15 | {page_info}(button和link的跳转页面的文件名为其链接地址) 16 | 17 | 您的目标是修改第二个网页的代码,使其更新为更像目标页面(第一张图片)的样子。 18 | 19 | - 确保您创建的页面与截图完全相同。 20 | - 注意页面的布局,相关文本,按钮,链接等要与截图完全一致。 21 | - 注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。完全匹配颜色和大小要与截图完全一致。 22 | - 使用截图中的确切文本,不要更改文本 23 | - 避免将placehold用作背景。例如:background: url('https://placehold.co/1600x900'),你可以使用渐变的颜色作为背景或者本地图片作为背景。 24 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 25 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 26 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 27 | """ 28 | 29 | 30 | refine_img_text_task = """ 31 | 第一张图片是参考网页的截图,第二张图片是您已经构建的网页截图。 32 | {local_img_storage} 33 | 页面信息如下: 34 | {page_info}(button和link的跳转页面的文件名为其链接地址) 35 | 36 | 您的目标是修改第二个图片的网页代码,模仿第一个图片的布局结构,并满足页面信息要求。 37 | - 请根据已知信息进行修改,避免虚构内容 38 | - 不要匹配参考网页上的文本!根据自己的需要以及用户提供的信息添加文本! 39 | - 不需要与参考网页一致,只需从其优点中学习。 40 | - 您可以添加js或修改css布局,或添加元素以满足用户反馈。 41 | - 注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。 42 | - 尽量使页面看起来丰富,注意协调,个人主页应设计简洁。 43 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 44 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 45 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 46 | - 鼓励您使用更多的颜色、更多的按钮、更精致的布局,并尝试添加更多的特效,例如波浪效果、渐变效果、滚动效果等。 47 | - 避免将placehold用作背景。例如:background: url('https://placehold.co/1600x900'),你可以使用渐变的颜色作为背景或者本地图片作为背景。 48 | - 增强功能性和实用性:思考如何通过修改代码来提高页面的功能性和实用性。 49 | - 请分析当前页面的页眉、导航、内容排列、侧边栏、页脚、视觉元素、布局、行动号召、响应性和其他功能,并分析如何优化这些功能,使布局更美观和和谐。 50 | - 请思考如何修改代码以使页面满足我们的需求(例如添加按钮或添加一些文本内容、详细内容等)。 51 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 52 | """ 53 | 54 | refine_text_task = """ 55 | {local_img_storage} 56 | 页面信息如下:{page_info}(button和link的跳转页面的文件名为其链接地址); 57 | 58 | 您的任务是根据用户反馈修改代码,以满足用户需求。 59 | - 您可以添加js或修改css布局,或添加元素以满足用户反馈。 60 | - 注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。 61 | - 尽量使页面看起来丰富,注意协调,个人主页应设计简洁。 62 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 63 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 64 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 65 | - 鼓励您使用更多的颜色、更多的按钮、更精致的布局,并尝试添加更多的特效,例如波浪效果、渐变效果、滚动效果等。 66 | - 避免将placehold用作背景。例如:background: url('https://placehold.co/1600x900'),你可以使用渐变的颜色作为背景或者本地图片作为背景。 67 | - 增强功能性和实用性:思考如何通过修改代码来提高页面的功能性和实用性。 68 | - 请分析当前页面的页眉、导航、内容排列、侧边栏、页脚、视觉元素、布局、行动号召、响应性和其他功能,并分析如何优化这些功能,使布局更美观和和谐。 69 | - 请思考如何修改代码以使页面满足我们的需求(例如添加按钮或添加一些文本内容、详细内容等)。 70 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 71 | """ 72 | 73 | refine_feedback_task = """ 74 | {local_img_storage} 75 | 页面信息如下:{page_info}(button和link的跳转页面的文件名为其链接地址); 76 | 77 | 您的任务是根据用户反馈修改代码,以满足用户需求。 78 | - 请根据已知信息进行修改,避免虚构内容 79 | - 您必须密切关注用户反馈,并尝试根据用户反馈修改您的代码,以便最终效果符合用户需求。 80 | - 您可以添加js或修改css布局,或添加元素以满足用户反馈。 81 | - 注意背景颜色、文字颜色、字体大小、字体系列、内边距、外边距、边框等。 82 | - 尽量使页面看起来丰富,注意协调,个人主页应设计简洁。 83 | - 请你严格关注图片的尺寸问题,确保最后页面的呈现效果美观。 84 | - 对于图像,尽量使用页面信息里的本地图片而且不要修改它的文件路径。 85 | - 注意,不要让图片遮挡住文字,文字的图层应该是最顶层的。 86 | - 鼓励您使用更多的颜色、更多的按钮、更精致的布局,并尝试添加更多的特效,例如波浪效果、渐变效果、滚动效果等。 87 | - 避免将placehold用作背景。例如:background: url('https://placehold.co/1600x900'),你可以使用渐变的颜色作为背景或者本地图片作为背景。 88 | - 增强功能性和实用性:思考如何通过修改代码来提高页面的功能性和实用性。 89 | - 请分析当前页面的页眉、导航、内容排列、侧边栏、页脚、视觉元素、布局、行动号召、响应性和其他功能,并分析如何优化这些功能,使布局更美观和和谐。 90 | - 请思考如何修改代码以使页面满足我们的需求(例如添加按钮或添加一些文本内容、详细内容等)。 91 | - 鼓励您通过实现额外的JavaScript操作功能来增强网页的互动性和功能性(例如:滚动、点击、悬停,颜色变化,点击特效,页面切换等)。保证最终页面美观、信息完整、可用性良好。 92 | """ 93 | 94 | refine_original_output_format = """ 95 | 目前的页面的代码如下: 96 | HTML代码是: 97 | {html_code} 98 | CSS代码是: 99 | {css_code} 100 | 101 | {feedback} 102 | 103 | 请提供一个修改计划,然后输出第二个页面的修改后的html(包含js代码),css代码。 104 | 105 | 输出格式如下: 106 | 修改计划: 107 | 1. 108 | 2. 109 | ... 110 | 111 | 修改后的html: 112 | 113 | 修改后的css: 114 | 115 | """ 116 | 117 | refine_Tailwind_output_format = """ 118 | 目前的页面的代码如下: 119 | 使用Tailwind CSS框架的HTML代码是: 120 | {html_code} 121 | 122 | {feedback} 123 | 124 | 请提供一个修改计划,然后输出第二个页面的修改后的html代码。 125 | 126 | 输出格式如下: 127 | 修改计划: 128 | 1. 129 | 2. 130 | ... 131 | 132 | 修改后的html: 133 | """ 134 | 135 | refine_Boostrap_output_format = """ 136 | 目前的页面的代码如下: 137 | 使用Bootstrap CSS框架的HTML代码是: 138 | {html_code} 139 | 140 | {feedback} 141 | 142 | 请提供一个修改计划,然后输出第二个页面的修改后的html代码。 143 | 144 | 输出格式如下: 145 | 修改计划: 146 | 1. 147 | 2. 148 | ... 149 | 150 | 修改后的html: 151 | """ 152 | 153 | refine_Materialize_output_format = """ 154 | 目前的页面的代码如下: 155 | 使用Materialize CSS框架的HTML代码是: 156 | {html_code} 157 | 158 | {feedback} 159 | 160 | 请提供一个修改计划,然后输出第二个页面的修改后的html代码。 161 | 162 | 输出格式如下: 163 | 修改计划: 164 | 1. 165 | 2. 166 | ... 167 | 168 | 修改后的html: 169 | """ 170 | 171 | refine_Bulma_output_format = """ 172 | 目前的页面的代码如下: 173 | 使用Bulma CSS框架的HTML代码是: 174 | {html_code} 175 | 176 | {feedback} 177 | 178 | 请提供一个修改计划,然后输出第二个页面的修改后的html代码。 179 | 180 | 输出格式如下: 181 | 修改计划: 182 | 1. 183 | 2. 184 | ... 185 | 186 | 修改后的html: 187 | 188 | """ 189 | 190 | 191 | 192 | refine_prompt = """ 193 | {role} 194 | {task} 195 | {output_format} 196 | """ 197 | 198 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[codz] 4 | *$py.class 5 | 6 | # C extensions 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | share/python-wheels/ 20 | *.egg-info/ 21 | .installed.cfg 22 | *.egg 23 | MANIFEST 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .nox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *.cover 45 | *.py.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | cover/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | db.sqlite3-journal 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | .pybuilder/ 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | # For a library or package, you might want to ignore these files since the code is 83 | # intended to run in multiple environments; otherwise, check them in: 84 | # .python-version 85 | 86 | # pipenv 87 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 88 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 89 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 90 | # install all needed dependencies. 91 | #Pipfile.lock 92 | 93 | # UV 94 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 95 | # This is especially recommended for binary packages to ensure reproducibility, and is more 96 | # commonly ignored for libraries. 97 | #uv.lock 98 | 99 | # poetry 100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 101 | # This is especially recommended for binary packages to ensure reproducibility, and is more 102 | # commonly ignored for libraries. 103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 104 | #poetry.lock 105 | #poetry.toml 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. 110 | # https://pdm-project.org/en/latest/usage/project/#working-with-version-control 111 | #pdm.lock 112 | #pdm.toml 113 | .pdm-python 114 | .pdm-build/ 115 | 116 | # pixi 117 | # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. 118 | #pixi.lock 119 | # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one 120 | # in the .venv directory. It is recommended not to include this directory in version control. 121 | .pixi 122 | 123 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 124 | __pypackages__/ 125 | 126 | # Celery stuff 127 | celerybeat-schedule 128 | celerybeat.pid 129 | 130 | # SageMath parsed files 131 | *.sage.py 132 | 133 | # Environments 134 | .env 135 | .envrc 136 | .venv 137 | env/ 138 | venv/ 139 | ENV/ 140 | env.bak/ 141 | venv.bak/ 142 | 143 | # Spyder project settings 144 | .spyderproject 145 | .spyproject 146 | 147 | # Rope project settings 148 | .ropeproject 149 | 150 | # mkdocs documentation 151 | /site 152 | 153 | # mypy 154 | .mypy_cache/ 155 | .dmypy.json 156 | dmypy.json 157 | 158 | # Pyre type checker 159 | .pyre/ 160 | 161 | # pytype static type analyzer 162 | .pytype/ 163 | 164 | # Cython debug symbols 165 | cython_debug/ 166 | 167 | # PyCharm 168 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 169 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 170 | # and can be added to the global gitignore or merged into this file. For a more nuclear 171 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 172 | #.idea/ 173 | 174 | # Abstra 175 | # Abstra is an AI-powered process automation framework. 176 | # Ignore directories containing user credentials, local state, and settings. 177 | # Learn more at https://abstra.io/docs 178 | .abstra/ 179 | 180 | # Visual Studio Code 181 | # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore 182 | # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore 183 | # and can be added to the global gitignore or merged into this file. However, if you prefer, 184 | # you could uncomment the following to ignore the entire vscode folder 185 | # .vscode/ 186 | 187 | # Ruff stuff: 188 | .ruff_cache/ 189 | 190 | # PyPI configuration file 191 | .pypirc 192 | 193 | # Cursor 194 | # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to 195 | # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data 196 | # refer to https://docs.cursor.com/context/ignore-files 197 | .cursorignore 198 | .cursorindexingignore 199 | 200 | # Marimo 201 | marimo/_static/ 202 | marimo/_lsp/ 203 | __marimo__/ 204 | ======= 205 | __pycache__/ 206 | *.pyc 207 | >>>>>>> eaaa969 (Initial commit: upload resumeagent folder) 208 | -------------------------------------------------------------------------------- /prompts/write_original_website_prompts_en.py: -------------------------------------------------------------------------------- 1 | original_role = "You are an expert-level HTML developer, proficient in creating web pages using HTML, CSS, and JavaScript. (No need to output a separate JS file; embed JS directly in the HTML file.)" 2 | 3 | Tailwind_role = "You are an expert-level Tailwind developer, proficient in creating web pages using the Tailwind CSS framework." 4 | 5 | Boostrap_role = "You are an expert-level Bootstrap developer, proficient in creating web pages using the Bootstrap CSS framework." 6 | 7 | Materialize_role = "You are an expert-level Materialize developer, proficient in creating web pages using the Materialize CSS framework." 8 | 9 | Bulma_role = "You are an expert-level Bulma developer, proficient in creating web pages using the Bulma CSS framework." 10 | 11 | img_task = """ 12 | The above image is a screenshot of the webpage we provide. 13 | Page information is as follows: {page_info} (the filename of the button or link is its target page); 14 | Your task is to build a single-page application based on the screenshot and page relationships. 15 | - Ensure that the page you create matches the screenshot exactly. 16 | - Pay attention to layout, image placement, image size, relevant text, buttons, links, etc., making them consistent with the screenshot. 17 | - Match background color, text color, font size, font family, padding, margin, border, etc., exactly as in the screenshot. 18 | - Use the exact text from the screenshot. 19 | - Make sure that images do not cover text; text layers should always be on top. 20 | - For images, try to use local images from the page information and do not change their file paths. 21 | - Avoid using images as background (e.g., background: url('https://placehold.co/1600x900')). You can use gradient colors or local images as background instead. 22 | - Ensure that the generated page matches our provided page perfectly (layout, format, text, content)! 23 | - Pay strict attention to image dimensions to ensure a visually appealing final rendering. 24 | - You are encouraged to enhance interactivity and functionality using JavaScript (e.g., scrolling, clicking, hovering, color changes, click effects, page transitions, etc.) to make the page more practical and attractive to users. 25 | """ 26 | 27 | text_img_task = """ 28 | The above image is a screenshot of the reference webpage we provide. 29 | Page information is as follows: {page_info} (the filename of the button or link is its target page); 30 | Your task is to build a new webpage based on the structure and layout of the reference webpage and the provided page information. 31 | - Modify based on the known information; avoid inventing content! 32 | - Do not copy text from the reference webpage! 33 | - The page does not need to be identical to the reference page; just learn from its strengths. 34 | - Pay attention to background color, text color, font size, font family, padding, margin, border, etc. 35 | - Ensure that images do not cover text; text layers should always be on top. 36 | - For images, try to use local images from the page information and do not change their file paths. 37 | - Strictly pay attention to image dimensions to ensure a visually appealing final rendering. 38 | - Make the page visually rich while maintaining coordination; for example, personal homepages should be simple, while technical sites should feel technical. 39 | - Avoid using images as background (e.g., background: url('https://placehold.co/1600x900')). You can use gradient colors or local images as background instead. 40 | - You are encouraged to use more colors, more buttons, more refined layout, and try effects like wave, gradient, scrolling, etc. 41 | - Enhance interactivity and functionality using JavaScript (e.g., scrolling, clicking, hovering, color changes, click effects, page transitions, etc.) to make the page more practical and attractive to users. 42 | """ 43 | 44 | text_task = """ 45 | Page information is as follows: {page_info} (the filename of the button or link is its target page); 46 | Your task is to build a single-page application based on the page information. 47 | - Pay attention to background color, text color, font size, font family, padding, margin, border, etc. 48 | - Ensure that images do not cover text; text layers should always be on top. 49 | - For images, try to use local images from the page information and do not change their file paths. 50 | - Make the page visually rich while maintaining coordination; personal homepages should be simple. 51 | - You are encouraged to use more colors, more buttons, more refined layout, and try effects like wave, gradient, scrolling, etc. 52 | - Pay strict attention to image dimensions to ensure a visually appealing final rendering. 53 | - Avoid using images as background (e.g., background: url('https://placehold.co/1600x900')). You can use gradient colors or local images as background instead. 54 | - Enhance interactivity and functionality using JavaScript (e.g., scrolling, clicking, hovering, color changes, click effects, page transitions, etc.) to make the page more practical and attractive to users. 55 | """ 56 | 57 | original_output_format = """ 58 | {feedback} 59 | 60 | Please output HTML (including JS code) and CSS code. 61 | """ 62 | 63 | Tailwind_output_format = """ 64 | {feedback} 65 | 66 | Now output HTML code using the Tailwind CSS framework. (Be sure to include: in your HTML file. The website content should be in Chinese.) 67 | """ 68 | 69 | Boostrap_output_format = """ 70 | {feedback} 71 | 72 | Now output HTML code using the Bootstrap CSS framework. (Be sure to include: in your HTML file. The website content should be in Chinese.) 73 | """ 74 | 75 | Materialize_output_format = """ 76 | {feedback} 77 | 78 | Now output HTML code using the Materialize CSS framework. (Be sure to include: in your HTML file. The website content should be in Chinese.) 79 | """ 80 | 81 | Bulma_output_format = """ 82 | {feedback} 83 | 84 | Now output HTML code using the Bulma CSS framework. (Be sure to include: in your HTML file. The website content should be in Chinese.) 85 | """ 86 | 87 | write_original_prompt = """ 88 | {role} 89 | {task} 90 | {output_format} 91 | """ 92 | -------------------------------------------------------------------------------- /webserver.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | from selenium import webdriver 3 | from selenium.webdriver.common.by import By 4 | from selenium.webdriver.support import expected_conditions as EC 5 | from selenium.webdriver.common.action_chains import ActionChains 6 | from selenium.webdriver.support.ui import WebDriverWait 7 | 8 | from webdriver_manager.chrome import ChromeDriverManager 9 | from selenium.webdriver.chrome.options import Options as ChromeOptions 10 | from selenium.webdriver.chrome.service import Service as ChromeService 11 | 12 | from webdriver_manager.firefox import GeckoDriverManager 13 | from selenium.webdriver.firefox.options import Options as FirefoxOptions 14 | from selenium.webdriver.firefox.service import Service as FirefoxService 15 | 16 | from selenium.webdriver.edge.service import Service as EdgeService 17 | from selenium.webdriver.edge.options import Options as EdgeOptions 18 | from webdriver_manager.microsoft import EdgeChromiumDriverManager 19 | from PIL import Image 20 | import time 21 | import os 22 | import io 23 | import logging 24 | 25 | 26 | with open("config.yaml", "r") as ymlfile: 27 | cfg = yaml.safe_load(ymlfile) 28 | 29 | class Webserver: 30 | def __init__(self, save_file="saves/", preview_mode=False): 31 | if os.path.isabs(save_file): 32 | download_dir = save_file 33 | else: 34 | download_dir = os.path.join(os.getcwd(), save_file) 35 | 36 | web_type = cfg.get("web_type", "chrome") 37 | self.web_type = web_type 38 | if web_type == "chrome": 39 | options = ChromeOptions() 40 | options.add_argument("start-maximized") 41 | options.add_argument("--headless") 42 | options.add_experimental_option("prefs", { 43 | "download.default_directory": download_dir, 44 | "directory_upgrade": True, 45 | "safebrowsing.enabled": True, 46 | }) 47 | print("begin install the chrome manager") 48 | driver_path = ChromeDriverManager().install() 49 | print("successfully install the chrome manager") 50 | service = ChromeService(driver_path) 51 | self.driver = webdriver.Chrome(service=service, options=options) 52 | 53 | preview_options = ChromeOptions() 54 | preview_options.add_argument("start-maximized") 55 | self.preview_driver = webdriver.Chrome(service=service, options=preview_options) 56 | 57 | elif web_type == "firefox": 58 | logging.basicConfig(level=logging.DEBUG) 59 | options = FirefoxOptions() 60 | options.add_argument("headless") 61 | 62 | options.set_preference("browser.download.folderList", 2) 63 | 64 | options.set_preference("browser.download.dir", download_dir) 65 | 66 | print("begin install the firefox manager") 67 | 68 | driver_path = "/home/xxx/.wdm/drivers/geckodriver/linux64/v0.35.0/geckodriver" 69 | print("successfully install the firefox manager") 70 | service = FirefoxService(driver_path) 71 | self.driver = webdriver.Firefox(service=service, options=options) 72 | 73 | if preview_mode: 74 | preview_options = FirefoxOptions() 75 | preview_options.add_argument("start-maximized") 76 | 77 | self.preview_driver = webdriver.Firefox(service=service, options=preview_options) 78 | else: 79 | self.preview_driver = None 80 | 81 | elif web_type == "edge": 82 | options = EdgeOptions() 83 | options.add_argument("headless") 84 | options.add_argument("start-maximized") 85 | options.add_experimental_option("prefs", { 86 | "download.default_directory": download_dir, 87 | "directory_upgrade": True, 88 | "safebrowsing.enabled": True, 89 | }) 90 | print("begin install the edge manager") 91 | driver_path = EdgeChromiumDriverManager().install() 92 | print("successfully install the edge manager") 93 | service = EdgeService(driver_path) 94 | self.driver = webdriver.Edge(service=service, options=options) 95 | 96 | preview_options = EdgeOptions() 97 | preview_options.add_argument("start-maximized") 98 | self.preview_driver = webdriver.Edge(service=service, options=preview_options) 99 | else: 100 | raise ValueError(f"Unsupported web browser type: {web_type}") 101 | 102 | self.save_file = save_file 103 | print("Successfully initialized WebDriver") 104 | 105 | def get_screenshot(self, local_html_path , save_path = None , is_local = True): 106 | if save_path is None: 107 | save_path = os.path.join(self.save_file, "screenshot.png") 108 | try: 109 | if is_local: 110 | if os.path.isabs(local_html_path): 111 | html_path = local_html_path 112 | else: 113 | html_path = os.path.join(os.getcwd() ,local_html_path) 114 | 115 | print(f"Opening local HTML file {html_path}") 116 | self.driver.get("file:///" + html_path.replace("\\", "/")) 117 | time.sleep(5) 118 | else: 119 | print(f"Opening website {local_html_path}") 120 | self.driver.get(local_html_path) 121 | time.sleep(15) 122 | 123 | if self.web_type in ["chrome","firefox","edge"]: 124 | try: 125 | alert = self.driver.switch_to.alert 126 | alert.accept() 127 | except Exception as e: 128 | pass 129 | screen_width = self.driver.execute_script("return window.screen.width") 130 | scroll_height = self.driver.execute_script("return document.documentElement.scrollHeight") 131 | 132 | self.driver.set_window_size(screen_width, scroll_height) 133 | self.driver.save_screenshot(save_path) 134 | else: 135 | self.driver.fullscreen_window() 136 | window_height = self.driver.get_window_size()["height"] 137 | window_width = self.driver.get_window_size()["width"] 138 | scroll_height = self.driver.execute_script("return document.body.scrollHeight") 139 | stitched_image = Image.new('RGB', (window_width, scroll_height)) 140 | for y in range(0, scroll_height, window_height): 141 | self.driver.execute_script(f"window.scrollTo(0, {y});") 142 | time.sleep(0.5) 143 | img = Image.open(io.BytesIO(self.driver.get_screenshot_as_png())).resize((window_width, window_height)) 144 | if y + window_height > scroll_height: 145 | img = img.crop((0, window_height - scroll_height + y, window_width, scroll_height)) 146 | stitched_image.paste(img, (0, y)) 147 | break 148 | stitched_image.paste(img, (0, y)) 149 | stitched_image.save(save_path) 150 | img = Image.open(save_path) 151 | if img.size[1] > 4096: 152 | img = img.resize((img.size[0] // 2, img.size[1] // 2)) 153 | img.save(save_path) 154 | print(f"Screenshot of local HTML saved as {save_path}") 155 | finally: 156 | pass 157 | 158 | def open_website(self, local_html_path): 159 | if os.path.isabs(local_html_path): 160 | html_path = local_html_path 161 | else: 162 | html_path = os.path.join(os.getcwd(), local_html_path) 163 | self.preview_driver.get("file:///" + html_path.replace("\\", "/")) 164 | 165 | 166 | def stop(self): 167 | self.driver.quit() 168 | print("WebDriver stopped") 169 | 170 | if __name__ == "__main__": 171 | webserver = Webserver() 172 | webserver.get_screenshot("https://xxx.github.io/","example1.png",is_local = False) 173 | webserver.stop() -------------------------------------------------------------------------------- /prompts/plan_prompts_zh.py: -------------------------------------------------------------------------------- 1 | original_page_template = """ 2 | [ 3 | { 4 | "html_name": {xxx.html}, 5 | "css_name": {css文件名}, 6 | "js_description": {为该页面设计的JavaScript功能}, 7 | "description": {页面描述}, 8 | "relationship": { 9 | "buttons":[ 10 | { 11 | "button_name" : {button_name}, 12 | "jump_page" : {target_page} 13 | }, 14 | ... 15 | ], 16 | "links":[ 17 | { 18 | "link_name" : {link_name}, 19 | "jump_page" : {target_page} 20 | }, 21 | ... 22 | ] 23 | }, 24 | "practice_features": {页面需要具备的实用功能}, 25 | "additional_features": {您想要添加到页面的其他功能}, 26 | "page_style": {page_style}, 27 | "is_main_page" : {true或false,如果页面是我们提供的图片请设置为true} 28 | } 29 | ... 30 | ] 31 | """ 32 | 33 | 34 | original_page_example = """ 35 | { 36 | "html_name": "personal_homepage.html", 37 | "css_name": "personal_homepage.css", 38 | "js_description": "动态展示用户信息、项目、技能等,支持用户与不同内容模块的交互,包括个人信息的切换展示、项目详情展开、联系方式的点击复制等功能。", 39 | "description": "此页面展示用户的个人信息,包括头像、个人简介、项目经历、技能展示及联系方式。设计注重简洁现代,易于浏览和交互。", 40 | "relationship": { 41 | "buttons": [ 42 | {"button_name": "查看项目", "action": "showProjects()"}, 43 | {"button_name": "展开技能", "action": "expandSkills()"}, 44 | {"button_name": "查看联系方式", "action": "showContactInfo()"}, 45 | {"button_name": "返回顶部", "action": "scrollToTop()"}, 46 | {"button_name": "切换暗色模式", "action": "toggleDarkMode()"}, 47 | {"button_name": "返回", "jump_page": "previous_page.html"}, 48 | {"button_name": "下一页", "jump_page": "next_page.html"} 49 | ], 50 | "links": [ 51 | {"link_name": "GitHub", "jump_page": "https://github.com/username"}, 52 | {"link_name": "LinkedIn", "jump_page": "https://linkedin.com/in/username"}, 53 | {"link_name": "主页", "jump_page": "index.html"} 54 | ] 55 | }, 56 | "practice_features": "个人信息展示,项目详情页,技能可视化展示,动态联系方式显示,暗色模式切换。", 57 | "additional_features": "支持点击复制功能用于联系方式,响应式布局适应不同设备尺寸,社交媒体图标与个人链接集成。", 58 | "page_style": "简洁现代的设计,使用渐变背景色和圆角元素,注重可读性和个性化元素。项目展示模块使用卡片式布局,支持动态展开和收起。", 59 | "is_main_page": true 60 | } 61 | } 62 | """ 63 | 64 | local_img_storage_page_template = """ 65 | [ 66 | { 67 | "html_name": {xxx.html}, 68 | "css_name": {css文件名}, 69 | "js_description": {为该页面设计的JavaScript功能}, 70 | "description": {页面描述}, 71 | "relationship": { 72 | "buttons":[ 73 | { 74 | "button_name" : {button_name}, 75 | "jump_page" : {target_page} 76 | }, 77 | ... 78 | ], 79 | "links":[ 80 | { 81 | "link_name" : {link_name}, 82 | "jump_page" : {target_page} 83 | }, 84 | ... 85 | ], 86 | "imgs": [ 87 | { 88 | "src": {图片路径,必须是本地图片}, 89 | "alt": {图片描述}, 90 | "description": {图片在该页面中的作用} 91 | }, 92 | ... 93 | ], 94 | }, 95 | "practice_features": {页面需要具备的实用功能}, 96 | "additional_features": {您想要添加到页面的其他功能}, 97 | "page_style": {page_style}, 98 | "is_main_page" : {true或false,如果页面是我们提供的图片请设置为true} 99 | } 100 | ... 101 | ] 102 | """ 103 | 104 | local_img_storage_page_example = """ 105 | { 106 | "html_name": "personal_homepage.html", 107 | "css_name": "personal_homepage.css", 108 | "js_description": "动态展示个人信息、项目、技能,并支持图片展示(如头像、项目图片)和用户交互功能。", 109 | "description": "此页面展示用户的个人信息,包括头像、项目展示、技能展示和联系方式。页面设计简洁现代,注重用户体验与视觉美观。", 110 | "relationship": { 111 | "buttons": [ 112 | {"button_name": "查看项目", "action": "showProjects()"}, 113 | {"button_name": "展开技能", "action": "expandSkills()"}, 114 | {"button_name": "查看联系方式", "action": "showContactInfo()"}, 115 | {"button_name": "切换暗色模式", "action": "toggleDarkMode()"}, 116 | {"button_name": "返回顶部", "action": "scrollToTop()"}, 117 | {"button_name": "返回", "jump_page": "previous_page.html"}, 118 | {"button_name": "下一页", "jump_page": "next_page.html"} 119 | ], 120 | "links": [ 121 | {"link_name": "GitHub", "jump_page": "https://github.com/username"}, 122 | {"link_name": "LinkedIn", "jump_page": "https://linkedin.com/in/username"}, 123 | {"link_name": "主页", "jump_page": "index.html"} 124 | ] 125 | }, 126 | "imgs": [ 127 | { 128 | "src": "profile_picture.png", 129 | "alt": "用户头像", 130 | "description": "显示用户的头像,用于个人主页的顶部个人信息部分。" 131 | }, 132 | { 133 | "src": "project_1.png", 134 | "alt": "项目展示图1", 135 | "description": "展示项目1的图片,点击图片可查看项目详情。" 136 | }, 137 | { 138 | "src": "project_2.png", 139 | "alt": "项目展示图2", 140 | "description": "展示项目2的图片,点击图片可查看项目详情。" 141 | }, 142 | { 143 | "src": "background_image.jpg", 144 | "alt": "背景图片", 145 | "description": "个人主页的背景图片,提升页面的视觉美感。" 146 | } 147 | ], 148 | "practice_features": "个人信息展示,项目图片展示,动态技能展示,联系方式展示,支持图片点击查看详情。", 149 | "additional_features": "响应式设计,适应不同设备尺寸;暗色模式切换;社交媒体链接集成。", 150 | "page_style": "简洁现代的设计,使用渐变背景和图片元素,注重视觉效果和可读性。", 151 | "is_main_page": true 152 | } 153 | """ 154 | 155 | 156 | plan_output_format_prompt = """ 157 | 请确保所有页面都可以通过按钮或链接访问,并且所有页面最终都可以跳转回主页(注意,你设计的html_name必须与其他page里的jump_page对应,不能出现无法跳转的情况)。 158 | 页面样例如下: 159 | {page_template} 160 | 161 | 请严格按照以下格式输出(不允许额外信息): 162 | 163 | {{设计的页面}} 164 | 165 | """ 166 | 167 | plan_output_format_prompt_local_img = """ 168 | 以下是你可能要用到的本地图片信息(请注意,这些图片可能需要在你的设计中使用,且你需要尽量使用这些图片): 169 | {local_img_storage} 170 | 171 | 请确保所有页面都可以通过按钮或链接访问,并且所有页面最终都可以跳转回主页(注意,你设计的html_name必须与其他page里的jump_page对应,不能出现无法跳转的情况)。 172 | 页面样例如下: 173 | {page_template} 174 | 175 | 请严格按照以下格式输出(不允许额外信息): 176 | 177 | {{设计的页面}} 178 | 179 | """ 180 | 181 | 182 | refine_page_prompt = """ 183 | 你是一个个人主页设计大师,你当前的任务是准确且基于事实地修改一个个人主页的细节。 184 | {task_info} 185 | 以下页面是个人主页的页面之一。请完全根据提供的信息以及该页主题帮我丰富其细节,特别是展示个人成果、工作经历、项目经验。注意不要改变格式,不要凭空捏造! 186 | 修改后的页面应该是dict格式,不要有额外输出(不要添加新的链接到其他页面,同时不要修改任何html_name以免跳转失败)。 187 | 例如: 188 | {page_example} 189 | 您可以通过丰富描述和其他方法(例如,添加布局描述,添加网页效果,添加实用功能,添加按钮等)来丰富页面细节,确保能够充分展示个人特色和优势。 190 | 191 | 页面是: 192 | {page_info}(请注意button和link的跳转页面的文件名是它们的链接地址) 193 | 194 | {feedback} 195 | 196 | 输出格式应该如下: 197 | 198 | {{你修改后的页面}} 199 | 200 | """ 201 | 202 | 203 | refine_page_local_img_prompt = """ 204 | 你是一个个人主页设计大师,你当前的任务是准确且基于事实地修改一个个人主页的细节。 205 | {task_info} 206 | 以下页面是个人主页的页面之一。请完全根据我提供的信息帮我丰富其细节,要把我提供的信息准确完整的展示上去,特别是展示个人成果的方式、教育背景、工作经历、项目经验和专业技能,注意不要改变格式。 207 | 修改后的页面应该是dict格式,不要有额外输出(不要添加新的链接到其他页面,同时不要修改任何html_name以免跳转失败)。 208 | 例如: 209 | {page_example} 210 | 您可以通过丰富描述和其他方法(例如,添加布局描述,添加网页效果,添加实用功能,添加按钮等)来丰富页面细节。 211 | 212 | 以下是你可能要用到的本地图片信息,如果你需要添加图片,请从以下图片中选择(请根据图片的内容,大小跟描述来选择图片,并决定图片的用途): 213 | {local_img_storage} 214 | 215 | 页面是: 216 | {page_info}(请注意button和link的跳转页面的文件名是它们的链接地址,同时不要修改页面的html_name) 217 | 218 | {feedback} 219 | 220 | 输出格式应该如下: 221 | 222 | {{你修改后的页面}} 223 | 224 | """ 225 | 226 | page_complete_prompt = """ 227 | 你是一个个人主页设计大师,你的任务是准确且基于事实地完成和完善一个未完成的个人主页。 228 | 229 | 其他页面信息如下: 230 | {other_pages_info} 231 | 232 | 当前页面信息如下: 233 | {page_info}(请注意button和link的跳转页面的文件名是它们的链接地址,同时不要修改页面的html_name) 234 | 235 | {feedback}(请特别关注feedback,并满足用户的需求) 236 | 237 | 请根据其他页面的信息和当前页面的已知信息(注意页面之间的跳转关系),完成和完善此页面的信息,请特别注重展示个人信息、成果、教育背景、工作经历、项目经验和专业技能的方式,确保能够突出个人形象,并考虑哪些页面需要设计按钮以跳转到当前页面。 238 | 239 | 你的输出应该是以下格式: 240 | 思考步骤: 241 | {{你的思考步骤}} 242 | 243 | 244 | {{你完成和完善的页面,不要改变或添加或删除页面的原始键,你可以修改键的值,页面应该是dict格式}} 245 | 246 | 247 | 248 | {{需要设计按钮以跳转到当前页面的页面名称,输出格式应该是一个列表。例如:[{{"html_name":"index.html","button_name":"jump"}},{{"html_name":"home.html","button_name":"jump"}}]}} 249 | 250 | 251 | """ 252 | 253 | page_complete_prompt_local_img = """ 254 | 你是一个个人主页设计大师,你的任务是根据已知信息准确且基于事实地完成和完善一个未完成的个人主页。 255 | 256 | 其他页面信息如下: 257 | {other_pages_info} 258 | 259 | 当前页面信息如下: 260 | {page_info}(请注意button和link的跳转页面的文件名是它们的链接地址,同时不要修改页面的html_name) 261 | 262 | {feedback} 263 | 264 | 以下是你可能要用到的本地图片信息(请注意,这些图片可能需要在你的设计中使用,且你需要尽量使用这些图片): 265 | {local_img_storage} 266 | 267 | 请根据其他页面的信息和当前页面的已知信息(注意页面之间的跳转关系),完成和完善此页面的信息,并考虑哪些页面需要设计按钮以跳转到当前页面,删除无用的按钮 268 | 请特别注重展示个人成果、教育背景、工作经历、项目经验和专业技能的方式,确保能够突出个人形象。 269 | 270 | 你的输出应该是以下格式: 271 | 思考步骤: 272 | {{你的思考步骤}} 273 | 274 | 275 | {{你完成和完善的页面,不要改变或添加或删除页面的原始键,你可以修改键的值,页面应该是dict格式}} 276 | 277 | 278 | 279 | {{需要设计按钮以跳转到当前页面的页面名称,输出格式应该是一个列表。例如:[{{"html_name":"index.html","button_name":"jump"}},{{"html_name":"home.html","button_name":"jump"}}]}} 280 | 281 | """ 282 | 283 | if __name__ == "__main__": 284 | import json 285 | print(json.loads(local_img_storage_page_example)) -------------------------------------------------------------------------------- /prompts/refine_prompts_en.py: -------------------------------------------------------------------------------- 1 | original_role = "You are an expert HTML developer, skilled in creating webpages using HTML, CSS, and JavaScript. (No need to output separate JS files; embed all JS code directly in the HTML.)" 2 | 3 | Tailwind_role = "You are an expert Tailwind developer, proficient in building webpages using the Tailwind CSS framework." 4 | 5 | Boostrap_role = "You are an expert Bootstrap developer, proficient in building webpages using the Bootstrap CSS framework." 6 | 7 | Materialize_role = "You are an expert Materialize developer, proficient in building webpages using the Materialize CSS framework." 8 | 9 | Bulma_role = "You are an expert Bulma developer, proficient in building webpages using the Bulma CSS framework." 10 | 11 | 12 | refine_img_task = """ 13 | The first image is a screenshot of the target webpage, and the second image is a screenshot of the webpage you have already built. 14 | {local_img_storage} 15 | Page information: 16 | {page_info} (button and link targets correspond to their respective file names) 17 | 18 | Your goal is to modify the code of the second webpage to make it more closely match the first image. 19 | 20 | - Ensure the page layout matches the screenshot exactly. 21 | - Pay attention to layout, text, buttons, and links. 22 | - Match background colors, text colors, font sizes, font families, padding, margins, and borders exactly. 23 | - Use the exact text from the screenshot; do not modify it. 24 | - Avoid placeholder backgrounds (e.g., background: url('https://placehold.co/1600x900')); you may use gradients or local images instead. 25 | - For images, use the local images specified in the page information and do not change file paths. 26 | - Strictly ensure image dimensions are correct for a visually pleasing final page. 27 | - You may enhance interactivity with JavaScript features (scrolling, click events, hover effects, color changes, transitions, page switching, etc.) to make the page both functional and visually appealing. 28 | """ 29 | 30 | 31 | refine_img_text_task = """ 32 | The first image is a reference webpage screenshot, and the second image is a screenshot of the webpage you have already built. 33 | {local_img_storage} 34 | Page information: 35 | {page_info} (button and link targets correspond to their respective file names) 36 | 37 | Your goal is to modify the second webpage's code to follow the layout and structure of the first image while meeting the page information requirements. 38 | - Make modifications based on known information only; do not fabricate content. 39 | - Do not copy the text from the reference page; add text as needed according to user requirements. 40 | - You may add or modify JavaScript, CSS layout, or HTML elements to improve the page. 41 | - Pay attention to background colors, text colors, font sizes, font families, padding, margins, and borders. 42 | - Aim for a rich but visually balanced design; keep personal homepage layouts clean. 43 | - Strictly ensure image dimensions are correct for a visually pleasing result. 44 | - Use local images as specified in the page information and do not modify file paths. 45 | - Ensure images do not cover text; text layers should always be on top. 46 | - Encourage the use of colors, buttons, and effects (waves, gradients, scrolling) to enhance aesthetics. 47 | - Avoid placeholder backgrounds; use gradients or local images instead. 48 | - Consider functionality and usability when modifying the code. 49 | - Analyze headers, navigation, content arrangement, sidebars, footers, visuals, layout, calls to action, responsiveness, and other features to optimize the page. 50 | - Think about how code changes can make the page meet requirements (e.g., adding buttons or text content). 51 | - You may enhance interactivity using JavaScript features (scroll, click, hover, color changes, transitions, page switching, etc.) to ensure usability and visual appeal. 52 | """ 53 | 54 | 55 | refine_text_task = """ 56 | {local_img_storage} 57 | Page information: 58 | {page_info} (button and link targets correspond to their respective file names) 59 | 60 | Your task is to modify the code according to user feedback to meet their requirements. 61 | - You may add or modify JavaScript, CSS layout, or HTML elements based on feedback. 62 | - Pay attention to background colors, text colors, font sizes, font families, padding, margins, and borders. 63 | - Aim for a visually rich and coordinated layout; personal homepages should remain clean. 64 | - Ensure images follow the page information and maintain correct file paths. 65 | - Ensure images do not cover text; text layers should always be on top. 66 | - Encourage the use of colors, buttons, effects (waves, gradients, scrolling) to enhance the page. 67 | - Avoid placeholder backgrounds; use gradients or local images instead. 68 | - Consider functionality and usability when modifying the code. 69 | - Analyze headers, navigation, content arrangement, sidebars, footers, visuals, layout, calls to action, responsiveness, and other features to optimize the page. 70 | - Think about how code changes can make the page meet requirements (e.g., adding buttons or text content). 71 | - You may enhance interactivity using JavaScript features (scroll, click, hover, color changes, transitions, page switching, etc.) to ensure usability and visual appeal. 72 | """ 73 | 74 | 75 | refine_feedback_task = """ 76 | {local_img_storage} 77 | Page information: 78 | {page_info} (button and link targets correspond to their respective file names) 79 | 80 | Your task is to modify the code according to user feedback to meet their requirements. 81 | - Make changes strictly according to known information; do not fabricate content. 82 | - Pay close attention to user feedback and adjust the code accordingly to meet user needs. 83 | - You may add or modify JavaScript, CSS layout, or HTML elements based on feedback. 84 | - Pay attention to background colors, text colors, font sizes, font families, padding, margins, and borders. 85 | - Aim for a visually rich and coordinated layout; personal homepages should remain clean. 86 | - Ensure images follow the page information and maintain correct file paths. 87 | - Ensure images do not cover text; text layers should always be on top. 88 | - Encourage the use of colors, buttons, effects (waves, gradients, scrolling) to enhance the page. 89 | - Avoid placeholder backgrounds; use gradients or local images instead. 90 | - Consider functionality and usability when modifying the code. 91 | - Analyze headers, navigation, content arrangement, sidebars, footers, visuals, layout, calls to action, responsiveness, and other features to optimize the page. 92 | - Think about how code changes can make the page meet requirements (e.g., adding buttons or text content). 93 | - You may enhance interactivity using JavaScript features (scroll, click, hover, color changes, transitions, page switching, etc.) to ensure usability and visual appeal. 94 | """ 95 | 96 | 97 | refine_original_output_format = """ 98 | The current page code is as follows: 99 | HTML code: 100 | {html_code} 101 | CSS code: 102 | {css_code} 103 | 104 | {feedback} 105 | 106 | Please provide a modification plan, then output the updated HTML (including embedded JS) and CSS code. 107 | 108 | Output format: 109 | Modification plan: 110 | 1. 111 | 2. 112 | ... 113 | 114 | Updated HTML: 115 | 116 | Updated CSS: 117 | """ 118 | 119 | 120 | refine_Tailwind_output_format = """ 121 | The current page code is as follows: 122 | HTML code using Tailwind CSS: 123 | {html_code} 124 | 125 | {feedback} 126 | 127 | Please provide a modification plan, then output the updated HTML code. 128 | 129 | Output format: 130 | Modification plan: 131 | 1. 132 | 2. 133 | ... 134 | 135 | Updated HTML: 136 | """ 137 | 138 | 139 | refine_Boostrap_output_format = """ 140 | The current page code is as follows: 141 | HTML code using Bootstrap CSS: 142 | {html_code} 143 | 144 | {feedback} 145 | 146 | Please provide a modification plan, then output the updated HTML code. 147 | 148 | Output format: 149 | Modification plan: 150 | 1. 151 | 2. 152 | ... 153 | 154 | Updated HTML: 155 | """ 156 | 157 | 158 | refine_Materialize_output_format = """ 159 | The current page code is as follows: 160 | HTML code using Materialize CSS: 161 | {html_code} 162 | 163 | {feedback} 164 | 165 | Please provide a modification plan, then output the updated HTML code. 166 | 167 | Output format: 168 | Modification plan: 169 | 1. 170 | 2. 171 | ... 172 | 173 | Updated HTML: 174 | """ 175 | 176 | 177 | refine_Bulma_output_format = """ 178 | The current page code is as follows: 179 | HTML code using Bulma CSS: 180 | {html_code} 181 | 182 | {feedback} 183 | 184 | Please provide a modification plan, then output the updated HTML code. 185 | 186 | Output format: 187 | Modification plan: 188 | 1. 189 | 2. 190 | ... 191 | 192 | Updated HTML: 193 | """ 194 | 195 | 196 | refine_prompt = """ 197 | {role} 198 | {task} 199 | {output_format} 200 | """ 201 | -------------------------------------------------------------------------------- /llm_api.py: -------------------------------------------------------------------------------- 1 | from openai import AzureOpenAI, OpenAI,AsyncAzureOpenAI,AsyncOpenAI 2 | from anthropic import Anthropic,AsyncAnthropic 3 | from urllib.parse import urlparse, unquote 4 | from pathlib import PurePosixPath 5 | import requests 6 | import dashscope 7 | from zhipuai import ZhipuAI 8 | from dashscope import Generation 9 | from dashscope.aigc.generation import AioGeneration 10 | from dashscope import MultiModalConversation 11 | from abc import abstractmethod 12 | from http import HTTPStatus 13 | import platform 14 | import dashscope 15 | import yaml 16 | import os 17 | with open('config.yaml', 'r') as file: 18 | config = yaml.safe_load(file) 19 | for key, value in config.items(): 20 | os.environ[key] = str(value) 21 | import httpx 22 | import logging 23 | import json 24 | import time 25 | from tenacity import ( 26 | retry, 27 | stop_after_attempt, 28 | wait_fixed, 29 | ) 30 | import asyncio 31 | import requests 32 | from PIL import Image 33 | from io import BytesIO 34 | from utils import fetch_image,get_openai_url,encode_image 35 | from dashscope import MultiModalConversation 36 | 37 | 38 | 39 | def before_retry_fn(retry_state): 40 | if retry_state.attempt_number > 1: 41 | logging.info(f"Retrying API call. Attempt #{retry_state.attempt_number}, f{retry_state}") 42 | token_log_file = os.environ.get("TOKEN_LOG_FILE", "logs/token.json") 43 | 44 | 45 | class base_llm: 46 | def __init__(self) -> None: 47 | pass 48 | 49 | @abstractmethod 50 | def response(self,messages,**kwargs): 51 | pass 52 | 53 | def get_imgs(self,prompt, save_path="saves/dalle3.jpg"): 54 | pass 55 | 56 | class base_img_llm(base_llm): 57 | def __init__(self) -> None: 58 | pass 59 | 60 | @abstractmethod 61 | def get_img(self,prompt, save_path="saves/dalle3.jpg"): 62 | pass 63 | 64 | 65 | class openai_llm(base_llm): 66 | def __init__(self) -> None: 67 | super().__init__() 68 | is_azure = config.get("is_azure", True) 69 | if is_azure: 70 | if "AZURE_OPENAI_ENDPOINT" not in os.environ or os.environ["AZURE_OPENAI_ENDPOINT"] == "": 71 | raise ValueError("AZURE_OPENAI_ENDPOINT is not set") 72 | if "AZURE_OPENAI_KEY" not in os.environ or os.environ["AZURE_OPENAI_KEY"] == "": 73 | raise ValueError("AZURE_OPENAI_KEY is not set") 74 | 75 | api_version = os.environ.get("AZURE_OPENAI_API_VERSION",None) 76 | if api_version == "": 77 | api_version = None 78 | self.client = AzureOpenAI( 79 | azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], 80 | api_key=os.environ["AZURE_OPENAI_KEY"], 81 | api_version= api_version 82 | ) 83 | self.async_client = AsyncAzureOpenAI( 84 | azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], 85 | api_key=os.environ["AZURE_OPENAI_KEY"], 86 | api_version= api_version 87 | ) 88 | 89 | else: 90 | if "OPENAI_API_KEY" not in os.environ or os.environ["OPENAI_API_KEY"] == "": 91 | raise ValueError("OPENAI_API_KEY is not set") 92 | 93 | api_key = os.environ.get("OPENAI_API_KEY",None) 94 | proxy_url = os.environ.get("OPENAI_PROXY_URL", None) 95 | if proxy_url == "": 96 | proxy_url = None 97 | base_url = os.environ.get("OPENAI_BASE_URL", None) 98 | if base_url == "": 99 | base_url = None 100 | http_client = httpx.Client(proxy=proxy_url) if proxy_url else None 101 | async_http_client = httpx.AsyncClient(proxy=proxy_url) if proxy_url else None 102 | 103 | self.client = OpenAI(api_key=api_key,base_url=base_url,http_client=http_client) 104 | 105 | self.async_client = AsyncOpenAI(api_key=api_key,base_url=base_url,http_client=async_http_client) 106 | 107 | def process_messages(self, messages): 108 | new_messages = [] 109 | for message in messages: 110 | if message["role"] == "user": 111 | content = message["content"] 112 | if isinstance(content, list): 113 | new_content= [] 114 | for c in content: 115 | if c["type"] == "image": 116 | new_content.append({"type":"image_url","image_url":{"url":get_openai_url(c["url"]),"detail":"high"}}) 117 | else: 118 | new_content.append(c) 119 | new_messages.append({"role":"user","content":new_content}) 120 | else: 121 | new_messages.append(message) 122 | else: 123 | new_messages.append(message) 124 | return new_messages 125 | 126 | @retry(wait=wait_fixed(10), stop=stop_after_attempt(10), before=before_retry_fn) 127 | 128 | def response(self,messages,**kwargs): 129 | messages = self.process_messages(messages) 130 | try: 131 | response = self.client.chat.completions.create( 132 | model=kwargs.get("model", "gpt-4o-mini"), 133 | messages=messages, 134 | n = kwargs.get("n", 1), 135 | temperature= kwargs.get("temperature", 0.3), 136 | top_p=0.7, 137 | max_tokens=kwargs.get("max_tokens", 4000), 138 | timeout=kwargs.get("timeout", 180) 139 | ) 140 | except Exception as e: 141 | model = kwargs.get("model", "gpt-4o-mini") 142 | print(f"get {model} response failed: {e}") 143 | logging.info(e) 144 | return 145 | 146 | if not os.path.exists(token_log_file): 147 | with open(token_log_file, "w") as f: 148 | json.dump({},f) 149 | with open(token_log_file, "r") as f: 150 | tokens = json.load(f) 151 | current_model = kwargs.get("model", "gpt-4o-mini") 152 | if current_model not in tokens: 153 | tokens[current_model] = [0,0] 154 | tokens[current_model][0] += response.usage.prompt_tokens 155 | tokens[current_model][1] += response.usage.completion_tokens 156 | with open(token_log_file, "w") as f: 157 | json.dump(tokens, f) 158 | 159 | return response.choices[0].message.content 160 | 161 | 162 | class qwen_llm(base_llm): 163 | def __init__(self) -> None: 164 | super().__init__() 165 | if "DASHSCOPE_API_KEY" not in os.environ or os.environ["DASHSCOPE_API_KEY"] == "": 166 | raise ValueError("DASHSCOPE_API_KEY is not set") 167 | dashscope.api_key = os.environ["DASHSCOPE_API_KEY"] 168 | 169 | def process_messages(self, messages): 170 | new_messages = [] 171 | for message in messages: 172 | if message["role"] == "user": 173 | content = message["content"] 174 | if isinstance(content, list): 175 | new_content= [] 176 | for c in content: 177 | if c["type"] == "image": 178 | new_content.append({"type":"image_url","image_url":{"url":get_openai_url(c["url"]),"detail":"high"}}) 179 | else: 180 | new_content.append(c) 181 | new_messages.append({"role":"user","content":new_content}) 182 | else: 183 | new_messages.append(message) 184 | else: 185 | new_messages.append(message) 186 | return new_messages 187 | 188 | @retry(wait=wait_fixed(10), stop=stop_after_attempt(10), before=before_retry_fn, reraise=True) 189 | def response(self, messages, **kwargs): 190 | messages = self.process_messages(messages) 191 | try: 192 | response = Generation.call( 193 | model = kwargs.get("model", "qwen3-max"), 194 | messages = messages, 195 | top_p = 0.7, 196 | temperature = 0.3, 197 | result_format = "message" 198 | ) 199 | except Exception as e: 200 | model = kwargs.get("model", "qwen3-max") 201 | print(f"get {model} response failed: {e}") 202 | print(e) 203 | logging.info(e) 204 | return 205 | if not os.path.exists(token_log_file): 206 | with open(token_log_file, "w") as f: 207 | json.dump({},f) 208 | with open(token_log_file, "r") as f: 209 | tokens = json.load(f) 210 | current_model = kwargs.get("model", "qwen3-max") 211 | if current_model not in tokens: 212 | tokens[current_model] = [0,0] 213 | tokens[current_model][0] += response.usage.input_tokens 214 | tokens[current_model][1] += response.usage.output_tokens 215 | with open(token_log_file, "w") as f: 216 | json.dump(tokens, f) 217 | return response.output.choices[0].message.content 218 | 219 | 220 | def get_llm(): 221 | llm_type = config.get("LLM_TYPE", "openai") 222 | llm = None 223 | if llm_type in ["openai"]: 224 | llm = openai_llm() 225 | elif llm_type == "qwen": 226 | llm = qwen_llm() 227 | else: 228 | raise ValueError(f"Unknown LLM type: {llm_type}") 229 | return llm 230 | 231 | if __name__ == "__main__": 232 | llm = get_llm() 233 | prompt = "简洁的简历模板,包含个人信息、工作经验和教育背景的分区" 234 | img_llm.get_img(prompt,"resume_template.jpg") 235 | 236 | 237 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /prompts/plan_prompts_en.py: -------------------------------------------------------------------------------- 1 | original_page_template = """ 2 | [ 3 | { 4 | "html_name": {xxx.html}, 5 | "css_name": {css_filename}, 6 | "js_description": {JavaScript functionality designed for this page}, 7 | "description": {page description}, 8 | "relationship": { 9 | "buttons":[ 10 | { 11 | "button_name" : {button_name}, 12 | "jump_page" : {target_page} 13 | }, 14 | ... 15 | ], 16 | "links":[ 17 | { 18 | "link_name" : {link_name}, 19 | "jump_page" : {target_page} 20 | }, 21 | ... 22 | ] 23 | }, 24 | "practice_features": {practical features required for this page}, 25 | "additional_features": {additional features you want to add to this page}, 26 | "page_style": {page_style}, 27 | "is_main_page" : {true or false, set to true if the page is provided by us as the main page} 28 | } 29 | ... 30 | ] 31 | """ 32 | 33 | 34 | original_page_example = """ 35 | { 36 | "html_name": "personal_homepage.html", 37 | "css_name": "personal_homepage.css", 38 | "js_description": "Dynamically displays user information, projects, skills, and more. Supports interactive switching of personal info, expanding project details, and copying contact info with one click.", 39 | "description": "This page presents the user's personal information, including avatar, bio, project experience, skill showcase, and contact info. The design focuses on modern simplicity and good readability.", 40 | "relationship": { 41 | "buttons": [ 42 | {"button_name": "View Projects", "action": "showProjects()"}, 43 | {"button_name": "Expand Skills", "action": "expandSkills()"}, 44 | {"button_name": "View Contacts", "action": "showContactInfo()"}, 45 | {"button_name": "Back to Top", "action": "scrollToTop()"}, 46 | {"button_name": "Toggle Dark Mode", "action": "toggleDarkMode()"}, 47 | {"button_name": "Back", "jump_page": "previous_page.html"}, 48 | {"button_name": "Next", "jump_page": "next_page.html"} 49 | ], 50 | "links": [ 51 | {"link_name": "GitHub", "jump_page": "https://github.com/username"}, 52 | {"link_name": "LinkedIn", "jump_page": "https://linkedin.com/in/username"}, 53 | {"link_name": "Home", "jump_page": "index.html"} 54 | ] 55 | }, 56 | "practice_features": "Displays personal info, project details, skill visualization, dynamic contact info display, dark mode.", 57 | "additional_features": "Click-to-copy feature for contact info, responsive layout for multiple devices, integrated social icons and links.", 58 | "page_style": "Modern minimalist with gradient backgrounds and rounded components. Card-style layout for project display with expandable sections.", 59 | "is_main_page": true 60 | } 61 | """ 62 | 63 | 64 | local_img_storage_page_template = """ 65 | [ 66 | { 67 | "html_name": {xxx.html}, 68 | "css_name": {css_filename}, 69 | "js_description": {JavaScript functionality designed for this page}, 70 | "description": {page description}, 71 | "relationship": { 72 | "buttons":[ 73 | { 74 | "button_name" : {button_name}, 75 | "jump_page" : {target_page} 76 | }, 77 | ... 78 | ], 79 | "links":[ 80 | { 81 | "link_name" : {link_name}, 82 | "jump_page" : {target_page} 83 | }, 84 | ... 85 | ], 86 | "imgs": [ 87 | { 88 | "src": {image path, must be local}, 89 | "alt": {image description}, 90 | "description": {image usage within the page} 91 | }, 92 | ... 93 | ], 94 | }, 95 | "practice_features": {practical features required for this page}, 96 | "additional_features": {additional features you want to add}, 97 | "page_style": {page_style}, 98 | "is_main_page" : {true or false, set to true if this page uses our provided main image} 99 | } 100 | ... 101 | ] 102 | """ 103 | 104 | 105 | local_img_storage_page_example = """ 106 | { 107 | "html_name": "personal_homepage.html", 108 | "css_name": "personal_homepage.css", 109 | "js_description": "Dynamically displays info, projects, skills, and supports image display (avatar, project images) plus interactivity.", 110 | "description": "This page shows personal info, including avatar, project showcases, skills, and contact details. Clean modern design focusing on visual experience.", 111 | "relationship": { 112 | "buttons": [ 113 | {"button_name": "View Projects", "action": "showProjects()"}, 114 | {"button_name": "Expand Skills", "action": "expandSkills()"}, 115 | {"button_name": "View Contacts", "action": "showContactInfo()"}, 116 | {"button_name": "Toggle Dark Mode", "action": "toggleDarkMode()"}, 117 | {"button_name": "Back to Top", "action": "scrollToTop()"}, 118 | {"button_name": "Back", "jump_page": "previous_page.html"}, 119 | {"button_name": "Next", "jump_page": "next_page.html"} 120 | ], 121 | "links": [ 122 | {"link_name": "GitHub", "jump_page": "https://github.com/username"}, 123 | {"link_name": "LinkedIn", "jump_page": "https://linkedin.com/in/username"}, 124 | {"link_name": "Home", "jump_page": "index.html"} 125 | ] 126 | }, 127 | "imgs": [ 128 | { 129 | "src": "profile_picture.png", 130 | "alt": "User avatar", 131 | "description": "Displayed at the top personal info section." 132 | }, 133 | { 134 | "src": "project_1.png", 135 | "alt": "Project image 1", 136 | "description": "Shows project 1 with clickable detail view." 137 | }, 138 | { 139 | "src": "project_2.png", 140 | "alt": "Project image 2", 141 | "description": "Shows project 2 with interactive details." 142 | }, 143 | { 144 | "src": "background_image.jpg", 145 | "alt": "Background image", 146 | "description": "Used as background to improve visual aesthetics." 147 | } 148 | ], 149 | "practice_features": "Displays personal info, project images, dynamic skill display, contact info, image click-to-view.", 150 | "additional_features": "Responsive design, dark mode, social media integration.", 151 | "page_style": "Modern gradient background, visually polished layout.", 152 | "is_main_page": true 153 | } 154 | """ 155 | 156 | 157 | plan_output_format_prompt = """ 158 | Ensure all pages can be accessed via buttons or links, and all pages can navigate back to the homepage (Note: html_name must match the jump_page in other pages. No dead links allowed). 159 | Page example: 160 | {page_template} 161 | 162 | Strict output format: 163 | 164 | {{designed pages}} 165 | 166 | """ 167 | 168 | 169 | plan_output_format_prompt_local_img = """ 170 | Below are the local images you may need (please try to use them when appropriate): 171 | {local_img_storage} 172 | 173 | Ensure all pages are connected via buttons/links and can return to home. The html_name must match jump_page references. 174 | 175 | Page example: 176 | {page_template} 177 | 178 | Strict output format: 179 | 180 | {{designed pages}} 181 | 182 | """ 183 | 184 | 185 | refine_page_prompt = """ 186 | You are a master of personal homepage design. Your task is to accurately and factually refine page details. 187 | {task_info} 188 | Below is one page of the homepage. Enrich the details based on the given info and page theme—especially personal achievements, work experience, and project experience. Do not change the format or invent facts! 189 | Output must be a dict. No extra output. Do not add new links/pages. Do not modify html_name. 190 | Example: 191 | {page_example} 192 | 193 | You may enrich layout, visual effects, functional elements, etc., to highlight personal strengths. 194 | 195 | Page: 196 | {page_info} (Note: button/link jump_page must match actual filenames) 197 | 198 | {feedback} 199 | 200 | Output format: 201 | 202 | {{your modified page}} 203 | 204 | """ 205 | 206 | 207 | refine_page_local_img_prompt = """ 208 | You are a personal homepage design master. Your task is to refine the page based on accurate info. 209 | {task_info} 210 | Below is one page of the homepage. Enrich the details while accurately including user-provided info—achievements, education, work experience, project experience, technical skills. Do not change format or invent content. 211 | Output must be a dict. No extra output. Do not modify html_name. 212 | 213 | Example: 214 | {page_example} 215 | 216 | You may enrich layout, effects, functional components. 217 | 218 | Below are the local images you may use (choose only appropriate ones): 219 | {local_img_storage} 220 | 221 | Page: 222 | {page_info} 223 | 224 | {feedback} 225 | 226 | Output format: 227 | 228 | {{your modified page}} 229 | 230 | """ 231 | 232 | 233 | page_complete_prompt = """ 234 | You are a personal homepage design master. Your task is to accurately complete and refine an unfinished page. 235 | 236 | Other pages: 237 | {other_pages_info} 238 | 239 | Current page: 240 | {page_info} 241 | 242 | {feedback} (pay special attention) 243 | 244 | Complete the page based on existing pages and maintain page relationships. Highlight personal info, achievements, education, work history, projects, and skills. 245 | 246 | Output format: 247 | Thought process: 248 | {{your reasoning}} 249 | 250 | 251 | {{completed page in dict format}} 252 | 253 | 254 | 255 | {{list of pages that need buttons linking to this page}} 256 | 257 | """ 258 | 259 | 260 | page_complete_prompt_local_img = """ 261 | You are a personal homepage design master. Your task is to accurately complete and refine a homepage page. 262 | 263 | Other pages: 264 | {other_pages_info} 265 | 266 | Current page: 267 | {page_info} 268 | 269 | {feedback} 270 | 271 | Below are the local images you may use: 272 | {local_img_storage} 273 | 274 | Ensure consistency with other pages and maintain jump relationships. Remove useless buttons. 275 | 276 | Highlight achievements, education, work experience, project experience, skills. 277 | 278 | Output format: 279 | Thought process: 280 | {{your reasoning}} 281 | 282 | 283 | {{completed page in dict format}} 284 | 285 | 286 | 287 | {{list of pages that need buttons linking to this page}} 288 | 289 | """ 290 | 291 | if __name__ == "__main__": 292 | import json 293 | print(json.loads(local_img_storage_page_example)) 294 | -------------------------------------------------------------------------------- /demo/skills.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 张三 - 技能展示 8 | 9 | 10 | 11 | 50 | 51 | 52 | 53 | 63 | 64 |
65 | 66 |
67 |
68 | 69 | 70 | 71 |
72 |
73 | 74 |
75 |
76 | 77 | 78 |

79 | 语言能力 80 |

81 |
82 |
83 |
84 | 85 | 中文(母语) 86 | 87 |
88 |
89 | 90 | 英语 91 | 92 |
93 |
94 | 95 | 96 | IELTS 7.5 97 |
98 |
99 |
100 | 101 |
102 |
103 | 104 | 105 |

106 | 技术栈 107 |

108 |
109 |
110 | 111 |
112 |
113 | 114 |
115 |
116 | 117 | 118 |

119 | 研究领域 120 |

121 |
122 |
123 | 124 |
125 |
126 | 127 | 139 |
140 | 343 | 344 | 345 | -------------------------------------------------------------------------------- /demo/pages.json: -------------------------------------------------------------------------------- 1 | [{"html_name": "index.html", "js_description": "\u4e3b\u9875\u8f6e\u64ad\u5c55\u793a\u6700\u65b0\u8bba\u6587\u6458\u8981\uff0c\u70b9\u51fb\u6309\u94ae\u5e73\u6ed1\u6eda\u52a8\u5230\u5bf9\u5e94\u533a\u57df\uff1b\u9876\u90e8\u5bfc\u822a\u680f\u56fa\u5b9a\uff0c\u652f\u6301\u951a\u70b9\u8df3\u8f6c\u3002", "description": "\u4e2a\u4eba\u4e3b\u9875\u9996\u9875\uff0c\u96c6\u4e2d\u5c55\u793a\u7528\u6237\u57fa\u672c\u4fe1\u606f\u3001\u6559\u80b2\u80cc\u666f\u3001\u7814\u7a76\u65b9\u5411\u3001\u8054\u7cfb\u65b9\u5f0f\uff0c\u5e76\u63d0\u4f9b\u5bfc\u822a\u81f3\u5176\u4ed6\u9875\u9762\u7684\u5165\u53e3\u3002\u7a81\u51fa\u5f20\u4e09\u5728\u591a\u6a21\u6001\u5927\u6a21\u578b\u3001Embodied AI\u3001\u8ba1\u7b97\u673a\u89c6\u89c9\u4e0e\u7f51\u7edc\u53ef\u89e3\u91ca\u6027\u9886\u57df\u7684\u5b66\u672f\u79ef\u7d2f\u4e0e\u5de5\u4e1a\u754c\u5b9e\u8df5\u7ecf\u9a8c\uff0c\u901a\u8fc7\u7ed3\u6784\u5316\u9884\u89c8\u6a21\u5757\u5f15\u5bfc\u8bbf\u5ba2\u6df1\u5165\u4e86\u89e3\u5176\u7814\u7a76\u6210\u679c\u4e0e\u9879\u76ee\u7ecf\u5386\u3002", "relationship": {"buttons": [{"button_name": "\u5b66\u672f\u6210\u679c", "jump_page": "publications.html"}, {"button_name": "\u9879\u76ee\u7ecf\u5386", "jump_page": "projects.html"}, {"button_name": "\u4e13\u4e1a\u6280\u80fd", "jump_page": "skills.html"}], "links": [{"link_name": "\u90ae\u7bb1\u8054\u7cfb", "jump_page": "mailto:zhangsan@tsinghua.edu.com"}]}, "practice_features": ["\u54cd\u5e94\u5f0f\u5e03\u5c40\u9002\u914d\u79fb\u52a8\u7aef", "\u5feb\u901f\u8bbf\u95ee\u5404\u6a21\u5757\u5185\u5bb9", "\u7a81\u51fa\u663e\u793a\u7814\u7a76\u65b9\u5411\u5173\u952e\u8bcd\uff08\u5982\u2018\u591a\u6a21\u6001\u5927\u6a21\u578b\u2019\u3001\u2018Embodied AI\u2019\u3001\u2018\u8ba1\u7b97\u673a\u89c6\u89c9\u2019\u3001\u2018\u7f51\u7edc\u53ef\u89e3\u91ca\u6027\u2019\uff09", "\u8bba\u6587\u8f6e\u64ad\u81ea\u52a8\u5207\u6362\uff08\u6bcf8\u79d2\uff09\u5e76\u652f\u6301\u624b\u52a8\u6682\u505c/\u5207\u6362", "\u70b9\u51fb\u2018\u67e5\u770b\u66f4\u591a\u2019\u6309\u94ae\u5e73\u6ed1\u8df3\u8f6c\u81f3\u5bf9\u5e94\u8be6\u60c5\u9875"], "additional_features": ["\u6df1\u8272/\u6d45\u8272\u4e3b\u9898\u5207\u6362\uff08\u901a\u8fc7localStorage\u8bb0\u5fc6\u7528\u6237\u504f\u597d\uff09", "\u9875\u9762\u52a0\u8f7d\u52a8\u753b\uff08\u6de1\u5165+\u4e0a\u6d6e\u6548\u679c\uff09", "\u7814\u7a76\u65b9\u5411\u4ee5\u6807\u7b7e\u4e91\u5f62\u5f0f\u52a8\u6001\u5c55\u793a\uff0c\u5173\u952e\u8bcd\u6309\u91cd\u8981\u6027\u52a0\u6743\u5927\u5c0f", "\u5b66\u672f\u6210\u679c\u9884\u89c8\u533a\u5c55\u793a\u6700\u65b0\u4e00\u7bc7\u8bba\u6587\uff08CVPR 2024\uff09\u6807\u9898\u4e0e\u7b80\u77ed\u6458\u8981", "\u9879\u76ee\u7ecf\u5386\u9884\u89c8\u533a\u8f6e\u64ad\u5c55\u793a\u5b57\u8282\u8df3\u52a8\u3001\u817e\u8baf\u3001\u534e\u4e3a\u4e09\u6bb5\u5b9e\u4e60\u7684\u6838\u5fc3\u8d21\u732e\u4eae\u70b9"], "page_style": "\u6574\u4f53\u91c7\u7528\u6781\u7b80\u5b66\u672f\u98ce\u683c\uff1a\u4e3b\u8272\u8c03\u4e3a\u6df1\u84dd\uff08#0d1b2a\uff09\u4e0e\u767d\u8272\uff0c\u8f85\u4ee5\u6d45\u7070\u80cc\u666f\u533a\u5757\u533a\u5206\u5185\u5bb9\uff1b\u5b57\u4f53\u4f7f\u7528\u7cfb\u7edf\u65e0\u886c\u7ebf\u5b57\u4f53\uff08\u5982Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI'\uff09\uff0c\u6807\u9898\u52a0\u7c97\uff1b\u5361\u7247\u5f0f\u5e03\u5c40\uff0c\u5706\u89d28px\uff0c\u9634\u5f71\u8f7b\u5fae\uff1b\u5bfc\u822a\u680f\u56fa\u5b9a\u9876\u90e8\uff0c\u534a\u900f\u660e\u80cc\u666f\uff1b\u6240\u6709\u9875\u9762\u5171\u4eab\u6b64\u57fa\u7840\u6837\u5f0f\u3002\u672c\u9875\u7279\u6709\uff1a\u4e2a\u4eba\u4fe1\u606f\u533a\u5c45\u4e2d\u9876\u90e8\uff0c\u5305\u542b\u59d3\u540d\u5927\u6807\u9898\uff08\u2018\u5f20\u4e09\u2019\uff09\u3001\u2018\u6e05\u534e\u5927\u5b66\u8ba1\u7b97\u673a\u7cfb\u7855\u58eb\u2019\u526f\u6807\u9898\u3001\u5b66\u672f\u90ae\u7bb1\u94fe\u63a5\uff1b\u7814\u7a76\u65b9\u5411\u4ee5\u4ea4\u4e92\u5f0f\u6807\u7b7e\u4e91\u5448\u73b0\uff1b\u4e0b\u65b9\u5206\u4e09\u680f\u5feb\u901f\u9884\u89c8\u2014\u2014\u2018\u5b66\u672f\u6210\u679c\u2019\u680f\u5c55\u793aCVPR 2024\u8bba\u6587\u6807\u9898\u53ca\u2018\u5143\u80fd\u529b\u89e3\u8026\u2019\u5173\u952e\u8bcd\uff0c\u2018\u9879\u76ee\u7ecf\u5386\u2019\u680f\u8f6e\u64ad\u5c55\u793a\u5b57\u8282\u8df3\u52a8\uff08\u591a\u4efb\u52a1\u6307\u4ee4\u589e\u5f3a\uff09\u3001\u817e\u8baf\uff08\u591a\u6a21\u6001\u6570\u636e\u5f15\u64ce\uff09\u3001\u534e\u4e3a\uff08\u77e5\u8bc6\u6ce8\u5165\u7cfb\u7edf\uff09\u4e09\u5927\u9879\u76ee\u6838\u5fc3\u6210\u679c\uff0c\u2018\u4e13\u4e1a\u6280\u80fd\u2019\u680f\u7a81\u51fa\u2018\u96c5\u601d7.5\u2019\u53ca\u591a\u6a21\u6001/\u5927\u6a21\u578b\u76f8\u5173\u6280\u672f\u6808\u5173\u952e\u8bcd\uff1b\u6bcf\u680f\u5e95\u90e8\u8bbe\u2018\u67e5\u770b\u66f4\u591a\u2019\u6309\u94ae\uff0c\u7edf\u4e00\u8df3\u8f6c\u81f3\u5bf9\u5e94\u8be6\u60c5\u9875\u3002", "is_main_page": true}, {"html_name": "publications.html", "js_description": "\u8bba\u6587\u6761\u76ee\u6309\u53d1\u8868\u5e74\u4efd\u5012\u5e8f\u6392\u5217\uff0c\u9ed8\u8ba4\u5c55\u5f00\u5168\u90e8\uff1b\u652f\u6301\u6309\u4f1a\u8bae\uff08AAAI/ICLR/CVPR/NeurIPS/IJCAI\uff09\u7b5b\u9009\uff1b\u6bcf\u7bc7\u8bba\u6587\u53ef\u6298\u53e0/\u5c55\u5f00\u7b80\u4ecb\uff1b\u652f\u6301\u4e00\u952e\u590d\u5236BibTeX\u5f15\u7528\uff1b\u9f20\u6807\u60ac\u505c\u4f1a\u8bae\u540d\u79f0\u663e\u793a\u4f1a\u8baeLogo\uff1b\u70b9\u51fb\u6807\u9898\u8df3\u8f6c\u81f3\u865a\u62dfPDF\u951a\u70b9\uff08\u5982 #paper1\uff09\u3002", "description": "\u5b8c\u6574\u5c55\u793a\u5f20\u4e09\u5728\u591a\u6a21\u6001\u5927\u6a21\u578b\u3001Embodied AI\u3001\u8ba1\u7b97\u673a\u89c6\u89c9\u4e0e\u7f51\u7edc\u53ef\u89e3\u91ca\u6027\u65b9\u5411\u76845\u7bc7\u9876\u4f1a\u8bba\u6587\uff0c\u5305\u62ec\u9898\u76ee\u3001\u53d1\u8868\u4f1a\u8bae\uff08\u542b\u5e74\u4efd\uff09\u3001\u7ed3\u6784\u5316\u7b80\u4ecb\u53ca\u4f5c\u8005\u4fe1\u606f\uff0c\u7a81\u51fa\u5176\u5728\u5143\u80fd\u529b\u89e3\u8026\u4e0e\u5177\u8eab\u667a\u80fd\u9886\u57df\u7684\u7cfb\u7edf\u6027\u7814\u7a76\u8d21\u732e\u3002", "relationship": {"buttons": [{"button_name": "\u8fd4\u56de\u4e3b\u9875", "jump_page": "index.html"}, {"button_name": "\u6309\u4f1a\u8bae\u7b5b\u9009", "action": "filterByConference()"}, {"button_name": "\u5bfc\u51fa\u5168\u90e8\u53c2\u8003\u6587\u732e", "action": "exportAllBibTeX()"}], "links": []}, "practice_features": ["\u8bba\u6587\u5217\u8868\u6e05\u6670\u7ed3\u6784\u5316", "\u652f\u6301\u590d\u5236\u5f15\u7528\u683c\u5f0f\uff08BibTeX\uff09", "\u4f1a\u8bae\u540d\u79f0\u9ad8\u4eae\u663e\u793a", "\u6309\u5e74\u4efd\u5012\u5e8f\u81ea\u52a8\u6392\u5e8f", "\u6bcf\u7bc7\u8bba\u6587\u72ec\u7acb\u5361\u7247\u5e03\u5c40"], "additional_features": ["\u70b9\u51fb\u8bba\u6587\u6807\u9898\u53ef\u8df3\u8f6c\u81f3\u865a\u62dfPDF\u94fe\u63a5\uff08\u5982 #paper1\uff09", "\u9f20\u6807\u60ac\u505c\u663e\u793a\u4f1a\u8baeLogo", "\u652f\u6301\u7b80\u4ecb\u6298\u53e0/\u5c55\u5f00\u5207\u6362", "\u6df1\u84dd\u5de6\u4fa7\u7ad6\u6761\u6807\u8bc6\u9876\u4f1a\uff08AAAI/ICLR/CVPR/NeurIPS/IJCAI\uff09"], "page_style": "\u7ee7\u627f\u4e3b\u6837\u5f0f\u3002\u672c\u9875\u7279\u6709\uff1a\u6bcf\u7bc7\u8bba\u6587\u4ee5\u72ec\u7acb\u5361\u7247\u5448\u73b0\uff0c\u5de6\u4fa73px\u7ad6\u6761\u989c\u8272\u533a\u5206\u4f1a\u8bae\u7b49\u7ea7\uff08\u6240\u6709\u5217\u51fa\u4f1a\u8bae\u5747\u4e3a\u9876\u4f1a\uff0c\u7edf\u4e00\u4f7f\u7528\u6df1\u84dd\u8272\uff09\uff1b\u6807\u9898\u4f7f\u75281.4em\u52a0\u7c97\u5b57\u4f53\uff1b\u4f1a\u8bae\u4fe1\u606f\uff08\u5982 'CVPR 2024'\uff09\u4ee5\u5c0f\u53f7\u7070\u8272\u5b57\u4f53\u7f6e\u4e8e\u5361\u7247\u53f3\u4e0a\u89d2\uff1b\u7b80\u4ecb\u9ed8\u8ba4\u5c55\u5f00\uff0c\u4f46\u53ef\u901a\u8fc7JS\u6309\u94ae\u6298\u53e0\uff1b\u4f5c\u8005\u4fe1\u606f\u56fa\u5b9a\u4e3a\u201c\u5f20\u4e09\u201d\u5e76\u4ee5\u659c\u4f53\u6807\u6ce8\uff1b\u5e95\u90e8\u63d0\u4f9b\u2018\u5bfc\u51fa\u5168\u90e8\u53c2\u8003\u6587\u732e\u2019\u6309\u94ae\uff0c\u652f\u6301\u4e00\u952e\u590d\u5236\u5168\u90e8BibTeX\u6761\u76ee\u3002", "is_main_page": false}, {"html_name": "projects.html", "js_description": "\u9879\u76ee\u7ecf\u5386\u6309\u65f6\u95f4\u5012\u5e8f\u5c55\u793a\uff0c\u6bcf\u4e2a\u9879\u76ee\u53ef\u5c55\u5f00\u67e5\u770b\u8be6\u7ec6\u63cf\u8ff0\uff1b\u652f\u6301\u6309\u516c\u53f8\uff08\u5b57\u8282/\u817e\u8baf/\u534e\u4e3a\uff09\u8fc7\u6ee4\uff1b\u9f20\u6807\u60ac\u505c\u516c\u53f8\u6807\u7b7e\u663e\u793a\u7b80\u8981\u4ecb\u7ecd\uff1b\u70b9\u51fb\u2018\u590d\u5236\u6210\u679c\u6458\u8981\u2019\u6309\u94ae\u53ef\u4e00\u952e\u590d\u5236\u5173\u952e\u6210\u679c\u6587\u672c\u3002", "description": "\u8be6\u7ec6\u5217\u51fa\u7528\u6237\u5728\u5b57\u8282\u8df3\u52a8\u3001\u817e\u8bafAI Lab\u3001\u534e\u4e3a\u8bfa\u4e9a\u65b9\u821f\u5b9e\u9a8c\u5ba4\u7684\u4e09\u6bb5\u9879\u76ee\u7ecf\u5386\uff0c\u5305\u542b\u65f6\u95f4\u6bb5\u3001\u90e8\u95e8\u3001\u4efb\u52a1\u6311\u6218\u3001\u89e3\u51b3\u65b9\u6848\u4e0e\u6210\u679c\u3002\u6bcf\u6bb5\u7ecf\u5386\u7ed3\u6784\u5316\u5448\u73b0\u4e3a\u2018\u6311\u6218-\u65b9\u6cd5-\u6210\u679c\u2019\u4e09\u90e8\u5206\uff0c\u5e76\u7a81\u51fa\u5173\u952e\u6280\u672f\u6808\uff08\u5982LLaMA\u3001BLIP-2\u3001\u591a\u6a21\u6001\u601d\u7ef4\u94fe\u7b49\uff09\uff0c\u5f3a\u5316\u5176\u5728\u591a\u6a21\u6001\u5927\u6a21\u578b\u4e0e\u5177\u8eab\u667a\u80fd\u65b9\u5411\u7684\u5de5\u7a0b\u4e0e\u7814\u7a76\u80fd\u529b\u3002", "relationship": {"buttons": [{"button_name": "\u8fd4\u56de\u4e3b\u9875", "jump_page": "index.html"}, {"button_name": "\u4ec5\u663e\u793a\u5b57\u8282\u8df3\u52a8", "action": "filterByCompany('ByteDance')"}, {"button_name": "\u4ec5\u663e\u793a\u817e\u8baf", "action": "filterByCompany('Tencent')"}, {"button_name": "\u4ec5\u663e\u793a\u534e\u4e3a", "action": "filterByCompany('Huawei')"}, {"button_name": "\u663e\u793a\u5168\u90e8", "action": "filterByCompany('All')"}], "links": []}, "practice_features": ["\u65f6\u95f4\u8f74\u53ef\u89c6\u5316\u5e03\u5c40", "\u5173\u952e\u8bcd\u9ad8\u4eae\uff08\u5982LLaMA\u3001ChatGLM\u3001BLIP-2\u3001LLaVA\u3001\u591a\u6a21\u6001\u601d\u7ef4\u94fe\u3001\u77e5\u8bc6\u56fe\u8c31\u5bf9\u9f50\uff09", "\u516c\u53f8Logo\u5360\u4f4d\u7b26\uff08\u5b57\u8282\u7ea2 #FE2C55\u3001\u817e\u8baf\u7eff #00B050\u3001\u534e\u4e3a\u6a59 #FF6F00\uff09", "\u6bcf\u6bb5\u7ecf\u5386\u5185\u5d4c\u2018\u6311\u6218\u2019\u2018\u65b9\u6cd5\u2019\u2018\u6210\u679c\u2019\u56fe\u6807\u5f15\u5bfc\u9605\u8bfb", "\u6210\u679c\u90e8\u5206\u63d0\u4f9b\u2018\u590d\u5236\u6458\u8981\u2019\u6309\u94ae"], "additional_features": ["\u9f20\u6807\u60ac\u505c\u516c\u53f8\u540d\u663e\u793a\u516c\u53f8\u7b80\u4ecb\uff08\u5982\u2018\u5b57\u8282\u8df3\u52a8 AI\u5b9e\u9a8c\u5ba4\uff1a\u805a\u7126\u5927\u6a21\u578b\u57fa\u7840\u7814\u7a76\u4e0e\u5e94\u7528\u843d\u5730\u2019\uff09", "\u652f\u6301\u6253\u5370\u7b80\u5386\u683c\u5f0f\uff08\u9690\u85cf\u4ea4\u4e92\u6309\u94ae\uff0c\u4f18\u5316\u6392\u7248\uff09", "\u54cd\u5e94\u5f0f\u8bbe\u8ba1\u9002\u914d\u79fb\u52a8\u7aef\uff0c\u65f6\u95f4\u8f74\u81ea\u52a8\u8f6c\u4e3a\u5355\u5217\u5361\u7247\u5e03\u5c40"], "page_style": "\u7ee7\u627f\u4e3b\u6837\u5f0f\u3002\u672c\u9875\u7279\u6709\uff1a\u91c7\u7528\u5782\u76f4\u65f6\u95f4\u8f74\u8bbe\u8ba1\uff0c\u5de6\u4fa7\u4e3a\u65f6\u95f4\u70b9\uff08YYYY.MM\uff09\uff0c\u53f3\u4fa7\u4e3a\u9879\u76ee\u5361\u7247\uff1b\u516c\u53f8\u540d\u79f0\u4f7f\u7528\u54c1\u724c\u8272\u4f5c\u4e3a\u6807\u7b7e\u80cc\u666f\uff1b\u6bcf\u6bb5\u7ecf\u5386\u5206\u2018\u6311\u6218-\u65b9\u6cd5-\u6210\u679c\u2019\u4e09\u90e8\u5206\uff0c\u5206\u522b\u4ee5\u26a0\ufe0f\u3001\ud83d\udd27\u3001\u2705\u56fe\u6807\u6807\u8bc6\uff1b\u5361\u7247\u5185\u5d4c\u4ee3\u7801\u5757\u6837\u5f0f\u5c55\u793a\u5173\u952e\u6280\u672f\u6808\uff08\u5982 font-family: 'Courier New', monospace; background: #f5f5f5; padding: 8px; border-radius: 4px\uff09\uff1b\u9f20\u6807\u60ac\u505c\u5361\u7247\u65f6\u8f7b\u5fae\u4e0a\u6d6e\u52a8\u753b\u589e\u5f3a\u4ea4\u4e92\u611f\u3002", "is_main_page": false}, {"html_name": "skills.html", "js_description": "\u6280\u80fd\u5206\u7c7b\u5c55\u793a\uff08\u8bed\u8a00\u3001\u6846\u67b6\u3001\u9886\u57df\u77e5\u8bc6\u7b49\uff09\uff0c\u652f\u6301\u8fdb\u5ea6\u6761\u6216\u661f\u7ea7\u8bc4\u5206\u53ef\u89c6\u5316\u719f\u7ec3\u5ea6\uff1b\u70b9\u51fb\u4efb\u4e00\u6280\u80fd\u6807\u7b7e\u53ef\u52a8\u6001\u9ad8\u4eae\u5173\u8054\u7684\u8bba\u6587\u6216\u5b9e\u4e60\u9879\u76ee\uff1b\u652f\u6301\u6309\u7c7b\u522b\u7b5b\u9009\u4e0e\u5173\u952e\u8bcd\u641c\u7d22\u3002", "description": "\u672c\u9875\u9762\u7cfb\u7edf\u5316\u5c55\u793a\u5f20\u4e09\u7684\u4e13\u4e1a\u6280\u80fd\u4f53\u7cfb\uff0c\u6db5\u76d6\u8bed\u8a00\u80fd\u529b\u3001\u6838\u5fc3\u6280\u672f\u6808\u4e0e\u524d\u6cbf\u7814\u7a76\u9886\u57df\u3002\u660e\u786e\u5217\u51fa\u96c5\u601d7.5\u6210\u7ee9\uff0c\u5e76\u57fa\u4e8e\u5176\u5b66\u672f\u8bba\u6587\u4e0e\u5de5\u4e1a\u754c\u5b9e\u4e60\u7ecf\u5386\uff0c\u7ed3\u6784\u5316\u5448\u73b0\u9690\u542b\u6280\u672f\u80fd\u529b\uff0c\u5982\u591a\u6a21\u6001\u5927\u6a21\u578b\u67b6\u6784\u8bbe\u8ba1\u3001Embodied AI\u4e2d\u7684\u5143\u6280\u80fd\u89e3\u8026\u3001\u8ba1\u7b97\u673a\u89c6\u89c9\u4efb\u52a1\u4f18\u5316\u3001\u7f51\u7edc\u53ef\u89e3\u91ca\u6027\u65b9\u6cd5\u3001LLM\u6307\u4ee4\u5fae\u8c03\u3001\u8de8\u6a21\u6001\u5bf9\u9f50\u4e0e\u77e5\u8bc6\u589e\u5f3a\u7b49\u3002", "relationship": {"buttons": [{"button_name": "\u8fd4\u56de\u4e3b\u9875", "jump_page": "index.html"}], "links": []}, "practice_features": ["\u6280\u80fd\u5206\u7c7b\u6e05\u6670\uff1a\u5206\u4e3a\u2018\u8bed\u8a00\u80fd\u529b\u2019\u3001\u2018\u6280\u672f\u6808\u2019\u3001\u2018\u7814\u7a76\u9886\u57df\u2019\u4e09\u5927\u533a\u5757", "\u652f\u6301\u641c\u7d22\u8fc7\u6ee4\uff1a\u7528\u6237\u53ef\u8f93\u5165\u5173\u952e\u8bcd\u5b9e\u65f6\u7b5b\u9009\u6280\u80fd\u6807\u7b7e", "\u663e\u793a\u8ba4\u8bc1\u6210\u7ee9\uff1a\u96c5\u601d7.5\u4ee5\u7eff\u8272\u5fbd\u7ae0\u5f62\u5f0f\u9192\u76ee\u5c55\u793a"], "additional_features": ["\u6280\u80fd\u4e91\u56fe\u52a8\u6001\u751f\u6210\uff1a\u6280\u672f\u6808\u533a\u57df\u91c7\u7528\u54cd\u5e94\u5f0f\u6807\u7b7e\u4e91\uff0c\u5b57\u4f53\u5927\u5c0f\u53cd\u6620\u4f7f\u7528\u9891\u7387\u6216\u91cd\u8981\u6027\uff08\u5982LLaMA\u3001BLIP-2\u3001LLaVA\u3001ChatGLM\u3001\u591a\u6a21\u6001\u3001\u5143\u6280\u80fd\u89e3\u8026\u7b49\u9ad8\u9891\u8bcd\u66f4\u5927\uff09", "\u70b9\u51fb\u6280\u80fd\u663e\u793a\u76f8\u5173\u9879\u76ee/\u8bba\u6587\u5173\u8054\uff1a\u4f8b\u5982\u70b9\u51fb\u2018Embodied AI\u2019\u81ea\u52a8\u5c55\u5f00\u5176\u4e94\u7bc7\u9876\u4f1a\u8bba\u6587\u6458\u8981\uff1b\u70b9\u51fb\u2018\u591a\u6a21\u6001\u5bf9\u9f50\u2019\u5173\u8054\u534e\u4e3a\u8bfa\u4e9a\u65b9\u821f\u5b9e\u9a8c\u5ba4\u5de5\u4f5c\u5185\u5bb9"], "page_style": "\u7ee7\u627f\u4e3b\u6837\u5f0f\u3002\u672c\u9875\u7279\u6709\uff1a\u5206\u4e3a\u2018\u8bed\u8a00\u80fd\u529b\u2019\u3001\u2018\u6280\u672f\u6808\u2019\u3001\u2018\u7814\u7a76\u9886\u57df\u2019\u4e09\u5927\u533a\u5757\uff1b\u96c5\u601d\u6210\u7ee9\u4ee5\u7eff\u8272\u5fbd\u7ae0\uff08#4CAF50\u80cc\u666f + \u767d\u8272\u6587\u5b57\uff09\u5c55\u793a\uff0c\u6807\u6ce8\u2018IELTS 7.5\u2019\uff1b\u6280\u672f\u6808\u4f7f\u7528\u52a8\u6001\u6807\u7b7e\u4e91\u5e03\u5c40\uff0c\u652f\u6301\u60ac\u505c\u52a8\u753b\uff1b\u7814\u7a76\u9886\u57df\u4e0e\u4e3b\u9875\u7814\u7a76\u65b9\u5411\u4e25\u683c\u4e00\u81f4\uff08\u591a\u6a21\u6001\u5927\u6a21\u578b\u3001Embodied AI\u3001\u8ba1\u7b97\u673a\u89c6\u89c9\u3001\u7f51\u7edc\u53ef\u89e3\u91ca\u6027\uff09\uff0c\u6bcf\u4e2a\u9886\u57df\u914d\u4ee5\u82af\u7247\uff08\ud83d\udcbb\uff09\u3001\u673a\u5668\u4eba\uff08\ud83e\udd16\uff09\u6216\u663e\u5fae\u955c\uff08\ud83d\udd2c\uff09\u56fe\u6807\u88c5\u9970\uff1b\u6574\u4f53\u91c7\u7528\u5361\u7247\u5f0f\u5206\u533a\uff0c\u533a\u5757\u95f4\u4ee5\u6d45\u7070\u5206\u5272\u7ebf\u9694\u79bb\uff0c\u786e\u4fdd\u4fe1\u606f\u5c42\u6b21\u6e05\u6670\u3002", "is_main_page": false}] -------------------------------------------------------------------------------- /prompts/plan_prompts.py: -------------------------------------------------------------------------------- 1 | # 根据输入的任务、图片、用户反馈、语言和本地图片存储,生成用于规划网页设计的提示信息。 2 | def get_plan_prompt(basic_information = None, academic_achievements = None, experience=None, professional_skills=None,refine_times= None,img = None ,css_frame = None,feedback = "",language = "en",status = "student",industry = "tech",local_img_storage = []): 3 | """Get plan prompt""" 4 | if status == "student": 5 | prompt = f""" 6 | Create a personal homepage for a student. 7 | The page should focus on academic achievements, extracurricular activities, and career goals. 8 | Include a section competitions, core coursework, campus involvement, and academic accomplishments. 9 | """ 10 | elif status == "graduate": 11 | prompt = f""" 12 | Create a professional homepage for a entry-level professional 13 | The page should emphasize work experience, key skills, and career progression. 14 | Include sections for Include sections for projects, internships, papers, and certifications. 15 | """ 16 | elif status == "entry_level": 17 | prompt = f""" 18 | Create a personal homepage for an entry-level professional. 19 | The page should highlight their career start, skills acquired, and future career goals. 20 | Include sections for work experience, project involvement, and skill development, with a clean and modern design, with a clean and modern design. 21 | """ 22 | elif status == "mid_level": 23 | prompt = f""" 24 | Create a personal homepage for a mid-level manager. 25 | The page should highlight their leadership skills, strategic business achievements, and team management experience. 26 | Include sections for key projects, team achievements, and their role in driving team success, with a professional and refined design. 27 | """ 28 | elif status == "expert": 29 | prompt = f""" 30 | Create a personal homepage for an industry expert. 31 | The page should showcase their significant contributions to the field, innovation, and public recognition. 32 | Include sections for academic publications, media coverage, and industry talks, with a distinctive design that reflects their professional image. 33 | """ 34 | else: 35 | prompt = f""" 36 | Create a general personal homepage. 37 | The page should focus on the individual's key experiences, skills, and achievements. 38 | Include customizable sections that highlight their unique background and professional journey. 39 | """ 40 | 41 | # 根据行业生成Prompt 42 | if industry == "tech": 43 | prompt = f""" 44 | Create a personal homepage for a professional in the tech industry. 45 | The page should highlight their technical expertise, major projects, and contributions to open-source communities. 46 | Include sections for certifications, project demos, and a sleek, modern design that showcases their innovative capabilities. 47 | """ 48 | elif industry == "creative": 49 | prompt = f""" 50 | Design a personal homepage for a creative industry professional. 51 | Focus on showcasing a diverse portfolio, unique artistic processes, and collaborations with prominent brands. 52 | The design should be visually striking and reflect their unique artistic style and creative inspiration. 53 | """ 54 | elif industry == "finance": 55 | prompt = f""" 56 | Develop a professional homepage for a finance industry expert. 57 | Emphasize their career achievements, successful project case studies, and in-depth market insights. 58 | The page should convey professionalism and trustworthiness, featuring sections for certifications and client testimonials to highlight their expertise and credibility. 59 | """ 60 | elif industry == "healthcare": 61 | prompt = f""" 62 | Create a personal homepage for a professional in the healthcare industry. 63 | Highlight their medical expertise, extensive patient care experience, and contributions to medical research. 64 | Include sections for certifications, published research, and a clean, professional design that reflects their passion for healthcare. 65 | """ 66 | elif industry == "education": 67 | prompt = f""" 68 | Design a personal homepage for an educator. 69 | Focus on showcasing their teaching philosophy, student success stories, and contributions to educational programs. 70 | The design should be inviting and accessible, reflecting the educator's passion for student development. 71 | """ 72 | elif industry == "legal": 73 | prompt = f""" 74 | Develop a personal homepage for a professional in the legal industry. 75 | Emphasize their legal expertise, successful case histories, and professional certifications. 76 | The page should be formal and authoritative, with sections for client testimonials and published legal opinions to enhance their professional image. 77 | """ 78 | elif industry == "manufacturing": 79 | prompt = f""" 80 | Create a personal homepage for a professional in the manufacturing industry. 81 | Highlight their technical expertise, process improvements, and contributions to product development. 82 | Include sections for certifications, project case studies, and a clean, efficient design that showcases their industry proficiency. 83 | """ 84 | elif industry == "public_service": 85 | prompt = f""" 86 | Design a personal homepage for a public service professional. 87 | Focus on showcasing their community impact, policy contributions, and leadership in public initiatives. 88 | The design should be approachable and convey a strong commitment to social responsibility. 89 | """ 90 | else: 91 | prompt = f""" 92 | Create a generic personal homepage. 93 | Emphasize professional accomplishments and career highlights, with sections tailored to their field, ensuring the content reflects their unique characteristics. 94 | """ 95 | 96 | if language == "en": 97 | from .plan_prompts_en import plan_output_format_prompt,original_page_template,plan_output_format_prompt_local_img,local_img_storage_page_template 98 | if img and (basic_information or academic_achievements or experience or professional_skills): 99 | prompt = (f"Your task is to determine which modules or pages we need to create to design a personal homepage for the user. The information provided can be publicly displayed, so there is no need to deliberately avoid privacy issues. Please ensure that the page content is rich. The information typically provided by the user includes the following:" 100 | f"Basic Information: {basic_information}, including name, contact details, alma mater, study duration, major, degree, etc. Since this is a personal homepage for self-use, there is no need to consider sensitive information;" 101 | f"Academic Achievements: {academic_achievements}, providing text content for academic achievements, such as published papers, patents (title, summary, authors, links), etc.; " 102 | f"Work/Project Experience: {experience}, mainly introducing project experiences, competition experiences, and work history; " 103 | f"Professional Skills: {professional_skills}; " 104 | "The image above is a reference website we have provided. You need to imitate this image, but not copy it entirely; instead, design a brand new website based on the task you need to complete. Remember, you should maintain a consistent style across all pages, so be sure to describe the page_style in detail to ensure consistency between pages (by first describing a common style, then detailing the different styles for each page);" 105 | "It is particularly emphasized that the information I provided must be displayed completely and correctly. The educational background does not need a separate page; it should only be displayed in the personal information section on the homepage;" 106 | "Finally, the personal homepage must respect the information provided by the user. Please ensure that all user-provided information is displayed accurately and based on factual content! This is very important!") 107 | elif img: 108 | prompt = "Your task is to design which pages we should create to design the website based on the website images I provided you.Remember, you need to keep the style consistent between each web page, that is, describe the page_style in detail to ensure the consistency of style between pages (describe the commonality through a common style, and then describe the different styles of each page)" 109 | else: 110 | prompt = (f"Your task is to determine which pages we need to create to design a personal homepage for the user. The information provided can be publicly displayed, so there is no need to deliberately avoid privacy issues. Please ensure that the page content is rich. The information typically provided by the user includes the following:" 111 | f"Basic Information: {basic_information}, including name, contact details, alma mater, study duration, major, degree, etc. Since this is a personal homepage for self-use, there is no need to consider sensitive information; " 112 | f"Academic Achievements: {academic_achievements}, providing text content for academic achievements, such as published papers, patents (title, summary, authors, links), etc.; " 113 | f"Work/Project Experience: {experience}, mainly introducing project experiences, competition experiences, and work history; " 114 | f"Professional Skills: {professional_skills}; " 115 | "It is particularly emphasized that the information I provided must be displayed completely and correctly, with no need to avoid personal sensitive information. The educational background does not need a separate page; it should only be displayed in the personal information section on the homepage;" 116 | "Remember, you should maintain a consistent style across all pages, so be sure to describe the page_style in detail to ensure consistency between pages (by first describing a common style, then detailing the different styles for each page);" 117 | "Finally, the personal homepage must respect the information provided by the user. Please ensure that all user-provided information is displayed accurately and based on factual content! This is very important!") 118 | feedback = f"The user's requirements on the website(Very important! You must pay extra attention to the content here and meet user's needs) is : {feedback}" if feedback else "" 119 | elif language == "zh": 120 | from .plan_prompts_zh import plan_output_format_prompt,original_page_template,plan_output_format_prompt_local_img,local_img_storage_page_template 121 | if img and (basic_information or academic_achievements or experience or professional_skills): 122 | prompt = (f"你的任务是确定我们需要创建哪些模块或页面来设计用户自用的个人主页,所提供信息均可以公开展示,因此不需要刻意规避隐私问题,尽量保证页面内容丰满,用户提供信息一般包括以下内容:" 123 | f"基本信息:{basic_information},包括姓名、联系方式、毕业院校、在读时间、所学专业,学位等信息,因为是自用个人主页,无需考虑信息敏感问题;" 124 | f"学术成果:{academic_achievements},提供学术成果的文本内容,如发表论文,专利(题目,内容简介,作者,链接)等; " 125 | f"工作/项目经历:{experience},主要介绍项目经历,比赛经历和工作经历; " 126 | f"专业技能:{professional_skills}; " 127 | "上方的图片是我们提供的参考网站。你需要根据该图片进行模仿,但不是完全照搬,而是根据你需要完成的任务设计一个全新的网站。记住,你要保持各个网页之间的风格统一,即详细描述page_style来保证页面之间的风格一致(通过一段共性style描述共性,再描述各个页面的不同style);" 128 | "特别强调,要把我提供的信息完整正确的展示上去,教育背景不需要单独一页,仅在主页个人信息部分展示一下即可;" 129 | "最后,个人主页一定要尊重用户提供的信息,请根据已知信息准确且基于事实的要完整正确地展示用户提供的所有信息!这很重要!") 130 | elif img: 131 | prompt = "你的任务是确定我们需要创建哪些页面来设计基于我提供的网站图片的个人主页。(需要完全根据图片设计,尽量做到一致)。记住,你要保持各个网页之间的风格统一,即详细描述page_style来保证页面之间的风格一致(通过一段共性style描述共性,再描述各个页面的不同style)" 132 | else: 133 | prompt = ( 134 | f"你的任务是确定我们需要创建哪些页面来设计用户自用的的个人主页,所提供信息均可以公开展示,因此不需要刻意规避隐私问题,尽量保证页面内容丰满,用户提供信息一般包括以下内容:" 135 | f"基本信息:{basic_information},包括姓名、联系方式、毕业院校、在读时间、所学专业,学位等信息,因为是自用个人主页,无需考虑信息敏感问题; " 136 | f"学术成果:{academic_achievements},提供学术成果的文本内容,如发表论文,专利(题目,内容简介,作者,链接)等; " 137 | f"工作/项目经历:{experience},主要介绍项目经历,比赛经历和工作经历; " 138 | f"专业技能:{professional_skills}; " 139 | "特别强调,要把我提供的信息完整正确的展示上去,没有规避个人敏感信息的需要,教育背景不需要单独一页,仅在主页个人信息部分展示一下即可;" 140 | "记住,你要保持各个网页之间的风格统一,即详细描述page_style来保证页面之间的风格一致(通过一段共性style描述共性,再描述各个页面的不同style);" 141 | "最后,个人主页一定要尊重用户提供的信息,请根据已知信息准确且基于事实的完整正确地展示用户提供的所有信息!这很重要!") 142 | feedback = f"用户对个人主页的需求(非常重要!你必须特别注意这里的内容,并满足用户的需求)是:{feedback}" if feedback else "" 143 | if local_img_storage: 144 | prompt += plan_output_format_prompt_local_img.format(local_img_storage = local_img_storage,page_template = local_img_storage_page_template) 145 | else: 146 | prompt += plan_output_format_prompt.format(page_template = original_page_template) 147 | return prompt 148 | 149 | #根据输入的任务、页面信息、用户反馈、语言和本地图片存储,生成用于优化网页的提示信息。 150 | def get_refine_page_prompt(task,page_info,css_frame = None,feedback = "",language = "en",local_img_storage = []): 151 | if language == "en": 152 | from .plan_prompts_en import refine_page_prompt,original_page_example,local_img_storage_page_example,refine_page_local_img_prompt 153 | task_info = f"The requirements of the website is {task}" if task else "" 154 | feedback = f"The user feedback on the webpage(Very important! You must pay extra attention to the content here and prioritize making modifications to it) is : {feedback}" if feedback else "" 155 | elif language == "zh": 156 | from .plan_prompts_zh import refine_page_prompt,original_page_example,local_img_storage_page_example,refine_page_local_img_prompt 157 | task_info = f"个人主页的需求是{task}" if task else "" 158 | feedback = f"用户对个人主页的反馈(非常重要!你必须特别注意这里的内容,并优先进行修改)是:{feedback}" if feedback else "" 159 | if local_img_storage: 160 | page_example = local_img_storage_page_example 161 | prompt = refine_page_local_img_prompt.format(task_info = task_info,page_info = page_info,page_example = page_example,feedback = feedback,local_img_storage = local_img_storage) 162 | else: 163 | page_example = original_page_example 164 | prompt = refine_page_prompt.format(task_info = task_info,page_info = page_info,page_example = page_example,feedback = feedback) 165 | return prompt 166 | 167 | #用于生成提示信息,根据不同的语言和是否有本地图片存储来选择不同的模板格式,生成用于完成页面操作的提示信息。 168 | def get_page_complete_prompt(task= "",other_pages_info="",page_info="",feedback = "" ,language = "en",local_img_storage = []): 169 | if language == "en": 170 | from .plan_prompts_en import page_complete_prompt,page_complete_prompt_local_img 171 | feedback = f"The user feedback on how to complete the webpage(Very important! You must pay extra attention to the content here and prioritize making modifications to it) is : {feedback}" if feedback else "" 172 | task_info = f"The requirements of the website is {task}" if task else "" 173 | elif language == "zh": 174 | from .plan_prompts_zh import page_complete_prompt,page_complete_prompt_local_img 175 | feedback = f"用户对如何完成个人主页的反馈(非常重要!你必须特别注意这里的内容,并优先进行修改)是:{feedback}" if feedback else "" 176 | task_info = f"网站的需求是{task}" if task else "" 177 | if local_img_storage: #非空,使用包含本地图片存储的提示模板 178 | prompt = page_complete_prompt_local_img.format(task_info = task_info,other_pages_info = other_pages_info,page_info = page_info,feedback = feedback,local_img_storage = local_img_storage) 179 | else: 180 | prompt = page_complete_prompt.format(task_info = task_info,other_pages_info = other_pages_info,page_info = page_info,feedback = feedback) 181 | return prompt 182 | 183 | if __name__ == "__main__": 184 | print(get_plan_prompt("test",img = True)) 185 | print(get_refine_page_prompt("test","test")) 186 | print(get_page_complete_prompt("test","test")) -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 张三 - 个人主页 8 | 9 | 10 | 11 | 12 | 13 | 61 | 62 | 63 | 64 | 70 | 71 | 129 | 130 |
131 | 132 |
133 |
134 |

135 | 张三 136 |

137 |

138 | 清华大学计算机系硕士 139 |

140 | 141 | zhangsan@tsinghua.edu.com 142 | 143 |
144 |
145 | 146 |
147 |

148 | 研究方向 149 |

150 |
151 | 152 | 多模态大模型 153 | 154 | 155 | Embodied AI 156 | 157 | 158 | 计算机视觉 159 | 160 | 161 | 网络可解释性 162 | 163 |
164 |
165 | 166 |
167 |
168 | 169 |
170 |

171 | 学术成果 172 |

173 |
174 |

175 | CVPR 2024 176 |

177 |

178 | 提出一种新型元能力解耦框架,显著提升多模态模型在零样本任务中的泛化能力。 179 |

180 | 181 | 元能力解耦 182 | 183 |
184 | 185 | 查看更多 186 | 187 |
188 | 189 |
190 |

191 | 项目经历 192 |

193 | 227 | 228 | 查看更多 229 | 230 |
231 | 232 |
233 |

234 | 专业技能 235 |

236 |
237 |
238 | 239 | 语言能力: 240 | 241 | 242 | 雅思7.5 243 | 244 |
245 |
246 | 247 | 技术栈: 248 | 249 |
250 | 251 | PyTorch 252 | 253 | 254 | Transformer 255 | 256 | 257 | CLIP 258 | 259 | 260 | LangChain 261 | 262 |
263 |
264 |
265 | 266 | 查看更多 267 | 268 |
269 |
270 |
271 | 272 |
273 |

274 | 联系我 275 |

276 | 277 | 发送邮件 278 | 279 |
280 |
281 | 379 | 380 | 381 | -------------------------------------------------------------------------------- /demo/publications.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 张三 - 论文发表 8 | 9 | 11 | 12 | 117 | 118 | 119 | 120 |
121 | BibTeX 已复制! 122 |
123 |
124 | 125 |
126 |

127 | 张三 · 论文发表 128 |

129 | 130 | 返回主页 131 | 132 |
133 | 134 |
135 | 140 | 145 |
146 | 147 | 188 | 189 |
190 | 191 |
192 |
193 | 194 | CVPR 2024 195 | 196 | IEEE/CVF CVPR 197 | 198 | 199 |
200 |

201 | 202 | 元能力解耦驱动的多模态大模型架构 203 | 204 |

205 |

206 | 张三 207 |

208 |
209 |

210 | 本文提出一种新型多模态大模型架构,通过元能力解耦机制实现视觉与语言表征的高效融合,在多个基准任务上取得SOTA性能。 211 |

212 |
213 | 216 | 221 |
222 |
223 | 229 |
230 | 231 |
232 |
233 | 234 | NeurIPS 2023 235 | 236 | NeurIPS 237 | 238 | 239 |
240 |

241 | 242 | 具身智能中的跨模态策略迁移框架 243 | 244 |

245 |

246 | 张三 247 |

248 |
249 |

250 | 针对具身智能场景,我们设计了一种跨模态策略迁移框架,显著提升智能体在未知环境中的泛化能力与任务完成效率。 251 |

252 |
253 | 256 | 261 |
262 |
263 | 269 |
270 | 271 |
272 |
273 | 274 | ICLR 2023 275 | 276 | ICLR 277 | 278 | 279 |
280 |

281 | 282 | 基于可解释注意力的视觉-语言对齐机制 283 | 284 |

285 |

286 | 张三 287 |

288 |
289 |

290 | 提出一种可解释的注意力机制,用于增强视觉与语言模态间的语义对齐,并提供人类可理解的决策依据。 291 |

292 |
293 | 296 | 301 |
302 |
303 | 309 |
310 | 311 |
312 |
313 | 314 | AAAI 2022 315 | 316 | AAAI 317 | 318 | 319 |
320 |

321 | 322 | 网络可解释性驱动的鲁棒视觉识别 323 | 324 |

325 |

326 | 张三 327 |

328 |
329 |

330 | 利用网络可解释性技术构建鲁棒视觉识别系统,在对抗攻击和分布外数据下保持高准确率。 331 |

332 |
333 | 336 | 341 |
342 |
343 | 349 |
350 | 351 |
352 |
353 | 354 | IJCAI 2021 355 | 356 | IJCAI 357 | 358 | 359 |
360 |

361 | 362 | 多模态表示学习中的结构化先验建模 363 | 364 |

365 |

366 | 张三 367 |

368 |
369 |

370 | 引入结构化先验知识到多模态表示学习中,有效提升模型在低资源场景下的泛化能力。 371 |

372 |
373 | 376 | 381 |
382 |
383 | 389 |
390 |
391 | 392 |
393 | © 2024 张三. 保留所有权利。 394 |
395 |
396 | 479 | 480 | 481 | -------------------------------------------------------------------------------- /demo/projects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 项目经历 - 个人主页 8 | 9 | 10 | 11 | 135 | 136 | 137 | 138 | 141 | 142 |
143 | 144 | 返回主页 145 | 146 | 149 | 152 | 155 | 158 |
159 | 160 |
161 | 162 |
163 |
164 |
165 | 166 | 2023.06 – 2024.03 167 | 168 |
169 |
170 |
171 |
172 | 173 | 字节跳动 · AI Lab 174 | 175 |
176 | 字节跳动 AI实验室:聚焦大模型基础研究与应用落地 177 |
178 |
179 |
180 |
181 | 182 | 多模态大模型研发工程师 183 | 184 |
185 |

186 | 基于多模态思维链的视觉问答系统优化 187 |

188 |
189 |
190 |
191 | 192 | 193 |
194 |

195 | 挑战 196 |

197 |

198 | 现有VQA模型在复杂推理场景下准确率不足,缺乏对图像与文本语义的深度对齐能力。 199 |

200 |
201 |
202 |
203 |
204 |
205 | 206 | 207 |
208 |

209 | 方法 210 |

211 |

212 | 设计 213 | 214 | 多模态思维链(Multimodal Chain-of-Thought) 215 | 216 | 框架,结合 217 | 218 | LLaMA 219 | 220 | 语言模型与 221 | 222 | BLIP-2 223 | 224 | 视觉编码器,引入 225 | 226 | 知识图谱对齐 227 | 228 | 机制增强语义理解。 229 |

230 |
231 | LLaMA · BLIP-2 · PyTorch · HuggingFace Transformers · Knowledge Graph Alignment 232 |
233 |
234 |
235 |
236 |
237 |
238 | 239 | 240 |
241 |

242 | 成果 243 |

244 |

245 | 在ScienceQA数据集上准确率提升 246 | 247 | 12.3% 248 | 249 | ,推理速度优化 250 | 251 | 35% 252 | 253 | ;相关技术已集成至公司内部AI平台。 254 |

255 |
256 | 261 |
262 | 263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 | 273 |
274 |
275 |
276 | 277 | 2022.03 – 2023.05 278 | 279 |
280 |
281 |
282 |
283 | 284 | 腾讯 · AI Lab 285 | 286 |
287 | 腾讯 AI Lab:致力于人工智能前沿技术研究与产业应用 288 |
289 |
290 |
291 |
292 | 293 | 具身智能算法研究员 294 | 295 |
296 |

297 | 面向机器人任务的多模态指令理解系统 298 |

299 |
300 |
301 |
302 | 303 | 304 |
305 |

306 | 挑战 307 |

308 |

309 | 机器人难以理解自然语言指令中的空间关系与动作序列,导致任务执行失败率高。 310 |

311 |
312 |
313 |
314 |
315 |
316 | 317 | 318 |
319 |

320 | 方法 321 |

322 |

323 | 构建端到端 324 | 325 | 具身智能 326 | 327 | 模型,融合 328 | 329 | LLaVA 330 | 331 | 视觉语言模型与3D场景图,设计 332 | 333 | 动作-语言对齐损失函数 334 | 335 | 。 336 |

337 |
338 | LLaVA · ROS · 3D Scene Graph · PyTorch3D · NVIDIA Isaac Sim 339 |
340 |
341 |
342 |
343 |
344 |
345 | 346 | 347 |
348 |

349 | 成果 350 |

351 |

352 | 在ALFRED基准测试中任务成功率提升至 353 | 354 | 78.5% 355 | 356 | (SOTA +9.2%),获腾讯AI Lab年度创新奖。 357 |

358 |
359 | 364 |
365 | 366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 | 376 |
377 |
378 |
379 | 380 | 2021.07 – 2022.02 381 | 382 |
383 |
384 |
385 |
386 | 387 | 华为 · 诺亚方舟实验室 388 | 389 |
390 | 华为诺亚方舟实验室:专注AI基础研究与行业解决方案 391 |
392 |
393 |
394 |
395 | 396 | 多模态算法实习生 397 | 398 |
399 |

400 | 跨模态检索系统的轻量化部署 401 |

402 |
403 |
404 |
405 | 406 | 407 |
408 |

409 | 挑战 410 |

411 |

412 | 现有跨模态模型参数量大、推理延迟高,难以在边缘设备部署。 413 |

414 |
415 |
416 |
417 |
418 |
419 | 420 | 421 |
422 |

423 | 方法 424 |

425 |

426 | 基于 427 | 428 | ChatGLM 429 | 430 | 进行蒸馏压缩,结合 431 | 432 | 知识图谱对齐 433 | 434 | 保留语义信息,使用MindSpore Lite实现端侧推理。 435 |

436 |
437 | ChatGLM · Knowledge Distillation · MindSpore Lite · ONNX · Huawei Ascend 438 |
439 |
440 |
441 |
442 |
443 |
444 | 445 | 446 |
447 |

448 | 成果 449 |

450 |

451 | 模型体积压缩至原版 452 | 453 | 1/8 454 | 455 | ,推理速度提升 456 | 457 | 5.2倍 458 | 459 | ,成功部署于华为手机端搜索功能。 460 |

461 |
462 | 467 |
468 | 469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 | 515 | 516 | 517 | -------------------------------------------------------------------------------- /HomepageDesign.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import json 4 | import shutil 5 | from bs4 import BeautifulSoup 6 | from base_agent import BaseAgent 7 | from webserver import Webserver 8 | from utils import write_file,get_content_between_a_b,wrap_func,cal_cost,extract_img_from_html,create_file,extract,modify_input_dict,extract_dimensions,get_html_css_from_response,get_all_files_in_dir 9 | from PIL import Image 10 | import re 11 | import argparse 12 | import random 13 | from prompts import * 14 | 15 | 16 | class HomepageDesignAgent(BaseAgent): 17 | def __init__(self, model="gpt4o-0513",save_file = "saves/",**kwargs) -> None: 18 | super().__init__(model) 19 | self.task = {"img":None,"basic_information":None, "academic_achievements":None, "experience":None, "professional_skills":None} 20 | self.task_queue = [] 21 | self.memory = [] 22 | with open("logs/token.json", "r") as f: 23 | tokens = json.load(f) 24 | self.begin_prompt_tokens = tokens[self.model][0] 25 | self.begin_completion_tokens = tokens[self.model][1] 26 | self.max_cost_prompt_tokens = 1000000 27 | self.max_cost_completion_tokens = 1000000 28 | self.webserver = None 29 | self.webserver = Webserver(save_file) 30 | self.user_feedback = None 31 | if not os.path.exists(save_file): 32 | os.makedirs(save_file) 33 | 34 | self.css_frame = "Tailwind" 35 | self.status = "student" 36 | self.industry = "tech" 37 | self.gen_img = "Gen" 38 | self.local_img_storage = [] 39 | self.local_img_storage_path = None 40 | self.local_img_storage_copy = [] 41 | self.local_img_storage_en = [] 42 | self.local_img_storage_zh = [] 43 | self.language = "en" 44 | self.save_file = save_file 45 | self.total_prompt_cost_tokens = 0 46 | self.total_completion_cost_tokens = 0 47 | self.refine_times = 2 48 | self.vision = True 49 | 50 | def act(self,**kwargs): 51 | self.publish_task(**kwargs) 52 | self.deal_task() 53 | return "done" 54 | 55 | def deal_task(self): 56 | print("Planning the website...") 57 | self.plan() 58 | print("Successfully planned the website.") 59 | pages = self.task_queue 60 | asyncio.run(self.deal_task_async(pages)) 61 | 62 | async def deal_task_async(self,pages): 63 | tasks = [] 64 | for page_info in pages: 65 | task = self.generate_and_refine_single_page_async(page_info) 66 | tasks.append(task) 67 | await asyncio.gather(*tasks) 68 | 69 | def change_save_file(self,save_file): 70 | self.save_file = save_file 71 | self.webserver.save_path = save_file 72 | print(f"Attempting to create directory: {save_file}") 73 | if not os.path.exists(save_file): 74 | os.makedirs(save_file) 75 | 76 | def chat(self, query, **kwargs): 77 | self.memory.append({"role":"user","content":query}) 78 | if len(self.memory) > 10: 79 | self.memory = self.memory[-10:] 80 | response = wrap_func(self.get_answer, messages=self.memory, **kwargs) 81 | if not response: 82 | print("Failed to get the response.") 83 | return 84 | self.memory.append({"role":"assistant","content":response}) 85 | return response 86 | 87 | def publish_task(self,img = None, basic_information = None, academic_achievements = None, experience=None, professional_skills=None,feedback=None,refine_times= None, max_cost_prompt_tokens = 1000000,max_cost_completion_tokens = 1000000): 88 | self.task = {"img":img,"basic_information":basic_information, "academic_achievements":academic_achievements, "experience":experience, "professional_skills":professional_skills, "feedback":feedback} 89 | self.max_cost_prompt_tokens = max_cost_prompt_tokens 90 | self.max_cost_completion_tokens = max_cost_completion_tokens 91 | self.refine_times = refine_times if refine_times else 2 92 | 93 | 94 | def check_cost(self): 95 | with open("logs/token.json", "r") as f: 96 | tokens = json.load(f) 97 | current_prompt_tokens = tokens[self.model][0] 98 | current_completion_tokens = tokens[self.model][1] 99 | self.total_prompt_cost_tokens = current_prompt_tokens - self.begin_prompt_tokens 100 | self.total_completion_cost_tokens = current_completion_tokens - self.begin_completion_tokens 101 | if self.total_prompt_cost_tokens > self.max_cost_prompt_tokens or self.total_completion_cost_tokens > self.max_cost_completion_tokens: 102 | return False 103 | return True 104 | 105 | 106 | def plan(self): 107 | if os.path.exists(os.path.join(self.save_file,"pages.json")): 108 | with open(os.path.join(self.save_file,"pages.json"), "r",encoding='utf-8') as f: 109 | self.task_queue = json.load(f) 110 | return self.task_queue 111 | basic_information = self.task["basic_information"] 112 | academic_achievements = self.task["academic_achievements"] 113 | experience = self.task["experience"] 114 | professional_skills = self.task["professional_skills"] 115 | img = self.task["img"] 116 | feedback = self.user_feedback if self.user_feedback else "" 117 | prompt = get_plan_prompt(basic_information=basic_information, 118 | academic_achievements=academic_achievements, 119 | experience=experience, 120 | professional_skills=professional_skills, 121 | refine_times= None, 122 | img=img, 123 | css_frame=self.css_frame, 124 | feedback=feedback, 125 | language=self.language, 126 | local_img_storage=self.local_img_storage) 127 | if img: 128 | messages = [ 129 | {"role":"user","content":[ 130 | {"type":"image","url":img}, 131 | {"type":"text","text":prompt}, 132 | ]}, 133 | ] 134 | else: 135 | messages = [ 136 | {"role":"user","content":[ 137 | {"type":"text","text":prompt}, 138 | ]}, 139 | ] 140 | try_cnt = 0 141 | print("Planning the website...") 142 | while try_cnt < 3: 143 | response = wrap_func(self.get_answer, messages=messages,wrap_text="Planning now...") 144 | try: 145 | pages = extract(response,"designed_pages") 146 | pages = pages.strip() 147 | pages = json.loads(pages) 148 | if self.css_frame: 149 | for page in pages: 150 | if "css_name" in page: 151 | del page["css_name"] 152 | break 153 | except: 154 | print(response) 155 | try_cnt += 1 156 | print("Failed to get the designed pages. try again") 157 | if try_cnt == 3: 158 | assert False, "Failed to get the designed pages." 159 | 160 | with open(os.path.join(self.save_file,"pages.json"), "w",encoding='utf-8') as f: 161 | json.dump(pages,f) 162 | new_pages = asyncio.run(self.refine_pages_async(pages)) 163 | new_pages = sorted(new_pages,key = lambda x:x[1]) 164 | new_pages = [page[0] for page in new_pages] 165 | self.task_queue = new_pages 166 | print("The pages have been designed.") 167 | save_path = os.path.join(self.save_file,"pages.json") 168 | with open(save_path, "w",encoding='utf-8') as f: 169 | json.dump(self.task_queue,f) 170 | return self.task_queue 171 | 172 | async def refine_pages_async(self,pages): 173 | tasks = [] 174 | for i,page in enumerate(pages): 175 | task = self.refine_page_async(i,page) 176 | tasks.append(task) 177 | results = await asyncio.gather(*tasks) 178 | return results 179 | 180 | 181 | def get_modified_page_from_response(self,response,page_info): 182 | modified_page = extract(response,"modified_page") 183 | if "```python" in modified_page: 184 | modified_page = get_content_between_a_b("```python","```",modified_page) 185 | if "```json" in modified_page: 186 | modified_page = get_content_between_a_b("```json","```",modified_page) 187 | modified_page = modified_page.replace("True","true").replace("False","false") 188 | try: 189 | modified_page = json.loads(modified_page) 190 | except: 191 | modified_page = modify_input_dict(modified_page) 192 | try: 193 | modified_page = json.loads(modified_page) 194 | except: 195 | print("Failed to get the modified page. Please check the format.") 196 | print(modified_page) 197 | modified_page = page_info 198 | if self.css_frame: 199 | if "css_name" in modified_page: 200 | del modified_page["css_name"] 201 | return modified_page 202 | 203 | 204 | def refine_page(self,page_info,feedback = ""): 205 | 206 | basic_information = self.task.get("basic_information", "") 207 | academic_achievements = self.task.get("academic_achievements", "") 208 | experience = self.task.get("experience", "") 209 | professional_skills = self.task.get("professional_skills", "") 210 | 211 | 212 | combined_text = ( 213 | f"Basic Information: {basic_information}\n" 214 | f"academic_achievements: {academic_achievements}\n" 215 | f"Experience: {experience}\n" 216 | f"Professional Skills: {professional_skills}" 217 | ) 218 | 219 | task = combined_text 220 | 221 | prompt = get_refine_page_prompt(task=task,page_info=page_info,css_frame=self.css_frame,feedback=feedback,language=self.language,local_img_storage=self.local_img_storage) 222 | messages = [ 223 | {"role":"user","content":[ 224 | {"type":"text","text":prompt}, 225 | ]}, 226 | ] 227 | response = wrap_func(self.get_answer, messages=messages,wrap_text="Refining page now...") 228 | if not response: 229 | print("Failed to get the response.") 230 | return 231 | modified_page = self.get_modified_page_from_response(response,page_info) 232 | print(f"Page has been refined.") 233 | return modified_page 234 | 235 | async def refine_page_async(self,idx,page_info,feedback = ""): 236 | 237 | basic_information = self.task.get("basic_information", "") 238 | academic_achievements = self.task.get("academic_achievements", "") 239 | experience = self.task.get("experience", "") 240 | professional_skills = self.task.get("professional_skills", "") 241 | 242 | 243 | combined_text = ( 244 | f"Basic Information: {basic_information}\n" 245 | f"academic_achievements: {academic_achievements}\n" 246 | f"Experience: {experience}\n" 247 | f"Professional Skills: {professional_skills}" 248 | ) 249 | 250 | task = combined_text 251 | 252 | prompt = get_refine_page_prompt(task=task,page_info=page_info,css_frame=self.css_frame,feedback=feedback,language=self.language,local_img_storage=self.local_img_storage) 253 | messages = [ 254 | {"role":"user","content":[ 255 | {"type":"text","text":prompt}, 256 | ]}, 257 | ] 258 | response = await self.get_answer_async(messages=messages) 259 | if not response: 260 | print("Failed to get the response.") 261 | return 262 | modified_page = self.get_modified_page_from_response(response,page_info) 263 | print(f"Page {idx+1} has been refined.") 264 | return [modified_page,idx] 265 | 266 | 267 | def get_write_original_website_messages(self,page_info): 268 | img = self.task["img"] if page_info["is_main_page"] else None 269 | basic_information = self.task["basic_information"] 270 | academic_achievements = self.task["academic_achievements"] 271 | experience = self.task["experience"] 272 | professional_skills = self.task["professional_skills"] 273 | feedback = self.user_feedback if self.user_feedback else "" 274 | prompt = get_write_original_website_prompt(basic_information=basic_information, academic_achievements=academic_achievements, experience=experience, professional_skills=professional_skills,img=img,page_info=page_info,css_frame=self.css_frame,feedback=feedback,language=self.language) 275 | if img: 276 | messages = [ 277 | {"role":"user","content":[ 278 | {"type":"image","url":img}, 279 | {"type":"text","text":prompt}, 280 | ]}, 281 | ] 282 | else: 283 | messages = [ 284 | {"role":"user","content":[ 285 | {"type":"text","text":prompt}, 286 | ]}, 287 | ] 288 | return messages 289 | 290 | def write_original_website(self,page_info): 291 | if "html_name" not in page_info: 292 | print("The page_info is not correct.") 293 | return 294 | messages = self.get_write_original_website_messages(page_info) 295 | html,css,cnt = None,None,0 296 | while not html and cnt < 5 or (not self.css_frame and not css): 297 | response = wrap_func(self.get_answer, messages=messages,wrap_text="Writing original website now...") 298 | if not response: 299 | print("Failed to get the response.") 300 | return 301 | html,css = get_html_css_from_response(response) 302 | cnt += 1 303 | if not html or (not self.css_frame and not css): 304 | print(response) 305 | assert False, "Failed to get the original website." 306 | self.update_html_css(page_info,html,css) 307 | 308 | async def write_original_website_async(self,page_info): 309 | if "html_name" not in page_info: 310 | print("The page_info is not correct.") 311 | return 312 | messages = self.get_write_original_website_messages(page_info) 313 | html,css,cnt = None,None,0 314 | while not html and cnt < 5 or (not self.css_frame and not css): 315 | response = await self.get_answer_async(messages=messages) 316 | if not response: 317 | print("Failed to get the response.") 318 | return 319 | html,css = get_html_css_from_response(response) 320 | cnt += 1 321 | if not html or (not self.css_frame and not css): 322 | print(response) 323 | assert False, "Failed to get the original website." 324 | await self.update_html_css_async(page_info,html,css) 325 | page_name = page_info["html_name"] 326 | print(f"Original website has been written to {page_name}") 327 | 328 | def get_refine_messages(self,page_info): 329 | html_name = page_info["html_name"] 330 | css_name = page_info["css_name"] if "css_name" in page_info else None 331 | #img = self.task["img"] if page_info["is_main_page"] else None 332 | img = self.task["img"] if page_info.get("is_main_page", False) else None 333 | basic_information = self.task["basic_information"] 334 | academic_achievements = self.task["academic_achievements"] 335 | experience = self.task["experience"] 336 | professional_skills = self.task["professional_skills"] 337 | css_frame = self.css_frame 338 | html_path = os.path.join(self.save_file,html_name) 339 | css_path = os.path.join(self.save_file,css_name) if css_name else None 340 | 341 | html_code = open(html_path,encoding='utf-8').read() 342 | css_code = open(css_path,encoding='utf-8').read() if css_name else None 343 | page_name = html_name.split(".")[0] 344 | page_img_path = os.path.join(self.save_file,f"{page_name}.png") 345 | 346 | feedback = self.user_feedback if self.user_feedback else "" 347 | 348 | if len(self.local_img_storage) > 10: 349 | local_img_storage = random.sample(self.local_img_storage,10) 350 | else: 351 | local_img_storage = self.local_img_storage 352 | prompt = get_refine_prompt(basic_information=basic_information, academic_achievements=academic_achievements, experience=experience, professional_skills=professional_skills,img=img,html_code=html_code,css_code=css_code,feedback=feedback,page_info=page_info,css_frame=css_frame,language=self.language,local_img_storage=local_img_storage,vision=self.vision) 353 | if self.vision == False: 354 | messages = [ 355 | {"role":"user","content":[ 356 | {"type":"text","text":prompt}, 357 | ]}, 358 | ] 359 | elif img: 360 | messages = [ 361 | {"role":"user","content":[ 362 | {"type":"image","url":img}, 363 | {"type":"image","url":page_img_path}, 364 | {"type":"text","text":prompt}, 365 | ]}, 366 | ] 367 | else: 368 | messages = [ 369 | {"role":"user","content":[ 370 | {"type":"image","url":page_img_path}, 371 | {"type":"text","text":prompt}, 372 | ]}, 373 | ] 374 | return messages 375 | 376 | def refine(self,page_info): 377 | if "html_name" not in page_info: 378 | print("The page_info is not correct.") 379 | return 380 | messages = self.get_refine_messages(page_info) 381 | response = wrap_func(self.get_answer, messages=messages,wrap_text="Refining now...") 382 | if not response: 383 | print("Failed to get the response.") 384 | return 385 | html_code,css_code = get_html_css_from_response(response) 386 | self.update_html_css(page_info,html_code,css_code) 387 | 388 | async def refine_async(self,page_info): 389 | if "html_name" not in page_info: 390 | print("The page_info is not correct.") 391 | return 392 | messages = self.get_refine_messages(page_info) 393 | response = await self.get_answer_async(messages=messages) 394 | if not response: 395 | print("Failed to get the response.") 396 | return 397 | html_code,css_code = get_html_css_from_response(response) 398 | await self.update_html_css_async(page_info,html_code,css_code) 399 | page_name = page_info["html_name"] 400 | print(f"Page {page_name} has been refined.") 401 | 402 | 403 | async def generate_and_refine_single_page_async(self,page_info): 404 | print(f"Generating and refining page {page_info['html_name']}") 405 | await self.write_original_website_async(page_info) 406 | print(f"Original website has been written.") 407 | for _ in range(self.refine_times): 408 | print(f"Refining page {page_info['html_name']} {_} times.") 409 | await self.refine_async(page_info) 410 | print(f"Page {page_info['html_name']} has been refined.") 411 | 412 | 413 | def update_html_css(self,page_info,html,css): 414 | html_name = page_info["html_name"] 415 | css_name = page_info["css_name"] if "css_name" in page_info else None 416 | print(self.gen_img) 417 | if not self.css_frame: 418 | if not css_name: 419 | print("The css_name is not provided.") 420 | return 421 | write_file(os.path.join(self.save_file,html_name), html) 422 | write_file(os.path.join(self.save_file,css_name), css) 423 | if self.gen_img == "Gen": 424 | html_code = asyncio.run(self.add_imgs_async(html)) 425 | write_file(os.path.join(self.save_file,html_name), html_code) 426 | else: 427 | write_file(os.path.join(self.save_file,html_name), html) 428 | if self.gen_img == "Gen": 429 | html_code = asyncio.run(self.add_imgs_async(html)) 430 | write_file(os.path.join(self.save_file,html_name), html_code) 431 | page_name = html_name.split(".")[0] 432 | page_img_path = os.path.join(self.save_file,f"{page_name}.png") 433 | self.webserver.get_screenshot(os.path.join(self.save_file,html_name),page_img_path) 434 | print(f"Screenshot has been saved to {page_img_path}") 435 | 436 | async def update_html_css_async(self,page_info,html,css): 437 | html_name = page_info["html_name"] 438 | css_name = page_info["css_name"] if "css_name" in page_info else None 439 | if not self.css_frame: 440 | if not css_name: 441 | print("The css_name is not provided.") 442 | return 443 | 444 | write_file(os.path.join(self.save_file,html_name), html) 445 | write_file(os.path.join(self.save_file,css_name), css) 446 | if self.gen_img == "Gen": 447 | html_code = await self.add_imgs_async(html) 448 | write_file(os.path.join(self.save_file,html_name), html_code) 449 | else: 450 | write_file(os.path.join(self.save_file,html_name), html) 451 | if self.gen_img == "Gen": 452 | html_code = await self.add_imgs_async(html) 453 | write_file(os.path.join(self.save_file,html_name), html_code) 454 | page_name = html_name.split(".")[0] 455 | page_img_path = os.path.join(self.save_file,f"{page_name}.png") 456 | self.webserver.get_screenshot(os.path.join(self.save_file,html_name),page_img_path) 457 | print(f"Screenshot has been saved to {page_img_path}") 458 | 459 | 460 | def delete_all_backups(self,directory): 461 | backup_folder = os.path.join(directory, "backup") 462 | try: 463 | if os.path.exists(backup_folder): 464 | shutil.rmtree(backup_folder) 465 | print(f"已删除备用文件夹及其内容: {backup_folder}") 466 | else: 467 | print(f"备用文件夹 {backup_folder} 不存在。") 468 | except Exception as e: 469 | print(f"删除备用文件夹时出错: {e}") 470 | 471 | def complete_page(self,pages,idx): 472 | page_info = pages[idx] 473 | page_info = str(page_info) 474 | other_pages_info = "" 475 | for i,page in enumerate(pages): 476 | if i != idx: 477 | other_pages_info += f"Page {i+1}: {page}\n" 478 | other_pages_info = modify_input_dict(other_pages_info) 479 | page_info = modify_input_dict(page_info) 480 | feedback = self.user_feedback if self.user_feedback else "" 481 | basic_information = self.task.get("basic_information", "") 482 | academic_achievements = self.task.get("academic_achievements", "") 483 | experience = self.task.get("experience", "") 484 | professional_skills = self.task.get("professional_skills", "") 485 | combined_text = ( 486 | f"Basic Information: {basic_information}\n" 487 | f"academic_achievements: {academic_achievements}\n" 488 | f"Experience: {experience}\n" 489 | f"Professional Skills: {professional_skills}" 490 | ) 491 | task = combined_text if combined_text else "" 492 | prompt = get_page_complete_prompt(task = task,page_info=page_info,other_pages_info=other_pages_info,feedback=feedback,language=self.language,local_img_storage=self.local_img_storage) 493 | messages = [ 494 | {"role":"user","content":[ 495 | {"type":"text","text":prompt}, 496 | ]}, 497 | ] 498 | response = wrap_func(self.get_answer, messages=messages,wrap_text="Completing page now...") 499 | if not response: 500 | print("Failed to get the response.") 501 | return 502 | 503 | complete_page = extract(response,"completed_page") 504 | if "```python" in complete_page: 505 | complete_page = get_content_between_a_b("```python","```",complete_page) 506 | elif "```json" in complete_page: 507 | complete_page = get_content_between_a_b("```json","```",complete_page) 508 | try: 509 | complete_page = json.loads(complete_page) 510 | relationship = complete_page["relationship"] 511 | if isinstance(relationship,str): 512 | relationship = modify_input_dict(relationship) 513 | relationship = json.loads(relationship) 514 | complete_page["relationship"] = relationship 515 | pages[idx] = complete_page 516 | except: 517 | print("Failed to get the completed page. Please check the format.") 518 | print(complete_page) 519 | 520 | relationship = extract(response,"page_relationship") 521 | if "```python" in relationship: 522 | relationship = get_content_between_a_b("```python","```",relationship) 523 | elif "```json" in relationship: 524 | relationship = get_content_between_a_b("```json","```",relationship) 525 | try: 526 | relationship = json.loads(relationship) 527 | for page_relation in relationship: 528 | html_name = page_relation["html_name"] 529 | button_name = page_relation["button_name"] 530 | for i,page in enumerate(pages): 531 | if page["html_name"] == html_name: 532 | pages[i]["relationship"]["buttons"].append({"button_name": button_name,"jump_page":complete_page["html_name"]}) 533 | except: 534 | print("Failed to get the relationship. Please check the format.") 535 | print(relationship) 536 | 537 | return pages 538 | 539 | 540 | async def add_imgs_async(self,html_code): 541 | print("begin add imgs") 542 | soup = BeautifulSoup(html_code, "html.parser") 543 | images = soup.find_all("img") 544 | scripts = soup.find_all("script") 545 | tasks = [] 546 | 547 | tasks.append(self.add_imgs_to_background_async(html_code)) 548 | for image in images: 549 | tasks.append(self.add_img_async(image)) 550 | 551 | async def process_script(script): 552 | if script.string: 553 | script.string = await self.add_imgs_to_script_async(script.string) 554 | for script in scripts: 555 | tasks.append(process_script(script)) 556 | await asyncio.gather(*tasks) 557 | return soup.prettify() 558 | 559 | async def add_imgs_to_background_async(self,html_code): 560 | 561 | pattern = r"url\(['\"]?(.*?)['\"]?\)" 562 | 563 | matchs = re.findall(pattern, html_code) 564 | 565 | for match in matchs: 566 | image_filename = match 567 | if "png" in image_filename or "jpg" in image_filename or "jpeg" in image_filename: 568 | img_name = os.path.basename(image_filename).split(".")[0] 569 | img_path = os.path.join(self.save_file,f"{img_name}.png") 570 | if os.path.exists(img_path): 571 | continue 572 | basic_information = self.task.get("basic_information", "") 573 | academic_achievements = self.task.get("academic_achievements", "") 574 | experience = self.task.get("experience", "") 575 | professional_skills = self.task.get("professional_skills", "") 576 | combined_text = ( 577 | f"Basic Information: {basic_information}\n" 578 | f"academic_achievements: {academic_achievements}\n" 579 | f"Experience: {experience}\n" 580 | f"Professional Skills: {professional_skills}" 581 | ) 582 | text = combined_text if combined_text else "" 583 | if text: 584 | description = f"This is a image used in the background of the website. The website description is: {text}, The image name is {img_name}." 585 | else: 586 | description = f"This is a image used in the background of the website. The image name is {img_name}." 587 | img = await self.get_img_async(description) 588 | if img: 589 | create_file(img_path) 590 | img.save(img_path) 591 | print(f"Image {img_path} has been added to the folder.") 592 | 593 | 594 | 595 | async def add_imgs_to_script_async(self,script): 596 | pattern = re.compile(r'{[^}]*\bimgsrc:\s*\'([^\']*)\'[^}]*\balt:\s*\'([^\']*)\'[^}]*}') 597 | for match in pattern.finditer(script): 598 | imgsrc = match.group(1) 599 | alt = match.group(2) 600 | img = await self.get_img_async(alt) 601 | if img: 602 | image = self.save_img(img,{"src":imgsrc,"alt":alt}) 603 | new_imgsrc = image["src"] 604 | old_segment = match.group(0) 605 | new_segment = old_segment.replace(imgsrc,new_imgsrc) 606 | script = script.replace(old_segment,new_segment,1) 607 | return script 608 | 609 | 610 | async def add_img_async(self,img_content): 611 | try: 612 | src = img_content["src"] 613 | alt = img_content["alt"] 614 | except: 615 | return 616 | if os.path.exists(os.path.join(self.save_file,src)): 617 | return 618 | img = await self.get_img_async(alt) 619 | if img: 620 | self.save_img(img,img_content) 621 | 622 | 623 | 624 | def add_imgs(self,html_code): 625 | soup = BeautifulSoup(html_code, "html.parser") 626 | images = soup.find_all("img") 627 | scripts = soup.find_all("script") 628 | for image in images: 629 | try: 630 | src = image["src"] 631 | alt = image["alt"] 632 | except: 633 | continue 634 | if os.path.exists(os.path.join(self.save_file,src)): 635 | continue 636 | img = self.get_img(alt) 637 | if img: 638 | image = self.save_img(img,image) 639 | for script in scripts: 640 | if script.string: 641 | script.string = self.add_imgs_to_script(script.string) 642 | return soup.prettify() 643 | 644 | def add_imgs_to_script(self,script): 645 | pattern = re.compile(r'{[^}]*\bimgsrc:\s*\'([^\']*)\'[^}]*\balt:\s*\'([^\']*)\'[^}]*}') 646 | for match in pattern.finditer(script): 647 | imgsrc = match.group(1) 648 | alt = match.group(2) 649 | img = self.get_img(alt) 650 | if img: 651 | image = self.save_img(img,{"src":imgsrc,"alt":alt}) 652 | new_imgsrc = image["src"] 653 | old_segment = match.group(0) 654 | new_segment = old_segment.replace(imgsrc,new_imgsrc) 655 | script = script.replace(old_segment,new_segment,1) 656 | return script 657 | 658 | 659 | def load_local_img_storage(self): 660 | local_img_storage_path = self.local_img_storage_path 661 | if os.path.exists(os.path.join(local_img_storage_path,f"local_img_storage_en.json")): 662 | with open(os.path.join(local_img_storage_path,f"local_img_storage_en.json"), "r",encoding="utf-8") as f: 663 | self.local_img_storage_en = json.load(f) 664 | if os.path.exists(os.path.join(local_img_storage_path,f"local_img_storage_zh.json")): 665 | with open(os.path.join(local_img_storage_path,f"local_img_storage_zh.json"), "r",encoding="utf-8") as f: 666 | self.local_img_storage_zh = json.load(f) 667 | if os.path.exists(os.path.join(local_img_storage_path,f"local_img_storage_zh.json")): 668 | if self.language == "en": 669 | self.local_img_storage = self.local_img_storage_en 670 | elif self.language == "zh": 671 | self.local_img_storage = self.local_img_storage_zh 672 | else: 673 | self.local_img_storage = [] 674 | 675 | known_img_paths = [img["img_path"] for img in self.local_img_storage] 676 | all_files = get_all_files_in_dir(local_img_storage_path) 677 | img_files = [] 678 | for file in all_files: 679 | if file.endswith(".png") or file.endswith(".jpg") or file.endswith(".jpeg"): 680 | img_name = os.path.basename(file) 681 | new_img_path = os.path.join(self.save_file,img_name) 682 | shutil.copy(file,new_img_path) 683 | if img_name in known_img_paths: 684 | continue 685 | img_files.append(new_img_path) 686 | asyncio.run(self.add_imgs_to_storage_async(img_files)) 687 | with open(os.path.join(local_img_storage_path,"local_img_storage_en.json"), "w",encoding="utf-8") as f: 688 | json.dump(self.local_img_storage_en,f) 689 | with open(os.path.join(local_img_storage_path,"local_img_storage_zh.json"), "w",encoding="utf-8") as f: 690 | json.dump(self.local_img_storage_zh,f) 691 | 692 | if self.language == "en": 693 | self.local_img_storage = self.local_img_storage_en 694 | elif self.language == "zh": 695 | self.local_img_storage = self.local_img_storage_zh 696 | print(self.local_img_storage) 697 | 698 | async def add_imgs_to_storage_async(self,img_files): 699 | tasks = [] 700 | for img_path in img_files: 701 | tasks.append(self.add_img_to_storage_async(img_path)) 702 | await asyncio.gather(*tasks) 703 | 704 | 705 | async def add_img_to_storage_async(self,img_path): 706 | img = Image.open(img_path) 707 | width,height = img.size 708 | img_name = os.path.basename(img_path) 709 | process_img_prompt = get_process_img_prompt(language=self.language) 710 | messages = [ 711 | {"role":"user","content":[ 712 | {"type":"image","url":img_path}, 713 | {"type":"text","text":process_img_prompt}, 714 | ]}, 715 | ] 716 | response = await self.get_answer_async(messages=messages) 717 | if not response: 718 | print("Failed to get the response.") 719 | return 720 | chinese_description = extract(response,"zh") 721 | english_description = extract(response,"en") 722 | self.local_img_storage_en.append({"img_path":img_name,"description":english_description,"height":height,"width":width}) 723 | self.local_img_storage_zh.append({"img_path":img_name,"description":chinese_description,"height":height,"width":width}) 724 | print(f"Image {img_name} has been added to the local image storage.") 725 | 726 | def clean(self): 727 | srcs = [] 728 | for page in self.task_queue: 729 | html_name = page["html_name"] 730 | page_name = html_name.split(".")[0] 731 | page_img = os.path.join(self.save_file,f"{page_name}.png") 732 | srcs.append(page_img) 733 | html_path = os.path.join(self.save_file,html_name) 734 | if not os.path.exists(html_path): 735 | continue 736 | html_code = open(html_path,encoding='utf-8').read() 737 | soup = BeautifulSoup(html_code, "html.parser") 738 | images = soup.find_all("img") 739 | for image in images: 740 | src = image["src"] 741 | srcs.append(src) 742 | scripts = soup.find_all("script") 743 | for script in scripts: 744 | if script.string: 745 | pattern = re.compile(r'{[^}]*\bimgsrc:\s*\'([^\']*)\'[^}]*\balt:\s*\'([^\']*)\'[^}]*}') 746 | for match in pattern.finditer(script.string): 747 | src = match.group(1) 748 | srcs.append(src) 749 | all_files = get_all_files_in_dir(self.save_file) 750 | 751 | for file in all_files: 752 | if file.endswith(".png") or file.endswith(".jpg") or file.endswith(".jpeg"): 753 | img_name = os.path.basename(file) 754 | if img_name not in srcs: 755 | os.remove(file) 756 | print(f"Image {img_name} has been removed.") 757 | 758 | self.delete_all_backups(self.save_file) 759 | 760 | if __name__ == "__main__": 761 | parser = argparse.ArgumentParser() 762 | parser.add_argument("--save_file", type=str, default="saves/shopping/") 763 | parser.add_argument("--basic_information", type=str, default=None) 764 | parser.add_argument("--academic_achievements", type=str, default=None) 765 | parser.add_argument("--experience", type=str, default=None) 766 | parser.add_argument("--professional_skills", type=str, default=None) 767 | parser.add_argument("--img", type=str, default=None) 768 | parser.add_argument("--refine_times", type=int, default=2) 769 | 770 | args = parser.parse_args() 771 | save_file = args.save_file 772 | basic_information = args.basic_information 773 | academic_achievements = args.academic_achievements 774 | experience = args.experience 775 | professional_skills = args.professional_skills 776 | img = args.img 777 | refine_times = args.refine_times 778 | agent = HomepageDesignAgent(save_file=save_file) 779 | agent.act(basic_information=basic_information, academic_achievements=academic_achievements, experience=experience, professional_skills=professional_skills,img = img,refine_times = refine_times) 780 | print(f"Total prompt cost tokens: {agent.total_prompt_cost_tokens}, Total completion cost tokens: {agent.total_completion_cost_tokens}") 781 | cost = cal_cost(agent.total_prompt_cost_tokens,agent.total_completion_cost_tokens) 782 | print(f"Total cost: {cost}") 783 | 784 | 785 | 786 | --------------------------------------------------------------------------------