182 lines
6.7 KiB
Python
182 lines
6.7 KiB
Python
|
import os
|
|||
|
import re
|
|||
|
from openai import OpenAI
|
|||
|
|
|||
|
# 加载LLM配置的函数
|
|||
|
class LLMConfig:
|
|||
|
def __init__(self, api_key, model_name, base_url):
|
|||
|
self.api_key = api_key
|
|||
|
self.model_name = model_name
|
|||
|
self.base_url = base_url
|
|||
|
|
|||
|
def load_llm_config():
|
|||
|
"""
|
|||
|
加载LLM配置
|
|||
|
|
|||
|
返回:
|
|||
|
LLMConfig: 包含API密钥、模型名称和基础URL的配置对象
|
|||
|
"""
|
|||
|
try:
|
|||
|
from Config.Config import ALY_LLM_API_KEY, ALY_LLM_MODEL_NAME, ALY_LLM_BASE_URL
|
|||
|
return LLMConfig(
|
|||
|
api_key=ALY_LLM_API_KEY,
|
|||
|
model_name=ALY_LLM_MODEL_NAME,
|
|||
|
base_url=ALY_LLM_BASE_URL
|
|||
|
)
|
|||
|
except Exception as e:
|
|||
|
print(f"加载配置时出错: {str(e)}")
|
|||
|
return None
|
|||
|
|
|||
|
SYS_PROMPT = """
|
|||
|
你是一位资深 Manim 动画工程师,专注于教育场景。请遵循以下规范:
|
|||
|
1. 仅输出 Python 代码,不解释思路。
|
|||
|
2. 使用 Manim 社区版 v0.18.0 语法。
|
|||
|
3. 严格按提示的步骤进行编码,不要发挥、扩展。
|
|||
|
4. 所有公式用 MathTex,文字用 Text(更好地支持中文),颜色统一用 Manim 内置调色板。
|
|||
|
5. 关键坐标用常量定义在类顶部,例如 LEFT_MARGIN = 3.2。
|
|||
|
6. 每行不超过 88 字符,函数名用 snake_case。
|
|||
|
7. 动画时长 3-8 秒 / 步,默认 ease-in-out。
|
|||
|
8. 请严格按照以下模板:坐标轴范围、颜色、字体统一使用 Manim 社区版默认主题。
|
|||
|
9. 代码的最后,参考下面的代码添加相应内容:
|
|||
|
if __name__ == "__main__":
|
|||
|
# 导入必要的模块
|
|||
|
import sys
|
|||
|
import os
|
|||
|
from manim import *
|
|||
|
from manim.utils.rate_functions import ease_in_out_sine
|
|||
|
|
|||
|
# 设置 UTF-8 编码
|
|||
|
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
|||
|
sys.stdout.reconfigure(encoding='utf-8')
|
|||
|
sys.stderr.reconfigure(encoding='utf-8')
|
|||
|
|
|||
|
# 全局指定 ctex 模板(一次性)
|
|||
|
config.tex_template = TexTemplateLibrary.ctex
|
|||
|
# 设置输出格式为MP4
|
|||
|
config.output_format = "mp4"
|
|||
|
# 设置输出目录
|
|||
|
config.media_dir = "./output/"
|
|||
|
# 使用临时配置渲染场景(配置只在with块内有效)
|
|||
|
with tempconfig({{
|
|||
|
"quality": "high_quality",
|
|||
|
"background_color": BLACK,
|
|||
|
"preview": {Preview},
|
|||
|
"pixel_height": 1080,
|
|||
|
"pixel_width": 1920,
|
|||
|
"frame_rate": 30
|
|||
|
}}):
|
|||
|
# 实例化场景类
|
|||
|
scene = Skeleton()
|
|||
|
# 执行渲染流程(包含文件生成和预览)
|
|||
|
scene.render()
|
|||
|
10. 【输出要求】
|
|||
|
(1). 完整可运行 Python 脚本
|
|||
|
(2). 包含 `if __name__ == "__main__":` 可直接运行。
|
|||
|
(3). 代码内只出现中文注释,方便二次修改。
|
|||
|
本任务的具体需求如下:
|
|||
|
{XuQiu}
|
|||
|
"""
|
|||
|
|
|||
|
def generate_code_with_llm(prompt, save_path, config=None):
|
|||
|
"""
|
|||
|
调用大模型生成代码并保存
|
|||
|
|
|||
|
参数:
|
|||
|
prompt: 提示词
|
|||
|
save_path: 代码保存路径
|
|||
|
config: 可选的配置对象,如果未提供则自动加载
|
|||
|
|
|||
|
返回:
|
|||
|
bool: 是否成功保存代码
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 如果未提供配置,则加载默认配置
|
|||
|
if config is None:
|
|||
|
config = load_llm_config()
|
|||
|
if config is None:
|
|||
|
print("无法加载配置,生成代码失败")
|
|||
|
return False
|
|||
|
|
|||
|
# 初始化OpenAI客户端
|
|||
|
client = OpenAI(
|
|||
|
api_key=config.api_key,
|
|||
|
base_url=config.base_url
|
|||
|
)
|
|||
|
|
|||
|
reasoning_content = "" # 定义完整思考过程
|
|||
|
answer_content = "" # 定义完整回复
|
|||
|
is_answering = False # 判断是否结束思考过程并开始回复
|
|||
|
|
|||
|
# 创建聊天完成请求
|
|||
|
completion = client.chat.completions.create(
|
|||
|
model=config.model_name,
|
|||
|
messages=[
|
|||
|
{
|
|||
|
"role": "user",
|
|||
|
"content": [
|
|||
|
{"type": "text",
|
|||
|
"text": prompt},
|
|||
|
],
|
|||
|
},
|
|||
|
],
|
|||
|
stream=True,
|
|||
|
)
|
|||
|
|
|||
|
print("\n" + "=" * 20 + "思考过程" + "=" * 20 + "\n")
|
|||
|
|
|||
|
for chunk in completion:
|
|||
|
# 如果chunk.choices为空,则打印usage
|
|||
|
if not chunk.choices:
|
|||
|
print("\nUsage:")
|
|||
|
print(chunk.usage)
|
|||
|
else:
|
|||
|
delta = chunk.choices[0].delta
|
|||
|
# 打印思考过程
|
|||
|
if hasattr(delta, 'reasoning_content') and delta.reasoning_content != None:
|
|||
|
print(delta.reasoning_content, end='', flush=True)
|
|||
|
reasoning_content += delta.reasoning_content
|
|||
|
else:
|
|||
|
# 开始回复
|
|||
|
if delta.content != "" and is_answering is False:
|
|||
|
print("\n" + "=" * 20 + "完整回复" + "=" * 20 + "\n")
|
|||
|
is_answering = True
|
|||
|
# 打印回复过程
|
|||
|
print(delta.content, end='', flush=True)
|
|||
|
answer_content += delta.content
|
|||
|
|
|||
|
# 处理并保存生成的代码
|
|||
|
return save_code_to_file(answer_content, save_path)
|
|||
|
except Exception as e:
|
|||
|
print(f"调用大模型时出错: {str(e)}")
|
|||
|
return False
|
|||
|
|
|||
|
|
|||
|
def save_code_to_file(content, file_path):
|
|||
|
try:
|
|||
|
# 提取Python代码块
|
|||
|
code_match = re.search(r'```python(.*?)```', content, re.DOTALL)
|
|||
|
if code_match:
|
|||
|
code_content = code_match.group(1).strip()
|
|||
|
else:
|
|||
|
# 如果没有代码块,尝试提取所有内容
|
|||
|
code_content = content.strip()
|
|||
|
|
|||
|
# 替换全角符号为半角
|
|||
|
code_content = code_content.replace(',', ',').replace('。', '.').replace(';', ';')
|
|||
|
code_content = code_content.replace('(', '(').replace(')', ')').replace('【', '[').replace('】', ']')
|
|||
|
|
|||
|
# 添加UTF-8编码声明
|
|||
|
code_content = f"# -*- coding: utf-8 -*-.py\n{code_content}"
|
|||
|
|
|||
|
# 确保目录存在
|
|||
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|||
|
|
|||
|
# 写入文件
|
|||
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|||
|
f.write(code_content)
|
|||
|
|
|||
|
print(f"代码已成功保存到: {file_path}")
|
|||
|
return True
|
|||
|
except Exception as e:
|
|||
|
print(f"保存代码时出错: {str(e)}")
|
|||
|
return False
|