202 lines
5.8 KiB
Python
202 lines
5.8 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
内容审核引擎
|
|||
|
|
负责评估和优化生成的内容
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from typing import Dict, Any, Optional, Tuple
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class JudgeEngine:
|
|||
|
|
"""
|
|||
|
|
内容审核引擎
|
|||
|
|
|
|||
|
|
职责:
|
|||
|
|
- 评估内容质量
|
|||
|
|
- 提供优化建议
|
|||
|
|
- 自动修正内容
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, llm_client=None, prompt_builder=None):
|
|||
|
|
"""
|
|||
|
|
初始化审核引擎
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
llm_client: LLM 客户端
|
|||
|
|
prompt_builder: 提示词构建器
|
|||
|
|
"""
|
|||
|
|
self._llm = llm_client
|
|||
|
|
self._prompt = prompt_builder
|
|||
|
|
self.logger = logging.getLogger(f"{__name__}.JudgeEngine")
|
|||
|
|
|
|||
|
|
async def judge(
|
|||
|
|
self,
|
|||
|
|
content: Dict[str, Any],
|
|||
|
|
topic: Dict[str, Any],
|
|||
|
|
auto_fix: bool = True
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
审核内容
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
content: 待审核的内容
|
|||
|
|
topic: 原始选题
|
|||
|
|
auto_fix: 是否自动修正
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
审核结果
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
self.logger.info("开始内容审核")
|
|||
|
|
|
|||
|
|
# 1. 构建提示词
|
|||
|
|
system_prompt, user_prompt = self._build_prompts(content, topic, auto_fix)
|
|||
|
|
|
|||
|
|
# 2. 调用 LLM
|
|||
|
|
response = await self._llm.generate(
|
|||
|
|
prompt=user_prompt,
|
|||
|
|
system_prompt=system_prompt
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 3. 解析结果
|
|||
|
|
result = self._parse_result(response, content)
|
|||
|
|
|
|||
|
|
self.logger.info(f"内容审核完成: score={result.get('score', 'N/A')}")
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
self.logger.error(f"内容审核失败: {e}")
|
|||
|
|
# 返回原始内容
|
|||
|
|
return {
|
|||
|
|
'success': False,
|
|||
|
|
'error': str(e),
|
|||
|
|
'original_content': content,
|
|||
|
|
'judged_content': content
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def _build_prompts(
|
|||
|
|
self,
|
|||
|
|
content: Dict[str, Any],
|
|||
|
|
topic: Dict[str, Any],
|
|||
|
|
auto_fix: bool
|
|||
|
|
) -> Tuple[str, str]:
|
|||
|
|
"""构建提示词"""
|
|||
|
|
if self._prompt:
|
|||
|
|
system_prompt = self._prompt.get_system_prompt(
|
|||
|
|
"content_judge",
|
|||
|
|
auto_fix=auto_fix
|
|||
|
|
)
|
|||
|
|
user_prompt = self._prompt.get_user_prompt(
|
|||
|
|
"content_judge",
|
|||
|
|
content=content,
|
|||
|
|
topic=topic,
|
|||
|
|
auto_fix=auto_fix
|
|||
|
|
)
|
|||
|
|
return system_prompt, user_prompt
|
|||
|
|
|
|||
|
|
# 默认提示词
|
|||
|
|
system_prompt = """你是一个专业的内容审核专家。
|
|||
|
|
请评估以下小红书文案的质量,并给出评分和建议。
|
|||
|
|
|
|||
|
|
评估维度:
|
|||
|
|
1. 标题吸引力 (1-10)
|
|||
|
|
2. 内容质量 (1-10)
|
|||
|
|
3. 结构清晰度 (1-10)
|
|||
|
|
4. 情感共鸣 (1-10)
|
|||
|
|
5. 营销效果 (1-10)
|
|||
|
|
|
|||
|
|
输出格式为 JSON。"""
|
|||
|
|
|
|||
|
|
content_title = content.get('title', '')
|
|||
|
|
content_text = content.get('content', '')
|
|||
|
|
topic_title = topic.get('title', '')
|
|||
|
|
|
|||
|
|
fix_instruction = ""
|
|||
|
|
if auto_fix:
|
|||
|
|
fix_instruction = """
|
|||
|
|
如果评分低于 7 分,请提供优化后的版本。
|
|||
|
|
在 JSON 中添加 "improved_title" 和 "improved_content" 字段。"""
|
|||
|
|
|
|||
|
|
user_prompt = f"""请评估以下内容:
|
|||
|
|
|
|||
|
|
原始选题:{topic_title}
|
|||
|
|
|
|||
|
|
文案标题:{content_title}
|
|||
|
|
|
|||
|
|
文案内容:
|
|||
|
|
{content_text}
|
|||
|
|
|
|||
|
|
请以 JSON 格式输出评估结果,包含:
|
|||
|
|
- scores: 各维度评分
|
|||
|
|
- total_score: 总分 (满分 50)
|
|||
|
|
- suggestions: 优化建议数组
|
|||
|
|
- pass: 是否通过 (总分 >= 35)
|
|||
|
|
{fix_instruction}"""
|
|||
|
|
|
|||
|
|
return system_prompt, user_prompt
|
|||
|
|
|
|||
|
|
def _parse_result(self, response: str, original_content: Dict[str, Any]) -> Dict[str, Any]:
|
|||
|
|
"""解析审核结果"""
|
|||
|
|
import json
|
|||
|
|
import re
|
|||
|
|
|
|||
|
|
result = {
|
|||
|
|
'success': True,
|
|||
|
|
'original_content': original_content,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 尝试解析 JSON
|
|||
|
|
parsed = None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
parsed = json.loads(response)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
if not parsed:
|
|||
|
|
patterns = [
|
|||
|
|
r'\{[\s\S]*\}',
|
|||
|
|
r'```json\s*([\s\S]*?)\s*```',
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
for pattern in patterns:
|
|||
|
|
match = re.search(pattern, response)
|
|||
|
|
if match:
|
|||
|
|
try:
|
|||
|
|
json_str = match.group(1) if '```' in pattern else match.group(0)
|
|||
|
|
parsed = json.loads(json_str)
|
|||
|
|
break
|
|||
|
|
except:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if parsed:
|
|||
|
|
result['scores'] = parsed.get('scores', {})
|
|||
|
|
result['total_score'] = parsed.get('total_score', 0)
|
|||
|
|
result['score'] = result['total_score'] # 别名
|
|||
|
|
result['suggestions'] = parsed.get('suggestions', [])
|
|||
|
|
result['pass'] = parsed.get('pass', True)
|
|||
|
|
|
|||
|
|
# 如果有优化版本
|
|||
|
|
if parsed.get('improved_title') or parsed.get('improved_content'):
|
|||
|
|
result['judged_content'] = {
|
|||
|
|
**original_content,
|
|||
|
|
'title': parsed.get('improved_title', original_content.get('title')),
|
|||
|
|
'content': parsed.get('improved_content', original_content.get('content')),
|
|||
|
|
}
|
|||
|
|
result['improved'] = True
|
|||
|
|
else:
|
|||
|
|
result['judged_content'] = original_content
|
|||
|
|
result['improved'] = False
|
|||
|
|
else:
|
|||
|
|
# 无法解析,返回原始内容
|
|||
|
|
result['judged_content'] = original_content
|
|||
|
|
result['pass'] = True
|
|||
|
|
result['improved'] = False
|
|||
|
|
|
|||
|
|
return result
|