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

194 lines
10 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.
"""
按人生阶段 × 叙述槽位slot固化「问题大纲」供访谈提示词引用。
- 同一 slot key 在不同 stage 语义不同(如 education.change vs family.change必须分 stage 配置。
- 条目是采集方向/示意问法,模型须在对话中情景化改写,禁止照抄题库原句。
"""
from __future__ import annotations
from app.agents.stage_constants import STAGE_SLOT_KEYS, VALID_CHAT_STAGES
# (stage, slot) -> 有序大纲:从宽泛落到具体,供缺口推进时优先参考
SLOT_QUESTION_OUTLINES: dict[tuple[str, str], tuple[str, ...]] = {
# --- 童年时光 ---
("childhood", "place"): (
"小时候主要是在哪儿长大的——城市、镇上还是村里,能具体到一片街区或一座院子吗?",
"住的地方大概什么样,有没有到现在还记得清的角落(厨房、院子、门槛)?",
"外人一眼能认出的地标或某种气味、声音(河、庙、集市、某种树)是什么?",
),
("childhood", "people"): (
"那时候最黏、最怕或最佩服的是谁,和你们日常是怎么相处的?",
"带你玩或管你最多的是家人还是祖辈、邻居孩子?有没有一位到现在还会突然想起的人?",
"有没有某个人一句话或一个动作,小时候的你特别当回事?",
),
("childhood", "daily_life"): (
"不上学的一天通常怎么过,有没有固定的小规矩或小乐趣?",
"放学之后最常去哪儿、和谁在一起,那种日子现在回想是什么颜色的?",
"记忆里常重复出现的一个场景(吃饭、睡午觉、出门玩)愿意多说两句吗?",
),
("childhood", "emotion"): (
"回想小时候,身体哪个地方会先「有感觉」——收紧、发暖还是发酸?",
"那时候整体更像踏实、兴奋、还是有点孤单或紧张?能举一个具体小事吗?",
"有没有一件当时觉得天塌了或特别亮堂的事,现在看又不太一样?",
),
("childhood", "turning_event"): (
"有没有一件事让小时候的你一下子觉得「我跟以前不一样了」?",
"难忘不一定是大事——一次搬家、一次生病、一次被骂或一次被护着也算,你愿意从画面讲起吗?",
"那之后别人对待你的方式,或你对自己的看法,哪儿变了?",
),
# --- 求学经历 ---
("education", "school"): (
"印象最深的一所学校是哪段,校园里你记得清的细节是什么(光线、气味、铃声、操场)?",
"有没有一门课、一位老师或一间教室,想到还会心里一动?",
"那时候和同学之间,最典型的相处方式是什么样的?",
),
("education", "city"): (
"求学待过哪些城或镇,第一次到那儿下车或进校园时是什么感觉?",
"和老家比,那边最陌生的地方是什么——人、节奏还是规矩?",
"后来有没有哪条街、哪个小店或公交站,和你的一段日子绑在一起?",
),
("education", "motivation"): (
"当时为什么读书或选这个方向——是自愿、家里期待,还是偶然被推向那条路?",
"有没有谁的一句话、一本书或一件事,真正推了你一把?",
"撑不下去的时候,是靠什么念头把自己劝回书桌前的?",
),
("education", "challenge"): (
"求学阶段最难的一段是什么——考试、钱、适应环境,还是家里的事?",
"最难的那会儿,一天里通常怎么扛过去,有没有人帮你垫了一下?",
"回头看,那段难有没有改变你对自己的看法?",
),
("education", "change"): (
"毕业或离开那段求学生活时,和刚入学比,你觉得自己哪块最明显变了?",
"是更知道自己要什么,还是更知道不要什么?能各举一个细节吗?",
"如果有机会对当时的自己说一句话,你会叮嘱什么?",
),
# --- 职业生涯 ---
("career", "job"): (
"那段工作里你具体在做什么,一天里真实在忙的核心事是什么?",
"有没有一个小场景能代表那份活(开会、跑现场、对客户、写东西)?",
"你最熟练或最头疼的环节是哪一块?",
),
("career", "environment"): (
"上班或干活的场子什么样——吵、静、乱中有序,还是总在移动?",
"同事或搭档大概怎么相处,有没有固定的小 ritual抽烟、吃饭、对表",
"那个环境里,什么让你待得下去,什么让你想逃?",
),
("career", "decision"): (
"换工作、入行或接某个项目时,有没有一次关键决定是你拍板的?",
"当时心里在权衡什么——钱、体面、家庭、兴趣、还是别的东西?",
"如果重来,你还会那么选吗,哪一步可能会松一点或紧一点?",
),
("career", "pressure"): (
"压力最大的阶段是什么,外界逼你还是自己逼自己更多?",
"身体或情绪上最先亮红灯的信号是什么(睡不着、胃、脾气)?",
"后来是环境变了,还是你换了应对方式,才缓过来的?",
),
("career", "growth"): (
"哪一段经历让你觉得能力、心气或看人的眼光真的上了一层?",
"有没有一件当时觉得过不去的事,做完反而长出了新本事?",
"现在回望,你愿意把哪段职业经历称为自己的「底色」?",
),
# --- 家庭生活 ---
("family", "relationship"): (
"和伴侣、父母或孩子之间,最近最常见的一个相处画面是什么?",
"亲昵和疏远在你家里大概怎么分配——是话说得多还是活儿帮得多?",
"有没有一种默契是你们家的「规矩外规矩」?",
),
("family", "conflict"): (
"家里有没有一次争执或冷战让你印象最深,起因其实是什么?",
"后来是时间抹平的还是有人先伸手,中间最难受的是哪几天?",
"那段矛盾过后,关系里是多了道疤还是多了一层理解?",
),
("family", "support"): (
"难的时候谁最站你这边,对方具体做过一件什么事你记到现在?",
"你又是怎么支持家里人的——出钱、出力、还是稳稳在场?",
"有没有一次你觉得「没有ta那次就真的悬了」",
),
("family", "responsibility"): (
"在家里你扛的主责是什么——钱、照料、决策、还是情绪托底?",
"从什么时候开始你觉得自己是「要主动揽过来」的那个人?",
"这份责任有没有让你骄傲,也有没有让你委屈的时候?",
),
("family", "change"): (
"成家或角色变了(当父母、当主心骨)之后,生活节奏或心态最明显的一处变化是什么?",
"和以前单身或二人世界比,你最怀念和最怕失去的各是什么?",
"如果现在给家里关系打分,你会先改哪一件小事?",
),
# --- 人生信念 ---
("belief", "value"): (
"若要把诚实、自由、家庭、成就、平安排个序,你心里默认的前两位是什么?",
"有没有一条线是你不太愿意让步的,哪怕会吃亏?",
"这套看重的东西,是家里传下来的还是你自己摔打出来的?",
),
("belief", "regret"): (
"若重来会犹豫的一件选择是什么,当时缺的是信息还是勇气?",
"遗憾现在还在拉扯你,还是已经能放在一旁?你怎么和它相处?",
"这份遗憾有没有悄悄改了你后来做决定的方式?",
),
("belief", "pride"): (
"不一定要大成就——一件你心底觉得「这事我做得对、做得像我自己」的事?",
"那时候外人怎么看不重要,你自己凭什么觉得踏实?",
"骄傲里有没有夹着一点后怕或侥幸?",
),
("belief", "lesson"): (
"如果只能留一句话给后辈或给年轻时的自己,你会留什么?",
"这个道理是从谁身上、哪段经历里磨出来的?",
"有没有一次你差点不信这句话,最后还是被生活按着头认了?",
),
}
def _validate_outlines() -> None:
for stage, slots in STAGE_SLOT_KEYS.items():
for slot in slots:
key = (stage, slot)
if key not in SLOT_QUESTION_OUTLINES:
raise RuntimeError(f"slot_question_bank missing outline for {key}")
_validate_outlines()
def outlines_for_stage_slots(
stage: str, empty_slots: list[str]
) -> list[tuple[str, tuple[str, ...]]]:
"""返回 [(slot key, 大纲句...), ...],仅含本阶段且仍在 empty_slots 中的槽。"""
if stage not in VALID_CHAT_STAGES:
return []
empty_set = set(empty_slots)
order = STAGE_SLOT_KEYS.get(stage, ())
out: list[tuple[str, tuple[str, ...]]] = []
for slot in order:
if slot not in empty_set:
continue
key = (stage, slot)
lines = SLOT_QUESTION_OUTLINES.get(key, ())
if not lines:
continue
out.append((slot, lines))
return out
def format_slot_question_outline_block(stage: str, empty_slots: list[str]) -> str:
"""供 system prompt 注入;无缺口时返回空串。"""
from app.agents.chat.prompts_conversation import SLOT_NAME_MAP
rows = outlines_for_stage_slots(stage, empty_slots)
if not rows:
return ""
blocks = [
"## 本阶段问题大纲(仅列仍缺的叙述槽;条目是**采集目标与思路**,不是台词)\n"
"- 每条大纲按**由宽到细**排序;推进缺口时通常优先靠上的条目,但**用户情绪明显起伏时,宁可停在大纲外多接两轮**,不要为凑进度硬问。\n"
"- 发问前必须把大纲句**打散、改写**,嵌进对方刚说的名词/动作/比喻;禁止整段背诵题库原句。\n"
"- 每轮若需要推进缺口,**全段只服务一个槽、最多一个问句**(或无问句只承接)。\n\n"
]
for slot, lines in rows:
label = SLOT_NAME_MAP.get(slot, slot)
blocks.append(f"### {label}\n")
for i, line in enumerate(lines, start=1):
blocks.append(f"{i}. {line}\n")
blocks.append("\n")
return "".join(blocks).rstrip() + "\n\n"