1. 修复安卓部分机型顶部安全区遮挡回忆录标题的问题;
2. 降低封面图生成阈值和展示逻辑,独立封面图未生成时,使用正文图;
3. 去掉“嗯。”生硬回答,去掉不合理段首承接词;
4. 新增章节封面所需最少插图数的配置项
This commit is contained in:
yangshilin
2026-04-16 20:42:54 +08:00
parent 17b9fa3466
commit 9af2060259
15 changed files with 377 additions and 74 deletions

View File

@@ -9,14 +9,17 @@ def chat_output_rules() -> str:
"`[SPLIT]`(仅此一处方括号用法);**禁止**输出全角或半角括号及其中任何内容,包括:"
"策略/舞台说明(如「(先接住情绪)」「(共情)」),以及**表演性、声效、动作描写**"
"(如「(轻轻笑)」「(笑)」「(叹气)」「(顿了顿)」「(低声)」「(咳嗽)」「(清了清嗓子)」等——对用户说话就当口播,不要剧本括注);"
"若需停顿或语气,用口语里的「嗯」「唉」或省略号自然写出,**不要**用括号包装动作或旁白"
"**禁止**以「嗯。」**起头**(含「嗯。」后立刻接任何正文——一律不得用这种停顿起手)、禁止单独成泡只有「嗯。」——生硬、像生冷打字机"
"若需停顿或语气,优先用省略号、或把承接半句直接钉在对方原词上;可用「唉」等;**避免**每条消息都以「好。」「对。」单独打头再接一大段(易像程式客服);"
"**不要**用括号包装动作或旁白;"
"思考过程或任何元注释同样**绝不可**出现在对用户说的话中;"
"主持人口吻与播报腔(「那么接下来」「让我们」「首先」「感谢您的分享」类串联或晚会导语感);"
"课文式硬切话题(「下面我们聊聊」「接下来我想了解」「换个话题」「让我们把话题转向…」等未承接就上段话的起手或硬转向);"
"推白话轮与总结腔(空泛的「听起来你…」「听起来当时…」「听起来挺…」「听你这么说…」「照你这么说…」"
"等阶段总结或程序性过渡,而非贴着对方上一轮话头半句并肩地往下长);"
"强行搭话式「这让我想起…」接**与当前画面不沾边**的自己的故事或常识,制造虚假亲密;"
"采访腔(「我注意到」「我想了解」);尤其在用户长段倾诉或情绪很重时,**勿**整条回复仅单个语气词(孤立的「嗯」「好」「明白」等),须至少有半句贴着对方原词的承接;"
"采访腔(「我注意到」「我想了解」);尤其在用户长段倾诉或情绪很重时,**勿**整条回复仅单个语气词(孤立的「好」「明白」等),须至少有半句贴着对方原词的承接;"
"连续多轮都以「好,……」「对,……」式**同一套路起句**(发语词后接泛共情),须主动轮换——尽量**直接**从对方刚说的物象、人或半句并肩起笔;"
"书面评介腔(「值得一提的是」「总的来说」「从某种意义上」);"
"空话铺垫(「这确实是个好问题」类);**以核对为名**重复对方已明确说过的基础信息(如「所以您是……对吗」「刚才您说的是……吗」),"
"对方已交代清楚的事实应直接当作前提,在其上深化、延伸或关联提问;"
@@ -31,6 +34,7 @@ def chat_voice_style() -> str:
"语气像**温暖的谈话场主持人**:口语、自然、能接住人,但心里始终为**回忆录口述**服务——"
"不是冷冰冰盘问,也不是无底洞式的日常闲聊;更像懂行的老友在帮你把故事讲清楚。"
"接话允许带一点画面感或感官细节(一两句即可,不要堆砌);对方情绪重时别让整段只剩一个字。"
"起句尽量从对方**原词或具体画面**带入;**不要**用「嗯。」开场(**含**「嗯。」后立刻接正文),也不要「好。」「对。」单独一顿再接长句当习惯起手。"
"用对方刚说的**那个具体细节**回应,不要写成泛泛的总结。"
"不要用总结腔('听起来你的童年很快乐'),要用对话腔('那种……的感觉,现在想起来都觉得……')。"
"追问优先顺着对方刚说的具体细节往里走一层,不要跳到泛泛的新问题。"

View File

@@ -438,7 +438,7 @@ def get_guided_conversation_prompt(
### 第一步:先接住——让对方觉得你真的听进了情绪与细节
- 用对方刚说的**那个具体细节**回应,不要写成泛泛的"听起来很好"
- **节奏**:用户刚说完**一大段**或字里行间**情绪很重**(委屈、哽咽、后怕、赌气、突然抒情)时,**禁止**整条回复就只有孤立的一个「嗯」「好」「明白」——会显得敷衍、支持感不够;承接仍要**短**,但至少要**半句并肩**或**钉住对方原词的一点点展开**(例如「是哦,讲到那儿换谁都会堵得慌」「那种……光听你描述就挺沉的」),不必写成工整小结再接问。若用 `[SPLIT]`**前一泡也须有质感**,不能单字凑数,后一泡再追问或继续陪聊。
- **节奏**:用户刚说完**一大段**或字里行间**情绪很重**(委屈、哽咽、后怕、赌气、突然抒情)时,**禁止**整条回复就只有孤立的一个「嗯。」「嗯」「好」「明白」——会显得敷衍、支持感不够;承接仍要**短**,但至少要**半句并肩**或**钉住对方原词的一点点展开**(例如「是哦,讲到那儿换谁都会堵得慌」「那种……光听你描述就挺沉的」),不必写成工整小结再接问。若用 `[SPLIT]`**前一泡也须有质感**,不能单字凑数,后一泡再追问或继续陪聊。
- **跟随—沉浸**:长段叙述后,可插入**极短**一两句**并肩式画面或体感**(像朋友旁听时轻轻嘬一口气),打破纯「一问一答」节拍;须**贴着对方刚讲的物象/动作**,可用「那种…」「换我可能也会…」等**泛指****禁止**宣称自己身上发生的具体人名地名事件,**禁止**用「这让我想起…」硬接无关轶事抢戏。
- **留白**:用户抛出**强烈情绪**或**金句式人生总结**(如「爱是流动的」一类)时,允许**本轮不接任何问题**,只作更深一层的、贴着原句的共情,让情绪**淌一小会儿**;若用 `[SPLIT]`,第一泡可以**只有共情不讲题**,但仍须是**有内容的短句**(贴原词或并肩),第二泡仍可不问,或只在末尾留极轻的一句勾子,勿赶着交卷。
- 好的接法:借用对方话里的意象往下走一步,例如对方说"烤红薯",你可以说"那种外面焦焦的、掰开冒热气的感觉"
@@ -467,11 +467,13 @@ def get_guided_conversation_prompt(
- 不要每轮都像第一次见面。
## 语言与文笔(隐性执行,勿念给用户听)
- **句首习惯****禁止**「嗯。」起头(**含**「嗯。」后立刻接正文,一律不要);**禁止**单独成泡只有「嗯。」。「好。」「对。」也少当每轮固定发语词;更像真人时**直接**咬对方原词往下长——短停顿用省略号或半句并肩即可。
- 长短句掺着来;能少说一个字就不堆「很、特别、真的」。
- 同一个意思别用排比或同义词连打三遍;留一点空白,像聊天不像文章。
- 共情与小总结像朋友捎一句,不要像晚会主持人收口或卷首语(但仍要在恰当的轮次把话头**勾回人生故事**,见「主持人职责」)。
## 绝对不要做的
- **禁止**以「嗯。」起头,**即使**后面还有长正文也不行(不要用「嗯。」当停顿再接句);禁止单独成泡只有「嗯。」。
- 不要为了赶大纲无视用户刚露出来的情绪。
- 不要在用户**长段倾诉或情绪很满**时,用**整条消息只有单个语气词**打发;要短可以,但须有贴肉的半句承接。
- 不要用主持人口吻、晚会串联语、课文式硬切话题,或在已答信息上做复述式确认。

View File

@@ -62,6 +62,19 @@ def strip_parenthetical_asides_for_chat(text: str) -> str:
return s.strip()
def strip_leading_en_period_ack_for_chat(text: str) -> str:
"""
去掉段首生硬的「嗯。」(可重复),即使后面还有正文;只剥字符串开头,不误伤句中「嗯。」。
支持全角/半角句号。
"""
s = (text or "").strip()
if not s:
return s
# 允许多次「嗯。」/「嗯嗯。」叠在段首;句号仅匹配全角 。、. 与 ASCII `.`
s2 = re.sub(r"^(?:嗯+(?:。||\.)+\s*)+", "", s)
return s2.strip()
def segments_from_llm_response(
response_text: str,
*,
@@ -76,13 +89,22 @@ def segments_from_llm_response(
text = strip_parenthetical_asides_for_chat(text)
if not text:
return []
primary = [p.strip() for p in text.split("[SPLIT]") if p.strip()]
primary = [
strip_leading_en_period_ack_for_chat(p)
for p in text.split("[SPLIT]")
if strip_leading_en_period_ack_for_chat(p).strip()
]
if len(primary) > 1:
return primary[:max_segments]
blob = primary[0] if primary else text
blob = primary[0] if primary else strip_leading_en_period_ack_for_chat(text)
blob = strip_leading_en_period_ack_for_chat(blob)
if "\n" not in blob:
return [blob]
paras = [p.strip() for p in re.split(r"\n\s*\n+", blob) if p.strip()]
paras = [
strip_leading_en_period_ack_for_chat(p)
for p in re.split(r"\n\s*\n+", blob)
if strip_leading_en_period_ack_for_chat(p).strip()
]
if len(paras) < 2:
return [blob]
paras = [p for p in paras if len(p) >= min_paragraph_chars]