Files
meijiaka-zy/python-api/app/ai/prompts/loader.py
T
小鱼开发 04e467e433 feat(points): 积分系统收尾 + 充值弹窗改造 + 命名统一
后端:
- 微信回调 db.commit 失败仍返回 SUCCESS,避免无限重试
- recharge() 加 order_id 幂等保护,防重复充值
- time_expire 使用北京时间(UTC+8),修复时区 bug
- 充值档位后端配置化(points-config.yaml + /recharge-options API)
- 代码审查 20 项修复(认证加固、扣费顺序、错误响应、状态同步等)

前端:
- 充值弹窗:自动轮询 + 【我已支付】手动兜底
- 二维码倒计时显示,过期后遮罩 + 刷新按钮
- 充值档位从后端动态加载
- 去掉 select/qrcode 弹窗标题,金额红色突出显示
- 全项目命名统一(视频生成/压制成片/配音合成/声音复刻等)
- Modal 关闭按钮独立于 title 显示
2026-05-09 21:29:35 +08:00

324 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Prompt 简单加载器
=================
从文件加载 Prompt,支持热更新。
目录结构约定:
system/
├── <category>/ # 大类目录
│ ├── <subcategory>/ # 小类目录
│ │ ├── _meta.json # 元数据 {"name": "显示名称"}
│ │ ├── 1.txt # 提示词文件(随机取其一)
│ │ └── 2.txt
│ └── ...
└── ...
"""
import json
import random
from pathlib import Path
from string import Template
_PROMPTS_DIR = Path(__file__).parent
def load_prompt(path: str) -> str:
"""
加载 Prompt 文件
Args:
path: 相对路径,如 "script/system", "polish/scene"
Returns:
Prompt 内容,文件不存在返回空字符串
"""
file_path = _PROMPTS_DIR / f"{path}.txt"
if file_path.exists():
return file_path.read_text(encoding="utf-8")
return ""
def render_template(template: str, **kwargs) -> str:
"""
安全渲染模板变量
Args:
template: 模板字符串
**kwargs: 变量值
Returns:
渲染后的字符串
"""
try:
# 转义 $ 符号防止用户输入干扰
safe_kwargs = {k: str(v).replace("$", "$$") for k, v in kwargs.items()}
return Template(template).substitute(**safe_kwargs)
except KeyError as e:
raise ValueError(f"模板缺少变量: {e}")
# ====================== 新分类体系:动态扫描 ======================
SYSTEM_PROMPTS_DIR = _PROMPTS_DIR / "system"
_SYSTEM_META_PATH = SYSTEM_PROMPTS_DIR / "_meta.json"
def _load_system_meta() -> dict:
"""读取 system/_meta.json"""
if _SYSTEM_META_PATH.exists():
try:
return json.loads(_SYSTEM_META_PATH.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
pass
return {}
def list_categories() -> list[dict]:
"""
返回所有分类结构
以 system/_meta.json 为准,只返回配置中定义的分类。
count 动态扫描对应目录统计。
Returns:
[
{
"code": "bk",
"name": "装修避坑",
"subcategories": [
{"code": "ht", "name": "装修合同避坑", "count": 2},
...
]
},
...
]
"""
meta = _load_system_meta()
categories = []
for cat_meta in meta.get("categories", []):
cat_code = cat_meta["code"]
cat_name = cat_meta.get("name", cat_code)
cat_dir = SYSTEM_PROMPTS_DIR / cat_code
subcategories = []
for sub_meta in cat_meta.get("subcategories", []):
sub_code = sub_meta["code"]
sub_name = sub_meta.get("name", sub_code)
sub_dir = cat_dir / sub_code
# 统计提示词文件数量
count = 0
if sub_dir.exists():
count = len([
f for f in sub_dir.iterdir()
if f.is_file() and f.suffix == ".txt"
])
subcategories.append({
"code": sub_code,
"name": sub_name,
"count": count,
})
categories.append({
"code": cat_code,
"name": cat_name,
"subcategories": subcategories,
})
return categories
def load_system_prompt(category: str, subcategory: str) -> str:
"""
根据大类+小类随机加载一个 System Prompt
Args:
category: 大类代码,如 "bk"
subcategory: 小类代码,如 "ht"
Returns:
随机选中的提示词内容,未找到返回空字符串
"""
sub_dir = SYSTEM_PROMPTS_DIR / category / subcategory
if not sub_dir.exists():
return ""
# 收集所有提示词文件
prompt_files = [
f for f in sub_dir.iterdir()
if f.is_file() and f.suffix == ".txt"
]
if not prompt_files:
return ""
# 随机取一个
chosen = random.choice(prompt_files)
return chosen.read_text(encoding="utf-8")
def load_script_user_prompt(
topic: str,
extra_params: str | None = None,
) -> str:
"""
加载并渲染脚本生成 User Prompt
Args:
topic: 创作主题名称
extra_params: 额外参数(如风格、人设等),以换行分隔的字符串
Returns:
渲染后的用户提示词
"""
template = load_prompt("user/script")
return render_template(
template,
topic=topic,
extra_params=extra_params or "",
)
class ScriptPromptBuilder:
"""
脚本 Prompt 构建器
用于构建家装行业短视频脚本的 System Prompt。
"""
def build(
self,
duration: int = 30,
script_type: str = "干货型",
video_style: str = "口播",
industry: str = "家装",
tone: str | None = None,
custom_requirements: str | None = None,
) -> str:
"""
构建系统 Prompt
Args:
duration: 视频时长(秒)
script_type: 脚本类型(干货型、故事型等)
video_style: 视频风格(口播、剧情等)
industry: 行业(家装)
tone: 语气风格
custom_requirements: 自定义要求
Returns:
完整的 System Prompt
"""
# 基础 System Prompt(已废弃的脚本 system.txt,这里留空)
base_prompt = ""
# 构建上下文信息
context_parts = [
f"行业:{industry}",
f"时长:{duration}",
f"类型:{script_type}",
f"风格:{video_style}",
]
if tone:
context_parts.append(f"语气:{tone}")
context = "\n".join(context_parts)
# 构建完整 Prompt
full_prompt = f"""{base_prompt}
【创作要求】
{context}
"""
if custom_requirements:
full_prompt += f"""
【特殊要求】
{custom_requirements}
"""
# 添加输出格式要求
full_prompt += """
【输出格式】
请严格按照以下 JSON 数组格式返回,每个元素代表一个镜头:
[
{
"id": 1,
"type": "segment",
"scene": "画面描述",
"voiceover": "配音文本",
"duration": "5s"
}
]
type 可以是:
- "segment": 分镜(有画面+配音)
- "empty_shot": 空镜(纯画面,voiceover 可为空)
注意:
1. 只返回 JSON 数组,不要有其他文字
2. 确保 JSON 格式正确
3. 总时长必须严格控制在要求范围内
"""
return full_prompt
class PolishPromptBuilder:
"""
润色 Prompt 构建器
用于构建润色文案或画面描述的 Prompt。
"""
POLISH_TYPES = {
"scene": "画面描述",
"voiceover": "配音文本",
"text": "文案内容",
}
def build(self, polish_type: str = "voiceover") -> str:
"""
构建润色 Prompt
Args:
polish_type: 润色类型(scene/voiceover/text
Returns:
System Prompt
"""
if polish_type == "scene":
return self._build_scene_prompt()
else:
return self._build_voiceover_prompt()
def _build_scene_prompt(self) -> str:
"""构建画面描述润色 Prompt"""
return """你是一位专业的视频画面描述优化师。你的任务是优化画面描述,使其更加生动、具体、有画面感。
优化要求:
1. 增加细节描写(光线、色彩、构图)
2. 使用专业的影视语言
3. 描述要具体可执行
4. 保持简洁,不要过度渲染
5. 适合 AI 视频生成模型理解
请直接返回优化后的画面描述,不要添加解释。"""
def _build_voiceover_prompt(self) -> str:
"""构建配音文本润色 Prompt"""
return """你是一位专业的短视频文案编辑。你的任务是优化口播文案,使其更加流畅、有吸引力。
优化要求:
1. 语言口语化,适合朗读
2. 增加节奏感和停顿
3. 保留核心信息点
4. 适当使用修辞手法
5. 控制字数,不要过长
请直接返回优化后的文案,不要添加解释。"""