这是 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"));

// --- 核心:工具定义与分发 ---
// 1. 定义工具枚举
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.");
// ... 省略构造器
}

// 2. 工具执行接口
@FunctionalInterface
interface ToolExecutor {
String execute(Map args) throws Exception;
}

// 3. 注册工具处理逻辑
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) {
// ... 省略相同的 LLM 调用、消息追加逻辑

// 4. 执行工具
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;
}

// ... 省略与之前相同的 runBash 实现

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 -> {
// 工具实现1
});
TOOL_HANDLERS.put("read_file", args -> {
// 工具实现2
});
// 注册中心:工具名 -> 实现函数
// 新增工具只需在这里注册
}
  • 开闭原则:不修改主循环就能添加新工具

  • 统一管理:所有工具注册、调用逻辑一致

  • 类型安全:通过枚举定义工具,避免硬编码字符串

文件操作工具集

核心思想:为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(); // 统一错误处理
}
  • 动态分派:根据LLM选择的工具名调用对应实现

  • 统一错误处理:未知工具、执行异常都有统一格式的返回

  • 解耦:主循环不需要知道具体工具的实现细节

架构对比与价值

从AgentLoop到AgentWithTools的演进

维度 AgentLoop AgentWithTools
工具数量 1个(Bash) 4+个(可扩展)
架构设计 硬编码 策略模式
添加新工具 修改主代码 注册表添加
文件操作 读写编辑
安全性 命令检查 沙箱路径
代码复用

核心价值

  1. 可扩展性:添加新工具只需在注册表中添加一行

  2. 维护性:工具实现与主循环分离

  3. 安全性:统一的路径和权限控制

  4. 专业性:为开发任务优化的专用工具集