Skip to content

Commit 62d0be0

Browse files
authored
feat(AutoContextMemory): optimize current round msg compress (#182)
1 parent b1f3856 commit 62d0be0

6 files changed

Lines changed: 832 additions & 222 deletions

File tree

agentscope-core/src/main/java/io/agentscope/core/tool/file/ReadFileTool.java

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,24 @@
2222
import java.nio.file.Files;
2323
import java.nio.file.Path;
2424
import java.nio.file.Paths;
25+
import java.util.ArrayList;
26+
import java.util.Comparator;
2527
import java.util.List;
28+
import java.util.stream.Stream;
2629
import org.slf4j.Logger;
2730
import org.slf4j.LoggerFactory;
2831
import reactor.core.publisher.Mono;
2932

3033
/**
31-
* Tool for viewing text file content with optional line range specification.
34+
* Tool for viewing text file content with optional line range specification and listing directory
35+
* contents.
3236
*
33-
* <p>This tool allows reading file content with line numbers, supporting:
37+
* <p>This tool provides the following capabilities:
3438
* <ul>
3539
* <li>Viewing entire file content</li>
3640
* <li>Viewing specific line ranges (e.g., lines 1-100)</li>
3741
* <li>Viewing from the end using negative indices (e.g., last 100 lines: [-100, -1])</li>
42+
* <li>Listing files and directories in a specified directory with full paths</li>
3843
* </ul>
3944
*
4045
* <p>Security: When baseDir is specified, all file operations are restricted to that directory
@@ -209,6 +214,124 @@ public Mono<ToolResultBlock> viewTextFile(
209214
});
210215
}
211216

217+
/**
218+
* List files and directories in the specified directory.
219+
*
220+
* @param dirPath The target directory path
221+
* @return A list of files and directories with their full paths
222+
*/
223+
@Tool(
224+
name = "list_directory",
225+
description =
226+
"List all files and directories in the specified directory. Returns the full"
227+
+ " paths of all files and folders in the directory. Use this to explore"
228+
+ " the file system structure.")
229+
public Mono<ToolResultBlock> listDirectory(
230+
@ToolParam(
231+
name = "dir_path",
232+
description = "The target directory path to list files and folders")
233+
String dirPath) {
234+
235+
logger.debug("list_directory called: dirPath='{}'", dirPath);
236+
237+
return Mono.fromCallable(
238+
() -> {
239+
// Validate path is within base directory
240+
Path path;
241+
try {
242+
path = FileToolUtils.validatePath(dirPath, baseDir);
243+
} catch (Exception e) {
244+
logger.warn(
245+
"Path validation failed for '{}': {}",
246+
dirPath,
247+
e.getMessage());
248+
return ToolResultBlock.error(e.getMessage());
249+
}
250+
251+
// Check if path exists
252+
if (!Files.exists(path)) {
253+
logger.warn("Directory does not exist: {}", dirPath);
254+
return ToolResultBlock.error(
255+
String.format("The directory %s does not exist.", dirPath));
256+
}
257+
258+
// Check if path is a directory
259+
if (!Files.isDirectory(path)) {
260+
logger.warn("Path is not a directory: {}", dirPath);
261+
return ToolResultBlock.error(
262+
String.format("The path %s is not a directory.", dirPath));
263+
}
264+
265+
// List files and directories
266+
List<String> files = new ArrayList<>();
267+
List<String> directories = new ArrayList<>();
268+
269+
try (Stream<Path> paths = Files.list(path)) {
270+
paths.sorted(Comparator.comparing(Path::toString))
271+
.forEach(
272+
p -> {
273+
String fullPath = p.toAbsolutePath().toString();
274+
if (Files.isDirectory(p)) {
275+
directories.add(fullPath);
276+
} else {
277+
files.add(fullPath);
278+
}
279+
});
280+
} catch (Exception e) {
281+
logger.error(
282+
"Error listing directory '{}': {}",
283+
dirPath,
284+
e.getMessage(),
285+
e);
286+
return ToolResultBlock.error(
287+
"Error listing directory: " + e.getMessage());
288+
}
289+
290+
// Format output
291+
StringBuilder result = new StringBuilder();
292+
result.append(String.format("Contents of directory %s:\n\n", dirPath));
293+
294+
if (!directories.isEmpty()) {
295+
result.append("Directories:\n");
296+
for (String dir : directories) {
297+
result.append(" ").append(dir).append("\n");
298+
}
299+
result.append("\n");
300+
}
301+
302+
if (!files.isEmpty()) {
303+
result.append("Files:\n");
304+
for (String file : files) {
305+
result.append(" ").append(file).append("\n");
306+
}
307+
result.append("\n");
308+
}
309+
310+
if (directories.isEmpty() && files.isEmpty()) {
311+
result.append("(empty directory)\n");
312+
} else {
313+
result.append(
314+
String.format(
315+
"Total: %d directory(ies), %d file(s)",
316+
directories.size(), files.size()));
317+
}
318+
319+
logger.debug(
320+
"Listed {} directories and {} files in: {}",
321+
directories.size(),
322+
files.size(),
323+
dirPath);
324+
325+
return ToolResultBlock.text(result.toString());
326+
})
327+
.onErrorResume(
328+
e -> {
329+
logger.error(
330+
"Error listing directory '{}': {}", dirPath, e.getMessage(), e);
331+
return Mono.just(ToolResultBlock.error("Error: " + e.getMessage()));
332+
});
333+
}
334+
212335
/**
213336
* Format lines with line numbers for display.
214337
*

agentscope-examples/advanced/src/main/java/io/agentscope/examples/advanced/AutoMemoryExample.java

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
import io.agentscope.core.message.TextBlock;
2828
import io.agentscope.core.model.DashScopeChatModel;
2929
import io.agentscope.core.model.GenerateOptions;
30+
import io.agentscope.core.session.JsonSession;
31+
import io.agentscope.core.session.SessionManager;
3032
import io.agentscope.core.tool.Toolkit;
3133
import io.agentscope.core.tool.file.ReadFileTool;
3234
import io.agentscope.core.tool.file.WriteFileTool;
35+
import java.nio.file.Path;
36+
import java.nio.file.Paths;
3337
import java.util.Scanner;
34-
import java.util.UUID;
3538

3639
/**
3740
* auto memory example
@@ -42,8 +45,6 @@ public static void main(String[] args) {
4245

4346
String apiKey = ExampleUtils.getDashScopeApiKey();
4447

45-
String sessionId = UUID.randomUUID().toString();
46-
String baseDir = System.getProperty("user.home") + "/aiagent";
4748
DashScopeChatModel chatModel =
4849
DashScopeChatModel.builder().apiKey(apiKey).modelName("qwen3-max-preview").stream(
4950
true)
@@ -59,7 +60,8 @@ public static void main(String[] args) {
5960
.userId("example-user") // Use a placeholder user ID for example code
6061
.apiBaseUrl("https://api.mem0.ai");
6162
Mem0LongTermMemory longTermMemory = builder.build();
62-
AutoContextConfig autoContextConfig = AutoContextConfig.builder().lastKeep(10).build();
63+
AutoContextConfig autoContextConfig =
64+
AutoContextConfig.builder().tokenRatio(0.3).lastKeep(10).build();
6365
AutoContextMemory memory = new AutoContextMemory(autoContextConfig, chatModel);
6466

6567
Toolkit toolkit = new Toolkit();
@@ -81,42 +83,64 @@ public static void main(String[] args) {
8183
.enablePlan()
8284
.toolkit(toolkit)
8385
.build();
84-
86+
String sessionId = "session1234567";
87+
// Set up session path
88+
Path sessionPath =
89+
Paths.get(System.getProperty("user.home"), ".agentscope", "examples", "sessions");
90+
SessionManager sessionManager =
91+
SessionManager.forSessionId(sessionId)
92+
.withSession(new JsonSession(sessionPath))
93+
.addComponent(agent) // Automatically named "agent"
94+
.addComponent(memory); // Automatically named "memory"
95+
if (sessionManager.sessionExists()) {
96+
// Load existing session
97+
sessionManager.loadIfExists();
98+
}
8599
Scanner scanner = new Scanner(System.in);
86100
System.out.println("🚀 Auto Memory Example Started!");
87101
System.out.println("Enter your query (type 'exit' to quit):\n");
88102

89-
while (true) {
90-
System.out.print("You: ");
91-
String query = scanner.nextLine().trim();
92-
93-
// Check if user wants to exit
94-
if ("exit".equalsIgnoreCase(query)) {
95-
System.out.println("👋 Goodbye!");
96-
break;
103+
try {
104+
while (true) {
105+
System.out.print("You: ");
106+
String query = scanner.nextLine().trim();
107+
108+
// Check if user wants to exit
109+
if ("exit".equalsIgnoreCase(query)) {
110+
System.out.println("👋 Goodbye!");
111+
break;
112+
}
113+
114+
// Skip empty input
115+
if (query.isEmpty()) {
116+
System.out.println("Please enter a valid query.\n");
117+
continue;
118+
}
119+
120+
// Create user message
121+
Msg userMsg =
122+
Msg.builder()
123+
.role(MsgRole.USER)
124+
.content(TextBlock.builder().text(query).build())
125+
.build();
126+
127+
// Call agent and get response
128+
System.out.println("\n🤔 Processing...\n");
129+
Msg response = agent.call(userMsg).block();
130+
131+
// Output response
132+
System.out.println("Assistant: " + response.getTextContent() + "\n");
133+
sessionManager.saveSession();
97134
}
98135

99-
// Skip empty input
100-
if (query.isEmpty()) {
101-
System.out.println("Please enter a valid query.\n");
102-
continue;
103-
}
104-
105-
// Create user message
106-
Msg userMsg =
107-
Msg.builder()
108-
.role(MsgRole.USER)
109-
.content(TextBlock.builder().text(query).build())
110-
.build();
136+
} catch (Throwable e) {
137+
System.out.println("error save session: " + e.getMessage());
111138

112-
// Call agent and get response
113-
System.out.println("\n🤔 Processing...\n");
114-
Msg response = agent.call(userMsg).block();
139+
} finally {
140+
System.out.println("save session: ");
115141

116-
// Output response
117-
System.out.println("Assistant: " + response.getTextContent() + "\n");
142+
sessionManager.saveSession();
118143
}
119-
120144
scanner.close();
121145
}
122146
}

0 commit comments

Comments
 (0)