@@ -75,6 +75,23 @@ async def get_db_write_lock(user_id: str) -> Lock:
7575 return db_write_locks [user_id ]
7676
7777
78+ def _is_gemini_prompt_blocked_error (error : Optional [str ]) -> bool :
79+ if not error :
80+ return False
81+ markers = (
82+ "promptFeedback.blockReason=PROHIBITED_CONTENT" ,
83+ "Gemini 请求被拦截" ,
84+ )
85+ return any (marker in error for marker in markers )
86+
87+
88+ def _is_same_ai_service (left : AIService , right : AIService ) -> bool :
89+ return (
90+ left .api_provider == right .api_provider
91+ and left .default_model == right .default_model
92+ )
93+
94+
7895@router .post ("" , response_model = ChapterResponse , summary = "创建章节" )
7996async def create_chapter (
8097 chapter : ChapterCreate ,
@@ -969,11 +986,56 @@ async def on_retry_callback(attempt: int, max_retries: int, wait_time: int, erro
969986 on_retry = on_retry_callback ,
970987 characters_info = characters_info
971988 )
989+
990+ analysis_error = getattr (analyzer , "last_error" , None )
991+
992+ if not analysis_result and _is_gemini_prompt_blocked_error (analysis_error ):
993+ fallback_ai_service = await get_user_ai_service_from_db_by_usage (
994+ user_id = user_id ,
995+ db = db_session ,
996+ usage = "default"
997+ )
998+
999+ if not _is_same_ai_service (ai_service , fallback_ai_service ):
1000+ logger .warning (
1001+ "⚠️ 章节分析专用 Gemini 模型被内容策略拦截,"
1002+ f"改用默认模型兜底: { fallback_ai_service .api_provider } /{ fallback_ai_service .default_model } "
1003+ )
1004+ async with write_lock :
1005+ task .status = 'running'
1006+ task .progress = max (task .progress or 0 , 40 )
1007+ task .error_message = f"专用 Gemini 模型被拦截,正在使用默认模型兜底:{ analysis_error [:150 ]} "
1008+ await db_session .commit ()
1009+
1010+ fallback_analyzer = PlotAnalyzer (fallback_ai_service )
1011+ analysis_result = await fallback_analyzer .analyze_chapter (
1012+ chapter_number = chapter .chapter_number ,
1013+ title = chapter .title ,
1014+ content = chapter .content ,
1015+ word_count = chapter .word_count or len (chapter .content ),
1016+ user_id = user_id ,
1017+ db = db_session ,
1018+ max_retries = 2 ,
1019+ existing_foreshadows = existing_foreshadows ,
1020+ on_retry = on_retry_callback ,
1021+ characters_info = characters_info
1022+ )
1023+
1024+ if not analysis_result :
1025+ fallback_error = getattr (fallback_analyzer , "last_error" , None )
1026+ analysis_error = (
1027+ f"{ analysis_error } ;默认模型兜底失败: "
1028+ f"{ fallback_error or '未知错误' } "
1029+ )
1030+ else :
1031+ analysis_error = None
1032+ else :
1033+ logger .warning ("⚠️ 默认模型与章节分析模型相同,跳过兜底重试" )
9721034
9731035 if not analysis_result :
9741036 async with write_lock :
9751037 task .status = 'failed'
976- task .error_message = 'AI分析失败,请检查日志'
1038+ task .error_message = ( analysis_error or 'AI分析失败,请检查日志' )[: 500 ]
9771039 task .completed_at = datetime .now ()
9781040 await db_session .commit ()
9791041 logger .error (f"❌ AI分析失败: { chapter_id } " )
@@ -1266,6 +1328,7 @@ async def on_retry_callback(attempt: int, max_retries: int, wait_time: int, erro
12661328 async with write_lock :
12671329 task .progress = 100
12681330 task .status = 'completed'
1331+ task .error_message = None
12691332 task .completed_at = datetime .now ()
12701333 await db_session .commit ()
12711334 update_success = True
@@ -3281,9 +3344,14 @@ async def execute_batch_generation_in_order(
32813344
32823345 # 直接根据返回值判断
32833346 if not analysis_result :
3284- last_analysis_error = "分析函数返回失败"
3347+ try :
3348+ await db_session .refresh (analysis_task )
3349+ last_analysis_error = analysis_task .error_message or "分析函数返回失败"
3350+ except Exception as refresh_error :
3351+ logger .warning (f"⚠️ 刷新分析任务错误信息失败: { refresh_error } " )
3352+ last_analysis_error = "分析函数返回失败"
32853353 logger .error (f"❌ 章节分析失败: 第{ chapter .chapter_number } 章" )
3286- raise Exception (f"章节分析失败" )
3354+ raise Exception (last_analysis_error )
32873355
32883356 # 分析成功
32893357 analysis_success = True
@@ -4486,4 +4554,3 @@ async def apply_partial_regenerate(
44864554 "old_word_count" : old_word_count ,
44874555 "message" : "局部重写已应用"
44884556 }
4489-
0 commit comments