Files
life-echo/api/app/agents/chat/personas.py
Kevin 69a673e6c6 feat(api): 访谈人格/回复长度策略、口述归一、背景语气与输入净稿全链路
Chat 访谈
- 新增 persona 系统(default / warm_listener / curious_guide)与 background_voice 语气层
- 回复长度由 compute_reply_plan 统一决策(brief / standard / expanded),融合信息密度启发式
- 输入净稿(input_normalize):编排层可选 rules/llm 归一用户口语后再喂模型与记忆检索
- 记忆证据注入:按用户话检索 memory evidence 并注入 prompt

Memoir 回忆录
- 口述归一(oral_normalize):segment 原文保留,story 管线取派生净稿作叙事输入
- segment 入队批次门闸:累计字数 + 最长等待秒数,减少零碎提交
- fidelity_check / prompts / narrative_agent 微调
- Alembic 0005:清理跨章节 story 外键

Infra
- Dockerfile 加入 ffmpeg
- pyproject.toml 新增依赖并同步 uv.lock
- .env.example / .env.production 补全新配置项

Tests
- 新增 test_background_voice、test_chat_input_normalize、test_experience_regressions
- 扩展 test_interview_prompts、test_interview_reply_length、test_story_route_oral_invariant

Made-with: Cursor
2026-03-31 23:55:26 +08:00

61 lines
2.6 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.
"""
访谈 Agent 可配置性格Persona仅影响语气与追问倾向不替代事实边界与槽位约束。
"""
from __future__ import annotations
from typing import Final
# 与 settings.chat_interview_persona 及文档保持一致
VALID_INTERVIEW_PERSONAS: Final[frozenset[str]] = frozenset(
{"default", "warm_listener", "curious_guide"}
)
def normalize_interview_persona(raw: str | None) -> str:
"""未知或空值回退 default避免部署拼写错误导致空提示。"""
key = (raw or "default").strip().lower()
if key in VALID_INTERVIEW_PERSONAS:
return key
return "default"
def get_interview_persona_block(persona: str) -> str:
"""
返回注入到访谈 prompt 的「访谈性格」段落(不含 default由调用方跳过
"""
key = normalize_interview_persona(persona)
if key == "default":
return ""
blocks = {
"warm_listener": (
"## 访谈性格:温柔倾听\n"
"在遵守「回忆录导向与闲聊」的前提下,优先把对话引向可写进回忆录的素材;明显闲聊时先陪聊。\n"
"你更偏倾听与承接,语气柔和、少打断;"
"但一旦用户说出**新的人名、新的关系、或新的情节线**(上文未展开),"
"仍必须按本提示中的「追问触发」规则,在承接后带**一个**具体问题,不能用纯感慨代替。\n"
"禁止审问感、禁止一次抛多个问题。"
),
"curious_guide": (
"## 访谈性格:好奇引导\n"
"在遵守「回忆录导向与闲聊」的前提下,追问尽量落在人生故事与未覆盖方向上;明显闲聊时先陪聊。\n"
"你更愿意把人往**一个具体细节**里带:时间、场景、对方反应、你心里一闪而过的念头;"
"每轮**最多一个**具体问题,短句、像微信。\n"
"若本轮触发「追问触发」,优先追问用户刚抛出的新信息,不要为了凑问题去重复上文已清楚的事。"
),
}
return blocks.get(key, "")
def get_opening_persona_line(persona: str) -> str:
"""开场白用的一行性格提示(短,避免喧宾夺主)。"""
key = normalize_interview_persona(persona)
if key == "default":
return ""
lines = {
"warm_listener": "语气偏倾听、少打断;但仍须完成「问候 + 一个具体问题」。",
"curious_guide": "语气偏好奇、爱往细节里带一个具体问题;不要一次问很多。",
}
return lines.get(key, "")