refactor: 进一步重构agents目录结构 符合多agent架构

This commit is contained in:
yangshilin
2026-03-19 10:54:48 +08:00
parent c21cda3e78
commit 67fb5d2cb6
23 changed files with 98 additions and 167 deletions

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from app.agents.chat.orchestrator import ChatOrchestrator from app.agents.chat.orchestrator import ChatOrchestrator
from app.agents.prompts import ConversationStage from app.agents.chat.prompts_conversation import ConversationStage
from app.agents.state_schema import MemoirStateSchema from app.agents.state_schema import MemoirStateSchema
from app.core.redis import redis_service from app.core.redis import redis_service

View File

@@ -8,8 +8,11 @@ from app.core.dependencies import get_llm_provider
from app.core.logging import get_logger from app.core.logging import get_logger
from app.agents.chat.helpers import format_history_string, get_history_messages from app.agents.chat.helpers import format_history_string, get_history_messages
from app.agents.prompts import get_guided_conversation_prompt, get_opening_prompt from app.agents.chat.prompts_conversation import (
from app.agents.prompts.conversation_prompts import SLOT_NAME_MAP SLOT_NAME_MAP,
get_guided_conversation_prompt,
get_opening_prompt,
)
from app.agents.state_schema import MemoirStateSchema from app.agents.state_schema import MemoirStateSchema
logger = get_logger(__name__) logger = get_logger(__name__)

View File

@@ -92,7 +92,7 @@ class ChatOrchestrator:
conversation.conversation_stage = state.current_stage conversation.conversation_stage = state.current_stage
await db.commit() await db.commit()
from app.agents.prompts.profile_prompts import format_user_profile_context from app.agents.chat.prompts_profile import format_user_profile_context
user_profile_context = "" user_profile_context = ""
if user: if user:

View File

@@ -11,7 +11,7 @@ from app.core.dependencies import get_llm_provider
from app.core.logging import get_logger from app.core.logging import get_logger
from app.agents.chat.helpers import format_history_string, get_history_messages from app.agents.chat.helpers import format_history_string, get_history_messages
from app.agents.prompts.profile_prompts import ( from app.agents.chat.prompts_profile import (
get_profile_extraction_prompt, get_profile_extraction_prompt,
get_profile_followup_prompt, get_profile_followup_prompt,
get_profile_greeting_prompt, get_profile_greeting_prompt,

View File

@@ -0,0 +1,41 @@
"""
Chat 模块提示词:用户资料收集 + 对话访谈
"""
# Profile prompts用户资料收集
from app.agents.chat.prompts_profile import (
PROFILE_FIELD_NAMES,
format_user_profile_context,
get_missing_profile_fields,
get_profile_extraction_prompt,
get_profile_followup_prompt,
get_profile_greeting_prompt,
)
# Conversation prompts对话访谈
from app.agents.chat.prompts_conversation import (
ConversationStage,
INTERVIEW_QUESTIONS,
SLOT_NAME_MAP,
get_conversation_prompt,
get_guided_conversation_prompt,
get_opening_prompt,
get_questions_for_stage,
get_system_prompt,
)
__all__ = [
"PROFILE_FIELD_NAMES",
"format_user_profile_context",
"get_missing_profile_fields",
"get_profile_extraction_prompt",
"get_profile_followup_prompt",
"get_profile_greeting_prompt",
"ConversationStage",
"INTERVIEW_QUESTIONS",
"SLOT_NAME_MAP",
"get_conversation_prompt",
"get_guided_conversation_prompt",
"get_opening_prompt",
"get_questions_for_stage",
"get_system_prompt",
]

View File

@@ -65,12 +65,12 @@ INTERVIEW_QUESTIONS: Dict[ConversationStage, List[str]] = {
def get_system_prompt(current_stage: ConversationStage, covered_topics: List[str], user_latest_response: str) -> str: def get_system_prompt(current_stage: ConversationStage, covered_topics: List[str], user_latest_response: str) -> str:
""" """
生成对话 Agent 的系统提示词 生成对话 Agent 的系统提示词
Args: Args:
current_stage: 当前对话阶段 current_stage: 当前对话阶段
covered_topics: 已聊过的话题列表 covered_topics: 已聊过的话题列表
user_latest_response: 用户最新回答 user_latest_response: 用户最新回答
Returns: Returns:
系统提示词字符串 系统提示词字符串
""" """
@@ -82,9 +82,9 @@ def get_system_prompt(current_stage: ConversationStage, covered_topics: List[str
ConversationStage.BELIEFS: "信念", ConversationStage.BELIEFS: "信念",
ConversationStage.SUMMARY: "人生总结", ConversationStage.SUMMARY: "人生总结",
} }
covered_topics_str = "".join(covered_topics) if covered_topics else "暂无" covered_topics_str = "".join(covered_topics) if covered_topics else "暂无"
prompt = f"""你是「岁月知己」,一位资深的人生故事访谈者,专注于帮助用户回忆和讲述人生经历。 prompt = f"""你是「岁月知己」,一位资深的人生故事访谈者,专注于帮助用户回忆和讲述人生经历。
## 角色定位 ## 角色定位
@@ -106,7 +106,7 @@ def get_system_prompt(current_stage: ConversationStage, covered_topics: List[str
已聊话题{covered_topics_str} 已聊话题{covered_topics_str}
请直接回应用户不要有任何元描述""" 请直接回应用户不要有任何元描述"""
return prompt return prompt
@@ -116,46 +116,39 @@ def get_questions_for_stage(stage: ConversationStage) -> List[str]:
SLOT_NAME_MAP = { SLOT_NAME_MAP = {
# 童年
"place": "成长的地方", "place": "成长的地方",
"people": "重要的人", "people": "重要的人",
"daily_life": "日常生活", "daily_life": "日常生活",
"emotion": "童年感受", "emotion": "童年感受",
"turning_event": "难忘的事", "turning_event": "难忘的事",
# 教育
"school": "学校经历", "school": "学校经历",
"city": "求学的城市", "city": "求学的城市",
"motivation": "学习动力", "motivation": "学习动力",
"challenge": "遇到的挑战", "challenge": "遇到的挑战",
"change": "成长变化", "change": "成长变化",
# 事业
"job": "工作内容", "job": "工作内容",
"environment": "工作环境", "environment": "工作环境",
"decision": "重要决定", "decision": "重要决定",
"pressure": "压力与困难", "pressure": "压力与困难",
"growth": "职业成长", "growth": "职业成长",
# 家庭
"relationship": "家人关系", "relationship": "家人关系",
"conflict": "矛盾与化解", "conflict": "矛盾与化解",
"support": "相互支持", "support": "相互支持",
"responsibility": "家庭责任", "responsibility": "家庭责任",
# 信念
"value": "核心价值观", "value": "核心价值观",
"regret": "遗憾与释怀", "regret": "遗憾与释怀",
"pride": "骄傲的事", "pride": "骄傲的事",
"lesson": "人生经验", "lesson": "人生经验",
} }
# 阶段关联话题(用于自然过渡)
STAGE_RELATED_TOPICS = { STAGE_RELATED_TOPICS = {
"childhood": ["family", "education"], # 童年可以自然聊到家庭、教育 "childhood": ["family", "education"],
"education": ["childhood", "career"], # 教育可以聊到童年、事业 "education": ["childhood", "career"],
"career": ["education", "family", "belief"], # 事业可以聊到教育、家庭、信念 "career": ["education", "family", "belief"],
"family": ["childhood", "career", "belief"], # 家庭可以聊到童年、事业、信念 "family": ["childhood", "career", "belief"],
"belief": ["career", "family"], # 信念可以聊到事业、家庭 "belief": ["career", "family"],
} }
# 轻松话题(用于调节气氛)
LIGHT_TOPICS = [ LIGHT_TOPICS = [
"有什么爱好或者特别喜欢的消遣方式吗?", "有什么爱好或者特别喜欢的消遣方式吗?",
"最近有什么让你开心的事吗?", "最近有什么让你开心的事吗?",
@@ -163,13 +156,12 @@ LIGHT_TOPICS = [
"平时喜欢看什么书或者电影吗?", "平时喜欢看什么书或者电影吗?",
] ]
# 回应风格模板(增加多样性)
RESPONSE_STYLES = [ RESPONSE_STYLES = [
"empathy", # 共情式回应 "empathy",
"curious", # 好奇追问 "curious",
"reflection", # 感慨反思 "reflection",
"lighthearted", # 轻松调侃 "lighthearted",
"connection", # 关联自身(虚构) "connection",
] ]
@@ -178,10 +170,7 @@ def get_opening_prompt(
empty_slots_readable: List[str], empty_slots_readable: List[str],
user_profile_context: str = "", user_profile_context: str = "",
) -> str: ) -> str:
""" """空对话时 AI 先开口的提示词"""
空对话时 AI 先开口的提示词用户通过打个招呼进入尚未发送任何消息
要求 AI 先发一条问候 + 一个具体问题引导用户开始分享
"""
stage_name_map = { stage_name_map = {
"childhood": "童年时光", "childhood": "童年时光",
"education": "求学经历", "education": "求学经历",
@@ -216,10 +205,7 @@ def get_opening_prompt(
def _build_era_context(current_stage: str, user_profile_context: str) -> str: def _build_era_context(current_stage: str, user_profile_context: str) -> str:
""" """根据用户的人生阶段和出生年份,生成对应时代的历史/政治/文化背景提示"""
根据用户的人生阶段和出生年份生成对应时代的历史/政治/文化背景提示
agent 在对话中自然融入时代感
"""
if not user_profile_context: if not user_profile_context:
return "" return ""
@@ -250,7 +236,6 @@ def _build_era_context(current_stage: str, user_profile_context: str) -> str:
era_end = birth_year + age_range[1] era_end = birth_year + age_range[1]
era_events = [] era_events = []
decade_events = { decade_events = {
1950: "新中国成立初期、土地改革、抗美援朝", 1950: "新中国成立初期、土地改革、抗美援朝",
1960: "大跃进、三年自然灾害、中苏关系变化", 1960: "大跃进、三年自然灾害、中苏关系变化",
@@ -270,7 +255,6 @@ def _build_era_context(current_stage: str, user_profile_context: str) -> str:
return "" return ""
place_hint = f"(用户来自{birth_place}" if birth_place else "" place_hint = f"(用户来自{birth_place}" if birth_place else ""
return f""" return f"""
## 时代背景参考{place_hint} ## 时代背景参考{place_hint}
用户在这个人生阶段大约经历了 {era_start}-{era_end} {age_range[0]}-{age_range[1]} 用户在这个人生阶段大约经历了 {era_start}-{era_end} {age_range[0]}-{age_range[1]}
@@ -293,20 +277,7 @@ def get_guided_conversation_prompt(
detected_user_stage: str = "", detected_user_stage: str = "",
user_profile_context: str = "", user_profile_context: str = "",
) -> str: ) -> str:
""" """生成状态感知的对话提示词"""
生成状态感知的对话提示词
Args:
current_stage: 系统当前跟踪的阶段
empty_slots: 当前阶段未填充的槽位
filled_slots: 当前阶段已填充的槽位
user_message: 用户消息
conversation_turn: 总对话轮数
same_topic_turns: 同一话题的轮数
all_stages_coverage: 所有阶段的覆盖情况 {stage: {total, filled, empty, ratio}}
detected_user_stage: 检测到用户正在谈论的阶段可能和 current_stage 不同
user_profile_context: 用户基础资料上下文
"""
stage_name_map = { stage_name_map = {
"childhood": "童年时光", "childhood": "童年时光",
"education": "求学经历", "education": "求学经历",
@@ -317,12 +288,8 @@ def get_guided_conversation_prompt(
current_stage_name = stage_name_map.get(current_stage, current_stage) current_stage_name = stage_name_map.get(current_stage, current_stage)
user_stage_name = stage_name_map.get(detected_user_stage, "") if detected_user_stage else "" user_stage_name = stage_name_map.get(detected_user_stage, "") if detected_user_stage else ""
# 判断用户是否在聊一个不同于系统当前阶段的话题
user_jumped = detected_user_stage and detected_user_stage != current_stage user_jumped = detected_user_stage and detected_user_stage != current_stage
# --- 构建当前聊天上下文 ---
# 转换 slot 名称为中文
empty_slots_readable = [SLOT_NAME_MAP.get(s, s) for s in empty_slots] 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 "已聊得很充分" empty_slots_str = "".join(empty_slots_readable) if empty_slots_readable else "已聊得很充分"
@@ -332,7 +299,6 @@ def get_guided_conversation_prompt(
filled_info.append(f"{readable_key}: {value[:50]}..." if len(value) > 50 else f"{readable_key}: {value}") 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 "刚开始聊" filled_slots_str = "\n".join(filled_info) if filled_info else "刚开始聊"
# --- 构建全局进度概览 ---
progress_lines = [] progress_lines = []
uncovered_stages = [] uncovered_stages = []
if all_stages_coverage: if all_stages_coverage:
@@ -350,17 +316,14 @@ def get_guided_conversation_prompt(
progress_lines.append(f" {sname}:已聊得很充分 ✓") progress_lines.append(f" {sname}:已聊得很充分 ✓")
progress_str = "\n".join(progress_lines) if progress_lines else "" progress_str = "\n".join(progress_lines) if progress_lines else ""
# --- 动态策略 ---
filled_count = len(filled_slots) filled_count = len(filled_slots)
should_switch_topic = same_topic_turns >= 3 or (filled_count >= 2 and same_topic_turns >= 2) 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_lighten_mood = conversation_turn > 0 and conversation_turn % 5 == 0
should_try_new_stage = filled_count >= 3 and len(empty_slots) <= 2 should_try_new_stage = filled_count >= 3 and len(empty_slots) <= 2
# 获取相关阶段
related_stages = STAGE_RELATED_TOPICS.get(current_stage, []) related_stages = STAGE_RELATED_TOPICS.get(current_stage, [])
related_stages_str = "".join([stage_name_map.get(s, s) for s in related_stages]) related_stages_str = "".join([stage_name_map.get(s, s) for s in related_stages])
# 选择回应风格
style = random.choice(RESPONSE_STYLES) style = random.choice(RESPONSE_STYLES)
style_guidance = { style_guidance = {
"empathy": "这次回应要特别体现共情,表达你能理解用户的感受", "empathy": "这次回应要特别体现共情,表达你能理解用户的感受",
@@ -370,7 +333,6 @@ def get_guided_conversation_prompt(
"connection": "这次回应可以分享一个类似的经历或感受(可以虚构)", "connection": "这次回应可以分享一个类似的经历或感受(可以虚构)",
}.get(style, "") }.get(style, "")
# --- 构建动态指导 ---
dynamic_guidance = "" dynamic_guidance = ""
if user_jumped: if user_jumped:
dynamic_guidance += f""" dynamic_guidance += f"""
@@ -385,19 +347,15 @@ def get_guided_conversation_prompt(
if should_try_new_stage and related_stages: if should_try_new_stage and related_stages:
dynamic_guidance += f"\n- 如果自然的话,可以尝试聊聊相关的话题,比如{related_stages_str}" dynamic_guidance += f"\n- 如果自然的话,可以尝试聊聊相关的话题,比如{related_stages_str}"
# --- 缺失章节补充提示(仅在用户没有跳转、且当前话题聊得差不多时) ---
uncovered_hint = "" uncovered_hint = ""
if not user_jumped and uncovered_stages and should_try_new_stage: if not user_jumped and uncovered_stages and should_try_new_stage:
uncovered_hint = f"\n- 还没聊到的人生阶段有:{''.join(uncovered_stages)},如果聊天中有自然的契机,可以轻轻带一句,但不要刻意" uncovered_hint = f"\n- 还没聊到的人生阶段有:{''.join(uncovered_stages)},如果聊天中有自然的契机,可以轻轻带一句,但不要刻意"
# --- 组合 prompt ---
# 根据是否跳转,调整主题描述
if user_jumped: if user_jumped:
topic_desc = f"你们原本在聊「{current_stage_name}」,但用户自然地聊到了「{user_stage_name}」的内容" topic_desc = f"你们原本在聊「{current_stage_name}」,但用户自然地聊到了「{user_stage_name}」的内容"
else: else:
topic_desc = f"你们聊到了「{current_stage_name}」这个话题" topic_desc = f"你们聊到了「{current_stage_name}」这个话题"
# --- 用户资料和时代背景 ---
profile_section = "" profile_section = ""
if user_profile_context: if user_profile_context:
profile_section = f"\n## 用户基本信息\n{user_profile_context}\n" profile_section = f"\n## 用户基本信息\n{user_profile_context}\n"
@@ -460,7 +418,6 @@ def get_guided_conversation_prompt(
return prompt return prompt
# 保留向后兼容的函数名
def get_conversation_prompt(current_stage: ConversationStage, covered_topics: List[str], user_latest_response: str) -> str: 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) return get_system_prompt(current_stage, covered_topics, user_latest_response)

View File

@@ -8,7 +8,7 @@ from typing import Any, Optional
from app.core.logging import get_logger from app.core.logging import get_logger
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
CHAPTER_CATEGORIES, CHAPTER_CATEGORIES,
get_chapter_classification_prompt, get_chapter_classification_prompt,
) )

View File

@@ -11,7 +11,7 @@ from typing import Any, Dict
from app.core.logging import get_logger from app.core.logging import get_logger
from app.features.memoir.memoir_images.json_payload import extract_json_payload from app.features.memoir.memoir_images.json_payload import extract_json_payload
from app.agents.prompts.memory_prompts import get_state_extraction_prompt from app.agents.memoir.prompts import get_state_extraction_prompt
logger = get_logger(__name__) logger = get_logger(__name__)

View File

@@ -8,12 +8,12 @@ from typing import Dict, List, Optional
from app.core.dependencies import get_llm_provider from app.core.dependencies import get_llm_provider
from app.core.logging import get_logger from app.core.logging import get_logger
from app.agents.prompts import ( from app.agents.memoir.prompts import (
CHAPTER_CATEGORIES,
STAGE_TO_ORDER,
get_chapter_classification_prompt, get_chapter_classification_prompt,
get_text_rewrite_prompt, get_text_rewrite_prompt,
inject_image_placeholder_template, inject_image_placeholder_template,
CHAPTER_CATEGORIES,
STAGE_TO_ORDER,
) )
logger = get_logger(__name__) logger = get_logger(__name__)

View File

@@ -8,7 +8,7 @@ from typing import Any, Dict, Optional
from app.core.logging import get_logger from app.core.logging import get_logger
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
get_creative_title_prompt, get_creative_title_prompt,
get_narrative_prompt, get_narrative_prompt,
) )

View File

@@ -3,7 +3,7 @@ PlaceholderInjectAgent对 narrative 做占位符模板注入。
对应现有逻辑inject_image_placeholder_template 对应现有逻辑inject_image_placeholder_template
纯函数式,无 LLM 调用。 纯函数式,无 LLM 调用。
""" """
from app.agents.prompts.memory_prompts import inject_image_placeholder_template from app.agents.memoir.prompts import inject_image_placeholder_template
def inject_placeholders(content: str) -> str: def inject_placeholders(content: str) -> str:

View File

@@ -13,7 +13,7 @@ from app.core.logging import get_logger
from app.core.task_tracker import task_tracker from app.core.task_tracker import task_tracker
from app.agents.state_schema import MemoirStateSchema from app.agents.state_schema import MemoirStateSchema
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
get_creative_title_prompt, get_creative_title_prompt,
get_narrative_prompt, get_narrative_prompt,
get_state_extraction_prompt, get_state_extraction_prompt,

View File

@@ -5,7 +5,6 @@ import json
import re import re
from typing import Optional from typing import Optional
# 章节分类映射
CHAPTER_CATEGORIES = { CHAPTER_CATEGORIES = {
"childhood": "童年与成长背景", "childhood": "童年与成长背景",
"education": "教育经历与青年时期", "education": "教育经历与青年时期",
@@ -17,7 +16,6 @@ CHAPTER_CATEGORIES = {
"summary": "人生总结", "summary": "人生总结",
} }
# 章节顺序
CHAPTER_ORDER = [ CHAPTER_ORDER = [
"childhood", "childhood",
"education", "education",
@@ -29,22 +27,19 @@ CHAPTER_ORDER = [
"summary", "summary",
] ]
# 统一的阶段名 → 排序索引映射
# 兼容 5 阶段简化名conversation/state 模型)和 8 分类详细名chapter 模型)
STAGE_TO_ORDER = { STAGE_TO_ORDER = {
"childhood": 0, "childhood": 0,
"education": 1, "education": 1,
"career": 2, # 5-stage 简化名 "career": 2,
"career_early": 2, # 8-category 详细名 "career_early": 2,
"career_achievement": 3, "career_achievement": 3,
"career_challenge": 4, "career_challenge": 4,
"family": 5, "family": 5,
"belief": 6, # 5-stage 简化名(单数) "belief": 6,
"beliefs": 6, # 8-category 详细名(复数) "beliefs": 6,
"summary": 7, "summary": 7,
} }
# 图片占位符入库前拼接的固定提示词模板(与原先 prompt 中要求一致,改为代码侧统一拼接)
IMAGE_PLACEHOLDER_TEMPLATE = ( IMAGE_PLACEHOLDER_TEMPLATE = (
"温暖怀旧风格,年代感复古色调,柔和光影,朴素温馨氛围,安静治愈,低饱和度," "温暖怀旧风格,年代感复古色调,柔和光影,朴素温馨氛围,安静治愈,低饱和度,"
"质感柔和细腻,简约构图,充满岁月沉淀感与故事感,高清唯美插画封面,不要包含文字," "质感柔和细腻,简约构图,充满岁月沉淀感与故事感,高清唯美插画封面,不要包含文字,"
@@ -53,8 +48,6 @@ IMAGE_PLACEHOLDER_TEMPLATE = (
"有朦胧怀旧的年代感。" "有朦胧怀旧的年代感。"
) )
# 匹配任意层数的图片占位符2/4/6/8...层花括号),整段替换为规范四层,避免多余花括号残留导致客户端显示异常
_IMAGE_PLACEHOLDER_ANY_BRACES_RE = re.compile( _IMAGE_PLACEHOLDER_ANY_BRACES_RE = re.compile(
r"(\{\{)+IMAGE:\s*([^}]+)(\}\})+", r"(\{\{)+IMAGE:\s*([^}]+)(\}\})+",
re.DOTALL, re.DOTALL,
@@ -64,9 +57,7 @@ _IMAGE_PLACEHOLDER_ANY_BRACES_RE = re.compile(
def inject_image_placeholder_template(content: str) -> str: def inject_image_placeholder_template(content: str) -> str:
""" """
入库前对章节正文做占位符处理用正则匹配所有图片占位符位置拼上固定模板 入库前对章节正文做占位符处理用正则匹配所有图片占位符位置拼上固定模板
支持任意层数花括号 {{{{{{{{{{{{ 输出统一为四层大括号 + 固定模板 + 描述 支持任意层数花括号输出统一为四层大括号 + 固定模板 + 描述
避免 LLM 输出花括号过多时只替换内层导致多余花括号残留在正文中在手机端被原样显示
若占位符内已包含固定模板前缀则不再重复拼接
""" """
if not content or not content.strip(): if not content or not content.strip():
return content return content
@@ -82,6 +73,8 @@ def inject_image_placeholder_template(content: str) -> str:
content = _IMAGE_PLACEHOLDER_ANY_BRACES_RE.sub(replace_one, content) content = _IMAGE_PLACEHOLDER_ANY_BRACES_RE.sub(replace_one, content)
return content return content
def get_system_prompt() -> str: def get_system_prompt() -> str:
"""获取整理 Agent 的系统提示词""" """获取整理 Agent 的系统提示词"""
return """你是一位专业的传记作家和文字编辑,擅长将口语化的对话内容整理成优雅的书面语回忆录章节。 return """你是一位专业的传记作家和文字编辑,擅长将口语化的对话内容整理成优雅的书面语回忆录章节。
@@ -156,9 +149,7 @@ def get_chapter_classification_prompt(segments_text: str) -> str:
def get_text_rewrite_prompt(segments_text: str, chapter_category: str, existing_content: str = "") -> str: def get_text_rewrite_prompt(segments_text: str, chapter_category: str, existing_content: str = "") -> str:
"""获取文本改写的提示词""" """获取文本改写的提示词"""
chapter_name = CHAPTER_CATEGORIES.get(chapter_category, chapter_category) chapter_name = CHAPTER_CATEGORIES.get(chapter_category, chapter_category)
existing_section = f"\n\n已有章节内容:\n{existing_content}" if existing_content else "" existing_section = f"\n\n已有章节内容:\n{existing_content}" if existing_content else ""
return f"""{get_system_prompt()} return f"""{get_system_prompt()}
请将以下口语化的对话内容改写为书面语归类到"{chapter_name}"章节 请将以下口语化的对话内容改写为书面语归类到"{chapter_name}"章节
@@ -193,8 +184,6 @@ def get_text_rewrite_prompt(segments_text: str, chapter_category: str, existing_
def get_state_extraction_prompt(user_message: str, current_stage: str, stage_slots: dict) -> str: def get_state_extraction_prompt(user_message: str, current_stage: str, stage_slots: dict) -> str:
"""抽取结构化信息并判断阶段""" """抽取结构化信息并判断阶段"""
slot_keys = list(stage_slots.keys()) slot_keys = list(stage_slots.keys())
# 提供所有阶段的 slot 参考,帮助 LLM 将内容归类到正确的阶段
all_stage_slots = { all_stage_slots = {
"childhood": ["place", "people", "daily_life", "emotion", "turning_event"], "childhood": ["place", "people", "daily_life", "emotion", "turning_event"],
"education": ["school", "city", "motivation", "challenge", "change"], "education": ["school", "city", "motivation", "challenge", "change"],
@@ -227,10 +216,10 @@ def get_state_extraction_prompt(user_message: str, current_stage: str, stage_slo
}} }}
要求 要求
1. **应的 slot 列表 1. **先忽略话语中的语气词填充词寒暄与AI的交互指令等无关内容**只关注涉及人生经历的实质信息
4. slots 只填写确实提到的与人生经历相关的实先忽略话语中的语气词填充词寒暄与AI的交互指令等无关内容**只关注涉及人生经历的实质信息
2. **detected_stage 必须根据用户话语的实际内容判断**不要默认沿用系统当前阶段用户可能在聊不同阶段的事情 2. **detected_stage 必须根据用户话语的实际内容判断**不要默认沿用系统当前阶段用户可能在聊不同阶段的事情
3. slots key 必须属于 detected_stage 质内容 3. slots key 必须属于 detected_stage 应的 slot 列表
4. slots 只填写确实提到的与人生经历相关的实质内容
5. **snippet 应是提炼后的核心信息**去除语气词和冗余表达50 字以内 5. **snippet 应是提炼后的核心信息**去除语气词和冗余表达50 字以内
6. 如果用户话语中没有任何与人生经历相关的实质内容如纯粹的寒暄指令语气词slots 为空对象 6. 如果用户话语中没有任何与人生经历相关的实质内容如纯粹的寒暄指令语气词slots 为空对象
""" """
@@ -275,7 +264,7 @@ def get_creative_title_prompt(
return f"""{get_system_prompt()} return f"""{get_system_prompt()}
请根据阶段和情绪生成 1 个有创意的章节标题 请根据阶段和情绪生成 1 个有创意的章节标题
阶段{stage} 阶段{stage}
情绪{emotion} 情绪{emotion}
可用信息{slots}{profile_section}{time_section} 可用信息{slots}{profile_section}{time_section}
@@ -307,9 +296,7 @@ def get_narrative_prompt(
context_tail = "" context_tail = ""
if existing_content: if existing_content:
context_tail = existing_content[-300:] if len(existing_content) > 300 else 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 "" context_section = f"\n\n【衔接上下文(已有内容的末尾,仅供参考衔接,不要重复)】:\n{context_tail}" if context_tail else ""
profile_section = f"\n\n用户基本信息:\n{user_profile}" if user_profile else "" profile_section = f"\n\n用户基本信息:\n{user_profile}" if user_profile else ""
age_hint = _build_age_hint(stage, birth_year) age_hint = _build_age_hint(stage, birth_year)
time_section = f"\n时间参考:{age_hint}" if age_hint else "" time_section = f"\n时间参考:{age_hint}" if age_hint else ""

View File

@@ -1,57 +0,0 @@
"""
提示词模块
"""
from .conversation_prompts import (
ConversationStage,
get_system_prompt as get_conversation_prompt,
get_questions_for_stage,
get_guided_conversation_prompt,
get_opening_prompt,
INTERVIEW_QUESTIONS,
)
from .memory_prompts import (
get_system_prompt as get_memory_prompt,
get_chapter_classification_prompt,
get_text_rewrite_prompt,
get_state_extraction_prompt,
get_creative_title_prompt,
get_narrative_prompt,
inject_image_placeholder_template,
CHAPTER_CATEGORIES,
CHAPTER_ORDER,
STAGE_TO_ORDER,
)
from .profile_prompts import (
get_profile_greeting_prompt,
get_profile_extraction_prompt,
get_profile_followup_prompt,
format_user_profile_context,
get_missing_profile_fields,
PROFILE_FIELD_NAMES,
)
__all__ = [
"ConversationStage",
"get_conversation_prompt",
"get_questions_for_stage",
"get_guided_conversation_prompt",
"get_opening_prompt",
"INTERVIEW_QUESTIONS",
"get_memory_prompt",
"get_chapter_classification_prompt",
"get_text_rewrite_prompt",
"get_state_extraction_prompt",
"get_creative_title_prompt",
"get_narrative_prompt",
"inject_image_placeholder_template",
"CHAPTER_CATEGORIES",
"CHAPTER_ORDER",
"STAGE_TO_ORDER",
"get_profile_greeting_prompt",
"get_profile_extraction_prompt",
"get_profile_followup_prompt",
"format_user_profile_context",
"get_missing_profile_fields",
"PROFILE_FIELD_NAMES",
]

View File

@@ -6,7 +6,7 @@ from app.features.user.models import User
def get_missing_profile_fields(user: User) -> list: def get_missing_profile_fields(user: User) -> list:
"""检查用户缺失的资料字段""" """检查用户缺失的资料字段"""
from app.agents.prompts.profile_prompts import get_missing_profile_fields as _get_missing from app.agents.chat.prompts_profile import get_missing_profile_fields as _get_missing
return _get_missing( return _get_missing(
birth_year=user.birth_year, birth_year=user.birth_year,
birth_place=user.birth_place, birth_place=user.birth_place,

View File

@@ -10,7 +10,7 @@ from datetime import datetime, timezone
from fastapi import WebSocket, WebSocketDisconnect, status from fastapi import WebSocket, WebSocketDisconnect, status
from starlette.websockets import WebSocketState from starlette.websockets import WebSocketState
from app.agents.prompts.profile_prompts import format_user_profile_context from app.agents.chat.prompts_profile import format_user_profile_context
from app.core.db import AsyncSessionLocal from app.core.db import AsyncSessionLocal
from app.core.security import verify_token from app.core.security import verify_token
from app.features.conversation.models import Conversation, Segment from app.features.conversation.models import Conversation, Segment

View File

@@ -8,7 +8,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
CHAPTER_CATEGORIES, CHAPTER_CATEGORIES,
CHAPTER_ORDER, CHAPTER_ORDER,
STAGE_TO_ORDER, STAGE_TO_ORDER,

View File

@@ -26,7 +26,7 @@ from app.features.memoir.models import (
from app.features.user.models import User from app.features.user.models import User
from app.core.dependencies import get_llm_provider from app.core.dependencies import get_llm_provider
from app.agents.state_schema import MemoirStateSchema, SlotData, default_state from app.agents.state_schema import MemoirStateSchema, SlotData, default_state
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
STAGE_TO_ORDER, STAGE_TO_ORDER,
get_narrative_prompt, get_narrative_prompt,
inject_image_placeholder_template, inject_image_placeholder_template,
@@ -34,7 +34,7 @@ from app.agents.prompts.memory_prompts import (
from app.agents.memoir import MemoirOrchestrator from app.agents.memoir import MemoirOrchestrator
from app.agents.memoir.narrative_agent import NarrativeAgent from app.agents.memoir.narrative_agent import NarrativeAgent
from app.agents.memoir.placeholder_agent import inject_placeholders from app.agents.memoir.placeholder_agent import inject_placeholders
from app.agents.prompts.profile_prompts import format_user_profile_context from app.agents.chat.prompts_profile import format_user_profile_context
from app.features.memoir.memoir_images.parser import ( from app.features.memoir.memoir_images.parser import (
build_initial_image_assets, build_initial_image_assets,
parse_image_placeholders, parse_image_placeholders,

View File

@@ -14,7 +14,7 @@ from database import get_async_db
from database.models import Chapter as ChapterModel, ChapterSection from database.models import Chapter as ChapterModel, ChapterSection
from database.models import User as UserModel from database.models import User as UserModel
from middleware.auth import get_current_user from middleware.auth import get_current_user
from app.agents.prompts.memory_prompts import CHAPTER_CATEGORIES, CHAPTER_ORDER, STAGE_TO_ORDER from app.agents.memoir.prompts import CHAPTER_CATEGORIES, CHAPTER_ORDER, STAGE_TO_ORDER
from services.memoir_images.schema import ( from services.memoir_images.schema import (
completed_image_assets, completed_image_assets,
IMAGE_STATUS_COMPLETED, IMAGE_STATUS_COMPLETED,

View File

@@ -23,7 +23,7 @@ from database.models import User as UserModel
from services.auth_service import verify_token from services.auth_service import verify_token
from services.memoir_state_service import get_or_create_state from services.memoir_state_service import get_or_create_state
from services import asr_service, redis_service from services import asr_service, redis_service
from app.agents.prompts.profile_prompts import format_user_profile_context from app.agents.chat.prompts_profile import format_user_profile_context
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
LEGACY_VOICE_SESSION_ID = "legacy" LEGACY_VOICE_SESSION_ID = "legacy"
@@ -924,7 +924,7 @@ async def websocket_endpoint(
def _get_missing_profile_fields(user: UserModel) -> list: def _get_missing_profile_fields(user: UserModel) -> list:
"""检查用户缺失的资料字段""" """检查用户缺失的资料字段"""
from app.agents.prompts.profile_prompts import get_missing_profile_fields from app.agents.chat.prompts_profile import get_missing_profile_fields
return get_missing_profile_fields( return get_missing_profile_fields(
birth_year=user.birth_year, birth_year=user.birth_year,
birth_place=user.birth_place, birth_place=user.birth_place,
@@ -935,7 +935,7 @@ def _get_missing_profile_fields(user: UserModel) -> list:
def _get_filled_profile_fields(user: UserModel) -> dict: def _get_filled_profile_fields(user: UserModel) -> dict:
"""获取用户已有的资料字段(中文展示)""" """获取用户已有的资料字段(中文展示)"""
from app.agents.prompts.profile_prompts import PROFILE_FIELD_NAMES from app.agents.chat.prompts_profile import PROFILE_FIELD_NAMES
filled = {} filled = {}
if user.birth_year: if user.birth_year:
filled["birth_year"] = str(user.birth_year) filled["birth_year"] = str(user.birth_year)
@@ -1045,7 +1045,7 @@ async def process_user_message(
# 构建用户资料上下文 # 构建用户资料上下文
user_profile_context = "" user_profile_context = ""
if user: if user:
from app.agents.prompts.profile_prompts import format_user_profile_context from app.agents.chat.prompts_profile import format_user_profile_context
user_profile_context = format_user_profile_context( user_profile_context = format_user_profile_context(
birth_year=user.birth_year, birth_year=user.birth_year,
birth_place=user.birth_place, birth_place=user.birth_place,

View File

@@ -50,7 +50,7 @@ from app.features.memoir.models import Book, Chapter, ChapterSection, MemoirStat
from app.features.user.models import User from app.features.user.models import User
from app.core.dependencies import get_llm_provider from app.core.dependencies import get_llm_provider
from app.agents.state_schema import MemoirStateSchema, SlotData, default_state from app.agents.state_schema import MemoirStateSchema, SlotData, default_state
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
get_creative_title_prompt, get_creative_title_prompt,
get_narrative_prompt, get_narrative_prompt,
get_state_extraction_prompt, get_state_extraction_prompt,

View File

@@ -1,7 +1,7 @@
"""测试 memory_prompts.inject_image_placeholder_template占位符花括号统一为四层避免多余花括号残留""" """测试 memory_prompts.inject_image_placeholder_template占位符花括号统一为四层避免多余花括号残留"""
import unittest import unittest
from app.agents.prompts.memory_prompts import ( from app.agents.memoir.prompts import (
IMAGE_PLACEHOLDER_TEMPLATE, IMAGE_PLACEHOLDER_TEMPLATE,
inject_image_placeholder_template, inject_image_placeholder_template,
) )