这是 Agent 进化的关键一步:从“只会说话”变成了“真正干活”。
Java 实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| public class AgentWithTools { private static final Path WORKDIR = Paths.get(System.getProperty("user.dir")); public enum ToolType { BASH("bash", "Run a shell command."), READ_FILE("read_file", "Read file contents."), WRITE_FILE("write_file", "Write content to file."), EDIT_FILE("edit_file", "Replace exact text in file."); }
@FunctionalInterface interface ToolExecutor { String execute(Map args) throws Exception; }
private static final Map TOOL_HANDLERS = new HashMap<>();
static { TOOL_HANDLERS.put(ToolType.BASH.name, args -> { String command = (String) args.get("command"); return runBash(command); }); TOOL_HANDLERS.put(ToolType.READ_FILE.name, args -> { String path = (String) args.get("path"); Integer limit = (Integer) args.get("limit"); return runRead(path, limit); }); TOOL_HANDLERS.put(ToolType.WRITE_FILE.name, args -> { String path = (String) args.get("path"); String content = (String) args.get("content"); return runWrite(path, content); }); TOOL_HANDLERS.put(ToolType.EDIT_FILE.name, args -> { String path = (String) args.get("path"); String oldText = (String) args.get("old_text"); String newText = (String) args.get("new_text"); return runEdit(path, oldText, newText); }); }
public static void agentLoop(List> messages) { while (true) { List> toolResults = new ArrayList<>(); List> content = (List>) response.get("content");
for (Map block : content) { if ("tool_use".equals(block.get("type"))) { String toolName = (String) block.get("name"); String toolId = (String) block.get("id"); Map inputArgs = (Map) block.get("input");
ToolExecutor handler = TOOL_HANDLERS.get(toolName); String output; try { if (handler != null) { output = handler.execute(inputArgs); } else { output = "Error: Unknown tool " + toolName; } } catch (Exception e) { output = "Error: " + e.getMessage(); }
System.out.println("> " + toolName + ": " + output.substring(0, Math.min(output.length(), 100)));
} } } }
private static Path safePath(String p) throws IOException { Path path = WORKDIR.resolve(p).normalize(); if (!path.startsWith(WORKDIR)) { throw new IOException("Path escapes workspace: " + p); } return path; }
private static String runRead(String pathStr, Integer limit) throws IOException { Path path = safePath(pathStr); String content = Files.readString(path); if (limit != null && limit args) throws Exception; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| private static final Map TOOL_HANDLERS = new HashMap<>();
static { TOOL_HANDLERS.put("bash", args -> { }); TOOL_HANDLERS.put("read_file", args -> { }); }
|
开闭原则:不修改主循环就能添加新工具
统一管理:所有工具注册、调用逻辑一致
类型安全:通过枚举定义工具,避免硬编码字符串
文件操作工具集
核心思想:为Agent提供文件系统读写能力,使其能像人类开发者一样操作文件。
1 2 3 4 5 6 7 8 9
| private static Path safePath(String p) throws IOException { Path path = WORKDIR.resolve(p).normalize(); if (!path.startsWith(WORKDIR)) { throw new IOException("Path escapes workspace: " + p); } return path; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private static String runRead(String pathStr, Integer limit) throws IOException { Path path = safePath(pathStr); String content = Files.readString(path); if (limit != null && limit inputArgs = (Map) block.get("input");
ToolExecutor handler = TOOL_HANDLERS.get(toolName); String output; try { if (handler != null) { output = handler.execute(inputArgs); } else { output = "Error: Unknown tool " + toolName; } } catch (Exception e) { output = "Error: " + e.getMessage(); }
|
架构对比与价值
从AgentLoop到AgentWithTools的演进:
| 维度 |
AgentLoop |
AgentWithTools |
| 工具数量 |
1个(Bash) |
4+个(可扩展) |
| 架构设计 |
硬编码 |
策略模式 |
| 添加新工具 |
修改主代码 |
注册表添加 |
| 文件操作 |
无 |
读写编辑 |
| 安全性 |
命令检查 |
沙箱路径 |
| 代码复用 |
低 |
高 |
核心价值:
可扩展性:添加新工具只需在注册表中添加一行
维护性:工具实现与主循环分离
安全性:统一的路径和权限控制
专业性:为开发任务优化的专用工具集