Skip to main content

agent_context/
message.rs

1use serde::Serialize;
2use serde::de::DeserializeOwned;
3
4use crate::Role;
5
6/// 后端消息类型必须实现此 trait。
7///
8/// `AgentContext` 不感知消息的具体内容结构,仅通过此 trait 获取两个关键信息:
9///
10/// - **角色**:用于按角色筛选和保留
11/// - **推理保留策略**:用于格式转换时决定是否剥离 `reasoning_content`
12///
13/// ## 实现要求
14///
15/// 由于孤儿规则,你无法为外部类型直接实现此 trait。需要在你的 crate 中
16/// 创建 newtype 包装器:
17///
18/// ```ignore
19/// #[derive(Debug, Clone, Serialize, Deserialize)]
20/// struct MyMessage(deepseek_sdk::Message);
21///
22/// impl ContextMessage for MyMessage {
23///     fn role(&self) -> Role { /* 映射 */ }
24///     fn preserve_reasoning(&self) -> bool { /* 按 provider 规则 */ }
25/// }
26/// ```
27pub trait ContextMessage: Send + Sync + Clone + Serialize + DeserializeOwned {
28    /// 返回此消息的角色。
29    fn role(&self) -> Role;
30
31    /// 此消息的 `reasoning_content` 是否需要在后续请求中保留。
32    ///
33    /// 各 LLM provider 有不同规则。例如 DeepSeek 要求:
34    /// - 工具调用场景中 assistant 的 `reasoning_content` 必须完整回传
35    /// - 非工具调用场景中可省略
36    fn preserve_reasoning(&self) -> bool;
37
38    /// 返回一个剥离 `reasoning_content` 的副本。
39    ///
40    /// 用于 [`ContextBackend::to_request_messages`](crate::ContextBackend::to_request_messages) 的默认实现:
41    /// 对 `!preserve_reasoning()` 的消息剥离思维链,减少 token 消耗。
42    fn without_reasoning(self) -> Self;
43
44    /// 返回一个角色被替换为新角色的副本。
45    ///
46    /// 用于 [`ContextBackend::to_system_message`](crate::ContextBackend::to_system_message) 的默认实现:
47    /// 将消息转换为 System 角色(压缩摘要等场景)。
48    fn with_role(self, role: Role) -> Self;
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
56    struct MockMsg {
57        role: Role,
58        content: String,
59    }
60
61    impl ContextMessage for MockMsg {
62        fn role(&self) -> Role {
63            self.role
64        }
65        fn preserve_reasoning(&self) -> bool {
66            false
67        }
68        fn without_reasoning(self) -> Self {
69            self
70        }
71        fn with_role(mut self, role: Role) -> Self {
72            self.role = role;
73            self
74        }
75    }
76
77    #[test]
78    fn mock_msg_role() {
79        let msg = MockMsg {
80            role: Role::User,
81            content: "hello".into(),
82        };
83        assert_eq!(msg.role(), Role::User);
84    }
85
86    #[test]
87    fn mock_msg_preserve_reasoning() {
88        let msg = MockMsg {
89            role: Role::Assistant,
90            content: "world".into(),
91        };
92        assert!(!msg.preserve_reasoning());
93    }
94
95    #[test]
96    fn mock_msg_serde() {
97        let msg = MockMsg {
98            role: Role::Assistant,
99            content: "world".into(),
100        };
101        let json = serde_json::to_string(&msg).unwrap();
102        let back: MockMsg = serde_json::from_str(&json).unwrap();
103        assert_eq!(back.content, "world");
104    }
105}