Files
life-echo/api/agents/prompts/conversation_prompts.py
penghanyuan 41ceb3dad8 feat: 增强对话代理和提示生成逻辑
- 在ConversationAgent中添加对话历史和轮数的计算,以支持更智能的对话管理
- 引入同一话题轮数的估算逻辑,优化对话的连贯性
- 更新get_guided_conversation_prompt函数,动态调整对话策略和回应风格
- 在UI组件中优化消息显示,支持流式消息和多部分消息的展示
- 更新应用设置管理,支持持久化存储和Compose状态观察
2026-01-29 20:09:09 +01:00

296 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
对话 Agent 提示词模板和访谈问题库
"""
from enum import Enum
from typing import List, Dict
import random
class ConversationStage(str, Enum):
"""对话阶段枚举"""
CHILDHOOD = "childhood" # 童年
EDUCATION = "education" # 教育
CAREER = "career" # 事业
FAMILY = "family" # 家庭
BELIEFS = "beliefs" # 信念
SUMMARY = "summary" # 人生总结
# 访谈问题库
INTERVIEW_QUESTIONS: Dict[ConversationStage, List[str]] = {
ConversationStage.CHILDHOOD: [
"你是在哪里长大的?小时候周围的环境是什么样的,有哪些让你印象深刻的童年记忆?",
"童年时期的你是个怎样的孩子?有没有做过什么淘气或有趣的事情,现在想起来还会让你发笑?",
"能聊聊你小时候的家庭吗?比如父母是怎样的人,他们对你的成长有什么影响吗?",
"小时候你有过什么梦想?那时候你最想长大后做什么?",
],
ConversationStage.EDUCATION: [
"上学的时候你是个怎么样的学生?你喜欢学校生活吗?",
"在求学过程中,有没有哪位老师或同学对你影响特别大?能说说他们的故事吗?",
"学生时代你参加过什么课外活动或者比赛吗?有没有哪段经历让你特别难忘?",
"那时候你对未来有什么打算吗?比如毕业后想从事什么职业,或者希望过怎样的生活?",
],
ConversationStage.CAREER: [
"第一次走出校园开始工作时,你还记得当时的情景吗?当时你的心情怎么样,有发生什么难忘的事吗?",
"你当初是怎么选择进入现在这个行业的?其中有什么契机或故事吗?",
"在工作中有没有遇到过特别大的挑战或低谷?当时你是怎么挺过来的?",
"职业生涯中有没有哪个成就或时刻让你特别自豪?能跟我分享一下那个故事吗?",
"在事业的发展过程中,有哪些重要的转折点?比如跳槽、升职或者创业,这些经历对你意味着什么?",
"回顾这一路,有哪些人对你的事业帮助最大或者影响最深?有没有特别想感谢的贵人或伙伴?",
],
ConversationStage.FAMILY: [
"可以聊聊你和你伴侣的故事吗?你们是怎么认识的,又是什么让你决定与他/她携手一生?",
"孩子在你的生活中意味着什么?做父母的过程中,有没有让你特别骄傲或者难忘的瞬间?",
"在家庭生活中,有没有什么传统或者特别的习惯,让你感到温馨和快乐?",
"平时你和家人喜欢一起做些什么?周末或假日你们通常会怎么度过?",
"你觉得家庭在你的人生中扮演了一个怎样的角色?",
"工作和家庭要怎么兼顾呢?你是如何平衡事业和家庭的?在两边兼顾的时候有没有遇到困难,后来又是怎么克服的?",
],
ConversationStage.BELIEFS: [
"你人生中有没有一些一直坚守的信念或者座右铭?这些信念给了你怎样的力量或者影响?",
"对你来说,哪些价值观是最重要的?这些价值观是受到哪些人或经历的影响而形成的呢?",
"当你遇到困难和低谷的时候,是什么支撑着你坚持下去?",
"你如何看待'成功''幸福'?对你来说它们分别意味着什么?",
],
ConversationStage.SUMMARY: [
"回顾你走过的路,你觉得这一生中最重要的经验或教训是什么?",
"在你的生活中,你最感激的人和事有哪些?有没有特别觉得自己很幸运的地方?",
"如果能对年轻时候的自己说几句话,你会想告诉他/她什么?",
"展望未来,你还有什么愿望或目标吗?有没有一直想尝试但还没来得及做的事情?",
"最后,你希望家人和后代记住你是一个怎样的人?",
],
}
def get_system_prompt(current_stage: ConversationStage, covered_topics: List[str], user_latest_response: str) -> str:
"""
生成对话 Agent 的系统提示词
Args:
current_stage: 当前对话阶段
covered_topics: 已聊过的话题列表
user_latest_response: 用户最新回答
Returns:
系统提示词字符串
"""
stage_name_map = {
ConversationStage.CHILDHOOD: "童年",
ConversationStage.EDUCATION: "教育",
ConversationStage.CAREER: "事业",
ConversationStage.FAMILY: "家庭",
ConversationStage.BELIEFS: "信念",
ConversationStage.SUMMARY: "人生总结",
}
covered_topics_str = "".join(covered_topics) if covered_topics else "暂无"
prompt = f"""你是一位资深的人生故事访谈者,专注于帮助用户回忆和讲述人生经历。
## 角色定位
你如同一位老朋友,用真诚、温暖的方式倾听用户的故事,通过自然的对话引导用户分享更多细节。
## 访谈技巧
1. 积极倾听:先对用户分享的内容给予简短回应,表达理解和共鸣
2. 深度追问:围绕用户提到的具体场景、人物、感受进行细节追问
3. 一次一问:每次只提一个问题,给用户充分思考和表达的空间
4. 自然过渡:当一个话题聊透后,自然引入下一个相关话题
## 输出要求
- 直接输出你要对用户说的话
- 禁止输出任何括号注释、思考过程、策略说明
- 禁止使用"我注意到""我想了解"等采访腔调
- 语气要像朋友聊天,自然亲切
当前阶段:{stage_name_map.get(current_stage, current_stage.value)}
已聊话题:{covered_topics_str}
请直接回应用户,不要有任何元描述。"""
return prompt
def get_questions_for_stage(stage: ConversationStage) -> List[str]:
"""获取指定阶段的所有问题"""
return INTERVIEW_QUESTIONS.get(stage, [])
SLOT_NAME_MAP = {
# 童年
"place": "成长的地方",
"people": "重要的人",
"daily_life": "日常生活",
"emotion": "童年感受",
"turning_event": "难忘的事",
# 教育
"school": "学校经历",
"city": "求学的城市",
"motivation": "学习动力",
"challenge": "遇到的挑战",
"change": "成长变化",
# 事业
"job": "工作内容",
"environment": "工作环境",
"decision": "重要决定",
"pressure": "压力与困难",
"growth": "职业成长",
# 家庭
"relationship": "家人关系",
"conflict": "矛盾与化解",
"support": "相互支持",
"responsibility": "家庭责任",
# 信念
"value": "核心价值观",
"regret": "遗憾与释怀",
"pride": "骄傲的事",
"lesson": "人生经验",
}
# 阶段关联话题(用于自然过渡)
STAGE_RELATED_TOPICS = {
"childhood": ["family", "education"], # 童年可以自然聊到家庭、教育
"education": ["childhood", "career"], # 教育可以聊到童年、事业
"career": ["education", "family", "belief"], # 事业可以聊到教育、家庭、信念
"family": ["childhood", "career", "belief"], # 家庭可以聊到童年、事业、信念
"belief": ["career", "family"], # 信念可以聊到事业、家庭
}
# 轻松话题(用于调节气氛)
LIGHT_TOPICS = [
"有什么爱好或者特别喜欢的消遣方式吗?",
"最近有什么让你开心的事吗?",
"有没有什么趣事想分享?",
"平时喜欢看什么书或者电影吗?",
]
# 回应风格模板(增加多样性)
RESPONSE_STYLES = [
"empathy", # 共情式回应
"curious", # 好奇追问
"reflection", # 感慨反思
"lighthearted", # 轻松调侃
"connection", # 关联自身(虚构)
]
def get_guided_conversation_prompt(
current_stage: str,
empty_slots: List[str],
filled_slots: Dict[str, str],
user_message: str,
conversation_turn: int = 0,
same_topic_turns: int = 0,
) -> str:
"""
生成状态感知的对话提示词
Args:
current_stage: 当前阶段
empty_slots: 未填充的槽位
filled_slots: 已填充的槽位
user_message: 用户消息
conversation_turn: 总对话轮数
same_topic_turns: 同一话题的轮数
"""
# 转换 slot 名称为中文
empty_slots_readable = [SLOT_NAME_MAP.get(s, s) for s in empty_slots]
empty_slots_str = "".join(empty_slots_readable) if empty_slots_readable else "已聊得很充分"
filled_info = []
for key, value in filled_slots.items():
readable_key = SLOT_NAME_MAP.get(key, key)
filled_info.append(f"{readable_key}: {value[:50]}..." if len(value) > 50 else f"{readable_key}: {value}")
filled_slots_str = "\n".join(filled_info) if filled_info else "刚开始聊"
stage_name_map = {
"childhood": "童年时光",
"education": "求学经历",
"career": "职业生涯",
"family": "家庭生活",
"belief": "人生信念",
}
stage_name = stage_name_map.get(current_stage, current_stage)
# 计算已填充的槽位数量
filled_count = len(filled_slots)
total_slots = filled_count + len(empty_slots)
# 动态调整策略
should_switch_topic = same_topic_turns >= 3 or (filled_count >= 2 and same_topic_turns >= 2)
should_lighten_mood = conversation_turn > 0 and conversation_turn % 5 == 0
should_try_new_stage = filled_count >= 3 and len(empty_slots) <= 2
# 获取相关阶段
related_stages = STAGE_RELATED_TOPICS.get(current_stage, [])
related_stages_str = "".join([stage_name_map.get(s, s) for s in related_stages])
# 选择回应风格
style = random.choice(RESPONSE_STYLES)
style_guidance = {
"empathy": "这次回应要特别体现共情,表达你能理解用户的感受",
"curious": "这次回应要表现出真诚的好奇,对细节充满兴趣",
"reflection": "这次回应可以加入一点感慨或人生感悟",
"lighthearted": "这次回应可以轻松一点,适当加入幽默",
"connection": "这次回应可以分享一个类似的经历或感受(可以虚构)",
}.get(style, "")
# 构建动态指导
dynamic_guidance = ""
if should_lighten_mood:
dynamic_guidance += "\n- 聊了一会儿了,可以适当轻松一下,聊点有趣的"
if should_switch_topic and empty_slots_readable:
dynamic_guidance += f"\n- 这个话题聊得差不多了,可以自然转到:{empty_slots_str}"
if should_try_new_stage and related_stages:
dynamic_guidance += f"\n- 如果自然的话,可以尝试聊聊相关的话题,比如{related_stages_str}"
prompt = f"""你是用户的老朋友,正在和他/她聊人生故事。你们聊到了「{stage_name}」这个话题。
## 已经聊到的内容
{filled_slots_str}
## 还可以聊的方向
{empty_slots_str}
## 用户刚才说
"{user_message}"
## 回应风格
{style_guidance}
## 你的任务
1. **回应用户**:先对用户说的内容做出真诚回应(不是总结,而是有温度的反馈)
2. **保持自然**:不要每次都追问,有时候可以分享感受、表达好奇、或者轻松聊两句
3. **适时换话题**:如果一个方向聊了几轮,自然地换到其他方向,保持新鲜感
4. **追问要具体**:如果要追问,问具体的细节,比如"那时候是什么季节""身边有谁陪着你""当时心里什么感觉"
{dynamic_guidance}
## 回复格式
- 如果内容较多,可以分成 2-3 条消息,用 [SPLIT] 分隔
- 每条消息保持自然,像微信聊天一样
- 第一条消息是回应,第二条可以是追问或者换话题
- 如果内容简单,一条消息即可
## 严格禁止
- 禁止输出括号、注释、思考过程
- 禁止说"我注意到""我想问""让我们聊聊"
- 禁止生硬地问"还有什么想分享的吗"
- 禁止反复追问同一件事
- 禁止每次都以问题结尾
## 好的回应示例
- "哈哈,你这说的让我想起..."(轻松)
- "这段经历听起来真不容易啊"(共情)
- "那个年代的xxx确实是这样"(理解)
- "所以后来怎么样了?"(好奇)
- "对了你刚才提到xxx那个时候..."(换话题)
直接输出你要说的话(多条消息用 [SPLIT] 分隔):"""
return prompt
# 保留向后兼容的函数名
def get_conversation_prompt(current_stage: ConversationStage, covered_topics: List[str], user_latest_response: str) -> str:
"""向后兼容的函数"""
return get_system_prompt(current_stage, covered_topics, user_latest_response)