feat: 提示词和 AI 参数移至数据库管理

- AiSetting 模型新增 6 个提示词字段 + 3 个阶段温度字段
- main.py 迁移逻辑改为检测 ai_settings 表的新字段并填充默认值
- generate.py 从数据库读取生成提示词,去掉硬编码
- optimizer 从 API 读取各阶段提示词和温度,删除 prompts.py
- crawler 从 API 读取提取/改写提示词和温度,删除 prompts.py
- settings/active 端点去掉 token 认证(供爬虫/优化器使用)
- 后台设置页新增提示词编辑区和温度调节控件
- 新增 _ensure_default_settings 自动创建默认配置
This commit is contained in:
bwstudio
2026-06-13 17:27:09 +08:00
parent 6d015bb56d
commit db8cea02f8
11 changed files with 457 additions and 195 deletions
+76 -23
View File
@@ -8,15 +8,6 @@ import time
import httpx
from openai import OpenAI
from optimizer.prompts import (
QUALITY_CHECK_SYSTEM_PROMPT,
QUALITY_CHECK_USER_PROMPT,
POLISH_SYSTEM_PROMPT,
POLISH_USER_PROMPT,
EVALUATE_SYSTEM_PROMPT,
EVALUATE_USER_PROMPT,
)
class Optimizer:
def __init__(self, api_base: str, username: str, password: str):
@@ -26,6 +17,7 @@ class Optimizer:
self.token = None
self.ai_client = None
self.model_name = ""
self.ai_config = None # 完整 AI 配置(含提示词和温度)
self.types = []
self.crowds = []
# 统计
@@ -67,6 +59,7 @@ class Optimizer:
print("[*] 登录成功")
ai_config = self._get("/api/admin/settings/active")
self.ai_config = ai_config
self.ai_client = OpenAI(
base_url=ai_config["api_base"],
api_key=ai_config["api_key"],
@@ -78,6 +71,22 @@ class Optimizer:
self.crowds = self._get("/api/categories/crowds")
print(f"[*] 分类: {len(self.types)} 种类型, {len(self.crowds)} 种人群")
def _get_prompt(self, key: str, default: str) -> str:
"""从数据库配置中读取提示词,没有则返回默认"""
if self.ai_config:
val = self.ai_config.get(key)
if val and val.strip():
return val
return default
def _get_temp(self, key: str, default: float) -> float:
"""从数据库配置中读取温度"""
if self.ai_config:
val = self.ai_config.get(key)
if val is not None:
return float(val)
return default
# === 读取笑话 ===
def get_jokes(self, status: str | None = None, limit: int | None = None,
ids: list[int] | None = None) -> list[dict]:
@@ -120,14 +129,32 @@ class Optimizer:
# === Stage 1: 质量检测 ===
def quality_check(self, content: str) -> dict:
"""判断笑话是否有笑点,返回 {"has_punchline": bool, "reason": str}"""
system_prompt = self._get_prompt("optimizer_quality_prompt",
"""你是一个幽默内容审核专家。判断以下内容是否是一个合格的笑话/段子。
合格标准(满足任一即可):
1. 有明确的笑点或反转(punchline)
2. 有幽默的语言表达或双关
3. 有意外结局或情理之中意料之外
不合格标准(符合任一即判定不合格):
1. 纯粹的事实陈述,没有任何幽默元素
2. 只是对话片段,没有笑点
3. 普通故事或叙事,没有幽默设计
4. 说教或道理阐述
5. 内容不完整或难以理解
始终返回 JSON 格式:{"has_punchline": true/false, "reason": "简要说明判断理由"}""")
temperature = self._get_temp("optimizer_quality_temperature", 0.3)
def _call():
return self.ai_client.chat.completions.create(
model=self.model_name,
messages=[
{"role": "system", "content": QUALITY_CHECK_SYSTEM_PROMPT},
{"role": "user", "content": QUALITY_CHECK_USER_PROMPT.format(content=content[:2000])},
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"请判断以下内容是否为合格笑话:\n\n{content[:2000]}\n\n返回 JSON 格式。"},
],
temperature=0.3,
temperature=temperature,
max_tokens=200,
)
resp = self._safe_api_call(_call)
@@ -137,14 +164,24 @@ class Optimizer:
# === Stage 2: AI 润色 ===
def polish(self, content: str) -> str:
"""润色笑话内容"""
system_prompt = self._get_prompt("optimizer_polish_prompt",
"""你是一个专业的幽默文案编辑。请润色以下笑话,要求:
1. 保持核心笑点不变
2. 优化语言表达,使其更通顺、更精炼
3. 增强节奏感和幽默效果,但不改变原意
4. 字数控制在原内容的 80%-120%
5. 不要添加额外解释或评论
6. 直接输出润色后的内容,不要加任何前缀""")
temperature = self._get_temp("optimizer_polish_temperature", 0.8)
def _call():
return self.ai_client.chat.completions.create(
model=self.model_name,
messages=[
{"role": "system", "content": POLISH_SYSTEM_PROMPT},
{"role": "user", "content": POLISH_USER_PROMPT.format(content=content)},
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"请润色以下笑话:\n\n{content}"},
],
temperature=0.8,
temperature=temperature,
max_tokens=1024,
)
resp = self._safe_api_call(_call)
@@ -156,24 +193,40 @@ class Optimizer:
type_names = [t.get("name", "") for t in self.types]
crowd_names = [c.get("name", "") for c in self.crowds]
system_prompt = self._get_prompt("optimizer_evaluate_prompt",
"""你是一个笑话分类和评价专家。对给定的笑话进行分析,返回 JSON 格式的分类和评分结果。
要求:
1. types: 从提供的类型列表中选择所有匹配的类型名称(数组,可以选多个)
2. crowds: 从提供的人群列表中选择所有匹配的人群名称(数组,可以选多个)
3. score: 1-10 分,基于幽默程度、创意和表达效果
4. comment: 简短评语(10字以内)
始终返回 JSON 格式。""")
temperature = self._get_temp("optimizer_evaluate_temperature", 0.3)
user_content = f"""笑话内容:
{content[:2000]}
可选类型:{', '.join(type_names)}
可选人群:{', '.join(crowd_names)}
返回 JSON 格式:{{"types": ["类型1", "类型2"], "crowds": ["人群1", "人群2"], "score": 8, "comment": "简短评语"}}"""
def _call():
return self.ai_client.chat.completions.create(
model=self.model_name,
messages=[
{"role": "system", "content": EVALUATE_SYSTEM_PROMPT},
{"role": "user", "content": EVALUATE_USER_PROMPT.format(
content=content[:2000],
known_types=", ".join(type_names),
known_crowds=", ".join(crowd_names),
)},
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_content},
],
temperature=0.3,
temperature=temperature,
max_tokens=300,
)
resp = self._safe_api_call(_call)
raw = resp.choices[0].message.content.strip()
result = self._parse_json(raw, {"types": [], "crowds": [], "score": 5, "comment": ""})
# Backward compatibility: if LLM returns old single format, convert to array
# Backward compatibility
if isinstance(result.get("types"), str):
result["types"] = [result["types"]] if result["types"] else []
if isinstance(result.get("crowds"), str):
-58
View File
@@ -1,58 +0,0 @@
"""AI 提示词模板 — 笑话质量检测、润色、评价分类。"""
# ===== Stage 1: 质量检测 =====
QUALITY_CHECK_SYSTEM_PROMPT = """你是一个幽默内容审核专家。判断以下内容是否是一个合格的笑话/段子。
合格标准(满足任一即可):
1. 有明确的笑点或反转(punchline)
2. 有幽默的语言表达或双关
3. 有意外结局或情理之中意料之外
不合格标准(符合任一即判定不合格):
1. 纯粹的事实陈述,没有任何幽默元素
2. 只是对话片段,没有笑点
3. 普通故事或叙事,没有幽默设计
4. 说教或道理阐述
5. 内容不完整或难以理解
始终返回 JSON 格式:{"has_punchline": true/false, "reason": "简要说明判断理由"}"""
QUALITY_CHECK_USER_PROMPT = """请判断以下内容是否为合格笑话:
{content}
返回 JSON 格式。"""
# ===== Stage 2: AI 润色 =====
POLISH_SYSTEM_PROMPT = """你是一个专业的幽默文案编辑。请润色以下笑话,要求:
1. 保持核心笑点不变
2. 优化语言表达,使其更通顺、更精炼
3. 增强节奏感和幽默效果,但不改变原意
4. 字数控制在原内容的 80%-120%
5. 不要添加额外解释或评论
6. 直接输出润色后的内容,不要加任何前缀"""
POLISH_USER_PROMPT = """请润色以下笑话:
{content}
只输出润色后的笑话内容。"""
# ===== Stage 3: 评价分类 =====
EVALUATE_SYSTEM_PROMPT = """你是一个笑话分类和评价专家。对给定的笑话进行分析,返回 JSON 格式的分类和评分结果。
要求:
1. types: 从提供的类型列表中选择所有匹配的类型名称(数组,可以选多个)
2. crowds: 从提供的人群列表中选择所有匹配的人群名称(数组,可以选多个)
3. score: 1-10 分,基于幽默程度、创意和表达效果
4. comment: 简短评语(10字以内)
始终返回 JSON 格式。"""
EVALUATE_USER_PROMPT = """笑话内容:
{content}
可选类型:{known_types}
可选人群:{known_crowds}
返回 JSON 格式:{{"types": ["类型1", "类型2"], "crowds": ["人群1", "人群2"], "score": 8, "comment": "简短评语"}}"""