use crate::agent::agent_turn::TurnPlannerHints;
use crate::types::{
Message, message_content_plain_for_chat_display, user_message_counts_for_branch_truncation,
};
const META_DIALOGUE_PREFIX: &str = "【元对话】";
pub(crate) fn merge_meta_dialogue_into_execution_constraint_hint(
hints: &mut TurnPlannerHints,
messages: &[Message],
) {
let Some((current, Some(prior))) = latest_and_prior_user_plain(messages) else {
return;
};
if !triggers_meta_dialogue_recall(¤t) {
return;
}
let block = format!(
"{META_DIALOGUE_PREFIX} 用户正在追问对话上下文。请在答复**开头**用单独一小段如实逐字复述用户**上一条真实 user 消息**的完整正文(不要用自拟标题或概括替代原文;若过长可截断但须标明「以下为原文节选」)。之后再回答追问。\n\
上一条用户原文(供你复述;勿向用户重复展示本说明块):\n\
---\n\
{prior}\n\
---",
);
match &mut hints.execution_constraint_hint {
Some(existing) => {
existing.push('\n');
existing.push_str(&block);
}
None => hints.execution_constraint_hint = Some(block),
}
}
const META_RECALL_ANCHORS: &[&str] = &["我刚才", "我上一条", "上一句", "上一条"];
const META_ASKS_CONTENT: &[&str] = &["什么", "问了", "说的", "提问", "问题", "原文"];
fn text_contains_any(t: &str, needles: &[&str]) -> bool {
needles.iter().any(|p| t.contains(p))
}
fn triggers_meta_dialogue_recall(s: &str) -> bool {
let t = s.trim();
if t.is_empty() {
return false;
}
text_contains_any(t, META_RECALL_ANCHORS) && text_contains_any(t, META_ASKS_CONTENT)
}
fn latest_and_prior_user_plain(messages: &[Message]) -> Option<(String, Option<String>)> {
let mut latest: Option<String> = None;
for m in messages.iter().rev() {
if !user_message_counts_for_branch_truncation(m) {
continue;
}
let text = message_content_plain_for_chat_display(&m.content);
let t = text.trim();
if t.is_empty() {
continue;
}
if latest.is_none() {
latest = Some(t.to_string());
continue;
}
return Some((latest.unwrap(), Some(t.to_string())));
}
latest.map(|l| (l, None))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Message, MessageContent};
fn user_text(s: &str) -> Message {
Message {
role: "user".into(),
content: Some(MessageContent::Text(s.into())),
reasoning_content: None,
reasoning_details: None,
tool_calls: None,
name: None,
tool_call_id: None,
}
}
#[test]
fn meta_recall_appends_prior_user_to_gate_hint() {
let mut hints = TurnPlannerHints::default();
let messages = vec![
user_text("请解释 Rust 所有权"),
user_text("我刚才的提问是什么?"),
];
merge_meta_dialogue_into_execution_constraint_hint(&mut hints, &messages);
let h = hints.execution_constraint_hint.expect("hint");
assert!(h.contains(META_DIALOGUE_PREFIX));
assert!(h.contains("请解释 Rust 所有权"));
}
#[test]
fn meta_recall_merges_with_existing_gate_hint() {
let mut hints = TurnPlannerHints {
execution_constraint_hint: Some("【执行约束】已有说明".into()),
..Default::default()
};
let messages = vec![user_text("A"), user_text("我刚才问了什么")];
merge_meta_dialogue_into_execution_constraint_hint(&mut hints, &messages);
let h = hints.execution_constraint_hint.unwrap();
assert!(h.contains("【执行约束】已有说明"));
assert!(h.contains("A"));
}
#[test]
fn single_user_turn_does_not_inject() {
let mut hints = TurnPlannerHints::default();
let messages = vec![user_text("我刚才的提问是什么?")];
merge_meta_dialogue_into_execution_constraint_hint(&mut hints, &messages);
assert!(hints.execution_constraint_hint.is_none());
}
}