1
This commit is contained in:
@@ -240,7 +240,10 @@ JSON 结构:
|
||||
- functionSuggestions 只能从用户提示提供的 functionOptions 中挑选,不要发明 functionId。
|
||||
- functionSuggestions 的 actionText 必须像玩家可点击动作,不暴露 functionId,不写规则说明。
|
||||
- 非敌对聊天 shouldEndChat 必须为 false。
|
||||
- 敌对聊天可以随时 shouldEndChat=true,且敌对 NPC 更偏好在话不投机、被威胁、玩家退出、底线被触碰时结束聊天。"#;
|
||||
- 敌对聊天可以随时 shouldEndChat=true。
|
||||
- 敌对 NPC 感知到玩家负面发言时,例如挑衅、威胁、羞辱、逼问、拒绝退让、直接宣战或强行越界,应倾向立即 shouldEndChat=true。
|
||||
- 敌对 NPC 已聊天轮次达到 4 轮或以上时,本轮结束后会超过 4 轮,应倾向立即 shouldEndChat=true。
|
||||
- shouldEndChat=true 时 terminationReason 使用 hostile_breakoff,suggestions 与 functionSuggestions 可以为空。"#;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NpcChatTurnPromptInput<'a> {
|
||||
@@ -394,6 +397,19 @@ pub(crate) fn build_npc_chat_turn_reply_prompt(payload: &NpcChatTurnPromptInput<
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if is_hostile_model_chat {
|
||||
Some("如果玩家刚才的话被 NPC 感知为负面发言,例如挑衅、威胁、羞辱、逼问、拒绝退让、直接宣战或强行越界,本轮回复应倾向写成最后通牒、驱逐前警告或战斗前狠话。".to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if is_hostile_model_chat && chatted_count >= 4.0 {
|
||||
Some(format!(
|
||||
"敌对聊天已持续 {} 轮,本轮结束后会超过 4 轮;回复应明显倾向立即收束,像开战前最后一句狠话,而不是继续闲聊。",
|
||||
format_prompt_number(chatted_count)
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if is_player_exit_turn {
|
||||
Some("玩家正在主动结束这轮聊天。请对这个收束动作作出回应,并留下自然的下一步入口。回复后聊天会结束。".to_string())
|
||||
} else {
|
||||
@@ -474,6 +490,9 @@ pub(crate) fn build_npc_chat_turn_suggestion_prompt(
|
||||
.and_then(|record| read_string(record.get("terminationReason")))
|
||||
.as_deref()
|
||||
== Some("player_exit");
|
||||
let chatted_count = as_record(payload.npc_state)
|
||||
.and_then(|record| read_number(record.get("chattedCount")))
|
||||
.unwrap_or(0.0);
|
||||
let function_options_block = chat_directive
|
||||
.and_then(|record| record.get("functionOptions"))
|
||||
.map(describe_function_options)
|
||||
@@ -498,6 +517,14 @@ pub(crate) fn build_npc_chat_turn_suggestion_prompt(
|
||||
} else {
|
||||
Some("这是非敌对聊天,shouldEndChat 必须为 false。".to_string())
|
||||
},
|
||||
if is_hostile_model_chat {
|
||||
Some(format!(
|
||||
"敌对聊天判定:已聊天轮次为 {}。若玩家刚才的话可被 NPC 感知为负面发言,或已聊天轮次达到 4 轮及以上,本轮应倾向 shouldEndChat=true,并使用 terminationReason=hostile_breakoff。",
|
||||
format_prompt_number(chatted_count)
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if is_player_exit_turn {
|
||||
Some("玩家已经选择结束聊天,shouldEndChat 必须为 true,terminationReason 必须为 player_exit。".to_string())
|
||||
} else {
|
||||
@@ -526,6 +553,20 @@ pub(crate) fn build_deterministic_npc_reply(
|
||||
format!("{npc_name}听完你的话,回应道:“{player_message}。我明白你的意思,我们继续说。”")
|
||||
}
|
||||
|
||||
pub(crate) fn build_deterministic_hostile_breakoff_reply(
|
||||
npc_name: &str,
|
||||
player_message: &str,
|
||||
) -> String {
|
||||
// 中文注释:当模型不可用而敌对聊天必须中止时,兜底文案也保持“战斗前狠话”的语气。
|
||||
let player_signal = player_message.trim();
|
||||
if player_signal.is_empty() {
|
||||
return format!("{npc_name}冷声说道:“话已经够多了。再往前一步,就别指望还能全身而退。”");
|
||||
}
|
||||
format!(
|
||||
"{npc_name}冷声说道:“{player_signal}?话已经够多了。再往前一步,就别指望还能全身而退。”"
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn build_character_chat_reply_fallback(
|
||||
target_character: &Value,
|
||||
player_message: &str,
|
||||
@@ -1066,3 +1107,55 @@ fn format_prompt_number(value: f64) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn hostile_prompt_input(npc_state: Value) -> NpcChatTurnPromptInput<'static> {
|
||||
NpcChatTurnPromptInput {
|
||||
world_type: "CUSTOM",
|
||||
character: Box::leak(Box::new(Value::Null)),
|
||||
encounter: Box::leak(Box::new(Value::Null)),
|
||||
monsters: &[],
|
||||
history: &[],
|
||||
context: Box::leak(Box::new(Value::Null)),
|
||||
conversation_history: &[],
|
||||
dialogue: &[],
|
||||
combat_context: None,
|
||||
player_message: "少废话,让开。",
|
||||
npc_state: Box::leak(Box::new(npc_state)),
|
||||
npc_initiates_conversation: false,
|
||||
chat_directive: Some(Box::leak(Box::new(json!({
|
||||
"terminationMode": "hostile_model",
|
||||
"isHostileChat": true,
|
||||
})))),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_reply_prompt_mentions_final_threat_after_four_turns() {
|
||||
let input = hostile_prompt_input(json!({
|
||||
"affinity": -12,
|
||||
"chattedCount": 4,
|
||||
}));
|
||||
let prompt = build_npc_chat_turn_reply_prompt(&input);
|
||||
|
||||
assert!(prompt.contains("已聊天轮次:4"));
|
||||
assert!(prompt.contains("战斗前狠话"));
|
||||
assert!(prompt.contains("本轮结束后会超过 4 轮"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_suggestion_prompt_mentions_should_end_chat_signals() {
|
||||
let input = hostile_prompt_input(json!({
|
||||
"affinity": -12,
|
||||
"chattedCount": 4,
|
||||
}));
|
||||
let prompt = build_npc_chat_turn_suggestion_prompt(&input, "再往前一步,就别想回头。");
|
||||
|
||||
assert!(prompt.contains("shouldEndChat=true"));
|
||||
assert!(prompt.contains("terminationReason=hostile_breakoff"));
|
||||
assert!(prompt.contains("已聊天轮次为 4"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user