From 59bb2d9865a1a16d94015fe812b45648ab304891 Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Thu, 24 Apr 2025 21:44:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E9=A5=BF=E4=BA=86=E8=B0=83?= =?UTF-8?q?=E5=BA=A6=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/__pycache__/__init__.cpython-310.pyc | Bin 438 -> 438 bytes core/__pycache__/__init__.cpython-312.pyc | Bin 452 -> 452 bytes core/__pycache__/contentGen.cpython-312.pyc | Bin 5023 -> 5023 bytes main.py | 11 +- poster_gen_config.json | 29 +- test_prompt_manager.py | 138 ++++++ utils/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 360 bytes .../content_generator.cpython-312.pyc | Bin 23116 -> 23116 bytes .../prompt_manager.cpython-312.pyc | Bin 15629 -> 28187 bytes .../resource_loader.cpython-310.pyc | Bin 0 -> 4090 bytes .../tweet_generator.cpython-310.pyc | Bin 0 -> 18887 bytes .../tweet_generator.cpython-312.pyc | Bin 29470 -> 29552 bytes utils/prompt_manager.py | 455 ++++++++++++++---- utils/tweet_generator.py | 2 + 14 files changed, 544 insertions(+), 91 deletions(-) create mode 100644 test_prompt_manager.py create mode 100644 utils/__pycache__/__init__.cpython-310.pyc create mode 100644 utils/__pycache__/resource_loader.cpython-310.pyc create mode 100644 utils/__pycache__/tweet_generator.cpython-310.pyc diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc index 1f64f05dd9b1d7b8d2644a06a50db5c251a11a50..31fef20a0052906a310fb0dc0aacfa064e961e1e 100644 GIT binary patch delta 20 acmdnSyp5SVpO=@50SKNMb8X~a%?JQ6b_ARN delta 20 acmdnSyp5SVpO=@50SFEiGHm2t%?JQ6yacQO diff --git a/core/__pycache__/__init__.cpython-312.pyc b/core/__pycache__/__init__.cpython-312.pyc index cf3c617c6dcfb4937ef14d5ffda189108699addc..c380f473435dfeec2bcac1eb2804a841629612c7 100644 GIT binary patch delta 20 acmX@Ye1w_%G%qg~0}wnj=Gw@;lMw(o3I!qn delta 20 acmX@Ye1w_%G%qg~0}vc4WZ1~PlMw(oPz5po diff --git a/core/__pycache__/contentGen.cpython-312.pyc b/core/__pycache__/contentGen.cpython-312.pyc index aa6f51fd686bcee0f055a117dc11f3c95314ebdd..da08a84bcbdabef1e10d698423c080f343e87b44 100644 GIT binary patch delta 19 ZcmbQQK3|>dG%qg~0}wnj-pDmo7yvVZ1p)v7 delta 19 ZcmbQQK3|>dG%qg~0}!+*Z{(UP3;;0Z1dsp# diff --git a/main.py b/main.py index 3997178..b180aa0 100644 --- a/main.py +++ b/main.py @@ -35,11 +35,18 @@ def load_config(config_path="poster_gen_config.json"): with open(config_path, 'r', encoding='utf-8') as f: config = json.load(f) # Basic validation (can be expanded) - required_keys = ["api_url", "model", "api_key", "resource_dir", "prompts_dir", "output_dir", "num", "variants", "topic_system_prompt", "topic_user_prompt", "content_system_prompt", "image_base_dir"] + required_keys = ["api_url", "model", "api_key", "resource_dir", "output_dir", "num", "variants", "topic_system_prompt", "topic_user_prompt", "content_system_prompt", "image_base_dir"] if not all(key in config for key in required_keys): missing_keys = [key for key in required_keys if key not in config] print(f"Error: Config file '{config_path}' is missing required keys: {missing_keys}") sys.exit(1) + + # 验证prompts_dir或prompts_config至少有一个存在 + if not ("prompts_dir" in config + or "prompts_config" in config): + print(f"Error: Config file '{config_path}' must contain either 'prompts_dir' or 'prompts_config'") + sys.exit(1) + # Resolve relative paths based on config location or a defined base path if necessary # For simplicity, assuming paths in config are relative to project root or absolute return config @@ -77,7 +84,7 @@ def generate_content_and_posters_step(config, run_id, topics_list, output_handle topic_system_prompt_path=config.get("topic_system_prompt"), topic_user_prompt_path=config.get("topic_user_prompt"), content_system_prompt_path=config.get("content_system_prompt"), - prompts_dir=config.get("prompts_dir"), + prompts_config=config.get("prompts_config"), # 新的配置方式 resource_dir_config=config.get("resource_dir", []), topic_gen_num=config.get("num", 1), # Topic gen num/date used by topic prompts topic_gen_date=config.get("date", "") diff --git a/poster_gen_config.json b/poster_gen_config.json index 6718ed1..64f0c01 100644 --- a/poster_gen_config.json +++ b/poster_gen_config.json @@ -15,6 +15,34 @@ "topic_user_prompt": "./SelectPrompt/userPrompt.txt", "content_system_prompt": "./genPrompts/systemPrompt.txt", "poster_content_system_prompt": "./genPrompts/poster_content_systemPrompt.txt", + "prompts_config": [ + { + "type": "Style", + "file_path": [ + "./genPrompts/Style/攻略风文案提示词.txt", + "./genPrompts/Style/轻奢风文案提示词.txt", + "./genPrompts/Style/极力推荐风文案提示词.txt", + "./genPrompts/Style/美食风文案提示词.txt" + ] + }, + { + "type": "Demand", + "file_path": [ + "./genPrompts/Demand/学生党文旅需求.txt", + "./genPrompts/Demand/情侣向文旅需求.txt", + "./genPrompts/Demand/职场人文旅需求.txt", + "./genPrompts/Demand/亲子向文旅需求.txt", + "./genPrompts/Demand/周边游文旅需求.txt", + "./genPrompts/Demand/夕阳红文旅需求.txt" + ] + }, + { + "type": "Refer", + "file_path": [ + "./genPrompts/Refer/标题参考格式.txt" + ] + } + ], "resource_dir": [ { "type": "Object", @@ -38,7 +66,6 @@ ] } ], - "prompts_dir": "./genPrompts", "output_dir": "./result", "image_base_dir": "/root/autodl-tmp/TravelContentCreator/hotel_img", "poster_assets_base_dir": "/root/autodl-tmp/poster_baseboard_0403", diff --git a/test_prompt_manager.py b/test_prompt_manager.py new file mode 100644 index 0000000..c9d61f0 --- /dev/null +++ b/test_prompt_manager.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import json +import os +import logging +from utils.prompt_manager import PromptManager + +# 设置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def test_prompt_manager(): + """测试新的PromptManager配置方式""" + logger.info("=== 开始测试PromptManager ===") + + # 加载配置文件 + try: + with open("poster_gen_config.json", "r", encoding="utf-8") as f: + config = json.load(f) + except Exception as e: + logger.error(f"加载配置文件失败: {e}") + return + + # 创建PromptManager实例 + try: + # 使用新的prompts_config配置 + prompt_manager = PromptManager( + topic_system_prompt_path=config.get("topic_system_prompt"), + topic_user_prompt_path=config.get("topic_user_prompt"), + content_system_prompt_path=config.get("content_system_prompt"), + prompts_config=config.get("prompts_config"), # 新的配置方式 + resource_dir_config=config.get("resource_dir", []), + topic_gen_num=config.get("num", 1), + topic_gen_date=config.get("date", "") + ) + logger.info("成功创建PromptManager实例") + except Exception as e: + logger.error(f"创建PromptManager实例失败: {e}") + return + + # 测试1: 生成topic提示词 + try: + logger.info("测试1: 生成topic提示词") + system_prompt, user_prompt = prompt_manager.get_topic_prompts() + if system_prompt and user_prompt: + logger.info(f"成功生成topic提示词: 系统提示词 ({len(system_prompt)} 字符), 用户提示词 ({len(user_prompt)} 字符)") + else: + logger.error("生成topic提示词失败,返回了None") + except Exception as e: + logger.error(f"测试topic提示词生成时出错: {e}") + + # 测试2: 测试Style文件加载 + try: + logger.info("测试2: 测试Style文件加载") + styles = ["攻略风文案提示词", "轻奢风文案提示词", "极力推荐风文案提示词", "美食风文案提示词"] + + for style in styles: + content = prompt_manager._get_style_content(style) + if content: + logger.info(f"成功加载Style文件 '{style}' ({len(content)} 字符)") + else: + logger.warning(f"未能加载Style文件 '{style}'") + except Exception as e: + logger.error(f"测试Style文件加载时出错: {e}") + + # 测试3: 测试Demand文件加载 + try: + logger.info("测试3: 测试Demand文件加载") + demands = ["学生党文旅需求", "情侣向文旅需求", "职场人文旅需求", "亲子向文旅需求", "周边游文旅需求", "夕阳红文旅需求"] + + for demand in demands: + content = prompt_manager._get_demand_content(demand) + if content: + logger.info(f"成功加载Demand文件 '{demand}' ({len(content)} 字符)") + else: + logger.warning(f"未能加载Demand文件 '{demand}'") + except Exception as e: + logger.error(f"测试Demand文件加载时出错: {e}") + + # 测试4: 测试Refer文件加载 + try: + logger.info("测试4: 测试Refer文件加载") + refer_content = prompt_manager._get_all_refer_contents() + if refer_content: + logger.info(f"成功加载所有Refer文件内容 ({len(refer_content)} 字符)") + else: + logger.warning("未能加载任何Refer文件内容") + except Exception as e: + logger.error(f"测试Refer文件加载时出错: {e}") + + # 测试5: 测试内容提示词生成 + try: + logger.info("测试5: 测试内容提示词生成") + + # 创建一个示例topic + mock_topic = { + "index": 1, + "logic": "这是一个测试的逻辑描述", + "object": "中山温泉宾馆", + "product": "", + "product_logic": "", + "style": "轻奢风文案提示词", + "style_logic": "应该用轻奢的风格来描述", + "target_audience": "职场人文旅需求", + "target_audience_logic": "针对职场人的需求" + } + + system_prompt, user_prompt = prompt_manager.get_content_prompts(mock_topic) + if system_prompt and user_prompt: + logger.info(f"成功生成内容提示词: 系统提示词 ({len(system_prompt)} 字符), 用户提示词 ({len(user_prompt)} 字符)") + + # 检查提示词中是否包含预期的内容 + expected_phrases = [ + "Demand Logic", + "Object信息", + "Style Info", + "Target Audience Info", + "Refer Info" + ] + + for phrase in expected_phrases: + if phrase in user_prompt: + logger.info(f"用户提示词中包含 '{phrase}'") + else: + logger.warning(f"用户提示词中缺少 '{phrase}'") + else: + logger.error("生成内容提示词失败,返回了None") + except Exception as e: + logger.error(f"测试内容提示词生成时出错: {e}") + + logger.info("=== 测试PromptManager完成 ===") + +if __name__ == "__main__": + test_prompt_manager() \ No newline at end of file diff --git a/utils/__pycache__/__init__.cpython-310.pyc b/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff9a401c3e0cbe85ba71cbefe48fb9d6c0aec8d4 GIT binary patch literal 360 zcmZ9Hy-vh149Amx+;QlRGBEH8m4J>c2qE2y7&xXbQPfyRrKGN07sRu$vGGb-5d*Kl zL~;~_u#{h7#~**=YPBzdEuO{wd$`}C^VgE#T!k~71Q=nY6;9St#&=>9m)5Dv>dfVJ zF2z=u^hMS)lWjzqf5=c$N`G(*1CG?dHEIJ?roPxh2c#JkW8XG|Kl}|e*fDhMk9&e% z)0Cm6{}#J4qo|3}h%6!xxGCqpplOb>yFt4=U+^OU|1?1QSLo+TX={~w6|{)mB0kS6 zLgY%1jK-d_>#IA`4`A2d>ed9Bk*bkfJ5)-wUCT(q350kE^Dr2UvB4N@V`Cfe3qNGc6&TFJeI-nz7S2r4 znb;X8jc1Y=(%ox2nZ$VVFq6z=NGF{*kDYiXnVGssZpj*EKJTob-|qJf?e6Smy8rB{ zy1J4OvgLMnzWHW%uSMN@o;r2vRNXq~RF!|7m>5sN^Mm#AC;#LuMg4ai5FTm3^WJ9g zT%s7NhhjvGcv#dUB5!ezn7k!D5=O#EhovL39+`*|Q*9I@dzE71z9r@%^u$ey&2s0q z_+7oDy#qEo?Ko+p`^HA?4tBiHF*r6#kM+~1*|Cw+4m;gH#?s7Sf4_~jjXLP2?eu_c z)W-IbY>r<0DZ3^9F$UZ5SOl(F*8Cyw=Wuj8aYl}{PB+t?NH89-47&@5V+gaCD|%pmJe5&}@5e{jH`%mxUE zQ>}r>M7|avzIAkb#Ge!jGQAF4pE8WfxR)jH_ulUakzFDeWep~~#1O(gMIM&~LIJLz zD3=sMxFnw@ds$=`i>Lt#Vg=r#TybHsvS400;N$i)&(W1uTZkVsLB{IDVDY z?wB05S^IkXPTKs5R>n30><|d5tgH>#KM>MI^e10rjWxXg`8c%|cg^tNr~o$w_``^jxlDq%rSDhyqoPkV;kPWbKe%$ z*6SE!%f}sq!}jt3-$LVH+_RR`lYWKOIygG$uv(p|d&0+vMTl2JZT8o|`2%WMN12;M z=cFD*fv>OuzYV_P7W_8(G7Irr@5?D&%&D9g<3Og*P_t-gSjfi#+Lv97-x-jDBreBM z*}2%4?&IGXzN{QRY$UMqeECKk&h_OQ@H-1iC=6uHbt?YD9tb5vcKu(qYty#v9V-;0+m2BT5acs1c=4*ssPI zDd_^;LhtCb7pO5MjsY<<1WE9@#G!S|lq5#^Xk%9*)XrAM7^lIU@TI|D1@gogdvcd# z6)kgwvZ1$KGzs6tp(rUW)U@d66N;LWx}-ySp%Q|RQ8Mwux^2iH#D?C&H*ZSzl=2A! zlu&*u&Lv}1r;6aKci9!!LLHrjdgU)bQ@KD>yXb-;M)`seu7b}MEI*})(I=stFi%2l zJ!GaV6d;A(E(IA4&z{@Oq5Oi+r4ZV}Bw~i1Qa-f?C9dJZ>r(21ALy7w=y>HnH64ou zDumvjFdd`S5TxU@=)$Lfl4?vzyBI=$M5uZ4KPgw1hNvj`cxqmNg$A_lw^C|SVb+Xe znmqp6tM@;D<9OFjJlj~@Q$GN1p^jW5*G5y@YgdvNZr!;<~-fG{#nhl9EQN1wg_-5;(3 z_FL!=s&Z}Ry8F|wK6vBYqtAc(!KH89|L)iC{rC#_AAb6W4=(-S@uk0fbal=$!mO+r zGCp|Y^24`&%D1AP?(j<;lc#Nd%CE$$U0!68k<~Vf2dh7hUu*kiWRY!7BgZo|1)yvFh??5BqZ9k!PpES6bW{CzaX zvuW6mB@e$ke7%pQxWwRMpP-$<%WYPGG2#!1dsae;_#hqZES|{x3LHW3(_q&+G3Yos zHtw)O<@R{UAT0H1n$_d_@%P+mh*+&hlPt<0qrDfF#ISUaqf{DiLeXME(YHHq9Jqeq z#b`SrchsrAbq&)}ug1)2%yTAA zQ@Nx`yQI3PLb-JdMsNKAuKob3JBYFmc{DwD6V%>>!o`HbZ&%)^yc9vyZFa z=PB90qJ~bcw$q*3#n(_(k81$nra3vMS-Y|ZTpHEwN7)BFnuDZ<;>CpGZ?iWhu20-J zd;RPJ<83_3H6HbpA6rqwey(=EJM{oxLv3TQ4bYJ4U=5pYO1+z{+-57(dNd~j zbi%Dz%Aqe0%nn>0n;n~Dyq4{pWxG3P$GP20Svi;6X4@|BoZUH>i|QG7mhIfGCAIdo ziSrY$ojrecX2hc|T{5lnn%X#1+pR>mY5%m;qcH|+ty?Ht$VJ)PJ(?X*okr(X=P#=B zeX2EgGjqL}7B18B9+jhP7M*MNWfWX4pDmv&oX>Y>tn+4U;4(J2Gd4m(y76-EZ0=m< z{0Vn@-MJli)fqDbi|PVjR^by$uH7M;Zdpo8zcg`i;?miRXRk1~(@K^M1=nh?)_M(9 zoS|yI#cimYQ7q-J_2xHo`OP=0-TB*Qx5jxbgpwAUvqasj~;`d&YGSIg8K#j`ZdyNUCM$v@_OaiV87UTcp_eV+T zQxq1{BK8L{co=`)BOc(DC;~2{a*4kc7+X_uF`(ivf5hX86L^EIbzrt5&k&vpBA!sr zDY;9|(k@V!Mw<;>MBVe$R52DnT=Fg|2Cg500zuJG5d&rvb!$j;V*jU-as-9%XkurnFyTTO|-=`8|j2E>lKKjfPgS!$1KE&JwcwT-g z2R)m=;(7k!XHO~EVv1$H)4Z2nYISS#{{l+c^exRO??L=I7mb}H#?(xjGu zPlzU{mnDa&36Xit_#r6y=!<`S|Fy4lVGVph_#tx1zy0{ay&uoq|LnPYKl&ms3-K8t z{{A;V{oud6ac|)ZARyg;WA6UB+57WvKDzN`ob&bX-hcbcym0k_)Z%~@JpL|^;}741 zFa7A@m%l;0_kZ}kM}K*3yb{OGU48id&)>iBLy*PjGWx;!pMdc7_%i_YkN8Ls&BBnY zr%k`b%W@MPC}R3RWFjyhz5S#6-}o9|(--F+ePNn-A71;yqc5E2y+8Tl;IzR(LMUd4X`tg@$K^8NwA;Jx7fkJ+T zZIrQNS%bwY8X}6YYjJ|~lx@<^VqFPaj2+xq77Mqm2|HL2V{x^9*`OUuKJsA@(6C3j z_2oX>X>1wv%O`r-Q4q+$27_l#f4rU8SBR7o@MEdRuOy}tE7a&$2fbEaBGZJ*1Y!au zv4F)(VM;y=u3U!UfzU|6@-h|=Mn2N(0ILq;R|P!03@WnOv06POToE6Uu>5PRAQ%cJ zA>NMSq$32bRXIihn%y6iwo+$w-t=M)ehJ0CEV*sBGp>F?$7Y5b2)Xc zTb<`s7jx?3xf+kU5|*hm4Obi}z4ZK!yXl5=JC@QjFQ?6>dDG2Yx_PeHoxX0qmrGxN z4vP*MIeZP?^l~n}+?`(W9;H`SpWE)sF=462ol|~pw=dW1%`NA0%iXyZNQG@Enp~7$ zxd>IIWqQ+!xU{0VbS|xGQB$?7ptR|*u;(NJ#D!l_H<6$`t$we2qgLZknr@mb{YS$rXUmMeh1 z%QZxl&U7ba20G_*)@;@l##>m!71k`sZ)UjDcS6U7NrXclwRtI{$eU5mWz;X!yE9s+ z<#&=Z?pAM@Zt?00I9A}iwtMxPj|c@M*$1SXXTewv#KlI-hy>p!Mgbgw|>*P z?Lr+>=ci^4dDNz5ai+Fggay#xB5&KUQ;b+s&$*{kj{9QE_gzvq?FG{ zeFf%gJy(0aeq<*8ZoX+o?#nWwqV*tUEo?*E4ss0#(XoCslRMlD&`uy`N*6?R9w&H)GwSwJ9@ar9(4R98a~4vKl8LQGj0ExnZHz0Ml=Ks*Bcg; zAbXZALQ!lRsRR;|w3z@hteal}#19a;RS$ zo08%6oAr6%{(XiByE)1ZrR48T`VOh&AEYwyM;8fy4x-op7YV`c@!kIzk?^@iK`9Xh zWkiUM`-q~TJRq3KpGz?NI5mAVq4VR^M3&7!z2_4)KTb`8SW86Bm?Hbfsfl0gS1|Fv z_kL0l0UFym&VuNPMQH4QHEcPpf$1gU0p$f&N4yXZK0}E9%?pS!(S_*5|L2A1#7{6BnazKbIH{0xmB`vmxj;&fHRO8qpUZ@5Lf-sF^vteBE zN;rO;B|sP;fl`hat|bi46%XTq7r9jdksD%($ldz_eELyDZVeH+ql~VPD02VRCna)+ z%?8ng>tymR(nVKj!90-?{*{_s^O1#jZ4}{s4Hn)n>|)3}On6Tn?}5pRm&semhPh8x zdS~kZd`Nh|_ty9Be`k8U3L>9JVh_g%M0U0w1KR)&FQ0$rv-f`Tb>e;WhZpX@@fPvk z|IRlbe(5stKK|1m+<)!jINsTH|Ifbsc;@>L-{K|v`xoaP{p5%KI37L-@tam76jO+g zKyDlh+u>3?pZ+v4ejM+UdGN-cKl;quyow+aoB`5JSWQBdB9DFw2IX(uTlnL9Z)00? zJ!~_Se}5)FXRjf{qccJAoa65aM5<`?8MG4>~vsfH#koh@RNyvAAl3E zBs=q>NcbzW`~n00H&B4^2Gz6~qc@|JgI_`^5%CMSG6ZrdLgKYTB*N z_o}fZJlE<`SNXDwW_Hc#P=@7v``wI!bL~qRS(i<-CT~VDmr*+5;kf z371~tO<&8UuXU$aV$NH0TI7y5vsx>zL`ri&w*3DsHp}KA4KZR zQchhuuV1io6+44cc_->Rf;x|)qXX!q9St~;V;p6l@n|Mi0r6qM{{JoznK#v3#qJlP zMFz?#=hWr%+Wy7sVG^q|{U#nCtN_(|v%E{Dt7l1^BQ}6z}sQ z&k)|&74d|b7oq0?iw%Sm3l|j3#MxE39hjX{U^asQ3@W@$r5cNiZjnjbvarfn8yC z-mnr{Xb}@jZ~^6y8?=+v5oT6mZ!s%*TZ)$Kl*0#u$nm1y;bFE0=OO#jlzbw#h=}_A z3492{vHRmg0l|+KRV&Yv@CJ}HvOWUH!qFGLF1xcJ${J~PHX|I3u}4`w6CcsJRIR7P0kWalC{Jc2i!5na4K<2kkDOU0!(imw;XA95FOdM9V}%HCgA z{;Kw8wZCfoS)-@3+uM1F>pbM`JjQh%L)MdM=#;y2`1Y2OufBwGMyC_N@#U92q2jcg zMbkT$@(kC~uBKhfx|%gNxRBw_Yk|0&Qg2Qpm(#f5g#BXEZT_?zpWY0+l5WN?xNg;; z&O>N#59&FF%B-B;iuC5Y>o?6vy?QgJ2LsQde%*2+2EMGJfD{V)-=8E?xnm+bo+*BA zX%cTumj0qb3hs(I7Tf5^9Q$eTU7{R7d;S@MnKAMSZ4eTHkr&28l=(2)IT$8*o&=vv zuunkAD4U4vsbRHaydoYZxX&KWX-OTy8E`1t3>UQqVYVUXBQ#`dRg@MmWV^(uF$OmH zghZ59z|0Z%<_b(9pC+Oy5R5@5_!<#9McIB5lTN|C@6Sc2C^ZD>1X!>deDW_^3!als z`20eYI#$z(QNnkaiZ)BLe-b?&z7c;2-w1pHBDjepL}>;#WC-7Zp(xvMgiU7T1Vps* zsud?7;^6DB9=-yyq2QLCP+bBnXjS5yVzPFSkZ)Kq`Gy6z_6%i($_T!pT)`K5ho5wC z!Il)40wAm3l<<%*l)~2rG+=?yXYelZQ8Unp4nYunus)GHo?Snd`Xip`umrwf{{{4o z!812Q#)!fYe0F)*%7eG+EC6ldW8j8&4n#cBVTWy(Nr(Y6T6+_Owm~oNirKngZdgC5 z!q!i!7&T*cO~ezUM6~uNxKsjPhhg31QU%seu(=8N6mJPqyAlcCNCTYmmXsPz37gmO z$|2%mbOKDWaSIem?Q*;KMC znJtAjYM^zgF(?>qbOj+18YP39_GVmwMy)aEqj#-YxlQQ;9CE(!c^Hw@(Ao%6L6A+x zBQ<&&hQrr7`o$Qnz}m}*2Vh2-SA#Ut#ef)%vvf>)ueX-o#R8!uZU9_YhNNLe2d4ME(;0eLPh ztZ$>OZ?YU%(Mz7t_GKua9TuM^v=QQS!nNkGo=EE#cacrRAOol-DA1zA{LeqI1+Eab zz=hUV(Z-&}l>ktAU3yTO6H%}cj_A;*#TQX` z9%x(cih8luH{xM(1-gaWGbxRPYNnkHTZ9WG`1>XJn-j<_3;5{Fiu)zV38%1&B`nU; zU}=0c!|AZQC|2dQSXlfucJAoLH#E?JdiWwV7I5g%F$V^5W{|NlG&9a(kSpXE8Xtil z>>VC-l6x&I*To$jPIGH7h)oRb7^Cs2BY3X{-w%ki*p-aeOTLGuySWudm08#(32Og;`|(%fSv-9b zIhD-=-#A`t2SnI&m*5~*J&37g^arANXZn`0@nMG04DaiMyFXZ4AKV}?Njt@`h0s|T zkxsA^Iw&l2U55?!7KS=HaFGQ74)#61-y+m85Pt@`fU)=!_Kltz9h(^KhJ$0{cx`^~ zr&k~R;f?xuwg&>v>@W;)#|YPD&{)i+S$xQL9Iw>lAOFGm$6uRy@a6f(pL-36up*M2 zQ-esSf$uk=1t3-P*Pz%K3*AL-`>3ZsNTT~r_OkX;dLOVZgynqL;>_F1vVeH76lN`+ zQ33$#&2*UE1S>JvGVI`ms$XWqr9(R^`7>*{#r!Q5_^a)Y@9RB1=)lZr3$x0U<4jn( z3NyJ71p<75;XK^#E`k^_!0?w{q~N?I*uh_nu)8lLaQKW^Yy3)^uz%C6=T!}fEABh- zYj9_W-*n+m#vCbJXTeN@ZD+8L#UDAg8aw#o!tX4_Q@vkd!Rd9&)cthxo0JFEHJt~<#zvBgv`rd4B& z?XHWvQ2wY#H|EPMSfVXndNW6FzNzyz9pjpgxoPWkA}&$aidFe#oUUwcU{P0dFHq39 z>lWkfIK_3GLc501@F>b3^XN_kBv7o-MsIEnms{h_t><#<7i!(PTVU(xon!;4zH%|G zGFbhvM>m42H&l2HTR6j(n=RfJE7xLm8;&Cljf>Q_;rdHBUCCVAqOLkv{~ok=pLg#t zw|5xr89^gsDF3ua_Y#kQVVel4am%c?~t3q2>vt)K*Wog9_$a`PK6I!i9Wy z{zh;97A}8_JAW%AuE4qP0#g z9e$YYWqdYa*Px77UnI(qT1lo80OZ@wRlT6bETV6 zNfR?g7+w5}h1EnWs3_sdMf3RLQUHJiilV zHvZUtbEA9HUewr$I*)sL8Du-{?tKXzhb`KZDBbB%z2ZxzuauzVGNdZ|_b0nWxSxM_ z&rl5dSKy;1+H_5QRgEg!Z?)grhz|Ck!$(lhQMC4$m$oj_R%{D`Y#-iB%fZ`aft3ZL z(au}l-d#tzT}RQ*W9Zm%)O!LQhwZbBS7%$)*@z9PU-aSopbyrh!)R0HEUA*cs%%b` zjdGeis^+ES>rw}1&j;}ATjl{SpOKM6+kCtIGrZtO#BpFCifHy2Q@xe z+sMX)gbF(`0Y;*rlUt!z(L|68f=rUoDxy1oN=unDv;r(<4LPP! z0?iWErq;L=tms0WOEs0~N*u!CMZ^=*MX^b)L`P6>7m8;z85Aj95K$C@Oi;%F`h`SB z6IMQ$l0fn00h=jTqAQLZPyMtjVM+m5DKC5uG&|o1&5oSi2tA(4>uOwbpi%-*DGB1z z@V>VY7hFgrn%P7?XPC4fP1~RkVHDE$0<`pSzKqFN8DUgaup=?*%ycPNYOi99&)_XL zSl`DQK@$VznJkwY-0U|4dc_!Z=0w&xx-P6lw6T&)YN1!qA=4GdrpG9k=ZXWo^25tL zbwn2kbOSAA^wC%t%%OE@*t;$*8TTfa7U~F%`&c*?5iRA4xdz7cnx~HG{m(U~&3H_+ z4bL^AL2jE$aA`iym?q<*0LH}{5j83T9$y0^X20NP>tk?HP~S>4w|Mp}c44h+h>8_4 zdbT7|>pH5BQKA&nJ^UuSr*cAhu%C4<9a{rE6Gcz&3hKqDQd}v76V|#?RVLjq;r3Q0>4wYcP&Xx9&u&;3ujjR9%`aax}5N#CK zhVqhC2-aUjJl_h0r}Qp8s|3ENbLm5R-2ss(E@9Lm{6L4RnUbBAO-nBXe4!oD0Jl&e48Tnn zh1=nyQPvO+7qr6mkBFBsynFyJ@rAGOzK?-dSvXucqw>~A#LE(1K7g0#!aqmB>!bBU zRaCft{;}b%j|z9v>M+Ktl**jW@q;nK}#B2w;#}{g=zO`tVLvKxMsRHpex1Op)Ag#AXKbH z70L;US$q#Q(XrLj{o*jS^M8}Tr6dM@qb0)QrE${SB90K+AAO_ibfIk`!hZ0CG6Ah=|>5)OZ9XA_V zX@h&WC&47aH<#QXMsDE_0OJW_aTwLyT#ej zF&5Iohim{?%{mFFkHgK{_@V(C-+xA%_)1KrxUGW@`ed&ii~_v!p6o}nIIEi+z!A^_ zqD|+`F9PK;9|R8ahhqU(;Nd+$#MVKq1eUJxK1`VY@!{b~0s`8A@Aw`Z#k>Q7Fo>{O zFNH^o$3v)~$XRHnPmDVtuV558VH1F1^zD3aS)8pQqeTB8)Y8i%iCYi=ywXeoRwW*h za5QL>(`2&HyGH?Id?!0Gz`$MXMCVOU0HL6_i5bh{Tpwa1zB70}Iz5I*0NAnsguqiP zSjplvtzw)2hCFA{b|4Sj;ZX0FLj(gi!ut~g9ypSL*;>7aj~DpbxgmthikwejZQO-{ ze6-0g4?uG!31GK_O20ndnJI`M8>0Zju;G{UgOMp+oeWbWlNkzrzSX0HMsmR#>F*b?|k z@Kx9`z||%{_7rp^JB)jC0}n5lEy~xao)$O?W*mO}ky+j3)S6b#C0)H zZzE1swA&^LO6(pScMv-aIOg=g-7UOn3xA|p7AKws$G;|UG%D$0OIExDz!~g*y>Kf_crqTy{247KHWUK(<7=W~lXEVh9{KGF_pc4xJO?2t8!X*EGZV8D2{!VY!F zlwUh`_1JuyyPyGS^6w-U_;k5ftY}>us@`@>j129ZuKi*=hKjH7Fk-vpa(s1ShsbBH zx{-4|XF=gMgQcm^S5%BN1-_DUq$%$Rx+BVVjHeXuqCD%pQ zl~#9J$=u8Cv|69J;zri>toc(nE8XVSCsa~;n`mbHN|T||ZED6Q!wPQ}xkW|q$!dNA z#?+d{#{Itan-RUvXILMwlJ52xx_u3MJ)Qf|ew(|qA9W6(hLgxxN3t|=hNhc^9>aFX zf;z39?gQxHF?Y8Wby-ovame+rhV^J;C)z)}xN+FGX&W*&_!?U|mO;XKn;RNRVMkD)y4ZQb!8i5(u@_`7kGt~8Jx834^Obl^0~d?m&!JH z%XV>PyHH2JyKDgIitnVALDi_Z37MO37NBj%P@5H5`;dv@^bD9c^||yq`N6`4ksDQb+rEM`== zEM^4TI15MdnWxk-@v|8JuAA8H>Ee2F}<3 zrp%P;8HF#?aJgZ&VJ^;{Sw3I4nAzYfTX*Bg^&<-N$$*sVcQq84iA(CdCGA{EJKA~F zU2<$D_fA#`94wt1TNpzdJ8qporF*&Ty|ah!Wg9PBXRW9l7^x5KWl)*zc6NWzI=UYn z=tKJ%WVU(I`!8^))7?Hb6bD-UZ8Ud4tr9LEXOA#?iRsqMAyzwdqmR8QvirNmlEk}^9rX!sGNLVevYQyNj2r3%A ztse{4WAmi+FUQLhTSNru&75(w+t>{D9Oul*4-PDCPL;s)f^ubkEth>*SPQanqS5UgkqnZjcrT*^yb}6OH#-~`? zMN3+}SDVji^O0e@SG!|Ty93)lA>qUKheVVv3+FnDa}j%a-eIry$Wk(i?FcWs)2rRJ zsNF@%c0k$9LRnj=Y@b)l1j@FBm)+{swk~R0N!d0in-xHAYj~zDUTw>wwuNNc8kT8y zc&1%mZO5XvgJjwbnREe2E#aA(z1l5{+ASng3uJ<0D|%pUW$ry7=h5>=QT9P}s0ST9 zj1CW@kqI>XGRmCvBs!OLIUg>mA?r$m@D`7%CCngv06tsaYCNc?eg-yRc>R2n7~Efo zE4wt*FV@v}HBi4?+a!nAe_b!e?uI4_y#8jB6x?4)w-mzb?@F4D`xMmQYvkC~H>JVr zAJ#VI?AK99-IQ~HqBzUi1EthrP6dE=Ym{Ae5_ft^SGB}#iNi6~G8|JU!TyGveKLtB z1N%MM3hXaP*+)q{^*HBkN(M2vWjOV=B4@u=a=VcD%?j+VNZGHFyc36WzN3;s%sX0~ z`i@Syf1~7`?3DfMCGS*{)b%nPvk|9yDdhpP#H&m>V3c@saH`iR!!c%(TB?;Q@L$gNgptPi3kux9p4$tEtzAPD++=|Zk1 zk61;rpTY^RgL9oC*Cdl=3=1m}xMC9=yk50`5u8`4JJR}P6;)vN6_xtRDtv_|pUM2L zHcJMdYs<0(-gnKV%QASwjBBY_mSdlS%FSO^VqZL!U$~rreJaXSvYd#0NmQP3S&e-f zD!<@84C_h5CRwxSNwc_1B-1?^g8f$TYL=wQ@}5+l5Xo}h+a@_FmgVrMVg$`{_Aa*TW19 zl9N=rnD2e>`rhw-@9X#8uX$&cd}~to8?9DF!1H>);=;CrGrCVt>2VgGZw`UDLIjC3 zBuku;u;dv^Nhk?c%SwVYOTmwZpA>$wpp=ydWvn7d59nCs8`3j!R`mvPM!~8jc@mx^s>Uel3#*T+lsU7P5QpV{tY%8AhBG3 z@Vv#L`@zr^M4on|!CD{sk3CxSMTZGpt5wn@F+rhoN*9{ZFsP$WsUvwZQV>mBL61A~ zBs7m}^WGs?=`8i`dKK=-6GIx5+~X3hz15*1MN*cU(dE9Rfz4c*Dz@X<#K237hQTm#q?TJDjOh#JzhU&$F#?1gcgXr68+dTtR!Oqy-r zJ%t&?_nS=8ckxQV6O&~(z$5&HS*eeZ)9qXAqhFd;)@I^uc|bBq1SCC;(0dEM|5BKg z+4kO3FqDj!qlKM}Up^z5Zl;NtA|~f4UJ5Q$@d_+w=}9dZqo)6cCc?fbiT>W=pje$a zA3ZeCZ}K$itT$_5a&JNh6|Wldp$}}P9M+JTkXkH!g)y3Q2Z>CnhR2_@dNxYo7d@gH7cQtv~ zy%rvQ(7!|aFc-ac!WFPaw~LDpf+y1hK`Yy)^4v}3_E~8Kdh^QlNuYlmT#gr+(9A?$L49seHB_O3YOuS66@|f~9nAAEdN#a-4XTdYKp} zw-~ET8>3jQ7!Mu)qd{`|$#V==ZbIk62GjYWY6O8&=PJ^hEdFMsrYo$5P@N;R3^G6 z6>SZV_OWg*7#Y391%hte$9<`oKtIuyq9X_YWXhJ5504HGhQfnMIusrl<>X*1DGhSm zD97P#Hfg{Xdd0y4qv2>U9OdwM9Nrp}lF>+##u$>)Py}WI)lzT^l?RcV@|P_RaPOM)#`0awUHzU+^5g zvoq0pA>MjHI2sZ{7X{bwvSDP!;$4#wCQHIt@u{%_j83JjURcfvVXLn4gzG@ubzqTB zyl^)D!r3KPx1e#X=v{)oC}X+(4x8u*#XCYm`;ag+EVxFN4dIk!-(gbFxL}AJQ$p`r z(z|}%INvhYGT%PezEF^;Zj4tqE*CVdNi>E5a<#}mKZ(9Pl4N!-L=!bF@tPK4_hI31 zui!bqZ0JKDAN_^&TMOaxptlWsi+oiHUsK%IwAjAnJ0aw_9~gZrO!19=p{hZsY!u3x z;!Kmk6r=B2Yo*`H2(1b2YTHd7LBnlhWHWl$=9{_(p1$HaCL{j3fd=`}eysn>EoOdQ zSPPktoekxkD&iBZxx*s)q@Y34VU*o2qOmO1LecG72~^*%J7j^zca2ggx@%E(%B6Qb z_D+fPZoLeu?@6Rka!*cSU8OumQTL36o%^W+_iAO(?cRPV6bTdsx*$^?tDpoubIeZ( zektgI^!yv#f;T3)il@Px8tG0JZr7wYshEtHaVj_gwBqino261`K$zLupVXtAlRmiGsH}=X zBBo>wF%|sOycEvD58$+bQxR2}7$&pXv&M_X@V=1Kmh~Fb#u*BI{t%y^>#+ZBFQd;K zj&O=9hdkIXE?pWY92E+%w3}CPl$9_V31caK|1alQ2RbUDmj#f{*@ zHs6777PFF#nyr!KQl;y}v%O&bE|QPWch)&eqszw&DHpFs*H73};}p8Z+lps%n1$`U zf_pZCyy6YZha*V6Ei+CoTIe+ID9YrGd@lDHZxo}dm^W_rL--k+K0lueEwX|fqRJ#j zRo6yT88@P;1VC%cgf%pgM3{v2Fu|I3rFE`ut0z(5 zRBnDL&t&1R+WJWDC${oB#L@69Mry>LW{*)3~PTGyTGuCXp4KUlj53_9@vmG$oV)m_=Yj}GGv*Q`e znTt~_pU)RD*Yf%Q3+6q%BO9~6E{&ChEjbH-o@VPaw`Avfh`@fC?@V_Aj--P9)fF|S z97_;G6{5Q{Er7AOH!&7>%1Yi)Rl37Qnl(1%PG zmaWlH)4=<%XVd*jV){J1GcHaE`&c9I!gcE>#WRs)ZQAJdoVTHwcZzHFyDtCBhc-<= zGZr9g*;Jk(#!!AZi#$kp3X=!8<>L#S_yA>a1Cb=r^}UWmY=-EN1keOSO#YhuvUK{d zQd$;GfGs6sz)BKZ-GR2hq|LibPw)7?kqS4aoEeFp?)ra>bQ_OEO~3K{kx>09RrRLX zP}B3z8RNO`sAqRQ2P8FJ{C_oo?!K(ANjXt6eRFHCbqePeDctt}IQSd@%zio2E{E%ph;N+C@}4Ul5hZNvvj{dirD>zTZSqC*FO8*W z=}ZvqJKIT`&|7D#1{jEtB*B>>nS9}U7@4~%#f99LB0=)v35V^yF+rXTd|sI{Sx!0&+Ig*UjWl(iPEHqnC70 zlPXU$yGcp88=>wGN&AN_J(H5XNogcH0o+P@5+{=yF>xOO5?801bft14-^-$uK_HzI z=(oKgMN*c6gwCJ0l72LKz6kx|{BT)PdLlRw4+}7fE9T=HwPDHVA8-8~yDr zYx$&o3)@UJ!$a4(CpD?uI;B~Qop}ZB7_ANFy_%HPJh@267HxP_OfHiJKfcpFV zGW;2V%SSVP6H`{si8C%NO0dAUb3x?UGDiZDEqLJ}X^a59bD-=*FW|NB6;6#eto`AI;2kN{HcgGi04(=(0zmUV< zOOvYT=vXMw8;S-;IDC$94OnE*By(9YLVDR?B*2A4mdouyi~R%YI@}PyYFsI*3jEPj zQYWsw$ZbWs4Co*P#5_idb&-^7Q~Ek@Pg-COK)8GRF0r9tIFRZf8ZbStCizUMXw;1U zEnxN~l^hO}Ud;6kDRq6A-oD{st_IMe&fSITquC~fNi_jz4Bpb8#$st zeQe286SdB-t3NeXr^#)a%VzQG?)bj$B}Y#-zizwpVxoOG-aag}jR+&7f@^Hq@JfmW z+wl0f2VmV8SPhJ%td1!95$a_5Ir!N(-&M!kdQ$r|*@;H{|~gZ3b~;whbX z&N&z4OP<=xx(7zj6T3fQ-yXMbU$R#v>~(Q_-L2ir_QsXs-9k~#LR2WMzp8ptxZ|g) zRgZ7pJZGMF%sFmcyj8P!bjj0s`RHmvb)uj-UeLUFW~t!V<@N{Wf)$JJM*c!~KV=+OdS`lt!n4av_nOM6?Ic%9cfG&= z=6<2Bb*Z#X$SHncEPZ0%k+AQH+xINl_a^MkaeMP(*Rs8BrTVB))p}>Au>DxvQubtf zi(n}OCSO+Ze$CCA_v>%g-%>BWBy`sd1&gTAwyBfCix-5G zA)$EazU5*DOmLYQSW}s_hsjlk^IFT*mTPTS+iutw2A3TBFDqA#%$1s%nk)Ne_FcE$ zHPd9<$GiH4 zz_1V=UFsUU-!k>e&#DA3cexeBs>yz(ZKmz|?)Q8*E-sbS3I4sp-Y(&Ezt9yBJnXV5 zxU#M4YeL4nM8ZN87R{fYJ3Ze$*S+Asb!MsXFqC;K6W+$Sw{fv<$=mT$`2)Lm#ZfNo zXj(kFSbygSLRXJ)>YQ*6K7a(`4)`m7>NxJZ)_Jv4s7S3yS=W8X=`}4L;VT_s@vm45 zZj=kPM}(TALUC){+IrRg)at(Gy6O`AC+=HMru|jaxVUGrR4D9NW;)RiLwhAnQgkbH zo@_+cp}k}=IyF>4mZ8ZZZ|Q%2okRE{2EMYa*u2*>Df-i%Nk40*@TwBf^SDxqOGKcLYn6Q(>IrE-@1dS}G*~ZZ`sCCXaw+H?ehEk6?@8Q2*Z}=16dT?@ zh5t9h!9Pg|ZVbdtg3HBv4i>Vsz#l~;eIVW-9?*4bIYd$MN|FC7yC8f@9UXmQ5 zq|Vgvcx1nvi(&xy%uFhyTwfsA-xs*ZInggiT((znRR-#Jiu#dJ_R?^0KlelQ<;XVu N08Ki-Ca@G6{WqWkaQy%P diff --git a/utils/__pycache__/resource_loader.cpython-310.pyc b/utils/__pycache__/resource_loader.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1e1097342ff476a184c2df4dae422e400e0d49b GIT binary patch literal 4090 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 klb!`F+5P8VE_OC literal 0 HcmV?d00001 diff --git a/utils/__pycache__/tweet_generator.cpython-310.pyc b/utils/__pycache__/tweet_generator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9354202479b66366a1f848965ff306bbcf8e16a GIT binary patch literal 18887 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 Tp^z@I7YI4DUZCYp$w&SH00QPC diff --git a/utils/prompt_manager.py b/utils/prompt_manager.py index bcbf72f..372beb8 100644 --- a/utils/prompt_manager.py +++ b/utils/prompt_manager.py @@ -16,46 +16,352 @@ class PromptManager: topic_system_prompt_path: str, topic_user_prompt_path: str, content_system_prompt_path: str, - prompts_dir: str, - resource_dir_config: list, + prompts_dir: str = None, # 兼容旧配置 + prompts_config: list = None, # 新的配置方式 + resource_dir_config: list = None, topic_gen_num: int = 1, # Default values if needed topic_gen_date: str = "" ): self.topic_system_prompt_path = topic_system_prompt_path self.topic_user_prompt_path = topic_user_prompt_path self.content_system_prompt_path = content_system_prompt_path - self.prompts_dir = prompts_dir - self.resource_dir_config = resource_dir_config + self.prompts_dir = prompts_dir # 保留兼容旧配置 + self.prompts_config = prompts_config or [] # 新的配置方式 + self.resource_dir_config = resource_dir_config or [] self.topic_gen_num = topic_gen_num self.topic_gen_date = topic_gen_date + + # 缓存加载的文件内容 + self._style_cache = {} + self._demand_cache = {} + self._refer_cache = {} + self._system_prompt_cache = {} # 新增:系统提示词缓存 + self._user_prompt_cache = {} # 新增:用户提示词缓存 + self._dateline_cache = None # 新增:日期线缓存 + + # 初始化时预加载配置的文件 + self._preload_prompt_files() + + def _preload_prompt_files(self): + """预加载配置中的提示文件到缓存""" + # 预加载系统提示词和用户提示词文件 + if self.topic_system_prompt_path and os.path.exists(self.topic_system_prompt_path): + content = ResourceLoader.load_file_content(self.topic_system_prompt_path) + if content: + self._system_prompt_cache["topic"] = content + logging.info(f"预加载系统提示词: {self.topic_system_prompt_path}") + + if self.topic_user_prompt_path and os.path.exists(self.topic_user_prompt_path): + content = ResourceLoader.load_file_content(self.topic_user_prompt_path) + if content: + self._user_prompt_cache["topic"] = content + logging.info(f"预加载用户提示词: {self.topic_user_prompt_path}") + + if self.content_system_prompt_path and os.path.exists(self.content_system_prompt_path): + content = ResourceLoader.load_file_content(self.content_system_prompt_path) + if content: + self._system_prompt_cache["content"] = content + logging.info(f"预加载内容系统提示词: {self.content_system_prompt_path}") + + # 预加载日期线文件 + if self.topic_user_prompt_path: + user_prompt_dir = os.path.dirname(self.topic_user_prompt_path) + dateline_path = os.path.join(user_prompt_dir, "2025各月节日宣传节点时间表.md") + if os.path.exists(dateline_path): + self._dateline_cache = ResourceLoader.load_file_content(dateline_path) + logging.info(f"预加载日期线文件: {dateline_path}") + + # 加载prompts_config配置的文件 + if not self.prompts_config: + return + + for config_item in self.prompts_config: + prompt_type = config_item.get("type", "").lower() + file_paths = config_item.get("file_path", []) + + if prompt_type == "style": + for path in file_paths: + if os.path.exists(path): + filename = os.path.basename(path) + content = ResourceLoader.load_file_content(path) + if content: + self._style_cache[filename] = content + name_without_ext = os.path.splitext(filename)[0] + self._style_cache[name_without_ext] = content # 同时缓存不带扩展名的版本 + + elif prompt_type == "demand": + for path in file_paths: + if os.path.exists(path): + filename = os.path.basename(path) + content = ResourceLoader.load_file_content(path) + if content: + self._demand_cache[filename] = content + name_without_ext = os.path.splitext(filename)[0] + self._demand_cache[name_without_ext] = content # 同时缓存不带扩展名的版本 + + elif prompt_type == "refer": + for path in file_paths: + if os.path.exists(path): + filename = os.path.basename(path) + content = ResourceLoader.load_file_content(path) + if content: + self._refer_cache[filename] = content + + def _get_style_content(self, style_name): + """获取Style文件内容,优先从缓存获取,如果不存在则尝试从目录加载""" + # 首先检查缓存 + if style_name in self._style_cache: + return self._style_cache[style_name] + + # 确保有扩展名 + if not style_name.lower().endswith('.txt'): + style_file = f"{style_name}.txt" + else: + style_file = style_name + style_name = os.path.splitext(style_name)[0] # 移除扩展名 + + # 尝试模糊匹配缓存中的文件名 + for cache_key in self._style_cache.keys(): + cache_key_lower = cache_key.lower() + style_name_lower = style_name.lower() + + # 完全匹配 + if cache_key_lower == style_name_lower: + return self._style_cache[cache_key] + + # 部分匹配 + # 攻略风格 + if ("攻略" in style_name_lower or "干货" in style_name_lower) and "攻略" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Style文件: '{cache_key}' 匹配 '{style_name}'") + return self._style_cache[cache_key] + # 轻奢风格 + if "轻奢" in style_name_lower and "轻奢" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Style文件: '{cache_key}' 匹配 '{style_name}'") + return self._style_cache[cache_key] + # 推荐风格 + if ("推荐" in style_name_lower or "种草" in style_name_lower) and "推荐" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Style文件: '{cache_key}' 匹配 '{style_name}'") + return self._style_cache[cache_key] + # 美食风格 + if "美食" in style_name_lower and "美食" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Style文件: '{cache_key}' 匹配 '{style_name}'") + return self._style_cache[cache_key] + + # 如果没有在缓存中找到模糊匹配,尝试从prompts_dir加载 + if self.prompts_dir: + style_path = os.path.join(self.prompts_dir, "Style", style_file) + if os.path.exists(style_path): + content = ResourceLoader.load_file_content(style_path) + if content: + # 保存到缓存 + self._style_cache[style_name] = content + self._style_cache[style_file] = content + return content + + # 如果直接加载失败,尝试列出目录中的所有文件并进行模糊匹配 + style_dir = os.path.join(self.prompts_dir, "Style") + if os.path.isdir(style_dir): + try: + files = os.listdir(style_dir) + style_name_lower = style_name.lower() + + for file in files: + file_lower = file.lower() + # 检查关键词匹配 + matched = False + if ("攻略" in style_name_lower or "干货" in style_name_lower) and "攻略" in file_lower: + matched = True + elif "轻奢" in style_name_lower and "轻奢" in file_lower: + matched = True + elif ("推荐" in style_name_lower or "种草" in style_name_lower) and "推荐" in file_lower: + matched = True + elif "美食" in style_name_lower and "美食" in file_lower: + matched = True + + if matched: + matched_path = os.path.join(style_dir, file) + logging.info(f"模糊匹配 - 在目录中找到部分匹配的Style文件: '{file}' 匹配 '{style_name}'") + content = ResourceLoader.load_file_content(matched_path) + if content: + # 保存到缓存 + self._style_cache[style_name] = content + self._style_cache[file] = content + return content + except Exception as e: + logging.warning(f"尝试列出Style目录内容时出错: {e}") + + return None + + def _get_demand_content(self, demand_name): + """获取Demand文件内容,优先从缓存获取,如果不存在则尝试从目录加载""" + # 首先检查缓存 + if demand_name in self._demand_cache: + return self._demand_cache[demand_name] + + # 确保有扩展名 + if not demand_name.lower().endswith('.txt'): + demand_file = f"{demand_name}.txt" + else: + demand_file = demand_name + demand_name = os.path.splitext(demand_name)[0] # 移除扩展名 + + # 尝试模糊匹配缓存中的文件名 + for cache_key in self._demand_cache.keys(): + cache_key_lower = cache_key.lower() + demand_name_lower = demand_name.lower() + + # 完全匹配 + if cache_key_lower == demand_name_lower: + return self._demand_cache[cache_key] + + # 部分匹配:检查需求名称是否是缓存键的一部分,或者缓存键是否是需求名称的一部分 + # 例如"亲子家庭文旅需求"能匹配到"亲子向文旅需求" + if "亲子" in demand_name_lower and "亲子" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "情侣" in demand_name_lower and "情侣" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "职场" in demand_name_lower and "职场" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "学生" in demand_name_lower and "学生" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "银发" in demand_name_lower and "夕阳红" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "夕阳红" in demand_name_lower and "银发" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + if "周边" in demand_name_lower and "周边" in cache_key_lower: + logging.info(f"模糊匹配 - 找到部分匹配的Demand文件: '{cache_key}' 匹配 '{demand_name}'") + return self._demand_cache[cache_key] + + # 如果没有在缓存中找到模糊匹配,尝试从prompts_dir加载(向后兼容) + if self.prompts_dir: + demand_path = os.path.join(self.prompts_dir, "Demand", demand_file) + if os.path.exists(demand_path): + content = ResourceLoader.load_file_content(demand_path) + if content: + # 保存到缓存 + self._demand_cache[demand_name] = content + self._demand_cache[demand_file] = content + return content + + # 如果直接加载失败,尝试列出目录中的所有文件并进行模糊匹配 + demand_dir = os.path.join(self.prompts_dir, "Demand") + if os.path.isdir(demand_dir): + try: + files = os.listdir(demand_dir) + demand_name_lower = demand_name.lower() + + for file in files: + file_lower = file.lower() + # 检查关键词匹配 + matched = False + if "亲子" in demand_name_lower and "亲子" in file_lower: + matched = True + elif "情侣" in demand_name_lower and "情侣" in file_lower: + matched = True + elif "职场" in demand_name_lower and "职场" in file_lower: + matched = True + elif "学生" in demand_name_lower and "学生" in file_lower: + matched = True + elif ("银发" in demand_name_lower or "夕阳红" in demand_name_lower) and ("银发" in file_lower or "夕阳红" in file_lower): + matched = True + elif "周边" in demand_name_lower and "周边" in file_lower: + matched = True + + if matched: + matched_path = os.path.join(demand_dir, file) + logging.info(f"模糊匹配 - 在目录中找到部分匹配的Demand文件: '{file}' 匹配 '{demand_name}'") + content = ResourceLoader.load_file_content(matched_path) + if content: + # 保存到缓存 + self._demand_cache[demand_name] = content + self._demand_cache[file] = content + return content + except Exception as e: + logging.warning(f"尝试列出Demand目录内容时出错: {e}") + + # 如果所有尝试都失败 + logging.warning(f"未能找到Demand文件: '{demand_name}',尝试过以下位置: 缓存, {self.prompts_dir}/Demand/") + return None + + def _get_all_refer_contents(self): + """获取所有Refer文件内容""" + # 如果缓存中有内容,先使用缓存 + if self._refer_cache: + refer_content_all = "" + for filename, content in self._refer_cache.items(): + refer_content_all += f"--- Refer File: {filename} ---\n{content}\n\n" + return refer_content_all + + # 如果缓存为空,尝试从prompts_dir加载(向后兼容) + refer_content_all = "" + if self.prompts_dir: + refer_dir = os.path.join(self.prompts_dir, "Refer") + if os.path.isdir(refer_dir): + refer_files = [f for f in os.listdir(refer_dir) if os.path.isfile(os.path.join(refer_dir, f))] + for refer_file in refer_files: + refer_path = os.path.join(refer_dir, refer_file) + content = ResourceLoader.load_file_content(refer_path) + if content: + refer_content_all += f"--- Refer File: {refer_file} ---\n{content}\n\n" + # 保存到缓存 + self._refer_cache[refer_file] = content + + return refer_content_all def get_topic_prompts(self): """Constructs the system and user prompts for topic generation.""" logging.info("Constructing prompts for topic generation...") try: # --- System Prompt --- - if not self.topic_system_prompt_path: - logging.error("Topic system prompt path not provided during PromptManager initialization.") - return None, None - system_prompt = ResourceLoader.load_file_content(self.topic_system_prompt_path) + system_prompt = self._system_prompt_cache.get("topic") if not system_prompt: - logging.error(f"Failed to load topic system prompt from '{self.topic_system_prompt_path}'.") - return None, None + if not self.topic_system_prompt_path: + logging.error("Topic system prompt path not provided during PromptManager initialization.") + return None, None + system_prompt = ResourceLoader.load_file_content(self.topic_system_prompt_path) + if system_prompt: + self._system_prompt_cache["topic"] = system_prompt + else: + logging.error(f"Failed to load topic system prompt from '{self.topic_system_prompt_path}'.") + return None, None # --- User Prompt --- - if not self.topic_user_prompt_path: - logging.error("Topic user prompt path not provided during PromptManager initialization.") - return None, None - base_user_prompt = ResourceLoader.load_file_content(self.topic_user_prompt_path) - if base_user_prompt is None: - logging.error(f"Failed to load base topic user prompt from '{self.topic_user_prompt_path}'.") - return None, None + base_user_prompt = self._user_prompt_cache.get("topic") + if not base_user_prompt: + if not self.topic_user_prompt_path: + logging.error("Topic user prompt path not provided during PromptManager initialization.") + return None, None + base_user_prompt = ResourceLoader.load_file_content(self.topic_user_prompt_path) + if base_user_prompt: + self._user_prompt_cache["topic"] = base_user_prompt + else: + logging.error(f"Failed to load base topic user prompt from '{self.topic_user_prompt_path}'.") + return None, None - # --- Build the dynamic part of the user prompt (Logic moved from prepare_topic_generation) --- + # --- Build the dynamic part of the user prompt --- user_prompt_dynamic = "你拥有的创作资料如下:\n" - # Add genPrompts directory structure - if self.prompts_dir and os.path.isdir(self.prompts_dir): + # 添加prompts_config配置的文件信息 + if self.prompts_config: + for config_item in self.prompts_config: + prompt_type = config_item.get("type", "").lower() + file_paths = config_item.get("file_path", []) + + if file_paths: + user_prompt_dynamic += f"{prompt_type.capitalize()}文件列表:\n" + for path in file_paths: + filename = os.path.basename(path) + user_prompt_dynamic += f"- {filename}\n" + user_prompt_dynamic += "\n" + + # 兼容旧配置:Add genPrompts directory structure + elif self.prompts_dir and os.path.isdir(self.prompts_dir): try: gen_prompts_list = os.listdir(self.prompts_dir) for gen_prompt_folder in gen_prompts_list: @@ -70,7 +376,7 @@ class PromptManager: except OSError as e: logging.warning(f"Could not list base prompts directory {self.prompts_dir}: {e}") else: - logging.warning(f"Prompts directory '{self.prompts_dir}' not found or invalid.") + logging.warning(f"Neither prompts_config nor prompts_dir provided or valid.") # Add resource directory contents for dir_info in self.resource_dir_config: @@ -85,18 +391,21 @@ class PromptManager: logging.warning(f"Could not load resource file {file_path}") # Add dateline information (optional) - user_prompt_dir = os.path.dirname(self.topic_user_prompt_path) - dateline_path = os.path.join(user_prompt_dir, "2025各月节日宣传节点时间表.md") # Consider making this configurable - if os.path.exists(dateline_path): - dateline_content = ResourceLoader.load_file_content(dateline_path) - if dateline_content: - user_prompt_dynamic += f"\n{dateline_content}" + if self._dateline_cache: + user_prompt_dynamic += f"\n{self._dateline_cache}" + else: + user_prompt_dir = os.path.dirname(self.topic_user_prompt_path) + dateline_path = os.path.join(user_prompt_dir, "2025各月节日宣传节点时间表.md") # Consider making this configurable + if os.path.exists(dateline_path): + dateline_content = ResourceLoader.load_file_content(dateline_path) + if dateline_content: + self._dateline_cache = dateline_content + user_prompt_dynamic += f"\n{dateline_content}" # Combine dynamic part, base template, and final parameters user_prompt = user_prompt_dynamic + base_user_prompt user_prompt += f"\n选题数量:{self.topic_gen_num}\n选题日期:{self.topic_gen_date}\n" - # --- End of moved logic --- logging.info(f"Topic prompts constructed. System: {len(system_prompt)} chars, User: {len(user_prompt)} chars.") return system_prompt, user_prompt @@ -110,22 +419,20 @@ class PromptManager: logging.info(f"Constructing content prompts for topic: {topic_item.get('object', 'N/A')}...") try: # --- System Prompt --- - if not self.content_system_prompt_path: - logging.error("Content system prompt path not provided during PromptManager initialization.") - return None, None - system_prompt = ResourceLoader.load_file_content(self.content_system_prompt_path) + system_prompt = self._system_prompt_cache.get("content") if not system_prompt: - logging.error(f"Failed to load content system prompt from '{self.content_system_prompt_path}'.") - return None, None + if not self.content_system_prompt_path: + logging.error("Content system prompt path not provided during PromptManager initialization.") + return None, None + system_prompt = ResourceLoader.load_file_content(self.content_system_prompt_path) + if system_prompt: + self._system_prompt_cache["content"] = system_prompt + else: + logging.error(f"Failed to load content system prompt from '{self.content_system_prompt_path}'.") + return None, None - # --- User Prompt (Logic moved from ResourceLoader.build_user_prompt) --- + # --- User Prompt --- user_prompt = "" - prompts_dir = self.prompts_dir - resource_dir_config = self.resource_dir_config - - if not prompts_dir or not os.path.isdir(prompts_dir): - logging.warning(f"Prompts directory '{prompts_dir}' not found or invalid. Content user prompt might be incomplete.") - # Decide whether to return error or continue with potentially incomplete prompt # 1. 添加Demand部分 (直接使用 topic_item['logic'] 的描述性文本) try: @@ -145,7 +452,7 @@ class PromptManager: matched_object_basename = None # 遍历查找 Object 文件 - for dir_info in resource_dir_config: + for dir_info in self.resource_dir_config: if dir_info.get("type") == "Object": for file_path in dir_info.get("file_path", []): basename = os.path.basename(file_path) @@ -205,7 +512,7 @@ class PromptManager: # Then, load Product Info file product_file_path = None - for dir_info in resource_dir_config: + for dir_info in self.resource_dir_config: if dir_info.get("type") == "Product": for file_path in dir_info.get("file_path", []): if product_name in os.path.basename(file_path): @@ -221,76 +528,48 @@ class PromptManager: logging.warning(f"Product file could not be loaded: {product_file_path}") else: logging.warning(f"Product file path not found in config for: {product_name}") - # Removed KeyError check for product_logic as it's now optional text except KeyError: logging.warning("Warning: Missing 'product' key in topic_item for Product prompt.") except Exception as e: logging.exception("Error processing Product prompt:") - # 4. 添加Style信息 (加载文件 based on topic_item['style'] from Style/ folder) + # 4. 添加Style信息 (加载文件 based on topic_item['style']) try: - style_filename = topic_item.get('style') - if style_filename: - # Ensure .txt extension - if not style_filename.lower().endswith('.txt'): - style_file = f"{style_filename}.txt" - else: - style_file = style_filename - style_path = os.path.join(prompts_dir, "Style", style_file) # Look in Style/ subfolder - style_content = ResourceLoader.load_file_content(style_path) + style_name = topic_item.get('style') + if style_name: + style_content = self._get_style_content(style_name) if style_content: - user_prompt += f"Style Info:\n{style_content}\n" # Changed label for clarity + user_prompt += f"Style Info:\n{style_content}\n" else: - logging.warning(f"Style file not found or empty: {style_path}") + logging.warning(f"Style file not found or empty for: {style_name}") else: logging.warning("Warning: 'style' key missing or empty in topic_item.") except Exception as e: logging.exception("Error processing Style prompt:") - # 5. 添加Target Audience信息 (加载文件 based on topic_item['target_audience'] from Demand/ folder) + # 5. 添加Target Audience信息 (加载文件 based on topic_item['target_audience']) try: - target_audience_filename = topic_item.get('target_audience') - if target_audience_filename: - # Ensure .txt extension (or use the check as done for style) - if not target_audience_filename.lower().endswith('.txt'): - target_audience_file = f"{target_audience_filename}.txt" - else: - target_audience_file = target_audience_filename - # Assuming target audience files are in the 'Demand' subdirectory based on old prompt - # Load from Demand/ subfolder as confirmed - target_audience_path = os.path.join(prompts_dir, "Demand", target_audience_file) - target_audience_content = ResourceLoader.load_file_content(target_audience_path) + target_audience_name = topic_item.get('target_audience') + if target_audience_name: + target_audience_content = self._get_demand_content(target_audience_name) if target_audience_content: user_prompt += f"Target Audience Info:\n{target_audience_content}\n" else: - logging.warning(f"Target Audience file not found or empty: {target_audience_path}") + logging.warning(f"Target Audience file not found or empty for: {target_audience_name}") else: logging.warning("Warning: 'target_audience' key missing or empty in topic_item.") except Exception as e: logging.exception("Error processing Target Audience prompt:") - # 6. 添加Refer信息 (加载 Refer/ 目录下所有文件的内容) + # 6. 添加Refer信息 (加载所有Refer文件的内容) try: - refer_dir = os.path.join(prompts_dir, "Refer") - if os.path.isdir(refer_dir): - refer_content_all = "" - refer_files = [f for f in os.listdir(refer_dir) if os.path.isfile(os.path.join(refer_dir, f))] - logging.info(f"Found {len(refer_files)} files in Refer directory: {refer_files}") - for refer_file in refer_files: - refer_path = os.path.join(refer_dir, refer_file) - content = ResourceLoader.load_file_content(refer_path) - if content: - refer_content_all += f"--- Refer File: {refer_file} ---\n{content}\n\n" - else: - logging.warning(f"Could not load Refer file: {refer_path}") - if refer_content_all: - user_prompt += f"Refer Info:\n{refer_content_all}" - else: - logging.warning("No content loaded from Refer directory.") + refer_content_all = self._get_all_refer_contents() + if refer_content_all: + user_prompt += f"Refer Info:\n{refer_content_all}" else: - logging.warning(f"Refer directory not found: {refer_dir}") + logging.warning("No content loaded from Refer files.") except Exception as e: - logging.exception("Error processing Refer directory:") + logging.exception("Error processing Refer files:") # --- End of prompt construction logic --- diff --git a/utils/tweet_generator.py b/utils/tweet_generator.py index 4ac2537..8d1f759 100644 --- a/utils/tweet_generator.py +++ b/utils/tweet_generator.py @@ -289,6 +289,7 @@ def run_topic_generation_pipeline(config, run_id=None): topic_user_prompt_path = config.get("topic_user_prompt") content_sys_prompt_path = config.get("content_system_prompt") # 虽然这里不用,但 PromptManager 可能需要 prompts_dir_path = config.get("prompts_dir") + prompts_config = config.get("prompts_config") # 新增:获取prompts_config配置 resource_config = config.get("resource_dir", []) topic_num = config.get("num", 1) topic_date = config.get("date", "") @@ -299,6 +300,7 @@ def run_topic_generation_pipeline(config, run_id=None): topic_user_prompt_path=topic_user_prompt_path, content_system_prompt_path=content_sys_prompt_path, prompts_dir=prompts_dir_path, + prompts_config=prompts_config, # 新增:传入prompts_config配置 resource_dir_config=resource_config, topic_gen_num=topic_num, topic_gen_date=topic_date