2025-07-08 18:24:23 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
提示词管理和构建模块
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import logging
|
|
|
|
|
|
import random
|
|
|
|
|
|
from typing import Dict, Any, Optional, List
|
2025-07-09 11:51:49 +08:00
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
import json
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
from core.config import (
|
|
|
|
|
|
ConfigManager,
|
|
|
|
|
|
GenerateTopicConfig,
|
|
|
|
|
|
GenerateContentConfig,
|
|
|
|
|
|
SystemConfig,
|
|
|
|
|
|
ResourceConfig
|
|
|
|
|
|
)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
from .file_io import ResourceLoader
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PromptTemplate:
|
2025-07-09 11:51:49 +08:00
|
|
|
|
"""
|
|
|
|
|
|
提示词模板基类
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, system_prompt_path: str, user_prompt_path: str):
|
|
|
|
|
|
"""
|
|
|
|
|
|
使用明确的系统和用户提示文件路径初始化
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
system_prompt_path (str): 系统提示模板文件的路径
|
|
|
|
|
|
user_prompt_path (str): 用户提示模板文件的路径
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.system_path = Path(system_prompt_path)
|
|
|
|
|
|
self.user_path = Path(user_prompt_path)
|
|
|
|
|
|
|
|
|
|
|
|
self.system_template = self._read_template(self.system_path)
|
|
|
|
|
|
self.user_template = self._read_template(self.user_path)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
def _read_template(self, path: Path) -> str:
|
|
|
|
|
|
if not path.is_file():
|
|
|
|
|
|
logger.warning(f"模板文件不存在,已跳过: {path}")
|
|
|
|
|
|
return ""
|
|
|
|
|
|
with open(path, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
return f.read()
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
def get_system_prompt(self, **kwargs) -> str:
|
|
|
|
|
|
"""获取格式化后的系统提示词"""
|
|
|
|
|
|
if not self.system_template:
|
|
|
|
|
|
return ""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not kwargs:
|
|
|
|
|
|
return self.system_template
|
|
|
|
|
|
return self.system_template.format(**kwargs)
|
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
|
logger.error(f"渲染系统提示 '{self.system_path}' 时缺少键: {e}")
|
|
|
|
|
|
raise
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
def build_user_prompt(self, **kwargs) -> str:
|
|
|
|
|
|
"""构建用户提示词"""
|
|
|
|
|
|
if not self.user_template:
|
|
|
|
|
|
return ""
|
2025-07-08 18:24:23 +08:00
|
|
|
|
try:
|
2025-07-09 11:51:49 +08:00
|
|
|
|
return self.user_template.format(**kwargs)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
except KeyError as e:
|
2025-07-09 11:51:49 +08:00
|
|
|
|
logger.error(f"渲染用户提示 '{self.user_path}' 时缺少键: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
class BasePromptBuilder(PromptTemplate):
|
|
|
|
|
|
"""
|
|
|
|
|
|
包含通用资源加载方法的构建器基类
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, config_manager: ConfigManager, system_prompt_path: str, user_prompt_path: str):
|
|
|
|
|
|
super().__init__(system_prompt_path, user_prompt_path)
|
|
|
|
|
|
self.config_manager = config_manager
|
|
|
|
|
|
self.system_config: SystemConfig = config_manager.get_config('system', SystemConfig)
|
|
|
|
|
|
self.resource_config: ResourceConfig = config_manager.get_config('resource', ResourceConfig)
|
|
|
|
|
|
|
|
|
|
|
|
def _get_full_path(self, path_str: str) -> Path:
|
|
|
|
|
|
"""根据基准目录解析相对或绝对路径"""
|
|
|
|
|
|
if not self.resource_config.resource_dirs:
|
|
|
|
|
|
raise ValueError("Resource directories list is empty in config.")
|
|
|
|
|
|
base_path = Path(self.resource_config.resource_dirs[0])
|
|
|
|
|
|
file_path = Path(path_str)
|
|
|
|
|
|
return file_path if file_path.is_absolute() else (base_path / file_path).resolve()
|
|
|
|
|
|
|
|
|
|
|
|
def _load_resource_as_list(self, name: str, paths: list[str]) -> str:
|
|
|
|
|
|
"""加载资源,格式化为文件名列表"""
|
|
|
|
|
|
if not paths:
|
|
|
|
|
|
return f"{name}:\n无"
|
|
|
|
|
|
|
|
|
|
|
|
content_parts = [f"{name}:"]
|
|
|
|
|
|
for path_str in paths:
|
|
|
|
|
|
try:
|
|
|
|
|
|
full_path = self._get_full_path(path_str)
|
|
|
|
|
|
if full_path.is_file():
|
|
|
|
|
|
content_parts.append(f"- {full_path.name}")
|
|
|
|
|
|
elif full_path.is_dir():
|
|
|
|
|
|
for f in sorted(p for p in full_path.iterdir() if p.is_file()):
|
|
|
|
|
|
content_parts.append(f"- {f.name}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"处理路径 '{path_str}' 失败: {e}", exc_info=True)
|
|
|
|
|
|
|
|
|
|
|
|
return "\n".join(content_parts)
|
|
|
|
|
|
|
|
|
|
|
|
def _load_resource_as_content(self, name: str, paths: list[str]) -> str:
|
|
|
|
|
|
"""加载资源,格式化为包含文件内容的块"""
|
|
|
|
|
|
if not paths:
|
|
|
|
|
|
return f"{name}:\n无"
|
|
|
|
|
|
|
|
|
|
|
|
content_parts = [f"{name}:"]
|
|
|
|
|
|
for path_str in paths:
|
|
|
|
|
|
try:
|
|
|
|
|
|
full_path = self._get_full_path(path_str)
|
|
|
|
|
|
files_to_read = []
|
|
|
|
|
|
if full_path.is_file():
|
|
|
|
|
|
files_to_read.append(full_path)
|
|
|
|
|
|
elif full_path.is_dir():
|
|
|
|
|
|
files_to_read.extend(sorted(p for p in full_path.iterdir() if p.is_file()))
|
|
|
|
|
|
|
|
|
|
|
|
for f_path in files_to_read:
|
|
|
|
|
|
with f_path.open('r', encoding='utf-8') as f:
|
|
|
|
|
|
file_content = f.read()
|
|
|
|
|
|
content_parts.append(f"--- {f_path.name} ---\n{file_content}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载资源 '{path_str}' 失败: {e}", exc_info=True)
|
|
|
|
|
|
|
|
|
|
|
|
return "\n\n".join(content_parts)
|
|
|
|
|
|
|
|
|
|
|
|
def _load_and_format_content(self, path: Path) -> str:
|
|
|
|
|
|
"""根据文件类型加载和格式化内容"""
|
|
|
|
|
|
if path.suffix == '.json':
|
|
|
|
|
|
try:
|
|
|
|
|
|
with path.open('r', encoding='utf-8') as f:
|
|
|
|
|
|
data = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
if "examples" in data and isinstance(data["examples"], list):
|
2025-07-09 14:53:24 +08:00
|
|
|
|
examples = data["examples"]
|
|
|
|
|
|
return f"参考标题列表:\n" + "\n".join([f"- {item.get('content', '')}" for item in examples])
|
|
|
|
|
|
else:
|
|
|
|
|
|
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"解析或格式化JSON文件 '{path}' 失败: {e}")
|
|
|
|
|
|
return f"加载文件 '{path.name}' 失败。"
|
|
|
|
|
|
else:
|
|
|
|
|
|
return path.read_text('utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
def _load_and_format_content_with_sampling(self, path: Path, sampling_rate: float) -> str:
|
|
|
|
|
|
"""根据文件类型加载和格式化内容,并应用采样率"""
|
|
|
|
|
|
if path.suffix == '.json':
|
|
|
|
|
|
try:
|
|
|
|
|
|
with path.open('r', encoding='utf-8') as f:
|
|
|
|
|
|
data = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
if "examples" in data and isinstance(data["examples"], list):
|
|
|
|
|
|
examples = data["examples"]
|
|
|
|
|
|
# 应用采样率
|
|
|
|
|
|
sample_size = max(1, int(len(examples) * sampling_rate))
|
|
|
|
|
|
sampled_examples = random.sample(examples, sample_size)
|
|
|
|
|
|
logger.info(f"文件 '{path.name}' 中的examples采样: {sample_size}/{len(examples)} (采样率: {sampling_rate:.2f})")
|
|
|
|
|
|
return f"参考标题列表:\n" + "\n".join([f"- {item.get('content', '')}" for item in sampled_examples])
|
2025-07-09 11:51:49 +08:00
|
|
|
|
else:
|
|
|
|
|
|
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"解析或格式化JSON文件 '{path}' 失败: {e}")
|
|
|
|
|
|
return f"加载文件 '{path.name}' 失败。"
|
|
|
|
|
|
else:
|
|
|
|
|
|
return path.read_text('utf-8')
|
|
|
|
|
|
|
2025-07-09 14:40:44 +08:00
|
|
|
|
def _load_refer_content(self, name: str, refer_configs: list, step: str = "") -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据带采样率的配置加载Refer内容
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
name: 资源名称
|
|
|
|
|
|
refer_configs: Refer配置列表
|
|
|
|
|
|
step: 当前阶段,用于过滤引用内容
|
|
|
|
|
|
"""
|
2025-07-09 11:51:49 +08:00
|
|
|
|
if not refer_configs:
|
|
|
|
|
|
return f"{name}:\n无"
|
|
|
|
|
|
|
|
|
|
|
|
content_parts = [f"{name}:"]
|
2025-07-09 14:40:44 +08:00
|
|
|
|
|
|
|
|
|
|
# 根据step过滤引用内容
|
|
|
|
|
|
filtered_configs = refer_configs
|
|
|
|
|
|
if step:
|
|
|
|
|
|
filtered_configs = [
|
|
|
|
|
|
item for item in refer_configs
|
|
|
|
|
|
if not item.step or item.step == step # 如果step为空或与当前阶段匹配
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
if not filtered_configs:
|
|
|
|
|
|
return f"{name}:\n无 (当前阶段: {step})"
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"阶段 '{step}' 过滤后剩余 {len(filtered_configs)}/{len(refer_configs)} 个引用项")
|
|
|
|
|
|
|
|
|
|
|
|
# 为每次调用重新随机采样
|
|
|
|
|
|
random.seed()
|
|
|
|
|
|
|
|
|
|
|
|
for ref_item in filtered_configs:
|
2025-07-09 11:51:49 +08:00
|
|
|
|
try:
|
|
|
|
|
|
path_str = ref_item.path
|
|
|
|
|
|
sampling_rate = ref_item.sampling_rate
|
|
|
|
|
|
|
|
|
|
|
|
full_path = self._get_full_path(path_str)
|
|
|
|
|
|
|
2025-07-09 14:53:24 +08:00
|
|
|
|
# 简化逻辑:对于单个文件,直接应用采样率决定是否加载
|
2025-07-09 11:51:49 +08:00
|
|
|
|
if full_path.is_file():
|
2025-07-09 14:53:24 +08:00
|
|
|
|
# 对于JSON文件,对内容进行采样
|
|
|
|
|
|
if full_path.suffix == '.json':
|
|
|
|
|
|
file_content = self._load_and_format_content_with_sampling(full_path, sampling_rate)
|
|
|
|
|
|
content_parts.append(f"--- {full_path.name} ---\n{file_content}")
|
|
|
|
|
|
logger.info(f"加载JSON文件 '{path_str}' 并应用内部采样")
|
|
|
|
|
|
# 对于其他文件,根据采样率决定是否完全加载
|
|
|
|
|
|
elif random.random() < sampling_rate:
|
|
|
|
|
|
file_content = self._load_and_format_content(full_path)
|
|
|
|
|
|
content_parts.append(f"--- {full_path.name} ---\n{file_content}")
|
|
|
|
|
|
logger.info(f"文件 '{path_str}' 采样成功 (采样率: {sampling_rate:.2f})")
|
2025-07-09 14:40:44 +08:00
|
|
|
|
else:
|
2025-07-09 14:53:24 +08:00
|
|
|
|
logger.info(f"文件 '{path_str}' 采样失败 (采样率: {sampling_rate:.2f})")
|
|
|
|
|
|
# 对于目录,直接选择指定比例的文件
|
2025-07-09 11:51:49 +08:00
|
|
|
|
elif full_path.is_dir():
|
|
|
|
|
|
all_files = sorted(p for p in full_path.iterdir() if p.is_file())
|
2025-07-09 14:53:24 +08:00
|
|
|
|
if all_files:
|
|
|
|
|
|
if sampling_rate < 1.0:
|
|
|
|
|
|
sample_size = max(1, int(len(all_files) * sampling_rate))
|
|
|
|
|
|
files_to_read = random.sample(all_files, sample_size)
|
|
|
|
|
|
logger.info(f"目录 '{path_str}' 采样: {sample_size}/{len(all_files)} 个文件 (采样率: {sampling_rate:.2f})")
|
|
|
|
|
|
else:
|
|
|
|
|
|
files_to_read = all_files
|
|
|
|
|
|
logger.info(f"目录 '{path_str}' 全部加载: {len(all_files)} 个文件")
|
|
|
|
|
|
|
|
|
|
|
|
for f_path in files_to_read:
|
|
|
|
|
|
file_content = self._load_and_format_content(f_path)
|
|
|
|
|
|
content_parts.append(f"--- {f_path.name} ---\n{file_content}")
|
2025-07-09 11:51:49 +08:00
|
|
|
|
else:
|
2025-07-09 14:53:24 +08:00
|
|
|
|
logger.warning(f"目录 '{path_str}' 中没有文件")
|
2025-07-09 11:51:49 +08:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载Refer资源 '{ref_item}' 失败: {e}", exc_info=True)
|
|
|
|
|
|
|
|
|
|
|
|
return "\n\n".join(content_parts)
|
|
|
|
|
|
|
|
|
|
|
|
def _find_and_read_file(self, filename_to_find: str, search_paths: List[str]) -> Optional[str]:
|
|
|
|
|
|
"""在指定的路径列表中查找并读取文件内容"""
|
|
|
|
|
|
if not self.resource_config.resource_dirs:
|
|
|
|
|
|
return "文件未找到"
|
|
|
|
|
|
base_path = Path(self.resource_config.resource_dirs[0])
|
2025-07-09 14:40:44 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取不带后缀的文件名,用于模糊匹配
|
|
|
|
|
|
filename_base = Path(filename_to_find).stem
|
|
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
for p_str in search_paths:
|
|
|
|
|
|
search_path = Path(p_str)
|
|
|
|
|
|
if not search_path.is_absolute():
|
|
|
|
|
|
search_path = base_path / search_path
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
if search_path.is_dir():
|
2025-07-09 14:40:44 +08:00
|
|
|
|
# 精确匹配
|
2025-07-09 11:51:49 +08:00
|
|
|
|
potential_path = search_path / filename_to_find
|
|
|
|
|
|
if potential_path.is_file():
|
|
|
|
|
|
return potential_path.read_text('utf-8')
|
2025-07-09 14:40:44 +08:00
|
|
|
|
|
|
|
|
|
|
# 基于文件名(不含后缀)的模糊匹配
|
|
|
|
|
|
for file_path in search_path.iterdir():
|
|
|
|
|
|
if file_path.is_file() and file_path.stem == filename_base:
|
|
|
|
|
|
logger.info(f"通过基础名称匹配找到文件: {filename_to_find} -> {file_path.name}")
|
|
|
|
|
|
return file_path.read_text('utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
elif search_path.is_file():
|
|
|
|
|
|
# 精确匹配
|
|
|
|
|
|
if search_path.name == filename_to_find:
|
|
|
|
|
|
return search_path.read_text('utf-8')
|
|
|
|
|
|
# 基于文件名(不含后缀)的模糊匹配
|
|
|
|
|
|
if search_path.stem == filename_base:
|
|
|
|
|
|
logger.info(f"通过基础名称匹配找到文件: {filename_to_find} -> {search_path.name}")
|
|
|
|
|
|
return search_path.read_text('utf-8')
|
|
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
logger.warning(f"在路径 {search_paths} 中未能找到文件: {filename_to_find}")
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
class TopicPromptBuilder(BasePromptBuilder):
|
|
|
|
|
|
"""
|
|
|
|
|
|
主题生成提示词构建器
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, config_manager: ConfigManager):
|
|
|
|
|
|
self.topic_config: GenerateTopicConfig = config_manager.get_config('topic_gen', GenerateTopicConfig)
|
|
|
|
|
|
super().__init__(
|
|
|
|
|
|
config_manager,
|
|
|
|
|
|
system_prompt_path=self.topic_config.topic_system_prompt,
|
|
|
|
|
|
user_prompt_path=self.topic_config.topic_user_prompt
|
|
|
|
|
|
)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
# 加载并格式化所有需要的资源
|
|
|
|
|
|
self.style_content = self._load_resource_as_list("Style文件列表", self.resource_config.style.paths)
|
|
|
|
|
|
self.demand_content = self._load_resource_as_list("Demand文件列表", self.resource_config.demand.paths)
|
2025-07-09 14:40:44 +08:00
|
|
|
|
self.refer_content = self._load_refer_content("Refer信息", self.resource_config.refer.refer_list, step="topic")
|
2025-07-09 11:51:49 +08:00
|
|
|
|
self.object_content = self._load_resource_as_content("Object信息", self.resource_config.object.paths)
|
|
|
|
|
|
self.product_content = self._load_resource_as_content("Product信息", self.resource_config.product.paths)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
def build_user_prompt(self, numTopics: int, month: str) -> str:
|
2025-07-09 11:51:49 +08:00
|
|
|
|
"""构建生成主题的用户提示词"""
|
|
|
|
|
|
creative_materials = (
|
|
|
|
|
|
f"你拥有的创作资料如下:\n"
|
|
|
|
|
|
f"{self.style_content}\n\n"
|
|
|
|
|
|
f"{self.demand_content}\n\n"
|
|
|
|
|
|
f"{self.refer_content}\n\n"
|
|
|
|
|
|
f"{self.object_content}\n\n"
|
|
|
|
|
|
f"{self.product_content}"
|
|
|
|
|
|
)
|
|
|
|
|
|
return super().build_user_prompt(
|
|
|
|
|
|
creative_materials=creative_materials,
|
2025-07-15 10:59:36 +08:00
|
|
|
|
numTopics=numTopics,
|
2025-07-09 11:51:49 +08:00
|
|
|
|
month=month
|
|
|
|
|
|
)
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
class ContentPromptBuilder(BasePromptBuilder):
|
|
|
|
|
|
"""
|
|
|
|
|
|
内容生成提示词构建器
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, config_manager: ConfigManager):
|
|
|
|
|
|
self.content_config: GenerateContentConfig = config_manager.get_config('content_gen', GenerateContentConfig)
|
|
|
|
|
|
super().__init__(
|
|
|
|
|
|
config_manager,
|
|
|
|
|
|
system_prompt_path=self.content_config.content_system_prompt,
|
|
|
|
|
|
user_prompt_path=self.content_config.content_user_prompt
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-09 14:40:44 +08:00
|
|
|
|
# 预加载静态的Product内容
|
2025-07-09 11:51:49 +08:00
|
|
|
|
self.product_content = self._load_resource_as_content("Product信息", self.resource_config.product.paths)
|
2025-07-09 14:40:44 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
def build_user_prompt(self, topic: Dict[str, Any]) -> str:
|
|
|
|
|
|
"""根据topic构建完整的用户提示词"""
|
2025-07-09 14:40:44 +08:00
|
|
|
|
# 每次构建提示词时重新加载Refer内容,确保随机性
|
|
|
|
|
|
refer_content = self._load_refer_content("Refer信息", self.resource_config.refer.refer_list, step="content")
|
|
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
style_filename = topic.get("style", "")
|
|
|
|
|
|
style_content = self._find_and_read_file(style_filename, self.resource_config.style.paths) or "通用风格"
|
|
|
|
|
|
|
|
|
|
|
|
demand_filename = topic.get("target_audience", "")
|
|
|
|
|
|
demand_content = self._find_and_read_file(demand_filename, self.resource_config.demand.paths) or "通用用户画像"
|
|
|
|
|
|
|
|
|
|
|
|
object_name = topic.get("object", "")
|
|
|
|
|
|
object_content = "无"
|
|
|
|
|
|
if object_name:
|
|
|
|
|
|
try:
|
|
|
|
|
|
object_path_str = next((p for p in self.resource_config.object.paths if object_name in p), None)
|
|
|
|
|
|
if object_path_str:
|
|
|
|
|
|
full_path = self._get_full_path(object_path_str)
|
|
|
|
|
|
if full_path.exists():
|
|
|
|
|
|
object_content = full_path.read_text('utf-8')
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning(f"找不到Object文件: {full_path}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning(f"在配置中找不到与 '{object_name}' 匹配的object路径")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载Object内容 '{object_name}' 失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
return super().build_user_prompt(
|
|
|
|
|
|
style_content=f"{style_filename}\n{style_content}",
|
|
|
|
|
|
demand_content=f"{demand_filename}\n{demand_content}",
|
|
|
|
|
|
object_content=f"{object_name}\n{object_content}",
|
2025-07-09 14:40:44 +08:00
|
|
|
|
refer_content=refer_content,
|
2025-07-09 11:51:49 +08:00
|
|
|
|
product_content=self.product_content,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class JudgerPromptBuilder(BasePromptBuilder):
|
|
|
|
|
|
"""
|
|
|
|
|
|
内容审核提示词构建器
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, config_manager: ConfigManager):
|
|
|
|
|
|
self.content_config = config_manager.get_config('content_gen', GenerateContentConfig)
|
|
|
|
|
|
super().__init__(
|
|
|
|
|
|
config_manager,
|
|
|
|
|
|
system_prompt_path=self.content_config.judger_system_prompt,
|
|
|
|
|
|
user_prompt_path=self.content_config.judger_user_prompt
|
2025-07-08 18:24:23 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
self.product_content = self._load_resource_as_content("Product信息", self.resource_config.product.paths)
|
|
|
|
|
|
|
|
|
|
|
|
def build_user_prompt(self, generated_content: str, topic: Dict[str, Any]) -> str:
|
|
|
|
|
|
"""构建审核内容的用户提示词"""
|
2025-07-09 14:40:44 +08:00
|
|
|
|
# 每次构建提示词时重新加载Refer内容,确保随机性
|
|
|
|
|
|
refer_content = self._load_refer_content("Refer信息", self.resource_config.refer.refer_list, step="judge")
|
|
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
if isinstance(generated_content, dict):
|
|
|
|
|
|
tweet_content = json.dumps(generated_content, ensure_ascii=False, indent=4)
|
|
|
|
|
|
else:
|
|
|
|
|
|
tweet_content = str(generated_content)
|
|
|
|
|
|
|
|
|
|
|
|
object_name = topic.get("object", "")
|
|
|
|
|
|
object_content = "无"
|
|
|
|
|
|
if object_name:
|
|
|
|
|
|
try:
|
|
|
|
|
|
object_path_str = next((p for p in self.resource_config.object.paths if object_name in p), None)
|
|
|
|
|
|
if object_path_str:
|
|
|
|
|
|
full_path = self._get_full_path(object_path_str)
|
|
|
|
|
|
if full_path.exists():
|
|
|
|
|
|
object_content = full_path.read_text('utf-8')
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning(f"找不到Object文件: {full_path}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning(f"在配置中找不到与 '{object_name}' 匹配的object路径")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载Object内容 '{object_name}' 失败: {e}")
|
2025-07-08 18:24:23 +08:00
|
|
|
|
|
2025-07-09 11:51:49 +08:00
|
|
|
|
return super().build_user_prompt(
|
|
|
|
|
|
tweet_content=tweet_content,
|
|
|
|
|
|
object_content=object_content,
|
2025-07-09 14:40:44 +08:00
|
|
|
|
product_content=self.product_content,
|
|
|
|
|
|
refer_content=refer_content
|
2025-07-09 11:51:49 +08:00
|
|
|
|
)
|