rucora_tools/memory/
mod.rs1use async_trait::async_trait;
6use rucora_core::{
7 error::{MemoryError, ToolError},
8 memory::{Memory, MemoryItem, MemoryQuery},
9 tool::{Tool, ToolCategory},
10};
11use serde_json::{Value, json};
12use std::collections::HashMap;
13use std::sync::{Arc, Mutex};
14
15struct SimpleMemory {
17 records: Mutex<HashMap<String, MemoryItem>>,
18}
19
20impl SimpleMemory {
21 fn new() -> Self {
22 Self {
23 records: Mutex::new(HashMap::new()),
24 }
25 }
26}
27
28#[async_trait]
29impl Memory for SimpleMemory {
30 async fn add(&self, item: MemoryItem) -> Result<(), MemoryError> {
31 self.records.lock().unwrap().insert(item.id.clone(), item);
32 Ok(())
33 }
34
35 async fn query(&self, _query: MemoryQuery) -> Result<Vec<MemoryItem>, MemoryError> {
36 Ok(self.records.lock().unwrap().values().cloned().collect())
37 }
38}
39
40pub struct MemoryStoreTool {
54 memory: Arc<dyn Memory>,
55}
56
57impl MemoryStoreTool {
58 pub fn new() -> Self {
60 Self::from_memory(Arc::new(SimpleMemory::new()))
61 }
62
63 pub fn from_memory(memory: Arc<dyn Memory>) -> Self {
64 Self { memory }
65 }
66}
67
68impl Default for MemoryStoreTool {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74#[async_trait]
75impl Tool for MemoryStoreTool {
76 fn name(&self) -> &str {
78 "memory_store"
79 }
80
81 fn description(&self) -> Option<&str> {
83 Some(
84 "存储事实、偏好或笔记到长期记忆。使用 category 'core' 表示永久记忆,'daily' 表示会话笔记,'conversation' 表示对话上下文",
85 )
86 }
87
88 fn categories(&self) -> &'static [ToolCategory] {
90 &[ToolCategory::Memory]
91 }
92
93 fn input_schema(&self) -> Value {
95 json!({
96 "type": "object",
97 "properties": {
98 "key": {
99 "type": "string",
100 "description": "记忆的唯一键(如 'user_lang', 'project_stack')"
101 },
102 "content": {
103 "type": "string",
104 "description": "要记忆的信息"
105 },
106 "category": {
107 "type": "string",
108 "description": "记忆类别: 'core' (永久), 'daily' (会话), 'conversation' (对话), 或自定义类别。默认为 'core'"
109 }
110 },
111 "required": ["key", "content"]
112 })
113 }
114
115 async fn call(&self, input: Value) -> Result<Value, ToolError> {
117 let key = input
118 .get("key")
119 .and_then(|v| v.as_str())
120 .ok_or_else(|| ToolError::Message("缺少必需的 'key' 字段".to_string()))?;
121
122 let content = input
123 .get("content")
124 .and_then(|v| v.as_str())
125 .ok_or_else(|| ToolError::Message("缺少必需的 'content' 字段".to_string()))?;
126
127 let category = input
128 .get("category")
129 .and_then(|v| v.as_str())
130 .unwrap_or("core");
131
132 let full_key = format!("{category}:{key}");
134 self.memory
135 .add(MemoryItem {
136 id: full_key,
137 content: content.to_string(),
138 metadata: None,
139 })
140 .await
141 .map_err(|e| ToolError::Message(e.to_string()))?;
142
143 Ok(json!({
144 "success": true,
145 "key": key,
146 "category": category,
147 "message": format!("已存储记忆: {}", key)
148 }))
149 }
150}
151
152pub struct MemoryRecallTool {
164 memory: Arc<dyn Memory>,
165}
166
167impl MemoryRecallTool {
168 pub fn new() -> Self {
170 Self::from_memory(Arc::new(SimpleMemory::new()))
171 }
172
173 pub fn from_store(store: &MemoryStoreTool) -> Self {
175 Self {
176 memory: store.memory.clone(),
177 }
178 }
179
180 pub fn from_memory(memory: Arc<dyn Memory>) -> Self {
181 Self { memory }
182 }
183}
184
185impl Default for MemoryRecallTool {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191#[async_trait]
192impl Tool for MemoryRecallTool {
193 fn name(&self) -> &str {
195 "memory_recall"
196 }
197
198 fn description(&self) -> Option<&str> {
200 Some("从长期记忆中检索存储的信息")
201 }
202
203 fn categories(&self) -> &'static [ToolCategory] {
205 &[ToolCategory::Memory]
206 }
207
208 fn input_schema(&self) -> Value {
210 json!({
211 "type": "object",
212 "properties": {
213 "key": {
214 "type": "string",
215 "description": "要检索的记忆键"
216 },
217 "category": {
218 "type": "string",
219 "description": "记忆类别,默认为 'core'"
220 }
221 },
222 "required": ["key"]
223 })
224 }
225
226 async fn call(&self, input: Value) -> Result<Value, ToolError> {
228 let key = input
229 .get("key")
230 .and_then(|v| v.as_str())
231 .ok_or_else(|| ToolError::Message("缺少必需的 'key' 字段".to_string()))?;
232
233 let category = input
234 .get("category")
235 .and_then(|v| v.as_str())
236 .unwrap_or("core");
237
238 let full_key = format!("{category}:{key}");
240 let mut results = self
241 .memory
242 .query(MemoryQuery {
243 text: full_key,
244 limit: 1,
245 })
246 .await
247 .map_err(|e| ToolError::Message(e.to_string()))?;
248
249 if let Some(item) = results.pop() {
250 Ok(json!({
251 "found": true,
252 "key": key,
253 "category": category,
254 "content": item.content
255 }))
256 } else {
257 Ok(json!({
258 "found": false,
259 "key": key,
260 "category": category,
261 "content": null
262 }))
263 }
264 }
265}