Files
life-echo/api/app/agents/chat/background_voice.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

121 lines
3.8 KiB
Python
Raw Permalink 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.
"""
从用户档案「职业」等文本推断访谈/叙事语气维度(干部形、军队形)。
与 chat_interview_persona温柔倾听等正交可叠加。
"""
from __future__ import annotations
from typing import Final, Literal
BackgroundVoice = Literal["default", "cadre", "military"]
# 军队系优先:含「军、部队」等则走军队形,避免与泛「干部」冲突。
_MILITARY_NEEDLES: Final[tuple[str, ...]] = (
"军人",
"军官",
"士兵",
"部队",
"入伍",
"服役",
"退伍",
"转业",
"武警",
"解放军",
"陆军",
"海军",
"空军",
"火箭军",
"军区",
"军营",
"军校",
"文职干部",
"军队文职",
"现役",
"预备役",
)
# 干部/机关系(避免过短词误判:如「机关」→机关枪、「主任」→班主任)
_CADRE_NEEDLES: Final[tuple[str, ...]] = (
"公务员",
"党政机关",
"党政",
"组织部",
"党委书记",
"党组书记",
"书记",
"处长",
"科长",
"局长",
"厅长",
"部长",
"国企",
"事业单位",
"干部",
"科级",
"处级",
"厅级",
)
def infer_background_voice(occupation: str | None) -> BackgroundVoice:
"""
据职业自由文本推断背景语气。军队关键词优先于干部关键词。
无匹配或未填 → default。
"""
if not occupation or not str(occupation).strip():
return "default"
t = str(occupation).strip().casefold()
for n in _MILITARY_NEEDLES:
if n.casefold() in t:
return "military"
for n in _CADRE_NEEDLES:
if n.casefold() in t:
return "cadre"
return "default"
def normalize_background_voice(voice: str | None) -> BackgroundVoice:
"""调用方传入已归一化枚举或原始职业文本均可。"""
if not voice:
return "default"
s = voice.strip()
if s in ("default", "cadre", "military"):
return s # type: ignore[return-value]
return infer_background_voice(s)
def get_background_voice_tone_hint(voice: str | None) -> str:
"""一句背景语气提示,融入主 system promptdefault 返回空串。"""
v = normalize_background_voice(voice)
if v == "default":
return ""
if v == "military":
return (
"语气简洁利落、得体;称呼自然;不写命令式、不堆砌军事辞藻;"
"仅当用户口述已出现相关事实时才呼应军旅语境,不编造经历。"
)
return (
"语气稳重有分寸、敬语适度;避免官样排比与公文套话;"
"不得编造用户未提及的职级、单位与荣誉。"
)
def get_background_voice_narrative_block(voice: str | None) -> str:
"""附在叙事系统提示后的文体补充default 返回空串。"""
v = normalize_background_voice(voice)
if v == "default":
return ""
if v == "military":
return (
"## 背景文体(军队,须遵守上文事实边界)\n"
"叙事紧凑、层次清楚;若口述已出现纪律、集体、任务等语境,可适度用书面语呼应,**禁止**堆砌口号式军事辞藻或虚构军旅细节。\n"
"不新增军衔、单位番号、表彰等口述未出现的信息。\n"
"用户已退役/转业,以回忆军旅岁月为基调,不要预设其仍在服役。"
)
return (
"## 背景文体(干部/机关,须遵守上文事实边界)\n"
"段落层次清晰,用语庄重自然,避免口语碎词与段子感;**不得编造**职务、荣誉、单位名称与组织细节。\n"
"文采服务于真实内容,不写成公文或汇报腔。\n"
"用户已退休,以回顾和怀念工作岁月为基调,不要预设其仍在岗。"
)