Files
life-echo/api/tests/test_experience_regressions.py
Kevin 2fded6fbd9 refactor(chat): AI-native prompts, remove interview heuristics
- Drop interview_reply_length and utterance_substance; always run stage LLM
  and memory retrieval when enabled; trim Settings fields and .env.example.
- Replace guided/opening prompts with compact fact blocks plus unified
  behavior guidance; slim background_voice and persona to tone hints.
- InterviewAgent uses fixed chat_interview max_tokens/chars/segments.

Also includes stacked work: profile followup/extract path, evaluation rubric
and judge schema updates, transcript SPLIT handling in execution service,
user export markdown split tests, and golden case fixture.
2026-04-06 22:23:46 +08:00

114 lines
4.3 KiB
Python

"""面向体验的回归测试:保护「聊得下去」与回忆录文笔两个核心目标。
与 test_interview_prompts 不同,这组测试不绑定已删除的启发式分档;
访谈侧仅验证 prompt 仍包含关键行为指引。
"""
from app.agents.chat.prompts_conversation import (
get_guided_conversation_prompt,
get_opening_prompt,
)
from app.agents.memoir.prompts import (
get_creative_title_json_prompt,
get_narrative_editor_system_prompt,
get_narrative_json_prompt,
)
from app.features.memoir import story_pipeline_sync as sps
class TestChatExperienceRegressions:
"""保护「聊得下去」体验。"""
def test_guided_prompt_encourages_flexible_followup(self) -> None:
"""模型自主判断追问 vs 承接,不应再出现「本轮判定」硬分支文案。"""
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place", "people"],
filled_slots={},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "本轮追问判定" not in p
assert "你自己判断" in p or "该追问" in p
def test_guided_prompt_topic_switch_not_hardcoded_in_prompt(self) -> None:
p = get_guided_conversation_prompt(
current_stage="childhood",
empty_slots=["place", "people", "emotion"],
filled_slots={"daily_life": "放学后去河边玩"},
detected_user_stage="childhood",
user_profile_context="",
persona="default",
)
assert "聊得差不多了" not in p
def test_guided_prompt_intro_mentions_connect_first(self) -> None:
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
def test_opening_prompt_stays_short_task_shape(self) -> None:
p = get_opening_prompt(
current_stage="childhood",
empty_slots_readable=["成长的地方"],
user_profile_context="",
persona="default",
)
assert "问候" in p
assert "任务" in p or "具体问题" in p
class TestMemoirStyleRegressions:
"""保护「回忆录有文笔」体验。"""
def test_title_prompt_allows_literary_expression(self) -> None:
prompt = get_creative_title_json_prompt(
stage="childhood",
emotion="warm",
slots={"place": "湖南老家", "turning_event": "爷爷背我过河"},
)
assert "禁止虚构" in prompt
assert "平实" not in prompt.lower()
def test_title_prompt_uses_facts_only_not_plain(self) -> None:
prompt = get_creative_title_json_prompt(
stage="childhood",
emotion="warm",
slots={"place": "老家"},
)
assert "优雅" in prompt or "书面语" in prompt or "文采" in prompt
def test_narrative_prompt_encourages_literary_quality(self) -> None:
sys_prompt = get_narrative_editor_system_prompt()
assert "温度" in sys_prompt or "优雅" in sys_prompt
assert "画面感" in sys_prompt or "生动" in sys_prompt
def test_narrative_json_prompt_allows_emotion_rendering(self) -> None:
prompt = get_narrative_json_prompt(
stage="childhood",
slots={"turning_event": "爷爷背我过河"},
new_content="【本段用户口述】\n那年下大雨,爷爷背我过河,鞋全湿了,他一直笑。",
)
assert "文采服务于真实" in prompt or "虚构描写" in prompt
def test_merge_shrink_only_on_extreme_loss(self) -> None:
existing = "这是一段已有的故事正文,讲述了童年在河边的回忆。" * 20
assert len(existing) > 400
half_content = existing[: len(existing) // 2]
import json
raw = json.dumps(
{"paragraphs": [{"content": half_content}]}, ensure_ascii=False
)
out, ft = sps._apply_narrative_fallbacks(
raw, "新的口述补充", existing, chapter_category="childhood"
)
assert ft == "none"