fix: 去除LLM直接生成图片占位符逻辑
This commit is contained in:
@@ -28,7 +28,7 @@ from app.core.dependencies import get_llm_provider
|
||||
from app.agents.state_schema import MemoirStateSchema, SlotData, default_state
|
||||
from app.agents.memoir.prompts import (
|
||||
STAGE_TO_ORDER,
|
||||
get_narrative_prompt,
|
||||
get_narrative_json_prompt,
|
||||
inject_image_placeholder_template,
|
||||
)
|
||||
from app.agents.memoir import MemoirOrchestrator
|
||||
@@ -38,6 +38,7 @@ from app.agents.chat.prompts_profile import format_user_profile_context
|
||||
from app.features.memoir.memoir_images.parser import (
|
||||
build_initial_image_assets,
|
||||
parse_image_placeholders,
|
||||
parse_narrative_to_sections,
|
||||
split_narrative_to_sections,
|
||||
)
|
||||
import hashlib
|
||||
@@ -67,6 +68,14 @@ logger = get_logger(__name__)
|
||||
_REDIS_CLIENTS: dict[bool, redis.Redis] = {}
|
||||
|
||||
|
||||
def _is_json_narrative(text: str) -> bool:
|
||||
"""检测 narrative 是否为 JSON 格式(paragraphs 结构)"""
|
||||
if not text or not text.strip():
|
||||
return False
|
||||
s = text.strip()
|
||||
return s.startswith("{") and "paragraphs" in s
|
||||
|
||||
|
||||
def _get_llm():
|
||||
"""Celery 任务内获取 LangChain LLM(通过 port)"""
|
||||
try:
|
||||
@@ -328,7 +337,7 @@ def _save_narrative_to_sections(db: Session, chapter, narrative: str, title: str
|
||||
img_settings = MemoirImageSettings.from_env()
|
||||
prompt_service = MemoirImagePromptService(llm=None, settings=img_settings) if img_settings.enabled else None
|
||||
|
||||
segments = split_narrative_to_sections(narrative_to_parse)
|
||||
segments = parse_narrative_to_sections(narrative_to_parse)
|
||||
if not segments:
|
||||
sec = ChapterSection(
|
||||
id=str(uuid.uuid4()),
|
||||
@@ -368,8 +377,11 @@ def _save_narrative_to_sections(db: Session, chapter, narrative: str, title: str
|
||||
chapter.source_segments = list(set((chapter.source_segments or []) + (source_segments or [])))
|
||||
return chapter
|
||||
|
||||
# 每 3 个 section 对应 1 张图片,其他 section 的 image_id 为空
|
||||
def _should_have_image(order_idx: int) -> bool:
|
||||
def _should_have_image(seg: dict, order_idx: int) -> bool:
|
||||
"""有 placeholder_info 的段落配图;无则兼容旧格式(每 3 段 1 图)"""
|
||||
ph = seg.get("placeholder_info")
|
||||
if ph and ph.get("description"):
|
||||
return True
|
||||
return (order_idx % 3) == 2
|
||||
|
||||
def _placeholder_for_segment(seg: dict, order_idx: int) -> dict | None:
|
||||
@@ -385,7 +397,7 @@ def _save_narrative_to_sections(db: Session, chapter, narrative: str, title: str
|
||||
order_idx = order_base + i
|
||||
content = (seg.get("content") or "").strip()
|
||||
image_asset = None
|
||||
if img_settings.enabled and _should_have_image(order_idx):
|
||||
if img_settings.enabled and _should_have_image(seg, order_idx):
|
||||
ph = _placeholder_for_segment(seg, order_idx)
|
||||
style = prompt_service.CATEGORY_STYLE_MAP.get(category, img_settings.default_style) if prompt_service else img_settings.default_style
|
||||
image_asset = build_initial_image_assets(
|
||||
@@ -640,12 +652,14 @@ def process_memoir_segments(self, user_id: str, segment_ids: List[str]):
|
||||
birth_year=birth_year,
|
||||
llm=llm,
|
||||
)
|
||||
if existing_content:
|
||||
if _is_json_narrative(new_narrative):
|
||||
narrative = new_narrative
|
||||
elif existing_content:
|
||||
narrative = f"{existing_content}\n\n{new_narrative}"
|
||||
else:
|
||||
narrative = new_narrative
|
||||
|
||||
if existing_content and len(narrative) < len(existing_content) * 0.8:
|
||||
if existing_content and not _is_json_narrative(narrative) and len(narrative) < len(existing_content) * 0.8:
|
||||
logger.warning(
|
||||
"内容长度异常: existing=%d, new=%d, category=%s. 回退为追加模式",
|
||||
len(existing_content),
|
||||
@@ -654,7 +668,8 @@ def process_memoir_segments(self, user_id: str, segment_ids: List[str]):
|
||||
)
|
||||
narrative = f"{existing_content}\n\n{combined_text}"
|
||||
|
||||
narrative = inject_placeholders(narrative)
|
||||
if not _is_json_narrative(narrative):
|
||||
narrative = inject_placeholders(narrative)
|
||||
calculated_order_index = STAGE_TO_ORDER.get(chapter_category, 999)
|
||||
|
||||
chapter = _save_narrative_to_sections(
|
||||
@@ -777,7 +792,7 @@ def generate_chapter_content(self, user_id: str, stage: str, new_content: str):
|
||||
)
|
||||
|
||||
if llm:
|
||||
prompt = get_narrative_prompt(
|
||||
prompt = get_narrative_json_prompt(
|
||||
stage=stage,
|
||||
slots={},
|
||||
new_content=new_content,
|
||||
@@ -785,24 +800,25 @@ def generate_chapter_content(self, user_id: str, stage: str, new_content: str):
|
||||
)
|
||||
response = llm.invoke(prompt)
|
||||
new_narrative = response.content.strip()
|
||||
# 追加而非替换
|
||||
if existing_content:
|
||||
if _is_json_narrative(new_narrative):
|
||||
narrative = new_narrative
|
||||
elif existing_content:
|
||||
narrative = f"{existing_content}\n\n{new_narrative}"
|
||||
else:
|
||||
narrative = new_narrative
|
||||
else:
|
||||
narrative = f"{existing_content}\n\n{new_content}" if existing_content else new_content
|
||||
|
||||
# 安全检查:新内容不应比旧内容短
|
||||
if existing_content and len(narrative) < len(existing_content) * 0.8:
|
||||
# 安全检查:新内容不应比旧内容短(仅非 JSON 格式)
|
||||
if existing_content and not _is_json_narrative(narrative) and len(narrative) < len(existing_content) * 0.8:
|
||||
logger.warning(
|
||||
f"内容长度异常: existing={len(existing_content)}, "
|
||||
f"new={len(narrative)}, stage={stage}. 回退为追加模式"
|
||||
)
|
||||
narrative = f"{existing_content}\n\n{new_content}"
|
||||
|
||||
# 入库前:占位符位置用正则匹配后拼上固定模板
|
||||
narrative = inject_image_placeholder_template(narrative)
|
||||
if not _is_json_narrative(narrative):
|
||||
narrative = inject_image_placeholder_template(narrative)
|
||||
calculated_order_index = STAGE_TO_ORDER.get(stage, 999)
|
||||
title = chapter.title if chapter else f"{stage} 回忆"
|
||||
chapter = _save_narrative_to_sections(
|
||||
|
||||
Reference in New Issue
Block a user