From 24781dec3c914a85969610f1062dcf91ace8e7cb Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Fri, 11 Jul 2025 17:39:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=8E=A8=E6=96=87?= =?UTF-8?q?=E7=9A=84=E4=BC=A0=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/__pycache__/main.cpython-312.pyc | Bin 2705 -> 2716 bytes api/main.py | 6 +- api/models/__pycache__/tweet.cpython-312.pyc | Bin 7424 -> 8128 bytes api/models/tweet.py | 34 +++++---- api/routers/__pycache__/tweet.cpython-312.pyc | Bin 8096 -> 8370 bytes api/routers/tweet.py | 32 +++++---- .../prompt_builder.cpython-312.pyc | Bin 9905 -> 11923 bytes .../prompt_service.cpython-312.pyc | Bin 29964 -> 31432 bytes .../__pycache__/tweet.cpython-312.pyc | Bin 11839 -> 12402 bytes api/services/prompt_builder.py | 68 +++++++++++++----- api/services/prompt_service.py | 31 ++++++-- api/services/tweet.py | 49 ++++++++----- .../__pycache__/manager.cpython-312.pyc | Bin 12412 -> 12412 bytes .../config/__pycache__/models.cpython-312.pyc | Bin 9601 -> 9594 bytes core/config/models.py | 2 +- 15 files changed, 155 insertions(+), 67 deletions(-) diff --git a/api/__pycache__/main.cpython-312.pyc b/api/__pycache__/main.cpython-312.pyc index 2ef7585ba920c8c6352372f95385240ffc9ae725..f29ad4c71936ea6956c46c01dc9767f67605c4b8 100644 GIT binary patch delta 148 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 diff --git a/api/models/tweet.py b/api/models/tweet.py index 874dedc..63d02a0 100644 --- a/api/models/tweet.py +++ b/api/models/tweet.py @@ -11,18 +11,22 @@ from pydantic import BaseModel, Field class TopicRequest(BaseModel): """选题生成请求模型""" - date: str = Field(..., description="选题日期,格式为YYYY-MM-DD") + dates: Optional[str] = Field(None, description="日期字符串,可能为单个日期、多个日期用逗号分隔或范围如'2023-01-01 to 2023-01-31'") num_topics: int = Field(5, description="要生成的选题数量", ge=1, le=10) - style: Optional[str] = Field(None, description="内容风格,如'旅游攻略'、'亲子游'等") - target_audience: Optional[str] = Field(None, description="目标受众,如'年轻人'、'家庭'等") + styles: Optional[List[str]] = Field(None, description="风格列表") + audiences: Optional[List[str]] = Field(None, description="受众列表") + scenic_spots: Optional[List[str]] = Field(None, description="景区列表") + products: Optional[List[str]] = Field(None, description="产品列表") class Config: schema_extra = { "example": { - "date": "2023-07-15", - "num_topics": 3, - "style": "旅游攻略", - "target_audience": "年轻人" + "dates": "2023-07-01 to 2023-07-31", + "num_topics": 5, + "styles": ["旅游攻略", "亲子游"], + "audiences": ["年轻人", "家庭"], + "scenic_spots": ["故宫", "长城"], + "products": ["门票", "导游服务"] } } @@ -141,19 +145,23 @@ class JudgeResponse(BaseModel): class PipelineRequest(BaseModel): """完整流程请求模型""" - date: str = Field(..., description="选题日期,格式为YYYY-MM-DD") + dates: Optional[str] = Field(None, description="日期字符串,可能为单个日期、多个日期用逗号分隔或范围如'2023-01-01 to 2023-01-31'") num_topics: int = Field(5, description="要生成的选题数量", ge=1, le=10) - style: Optional[str] = Field(None, description="内容风格,如'旅游攻略'、'亲子游'等") - target_audience: Optional[str] = Field(None, description="目标受众,如'年轻人'、'家庭'等") + styles: Optional[List[str]] = Field(None, description="风格列表") + audiences: Optional[List[str]] = Field(None, description="受众列表") + scenic_spots: Optional[List[str]] = Field(None, description="景区列表") + products: Optional[List[str]] = Field(None, description="产品列表") skip_judge: bool = Field(False, description="是否跳过内容审核步骤") class Config: schema_extra = { "example": { - "date": "2023-07-15", + "dates": "2023-07-01 to 2023-07-31", "num_topics": 3, - "style": "旅游攻略", - "target_audience": "年轻人", + "styles": ["旅游攻略", "亲子游"], + "audiences": ["年轻人", "家庭"], + "scenic_spots": ["故宫", "长城"], + "products": ["门票", "导游服务"], "skip_judge": False } } diff --git a/api/routers/__pycache__/tweet.cpython-312.pyc b/api/routers/__pycache__/tweet.cpython-312.pyc index 2dfc3caf1613838050d0fcc338bd211289c64b95..8275c03a9b0c6449ecfcd987bde261629c1f3834 100644 GIT binary patch delta 1870 zcmds1Urbw79KNTw|L&!KfcCbuP*w(v740UQbugi&B`(U`#F0hNIWprwAE&VC%FS9w{p!y6jxv^bC<68Vn zv7J#-RKKd9PMGmYW=nZ-K~+Nujs5CGX<^*Q;Xnz8XBey5UWkQtVRgcRe`1q|>z3%zcXY-dol)p^fx%TZ5@XiZkTa|=meUuq;?|-Uy~dpRI3MK&l4mu8@FY1% zp&6hBzz+}rXvH^+ng)VELj-b&7d7M;HtiO6K=_#g>>)&)A+SLGHAv+vi#3OlRQ*`C zx;0bXDwR4?-t%6`UYWIfGIoz-ai;v|{gTaSK5 zL9|5aYFN`fN>|Bd^v)sn?S0Fo_;rI-XCaaM%T7FOXy4;QZw76_1}g1D!JX=Kxdr$R z6!M|;P6WJx3GJfOdv}Mt^sHAy`0Oq^1 zPUw74&cl#Xfk~%jLS=&L2R-W5_2AoF%Z36~HYcYn&6FZKys}3S|r}nmkb~0*Z$xT z{=wONXx)V+LXRnD5R%cJcqGoBh!Af-+|WcVyaZ4J@W9&uP8W^9oJ*>BWSP+vvw2Fxuz8MX?=QRY_KNn#HZ^$)s>u4&wJ7LUr?=UGw;&Qv`w`)@ z6w`+2S!Q>rnVxOd5dICK+thfh;`Vm1drm9Fk_leOspBy*vGTYn7FO)mO%%dWJnnL_ z5ke8^;P1}TaE1NB%o3<*%+J@SyNw{1= z5J?rM4rOb3luT4sHm0DqvQ?x_;~;&Z!d?YmH0G7CCS5@>ZUNmJ@%dfaS_h(Uec<`> z{XO@)-#yR$?sHee^F7+@8VyI0q5U@b+zNk1n{Pr9T-ZN|E$n@Y&(UWJtk;_GEZc-s z_kt|tDEm#cHfwJ)xD9uT3(nXYwpd3Gn}0apWW+~M#YJk6dj!&pNz%FVo+@JgT>1H zo8@zvja+8CSb62kn_sVPWV3Rm^5R$J?80^#o89hFhzH0?N?d&U#i*1>PL8ESCs`2h zC4yH}PdZd3-9#)N6Gx@kP6FH^J_J%v#l7mFvJ2u6PODqeePBTjlbQOI$W7|^8LHZ* zWlo`@uA4jslc%_k&-mtiMZvKy_zQx+%2LiSx=k@o?l+3D za)qY0x47A(H7%vL6io+;2ENA7dag>*ELYW$ggdnsN^QJFVV9==unoN)rov__uRvj5 zk!K9xTT#S@@;m}>W+MWfclskHddI9|Za79SNEjz|_o({8 z9tAmuKhm}7?b2G`kQxkB|ay$^}BqXcCUMjCc;dVt{ZvyW| zWIf-GztIb-V{ diff --git a/api/routers/tweet.py b/api/routers/tweet.py index 2fe8913..7eb282d 100644 --- a/api/routers/tweet.py +++ b/api/routers/tweet.py @@ -60,17 +60,21 @@ async def generate_topics( """ 生成选题 - - **date**: 选题日期,格式为YYYY-MM-DD + - **dates**: 日期字符串,可能为单个日期、多个日期用逗号分隔或范围 - **num_topics**: 要生成的选题数量 - - **style**: 内容风格,如'旅游攻略'、'亲子游'等 - - **target_audience**: 目标受众,如'年轻人'、'家庭'等 + - **styles**: 风格列表 + - **audiences**: 受众列表 + - **scenic_spots**: 景区列表 + - **products**: 产品列表 """ try: request_id, topics = await tweet_service.generate_topics( - date=request.date, + dates=request.dates, num_topics=request.num_topics, - style=request.style, - target_audience=request.target_audience + styles=request.styles, + audiences=request.audiences, + scenic_spots=request.scenic_spots, + products=request.products ) return TopicResponse( @@ -172,18 +176,22 @@ async def run_pipeline( """ 运行完整流水线,包括生成选题、生成内容和审核内容 - - **date**: 选题日期,格式为YYYY-MM-DD + - **dates**: 日期字符串,可能为单个日期、多个日期用逗号分隔或范围 - **num_topics**: 要生成的选题数量 - - **style**: 内容风格,如'旅游攻略'、'亲子游'等 - - **target_audience**: 目标受众,如'年轻人'、'家庭'等 + - **styles**: 风格列表 + - **audiences**: 受众列表 + - **scenic_spots**: 景区列表 + - **products**: 产品列表 - **skip_judge**: 是否跳过内容审核步骤 """ try: request_id, topics, contents, judged_contents = await tweet_service.run_pipeline( - date=request.date, + dates=request.dates, num_topics=request.num_topics, - style=request.style, - target_audience=request.target_audience, + styles=request.styles, + audiences=request.audiences, + scenic_spots=request.scenic_spots, + products=request.products, skip_judge=request.skip_judge ) diff --git a/api/services/__pycache__/prompt_builder.cpython-312.pyc b/api/services/__pycache__/prompt_builder.cpython-312.pyc index 901ba5e6f58a707d66327a0b0f7f47af6d88657a..750111f3fe660edefe9b253b9dea2b5916765509 100644 GIT binary patch delta 4216 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 delta 1386 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 delta 2671 zcmcgtZA@F&89vAN!}nhM`UBfIb^;g^2sR%fVI!^!B!DqZLrAlvX&`A`#=Q_DFz8~B zR=X~dmbRp#&9w6)Q`5D{KD0z+85P<@+eo3QMvyj5rp;8#o1$zAbV$?cG}RVq)4Fr6 zfp}Xw?bnXw=e*}V=RM~+ug~-PvuCdzH~mr+1%TU6Q==y$Ti-T4suLV!jN85MphLQ< z38_%h%bdi?yu{0b%(?KCBy3m)*&uVWQ5G?p`gLTMd~;U6AQ?|FJ!VPFpOm6?Z~`rtoOk&JJHk zVeSQnQK8D9PdSIk;;d**)oGr+E}aT5)6=?~_c-p8Y*Md+K|8YYHZ;WB8CKO#>(JW{ zC&j58dW-)n;;(?AaRhte!l*`u*>&oc+q6W4Wz?;t)P8(8%^)sQFhB^2A zl~)Y6#S#KNW=7?Zm7@4YrQfATb{$ zu!}%|z#|xZdZn1yJ27Mo+QW!MW0L%w(oEdD33L!>LDy{U@EBt3e`9k~v#jH~jt!B? zcJyoKj)^cfyOhU>BaaCg?j`Uz2A@IMN9-IkVz~+IC%_T-UwGzG>mo5ZegGGpF+Coy z{jy34ncI)hrdk+B*EjtZcFex%`pX96*SCcJC1I;uDz_1e2@8g5&+?TEE9d7vc@M3; z`9{#=6Z3haKv8SwmonuaWHd)jBe{{zjA1Y#4aiai>#JrNozmE zB^|R4f$24$#IExo?9dNO%b*o4mhN^6s$qRbyRDcs;Y@h7!m?AaahRTt!T*Wb3UA&5nc9{K?_QGOB=1Y@>o<0sWX!Ga>4pB5Of2HOCOx?`&<8T~2 zM|Fd7s8t?&;w(fpWsm(IO3zDMZ*8f>Da!Dp(NTFQ8j~Xrl+}erezLr7U4TC?zvv`d z?D9eFsrsmluC^`%<4PAwRP2QxqH7hm0uOZeU)83x;++)ySU~RCA3(p^R$@3z#^iTY zoe;^3vA(4v($YaW)hRE9`|puO*(cB+s_(Sz-UHKhUr7g>)73lf>H|#E z`LQg(-5jNLX;0-v+XXR8Vb^W{&YAj4HCYDRSr%0Ivt)giMWNcbhnPw!p>N0F^C-jQ zR{Eilfq`gjKncvIYmLx1NET?XkgQTh31~s2T4%V%;Snh@BsVK666=nb+bP)e86c=F F`X>VtqAmac diff --git a/api/services/prompt_builder.py b/api/services/prompt_builder.py index db27d5f..a1c7c48 100644 --- a/api/services/prompt_builder.py +++ b/api/services/prompt_builder.py @@ -7,7 +7,7 @@ """ import logging -from typing import Dict, Any, Optional, Tuple +from typing import Dict, Any, Optional, Tuple, List from pathlib import Path from core.config import ConfigManager, GenerateContentConfig, GenerateTopicConfig, PosterConfig @@ -150,13 +150,17 @@ class PromptBuilderService: return system_prompt, user_prompt - def build_topic_prompt(self, num_topics: int, month: str) -> Tuple[str, str]: + def build_topic_prompt(self, products: Optional[List[str]] = None, scenic_spots: Optional[List[str]] = None, styles: Optional[List[str]] = None, audiences: Optional[List[str]] = None, dates: Optional[str] = None, num_topics: int = 5) -> Tuple[str, str]: """ 构建选题生成提示词 Args: + products: 产品列表 + scenic_spots: 景区列表 + styles: 风格列表 + audiences: 受众列表 + dates: 日期字符串,可能为单个日期、多个日期用逗号分隔或范围如'2023-01-01 to 2023-01-31' num_topics: 要生成的选题数量 - month: 月份 Returns: 系统提示词和用户提示词的元组 @@ -173,20 +177,51 @@ class PromptBuilderService: # 创建提示词模板 template = PromptTemplate(system_prompt_path, user_prompt_path) - # 获取风格列表 - styles = self.prompt_service.get_all_styles() - style_content = "Style文件列表:\n" + "\n".join([f"- {style['name']}" for style in styles]) + # 处理日期 + if dates: + if ' to ' in dates: + start_date, end_date = dates.split(' to ') + month = f"从 {start_date} 到 {end_date}" + elif ',' in dates: + month = ', '.join(dates.split(',')) + else: + month = dates + else: + month = '' - # 获取目标受众列表 - audiences = self.prompt_service.get_all_audiences() - demand_content = "Demand文件列表:\n" + "\n".join([f"- {audience['name']}" for audience in audiences]) + # 获取风格内容 + style_content = '' + if styles: + style_content = '\n'.join([f"{style}: {self.prompt_service.get_style_content(style)}" for style in styles]) + else: + all_styles = self.prompt_service.get_all_styles() + style_content = "Style文件列表:\n" + "\n".join([f"- {style['name']}" for style in all_styles]) + + # 获取受众内容 + demand_content = '' + if audiences: + demand_content = '\n'.join([f"{audience}: {self.prompt_service.get_audience_content(audience)}" for audience in audiences]) + else: + all_audiences = self.prompt_service.get_all_audiences() + demand_content = "Demand文件列表:\n" + "\n".join([f"- {audience['name']}" for audience in all_audiences]) # 获取参考内容 refer_content = self.prompt_service.get_refer_content("topic") - # 获取景区信息列表 - spots = self.prompt_service.get_all_scenic_spots() - object_content = "Object信息:\n" + "\n".join([f"- {spot['name']}" for spot in spots]) + # 获取景区内容 + object_content = '' + if scenic_spots: + object_content = '\n'.join([f"{spot}: {self.prompt_service.get_scenic_spot_info(spot)}" for spot in scenic_spots]) + else: + all_spots = self.prompt_service.get_all_scenic_spots() + object_content = "Object信息:\n" + "\n".join([f"- {spot['name']}" for spot in all_spots]) + + # 获取产品内容 + product_content = '' + if products: + product_content = '\n'.join([f"{product}: {self.prompt_service.get_product_info(product)}" for product in products]) + else: + product_content = '' # 假设没有默认产品列表 # 构建系统提示词 system_prompt = template.get_system_prompt() @@ -194,10 +229,11 @@ class PromptBuilderService: # 构建创作资料 creative_materials = ( f"你拥有的创作资料如下:\n" - f"{style_content}\n\n" - f"{demand_content}\n\n" - f"{refer_content}\n\n" - f"{object_content}" + f"风格信息:\n{style_content}\n\n" + f"受众信息:\n{demand_content}\n\n" + f"参考内容:\n{refer_content}\n\n" + f"景区信息:\n{object_content}\n\n" + f"产品信息:\n{product_content}" ) # 构建用户提示词 diff --git a/api/services/prompt_service.py b/api/services/prompt_service.py index d514420..12d0411 100644 --- a/api/services/prompt_service.py +++ b/api/services/prompt_service.py @@ -342,13 +342,36 @@ class PromptService: full_path = self._get_full_path(path_str) if full_path.exists() and full_path.is_file(): - with open(full_path, 'r', encoding='utf-8') as f: - lines = f.readlines() - if lines: + if full_path.suffix.lower() == '.json': + # 处理JSON文件 + with open(full_path, 'r', encoding='utf-8') as f: + data = json.load(f) + if isinstance(data, dict) and 'examples' in data: + examples = data['examples'] + if isinstance(examples, list): + sample_size = max(1, int(len(examples) * ref_item.sampling_rate)) + sampled_examples = random.sample(examples, sample_size) + sampled_content = json.dumps({'examples': sampled_examples}, ensure_ascii=False, indent=4) + elif isinstance(data, list): + sample_size = max(1, int(len(data) * ref_item.sampling_rate)) + sampled_examples = random.sample(data, sample_size) + sampled_content = json.dumps(sampled_examples, ensure_ascii=False, indent=4) + else: + # 如果不是预期结构,按原方式处理 + with open(full_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + sample_size = max(1, int(len(lines) * ref_item.sampling_rate)) + sampled_lines = random.sample(lines, sample_size) + sampled_content = ''.join(sampled_lines) + else: + # 非JSON文件,按原方式处理 + with open(full_path, 'r', encoding='utf-8') as f: + lines = f.readlines() sample_size = max(1, int(len(lines) * ref_item.sampling_rate)) sampled_lines = random.sample(lines, sample_size) sampled_content = ''.join(sampled_lines) - refer_content += f"--- {full_path.name} (sampled {ref_item.sampling_rate * 100}%) ---\n{sampled_content}\n\n" + + refer_content += f"--- {full_path.name} (sampled {ref_item.sampling_rate * 100}%) ---\n{sampled_content}\n\n" except Exception as e: logger.error(f"读取或采样参考文件失败 {ref_item.path}: {e}") except Exception as e: diff --git a/api/services/tweet.py b/api/services/tweet.py index 50d1a0b..12bf79e 100644 --- a/api/services/tweet.py +++ b/api/services/tweet.py @@ -48,32 +48,41 @@ class TweetService: self.prompt_service = PromptService(config_manager) self.prompt_builder = PromptBuilderService(config_manager, self.prompt_service) - async def generate_topics(self, date: str, num_topics: int = 5, - style: Optional[str] = None, - target_audience: Optional[str] = None) -> Tuple[str, List[Dict[str, Any]]]: + async def generate_topics(self, dates: Optional[str] = None, num_topics: int = 5, + styles: Optional[List[str]] = None, + audiences: Optional[List[str]] = None, + scenic_spots: Optional[List[str]] = None, + products: Optional[List[str]] = None) -> Tuple[str, List[Dict[str, Any]]]: """ 生成选题 Args: - date: 选题日期,格式为YYYY-MM-DD + dates: 日期字符串,可能为单个日期、多个日期用逗号分隔或范围 num_topics: 要生成的选题数量 - style: 内容风格 - target_audience: 目标受众 + styles: 风格列表 + audiences: 受众列表 + scenic_spots: 景区列表 + products: 产品列表 Returns: 请求ID和生成的选题列表 """ - logger.info(f"开始生成选题,日期: {date}, 数量: {num_topics}") + logger.info(f"开始生成选题,日期: {dates}, 数量: {num_topics}") # 获取并更新配置 topic_config = self.config_manager.get_config('topic_gen', GenerateTopicConfig) - topic_config.topic.date = date + if dates: + topic_config.topic.date = dates topic_config.topic.num = num_topics # 使用PromptBuilderService构建提示词 system_prompt, user_prompt = self.prompt_builder.build_topic_prompt( - num_topics=num_topics, - month=date + products=products, + scenic_spots=scenic_spots, + styles=styles, + audiences=audiences, + dates=dates, + num_topics=num_topics ) # 使用预构建的提示词生成选题 @@ -164,30 +173,34 @@ class TweetService: logger.info(f"内容审核完成,请求ID: {request_id}, 选题索引: {topic_index}, 审核结果: {judge_success}") return request_id, topic_index, judged_data, judge_success - async def run_pipeline(self, date: str, num_topics: int = 5, - style: Optional[str] = None, - target_audience: Optional[str] = None, + async def run_pipeline(self, dates: Optional[str] = None, num_topics: int = 5, + styles: Optional[List[str]] = None, + audiences: Optional[List[str]] = None, + scenic_spots: Optional[List[str]] = None, + products: Optional[List[str]] = None, skip_judge: bool = False) -> Tuple[str, List[Dict[str, Any]], Dict[str, Dict[str, Any]], Dict[str, Dict[str, Any]]]: """ 运行完整流水线 Args: - date: 选题日期,格式为YYYY-MM-DD + dates: 日期字符串,可能为单个日期、多个日期用逗号分隔或范围 num_topics: 要生成的选题数量 - style: 内容风格 - target_audience: 目标受众 + styles: 风格列表 + audiences: 受众列表 + scenic_spots: 景区列表 + products: 产品列表 skip_judge: 是否跳过内容审核步骤 Returns: 请求ID、生成的选题列表、生成的内容和审核后的内容 """ - logger.info(f"开始运行完整流水线,日期: {date}, 数量: {num_topics}") + logger.info(f"开始运行完整流水线,日期: {dates}, 数量: {num_topics}") # 生成请求ID request_id = f"pipeline_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{str(uuid.uuid4())[:8]}" # 步骤1: 生成选题 - _, topics = await self.generate_topics(date, num_topics, style, target_audience) + _, topics = await self.generate_topics(dates, num_topics, styles, audiences, scenic_spots, products) if not topics: logger.error("未能生成任何选题,流程终止") return request_id, [], {}, {} diff --git a/core/config/__pycache__/manager.cpython-312.pyc b/core/config/__pycache__/manager.cpython-312.pyc index d913be138dc74d6a6a0570bef009f987aa534e09..e3edd69f0a8e4d7eac134923d2ecaca52be38e6f 100644 GIT binary patch delta 19 Zcmey9@F#)mG%qg~0}uq=+Q?O8002kI20H)% delta 19 Zcmey9@F#)mG%qg~0}vFi+Q?O8002jz1}p#o diff --git a/core/config/__pycache__/models.cpython-312.pyc b/core/config/__pycache__/models.cpython-312.pyc index 76b04568e551432a3910f7a00264ad8859943738..c0851bf971edf1a9540663ad31774e845e9cb714 100644 GIT binary patch delta 50 zcmZql{^iAcnwOW00SNqV7i2hX> delta 61 zcmez6)#%N8nwOW00SNxAD#-BM$U8-n(QvY!qQvCCk{le#`FSO&c_qP{4@+)h00Csm3MF0Q* diff --git a/core/config/models.py b/core/config/models.py index e9655b7..030aeb1 100644 --- a/core/config/models.py +++ b/core/config/models.py @@ -130,7 +130,7 @@ class GenerateTopicConfig(BaseConfig): class GenerateContentConfig(BaseConfig): """内容生成配置""" - content_system_prompt: str = "resource/prompt/generateContent/contentSystem.txt" + content_system_prompt: str = "resource/prompt/generateContent/system.txt" content_user_prompt: str = "resource/prompt/generateContent/user.txt" judger_system_prompt: str = "resource/prompt/judgeContent/system.txt" judger_user_prompt: str = "resource/prompt/judgeContent/user.txt"