From 4ae68394c5de3924e479ec56241ade02af4327ad Mon Sep 17 00:00:00 2001 From: yangshilin <2157598560@qq.com> Date: Wed, 11 Mar 2026 09:48:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=8D=A0=E4=BD=8D=E7=AC=A6=E6=8F=90=E7=A4=BA=E8=AF=8D=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E6=A8=A1=E6=9D=BF=20=E5=85=A5=E5=BA=93=E6=97=B6?= =?UTF-8?q?=E6=8B=BC=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/agents/memory_agent.py | 7 +++- api/agents/prompts/__init__.py | 2 + api/agents/prompts/memory_prompts.py | 55 ++++++++++++++++++++++++---- api/scripts/reprocess_user_memoir.py | 5 ++- api/tasks/memoir_tasks.py | 7 ++++ 5 files changed, 66 insertions(+), 10 deletions(-) diff --git a/api/agents/memory_agent.py b/api/agents/memory_agent.py index 15b6454..a219c9c 100644 --- a/api/agents/memory_agent.py +++ b/api/agents/memory_agent.py @@ -12,6 +12,7 @@ from .prompts import ( get_memory_prompt, get_chapter_classification_prompt, get_text_rewrite_prompt, + inject_image_placeholder_template, CHAPTER_CATEGORIES, STAGE_TO_ORDER, ) @@ -106,13 +107,15 @@ class MemoryAgent: content = content.strip() result = json.loads(content) + result["content"] = inject_image_placeholder_template(result.get("content") or "") return result - + except json.JSONDecodeError: # 如果解析失败,返回基本结构 + raw = response.content if hasattr(response, 'content') else str(response) return { "title": CHAPTER_CATEGORIES.get(chapter_category, "章节"), - "content": response.content if hasattr(response, 'content') else str(response), + "content": inject_image_placeholder_template(raw), "summary": "", "image_suggestions": [] } diff --git a/api/agents/prompts/__init__.py b/api/agents/prompts/__init__.py index d5c04b2..7e84e39 100644 --- a/api/agents/prompts/__init__.py +++ b/api/agents/prompts/__init__.py @@ -15,6 +15,7 @@ from .memory_prompts import ( get_state_extraction_prompt, get_creative_title_prompt, get_narrative_prompt, + inject_image_placeholder_template, CHAPTER_CATEGORIES, CHAPTER_ORDER, STAGE_TO_ORDER, @@ -40,6 +41,7 @@ __all__ = [ "get_state_extraction_prompt", "get_creative_title_prompt", "get_narrative_prompt", + "inject_image_placeholder_template", "CHAPTER_CATEGORIES", "CHAPTER_ORDER", "STAGE_TO_ORDER", diff --git a/api/agents/prompts/memory_prompts.py b/api/agents/prompts/memory_prompts.py index d80c0c9..8b8d914 100644 --- a/api/agents/prompts/memory_prompts.py +++ b/api/agents/prompts/memory_prompts.py @@ -2,6 +2,7 @@ 回忆录整理 Agent 提示词模板 """ import json +import re from typing import Optional # 章节分类映射 @@ -43,6 +44,48 @@ STAGE_TO_ORDER = { "summary": 7, } +# 图片占位符入库前拼接的固定提示词模板(与原先 prompt 中要求一致,改为代码侧统一拼接) +IMAGE_PLACEHOLDER_TEMPLATE = ( + "温暖怀旧风格,人生章节封面,年代感复古色调,柔和光影,朴素温馨氛围,安静治愈,低饱和度," + "质感柔和细腻,简约构图,充满岁月沉淀感与故事感,高清唯美插画封面,不要包含文字," + "要适合老年人阅读风格,要有年代感。" +) + + +def inject_image_placeholder_template(content: str) -> str: + """ + 入库前对章节正文做占位符处理:用正则匹配所有图片占位符位置,拼上固定模板。 + 支持 {{IMAGE:...}} 与 {{{{IMAGE:...}}}} 两种格式,输出统一为四层大括号 + 固定模板 + 描述。 + 若占位符内已包含固定模板前缀则不再重复拼接,保证位置与逻辑与原先一致。 + """ + if not content or not content.strip(): + return content + + def replace_one(match: re.Match) -> str: + inner = match.group(1).strip() + if not inner: + return match.group(0) + if inner.startswith(IMAGE_PLACEHOLDER_TEMPLATE): + desc = inner[len(IMAGE_PLACEHOLDER_TEMPLATE):].lstrip("。").strip() + return "{{{{IMAGE:" + IMAGE_PLACEHOLDER_TEMPLATE + ("。" + desc if desc else "") + "}}}}" + return "{{{{IMAGE:" + IMAGE_PLACEHOLDER_TEMPLATE + "。" + inner + "}}}}" + + # 四层大括号 + content = re.sub( + r"\{\{\{\{IMAGE:\s*([^}]+)\}\}\}\}", + replace_one, + content, + flags=re.DOTALL, + ) + # 两层大括号(兼容旧格式) + content = re.sub( + r"\{\{IMAGE:\s*([^}]+)\}\}", + replace_one, + content, + flags=re.DOTALL, + ) + return content + def get_system_prompt() -> str: """获取整理 Agent 的系统提示词""" @@ -143,14 +186,12 @@ def get_text_rewrite_prompt(segments_text: str, chapter_category: str, existing_ 4. 在内容中适当位置插入图片占位符 ## 图片占位符格式 -在描述场景、人物、重要时刻的段落后,插入: -{{{{IMAGE:具体的图片描述}}}} +在描述场景、人物、重要时刻的段落后,插入占位符,格式为:{{{{IMAGE:具体的图片描述}}}} +占位符单独占一行,描述要具体、有画面感。系统会在入库前自动拼上统一风格模板,你只需写场景描述即可。 示例: {{{{IMAGE:南方小镇的青石板路,两旁是白墙黑瓦的老房子}}}} -{{{{IMAGE:奶奶坐在院子里的藤椅上,手里摇着蒲扇}}}} - -占位符单独占一行,描述要具体有画面感。""" +{{{{IMAGE:奶奶坐在院子里的藤椅上,手里摇着蒲扇}}}}""" def get_state_extraction_prompt(user_message: str, current_stage: str, stage_slots: dict) -> str: @@ -306,8 +347,8 @@ def get_narrative_prompt( 9. **不要在正文中插入章节标题或分类标签**(如"章节:信念与价值观"、"## 童年与成长背景"等),章节标题由系统单独管理 ## 图片占位符格式 -在描述场景、人物、重要时刻的段落后,插入图片占位符,格式为: -{{{{IMAGE:图片描述}}}} +在描述场景、人物、重要时刻的段落后,插入占位符,格式为:{{{{IMAGE:具体的图片描述}}}} +占位符单独占一行,描述要具体、有画面感。系统会在入库前自动拼上统一风格模板,你只需写场景描述即可。 示例: - {{{{IMAGE:南方小镇的青石板路,两旁是白墙黑瓦的老房子}}}} diff --git a/api/scripts/reprocess_user_memoir.py b/api/scripts/reprocess_user_memoir.py index ba5a899..bbab937 100644 --- a/api/scripts/reprocess_user_memoir.py +++ b/api/scripts/reprocess_user_memoir.py @@ -54,6 +54,7 @@ from agents.prompts.memory_prompts import ( get_creative_title_prompt, get_narrative_prompt, get_state_extraction_prompt, + inject_image_placeholder_template, STAGE_TO_ORDER, ) @@ -312,10 +313,12 @@ def generate_chapters_in_memory( if i + batch_size < len(seg_list): time.sleep(1) + # 入库前:占位符位置用正则匹配后拼上固定模板 + content_to_save = inject_image_placeholder_template(existing_content) results.append(GeneratedChapter( category=stage, title=title, - content=existing_content, + content=content_to_save, order_index=STAGE_TO_ORDER.get(stage, 999), source_segment_ids=all_source_ids, )) diff --git a/api/tasks/memoir_tasks.py b/api/tasks/memoir_tasks.py index e1c5b58..a513dd2 100644 --- a/api/tasks/memoir_tasks.py +++ b/api/tasks/memoir_tasks.py @@ -22,6 +22,7 @@ from agents.prompts.memory_prompts import ( get_narrative_prompt, get_state_extraction_prompt, get_chapter_classification_prompt, + inject_image_placeholder_template, STAGE_TO_ORDER, CHAPTER_CATEGORIES, ) @@ -346,6 +347,9 @@ def process_memoir_segments(self, user_id: str, segment_ids: List[str]): ) narrative = f"{existing_content}\n\n{combined_text}" + # 入库前:占位符位置用正则匹配后拼上固定模板 + narrative = inject_image_placeholder_template(narrative) + # 更新或创建章节 if chapter: chapter.content = narrative @@ -468,6 +472,9 @@ def generate_chapter_content(self, user_id: str, stage: str, new_content: str): ) narrative = f"{existing_content}\n\n{new_content}" + # 入库前:占位符位置用正则匹配后拼上固定模板 + narrative = inject_image_placeholder_template(narrative) + if chapter: chapter.content = narrative chapter.is_new = True