Files
life-echo/api/agents/prompts/memory_prompts.py

363 lines
15 KiB
Python
Raw Normal View History

2026-01-07 11:56:53 +08:00
"""
回忆录整理 Agent 提示词模板
"""
import json
import re
from typing import Optional
2026-01-07 11:56:53 +08:00
# 章节分类映射
CHAPTER_CATEGORIES = {
"childhood": "童年与成长背景",
"education": "教育经历与青年时期",
"career_early": "崭露头角",
"career_achievement": "主要成就与巅峰时刻",
"career_challenge": "挫折、挑战与重大转折",
"family": "家庭与情感",
"beliefs": "信念与价值观",
"summary": "人生总结",
}
# 章节顺序
CHAPTER_ORDER = [
"childhood",
"education",
"career_early",
"career_achievement",
"career_challenge",
"family",
"beliefs",
"summary",
]
# 统一的阶段名 → 排序索引映射
# 兼容 5 阶段简化名conversation/state 模型)和 8 分类详细名chapter 模型)
STAGE_TO_ORDER = {
"childhood": 0,
"education": 1,
"career": 2, # 5-stage 简化名
"career_early": 2, # 8-category 详细名
"career_achievement": 3,
"career_challenge": 4,
"family": 5,
"belief": 6, # 5-stage 简化名(单数)
"beliefs": 6, # 8-category 详细名(复数)
"summary": 7,
}
# 图片占位符入库前拼接的固定提示词模板(与原先 prompt 中要求一致,改为代码侧统一拼接)
IMAGE_PLACEHOLDER_TEMPLATE = (
2026-03-12 13:47:12 +08:00
"温暖怀旧风格,年代感复古色调,柔和光影,朴素温馨氛围,安静治愈,低饱和度,"
"质感柔和细腻,简约构图,充满岁月沉淀感与故事感,高清唯美插画封面,不要包含文字,"
2026-03-12 13:47:12 +08:00
"要适合老年人阅读风格,要有年代感,有朦胧感。"
)
2026-03-12 10:13:40 +08:00
# 匹配任意层数的图片占位符2/4/6/8...层花括号),整段替换为规范四层,避免多余花括号残留导致客户端显示异常
_IMAGE_PLACEHOLDER_ANY_BRACES_RE = re.compile(
r"(\{\{)+IMAGE:\s*([^}]+)(\}\})+",
re.DOTALL,
)
def inject_image_placeholder_template(content: str) -> str:
"""
入库前对章节正文做占位符处理用正则匹配所有图片占位符位置拼上固定模板
2026-03-12 10:13:40 +08:00
支持任意层数花括号 {{{{{{{{{{{{ 输出统一为四层大括号 + 固定模板 + 描述
避免 LLM 输出花括号过多时只替换内层导致多余花括号残留在正文中在手机端被原样显示
若占位符内已包含固定模板前缀则不再重复拼接
"""
if not content or not content.strip():
return content
def replace_one(match: re.Match) -> str:
2026-03-12 10:13:40 +08:00
inner = (match.group(2) or "").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 + "}}}}"
2026-03-12 10:13:40 +08:00
content = _IMAGE_PLACEHOLDER_ANY_BRACES_RE.sub(replace_one, content)
return content
2026-01-07 11:56:53 +08:00
def get_system_prompt() -> str:
"""获取整理 Agent 的系统提示词"""
return """你是一位专业的传记作家和文字编辑,擅长将口语化的对话内容整理成优雅的书面语回忆录章节。
你的任务
1. 接收对话段落文本口语化可能来自语音转写
2. **先提炼对话中与人生经历相关的核心内容**过滤掉无关信息
3. 识别内容主题归类到对应章节童年/教育/事业/家庭/信念/总结
4. 将口语化表达改写为书面语保持原意和情感
5. 生成合适的章节标题和段落结构
6. 提取关键信息形成连贯的叙述
7. 建议插图位置在描述场景人物地点的地方
## 内容筛选原则(最重要)
对话中往往夹杂大量与回忆录无关的噪音你必须严格筛选只保留有价值的内容
应该保留的内容
- 具体的人生事件经历故事
- 提到的人物及其关系家人朋友同事恩师等
- 地点时间场景描写
- 用户的情感表达内心感受
- 人生感悟价值观信念
- 具体的细节食物声音画面等
应该过滤掉的内容
- 语气词填充词那个就是说对对对然后呢等
- 对话中的寒暄问候你好谢谢好的等
- 用户与AI助手之间的交互指令你帮我我想问你说得对等
- 重复冗余的表述取核心含义即可
- 与个人经历完全无关的闲聊内容
## 改写原则
- 保持用户的真实情感
- 使用优雅但不失亲切的书面语不要直接引用对话原话
2026-01-07 11:56:53 +08:00
- 适当添加过渡句使段落连贯
- 保留生动的细节但将口语表达改写为书面叙述
- 去除口语中的填充词和无意义重复
2026-01-07 11:56:53 +08:00
- 保持时间顺序和逻辑清晰
## 章节分类规则
2026-01-07 11:56:53 +08:00
- 童年相关 "童年与成长背景"
- 学校老师同学 "教育经历与青年时期"
- 工作职业成就 "主要成就与巅峰时刻" "崭露头角"
- 困难挫折 "挫折、挑战与重大转折"
- 伴侣孩子家庭生活 "家庭与情感"
- 价值观信念座右铭 "信念与价值观"
- 总结感悟展望 "人生总结"
"""
def get_chapter_classification_prompt(segments_text: str) -> str:
"""获取章节分类的提示词"""
return f"""{get_system_prompt()}
请分析以下对话内容**忽略其中的语气词寒暄和无关对话**只关注涉及人生经历的实质内容判断应该归类到哪个章节类别
2026-01-07 11:56:53 +08:00
- childhood: 童年与成长背景
- education: 教育经历与青年时期
- career_early: 崭露头角早期事业
- career_achievement: 主要成就与巅峰时刻
- career_challenge: 挫折挑战与重大转折
- family: 家庭与情感
- beliefs: 信念与价值观
- summary: 人生总结
对话内容
{segments_text}
请只返回章节类别childhood不要返回其他内容
如果对话内容中没有任何与人生经历相关的实质内容返回 none"""
2026-01-07 11:56:53 +08:00
def get_text_rewrite_prompt(segments_text: str, chapter_category: str, existing_content: str = "") -> str:
"""获取文本改写的提示词"""
chapter_name = CHAPTER_CATEGORIES.get(chapter_category, chapter_category)
existing_section = f"\n\n已有章节内容:\n{existing_content}" if existing_content else ""
return f"""{get_system_prompt()}
请将以下口语化的对话内容改写为书面语归类到"{chapter_name}"章节
对话内容
{segments_text}
{existing_section}
请按照以下格式返回 JSON
{{
"title": "章节标题",
2026-01-21 22:31:03 +01:00
"content": "改写后的书面语内容(包含图片占位符)",
"summary": "章节摘要50字以内"
2026-01-07 11:56:53 +08:00
}}
要求
1. 标题要简洁有力能概括章节主题
2. 内容要流畅自然保持原意和情感
3. 如果已有章节内容请将新内容与已有内容自然融合
2026-01-21 22:31:03 +01:00
4. 在内容中适当位置插入图片占位符
2026-03-12 10:13:40 +08:00
## 图片占位符格式(必须严格遵守)
- **唯一合法格式**开头恰好四个左花括号结尾恰好四个右花括号中间为 IMAGE:具体描述{{{{IMAGE:具体的图片描述}}}}
- 禁止使用两层 {{ }}六层 {{{{{{ }}}}}} 或任意其它层数否则会在手机端显示异常
- 占位符单独占一行描述要具体有画面感系统会在入库前自动拼上统一风格模板你只需写场景描述即可
2026-01-21 22:31:03 +01:00
2026-03-12 10:13:40 +08:00
正确示例仅此格式
2026-01-21 22:31:03 +01:00
{{{{IMAGE:南方小镇的青石板路两旁是白墙黑瓦的老房子}}}}
{{{{IMAGE:奶奶坐在院子里的藤椅上手里摇着蒲扇}}}}"""
2026-01-21 22:31:03 +01:00
def get_state_extraction_prompt(user_message: str, current_stage: str, stage_slots: dict) -> str:
"""抽取结构化信息并判断阶段"""
slot_keys = list(stage_slots.keys())
# 提供所有阶段的 slot 参考,帮助 LLM 将内容归类到正确的阶段
all_stage_slots = {
"childhood": ["place", "people", "daily_life", "emotion", "turning_event"],
"education": ["school", "city", "motivation", "challenge", "change"],
"career": ["job", "environment", "decision", "pressure", "growth"],
"family": ["relationship", "conflict", "support", "responsibility", "change"],
"belief": ["value", "regret", "pride", "lesson"],
}
2026-01-21 22:31:03 +01:00
return f"""{get_system_prompt()}
你需要从用户话语中**先提炼与人生经历相关的核心内容**然后抽取结构化信息并判断用户实际在谈论哪个人生阶段
系统当前跟踪的阶段{current_stage}
该阶段可填 slots{slot_keys}
2026-01-21 22:31:03 +01:00
所有阶段及其 slots 参考
{json.dumps(all_stage_slots, ensure_ascii=False, indent=2)}
2026-01-21 22:31:03 +01:00
用户话语
{user_message}
请只返回 JSON格式如下
{{
"detected_stage": "childhood|education|career|family|belief",
"slots": {{
"slot_key": "snippet"
}},
"emotion": "neutral|warm|low|highlight",
"is_new_chapter": true
}}
要求
2026-03-12 13:47:12 +08:00
1. **应的 slot 列表
4. slots 只填写确实提到的与人生经历相关的实先忽略话语中的语气词填充词寒暄与AI的交互指令等无关内容**只关注涉及人生经历的实质信息
2. **detected_stage 必须根据用户话语的实际内容判断**不要默认沿用系统当前阶段用户可能在聊不同阶段的事情
2026-03-12 13:47:12 +08:00
3. slots key 必须属于 detected_stage 对质内容
5. **snippet 应是提炼后的核心信息**去除语气词和冗余表达50 字以内
6. 如果用户话语中没有任何与人生经历相关的实质内容如纯粹的寒暄指令语气词slots 为空对象
2026-01-21 22:31:03 +01:00
"""
def _build_age_hint(stage: str, birth_year: Optional[int] = None) -> str:
"""根据人生阶段和出生年份推算大致年龄区间"""
if not birth_year:
return ""
stage_age_ranges = {
"childhood": (0, 12),
"education": (6, 22),
"career": (18, 60),
"career_early": (18, 30),
"career_achievement": (25, 55),
"career_challenge": (20, 55),
"family": (20, 60),
"belief": (30, 70),
"beliefs": (30, 70),
"summary": (50, 80),
}
age_range = stage_age_ranges.get(stage)
if not age_range:
return ""
year_start = birth_year + age_range[0]
year_end = birth_year + age_range[1]
return f"大约 {year_start}-{year_end} 年({age_range[0]}-{age_range[1]} 岁)"
def get_creative_title_prompt(
stage: str,
emotion: str,
slots: dict,
user_profile: str = "",
birth_year: Optional[int] = None,
) -> str:
"""生成有创意的章节标题,包含年龄/时间信息"""
age_hint = _build_age_hint(stage, birth_year)
profile_section = f"\n用户基本信息:\n{user_profile}" if user_profile else ""
time_section = f"\n时间参考:{age_hint}" if age_hint else ""
2026-01-21 22:31:03 +01:00
return f"""{get_system_prompt()}
请根据阶段和情绪生成 1 个有创意的章节标题
2026-03-12 13:47:12 +08:00
阶段{stage}
2026-01-21 22:31:03 +01:00
情绪{emotion}
可用信息{slots}{profile_section}{time_section}
2026-01-21 22:31:03 +01:00
要求
1. 标题格式时间标注 · 标题正文
- 时间标注用年龄或年代表示"6-12岁""1980年代""二十出头"
- 标题正文 12-18 字以内
2026-01-21 22:31:03 +01:00
2. 情绪 + 人生阶段 + 意象
3. 示例风格
- 6-12 · 那条巷子尽头的蝉鸣
- 18 · 第一次离开家的夏天
- 25-35 · 在陌生城市站稳脚跟
- 四十不惑 · 慢下来人生开始发声
- 1990年代 · 不是所有选择都被理解
2026-01-21 22:31:03 +01:00
只输出标题文字不要加引号或书名号
2026-01-21 22:31:03 +01:00
"""
def get_narrative_prompt(
stage: str,
slots: dict,
new_content: str,
existing_content: str = "",
user_profile: str = "",
birth_year: Optional[int] = None,
) -> str:
"""将新对话改写为叙述(只输出新内容的改写,不重复已有内容)"""
context_tail = ""
if existing_content:
context_tail = existing_content[-300:] if len(existing_content) > 300 else existing_content
context_section = f"\n\n【衔接上下文(已有内容的末尾,仅供参考衔接,不要重复)】:\n{context_tail}" if context_tail else ""
profile_section = f"\n\n用户基本信息:\n{user_profile}" if user_profile else ""
age_hint = _build_age_hint(stage, birth_year)
time_section = f"\n时间参考:{age_hint}" if age_hint else ""
2026-01-21 22:31:03 +01:00
return f"""{get_system_prompt()}
请将以下新的对话内容改写为第一人称文学叙述
2026-01-21 22:31:03 +01:00
阶段{stage}
可用信息{slots}{profile_section}{time_section}
2026-01-21 22:31:03 +01:00
新的对话内容
{new_content}
{context_section}
2026-01-21 22:31:03 +01:00
## 第一步:提炼核心内容
在改写之前请先从对话内容中提炼出与人生经历相关的核心信息
- 提取具体的事件人物地点时间感受
- 丢弃语气词那个就是说寒暄你好谢谢与AI的交互你帮我整理一下对对对你说得对无意义的重复
- 如果对话内容中几乎没有与人生经历相关的实质内容请输出空字符串
## 第二步:改写为叙述
基于提炼后的核心内容进行文学改写
2026-01-21 22:31:03 +01:00
1. 使用第一人称叙述
2. **不要直接引用对话原话**将所有内容改写为流畅的书面叙述
3. **只输出新内容的改写结果**不要重复已有内容
4. 如果有衔接上下文确保新内容与之自然衔接语气时间线连贯
5. 语气自然有情绪
6. 在适合配图的地方插入图片占位符
7. 如果有用户的基本信息出生地成长地等在叙述中自然融入地域文化和时代背景
8. **不要将对话中的交互性语言"我跟你说""你知道吗"写入叙述**
9. **不要在正文中插入章节标题或分类标签**"章节:信念与价值观""## 童年与成长背景"章节标题由系统单独管理
2026-01-21 22:31:03 +01:00
2026-03-12 10:13:40 +08:00
## 图片占位符格式(必须严格遵守)
- **唯一合法格式**开头恰好四个左花括号结尾恰好四个右花括号{{{{IMAGE:具体的图片描述}}}}
- 禁止两层 {{ }}六层 {{{{{{ }}}}}} 或其它层数否则会在手机端显示多余花括号
- 占位符单独占一行描述要具体有画面感系统会在入库前自动拼上统一风格模板你只需写场景描述即可
2026-01-21 22:31:03 +01:00
2026-03-12 10:13:40 +08:00
正确示例仅此格式
2026-01-21 22:31:03 +01:00
- {{{{IMAGE:南方小镇的青石板路两旁是白墙黑瓦的老房子}}}}
- {{{{IMAGE:奶奶坐在院子里的藤椅上手里摇着蒲扇}}}}
- {{{{IMAGE:少年背着书包站在火车站台上回望身后的小镇}}}}
- {{{{IMAGE:泛黄的大学录取通知书压在一摞旧课本下}}}}
图片占位符要求
- 描述要具体有画面感便于后续生成或匹配图片
- 200-300 字左右可以插入一个
- 单独占一行不要嵌入段落中
- 不要使用括号或星号等其他格式
2026-03-12 10:13:40 +08:00
- **花括号必须且仅能为四层**{{{{ }}}} 各四个不多不少
2026-01-21 22:31:03 +01:00
只输出新对话内容的改写结果包含图片占位符如果对话中没有值得记录的人生经历内容输出空字符串
2026-01-21 22:31:03 +01:00
"""