Files
life-echo/api/tests/test_interview_prompts.py
yangshilin e1341c6d18 feat:
1. 建立问题库大纲,对应每个人生阶段槽位
2. 鼓励使用更生活化的交流语言共情与总结
3. 降低评审模型可能发生截断的概率
4. 成稿质量维度强化情感表达和上下文连贯性
2026-04-09 15:32:35 +08:00

355 lines
11 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.
"""访谈提示词:精简结构与人格/语气融合回归。"""
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from app.agents.chat.interview_state_hints import (
apply_duplicate_question_guard,
extract_scene_cues,
)
from app.agents.state_schema import (
KnownFact,
MemoirStateSchema,
PersonaThread,
default_slots,
)
from app.agents.chat.helpers import format_history_string
from app.agents.chat.personas import normalize_interview_persona
from app.agents.chat.prompts_conversation import (
get_guided_conversation_prompt,
get_opening_prompt,
)
from app.agents.chat.slot_question_bank import SLOT_QUESTION_OUTLINES
def test_guided_prompt_does_not_embed_raw_user_message_in_system_text():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "__USER_SECRET_PHRASE_XYZ__" not in p
# Signature no longer takes user_message; secret would only leak via profile
p2 = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="__USER_SECRET_PROFILE__",
persona="default",
)
assert "__USER_SECRET_PROFILE__" in p2
def test_guided_prompt_injects_slot_question_outline_for_empty_slots():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place", "people"],
filled_slots={"emotion": "挺开心的"},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "本阶段问题大纲" in p
assert "叙述槽" in p or "采集目标" in p
assert "成长的地方" in p
assert "重要的人" in p
def test_guided_prompt_emotion_priority_over_outline():
p = get_guided_conversation_prompt(
current_stage="education",
empty_slots=["school"],
filled_slots={},
detected_user_stage="education",
user_profile_context="",
persona="default",
)
assert "情绪" in p and "大纲" in p
def test_guided_prompt_includes_memoir_quality_rubric_hints():
p = get_guided_conversation_prompt(
current_stage="career",
empty_slots=["growth"],
filled_slots={"job": "做过工程师"},
detected_user_stage="career",
user_profile_context="",
persona="default",
)
assert "成稿质量导向" in p
assert "真实性与覆盖" in p
assert "出版就绪度" in p
def test_guided_prompt_follow_immersion_whitespace_style_examples():
p = get_guided_conversation_prompt(
current_stage="belief",
empty_slots=["value"],
filled_slots={},
detected_user_stage="belief",
user_profile_context="",
persona="default",
)
assert "跟随" in p and "沉浸" in p
assert "留白" in p
assert "风格参考" in p
assert "这让我想起" in p
def test_guided_prompt_narrative_weave_and_transition():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "编织" in p or "内在线" in p
assert "通感" in p or "比喻" in p
assert "下面我们聊聊" in p
def test_guided_prompt_host_tone_and_context_forward():
p = get_guided_conversation_prompt(
current_stage="career",
empty_slots=["job"],
filled_slots={},
detected_user_stage="career",
user_profile_context="",
persona="default",
)
assert "主持" in p or "播报" in p
assert "上下文" in p or "既定事实" in p
assert "行为" in p and "影响" in p
def test_education_and_family_change_outlines_differ():
edu = SLOT_QUESTION_OUTLINES[("education", "change")]
fam = SLOT_QUESTION_OUTLINES[("family", "change")]
assert edu[0] != fam[0]
def test_guided_prompt_mentions_empathy_and_scene_strategy():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "接住" in p
assert "画面" in p or "细节" in p
assert "深挖" in p
assert "串联" in p
def test_guided_prompt_era_popculture_open_questions_when_birth_year():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
profile_birth_year=1985,
profile_era_place="潍坊",
)
assert "时代与氛围参考" in p
assert "流行文化" in p
assert "开放式" in p
def test_opening_prompt_includes_era_task_when_birth_year_configured():
p = get_opening_prompt(
current_stage="childhood",
empty_slots_readable=["成长的地方"],
user_profile_context="出生年份1985年",
persona="default",
profile_birth_year=1985,
profile_era_place="潍坊",
)
assert "年代氛围" in p
def test_guided_prompt_persona_tone_warm_listener():
p = get_guided_conversation_prompt(
current_stage="education",
empty_slots=["school"],
filled_slots={},
detected_user_stage="education",
user_profile_context="",
persona="warm_listener",
)
assert "倾听" in p or "柔和" in p
def test_guided_prompt_persona_curious_guide():
p = get_guided_conversation_prompt(
current_stage="education",
empty_slots=["school"],
filled_slots={},
detected_user_stage="education",
user_profile_context="",
persona="curious_guide",
)
assert "细节" in p
def test_normalize_interview_persona_unknown_falls_back():
assert normalize_interview_persona("not_a_real_persona") == "default"
assert normalize_interview_persona("") == "default"
def test_guided_prompt_contains_memory_section_when_evidence():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
memory_evidence_text="[摘要:rolling] 1990年生于上海。",
)
assert "相关记忆摘录" in p
assert "过往口述" in p
assert "1990年生于上海" in p
def test_guided_prompt_military_tone_in_system():
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
background_voice="military",
)
assert "简洁" in p or "利落" in p or "得体" in p
def test_guided_prompt_includes_known_facts_persona_threads_and_recent_questions():
p = get_guided_conversation_prompt(
current_stage="career",
empty_slots=["job", "decision"],
filled_slots={"growth": "越做越确定自己适合产品"},
detected_user_stage="career",
user_profile_context="",
persona="default",
known_facts=[
KnownFact(
label="本轮新信息", value="我后来去了瑞士读书", stage="education"
),
],
persona_threads=[
PersonaThread(trait="执着坚持", evidence="为了训练咬牙坚持了很多年"),
],
recent_questions=["你当时为什么会想去瑞士?"],
)
assert "已确认事实" in p
assert "我后来去了瑞士读书" in p
assert "人物主线" in p
assert "执着坚持" in p
assert "最近已经问过的问题" in p
assert "为什么会想去瑞士" in p
def test_prompt_empty_slots_excludes_slots_already_covered_by_known_facts():
state = MemoirStateSchema(
stage_order=["education"],
current_stage="education",
covered_stages=[],
slots={"education": default_slots()["education"]},
known_facts=[
KnownFact(
label="求学城市",
value="后来在瑞士读书",
stage="education",
slot_name="city",
)
],
)
assert "city" not in state.prompt_empty_slots_for_current_stage()
assert "school" in state.prompt_empty_slots_for_current_stage()
def test_duplicate_question_guard_downgrades_recent_repeat_question():
state = MemoirStateSchema(
stage_order=["education"],
current_stage="education",
covered_stages=[],
slots={"education": default_slots()["education"]},
known_facts=[
KnownFact(label="本轮新信息", value="我后来去了瑞士读书", stage="education")
],
)
cleaned, touched = apply_duplicate_question_guard(
["我记住了。你后来去了瑞士读书吗?"],
state=state,
recent_questions=["你后来去了瑞士读书吗?"],
)
assert touched is True
assert cleaned == ["我记住了。"]
def test_extract_scene_cues_picks_up_sensory_keywords():
cues = extract_scene_cues("我们小时候在河里游泳,冬天溜冰")
assert any("" in c or "" in c for c in cues)
assert any("" in c or "咔嚓" in c for c in cues)
def test_extract_scene_cues_empty_for_abstract_text():
assert extract_scene_cues("我觉得人生需要坚持") == []
def test_default_persona_now_has_tone_hint():
from app.agents.chat.personas import get_interview_persona_tone_hint
hint = get_interview_persona_tone_hint("default")
assert hint
assert "画面" in hint or "细节" in hint
def test_opening_prompt_military_style_rules_not_dialogue_samples() -> None:
p = get_opening_prompt(
current_stage="childhood",
empty_slots_readable=["成长的地方"],
user_profile_context="",
persona="default",
background_voice="military",
)
assert "军队相关" in p
assert "示例" not in p
def test_format_history_string_includes_system_for_debug_logs() -> None:
s = format_history_string(
[
SystemMessage(content="SYS_INSTRUCTIONS"),
HumanMessage(content="hi"),
AIMessage(content="hello"),
]
)
assert "System: SYS_INSTRUCTIONS" in s
assert "Human: hi" in s
assert "Assistant: hello" in s
def test_format_history_string_omit_system_body() -> None:
s = format_history_string(
[
SystemMessage(content="SYS_INSTRUCTIONS"),
HumanMessage(content="hi"),
],
omit_system_body=True,
)
assert "SYS_INSTRUCTIONS" not in s
assert "System: <omitted total_len=16" in s
assert "sha12=" in s
assert "Human: hi" in s