【从0到1构建一个ClaudeAgent】工具与实验-工具 [复制链接]
发表于 7 天前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
这是 Agent 进化的关键一步:从“只会语言”变成了“真正干活”
Java 实今世码
  1. public class AgentWithTools {
  2.     // 配置
  3.     private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
  4.    
  5.     // --- 核心:工具定义与分发 ---
  6.     // 1. 定义工具枚举
  7.     public enum ToolType {
  8.         BASH("bash", "Run a shell command."),
  9.         READ_FILE("read_file", "Read file contents."),
  10.         WRITE_FILE("write_file", "Write content to file."),
  11.         EDIT_FILE("edit_file", "Replace exact text in file.");
  12.         // ... 省略构造器
  13.     }
  14.     // 2. 工具执行接口
  15.     @FunctionalInterface
  16.     interface ToolExecutor {
  17.         String execute(Map<String, Object> args) throws Exception;
  18.     }
  19.     // 3. 注册工具处理逻辑
  20.     private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
  21.     static {
  22.         TOOL_HANDLERS.put(ToolType.BASH.name, args -> {
  23.             String command = (String) args.get("command");
  24.             return runBash(command);
  25.         });
  26.         TOOL_HANDLERS.put(ToolType.READ_FILE.name, args -> {
  27.             String path = (String) args.get("path");
  28.             Integer limit = (Integer) args.get("limit");
  29.             return runRead(path, limit);
  30.         });
  31.         TOOL_HANDLERS.put(ToolType.WRITE_FILE.name, args -> {
  32.             String path = (String) args.get("path");
  33.             String content = (String) args.get("content");
  34.             return runWrite(path, content);
  35.         });
  36.         TOOL_HANDLERS.put(ToolType.EDIT_FILE.name, args -> {
  37.             String path = (String) args.get("path");
  38.             String oldText = (String) args.get("old_text");
  39.             String newText = (String) args.get("new_text");
  40.             return runEdit(path, oldText, newText);
  41.         });
  42.     }
  43.     // --- 核心循环 ---
  44.     public static void agentLoop(List<Map<String, Object>> messages) {
  45.         while (true) {
  46.             // ... 省略相同的 LLM 调用、消息追加逻辑
  47.             
  48.             // 4. 执行工具
  49.             List<Map<String, Object>> toolResults = new ArrayList<>();
  50.             List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content");
  51.             for (Map<String, Object> block : content) {
  52.                 if ("tool_use".equals(block.get("type"))) {
  53.                     String toolName = (String) block.get("name");  // 关键新增
  54.                     String toolId = (String) block.get("id");
  55.                     Map<String, Object> inputArgs = (Map<String, Object>) block.get("input");
  56.                     // 路由分发
  57.                     ToolExecutor handler = TOOL_HANDLERS.get(toolName);
  58.                     String output;
  59.                     try {
  60.                         if (handler != null) {
  61.                             output = handler.execute(inputArgs);
  62.                         } else {
  63.                             output = "Error: Unknown tool " + toolName;
  64.                         }
  65.                     } catch (Exception e) {
  66.                         output = "Error: " + e.getMessage();
  67.                     }
  68.                     System.out.println("> " + toolName + ": " + output.substring(0, Math.min(output.length(), 100)));
  69.                     // ... 省略相同的工具结果构造逻辑
  70.                 }
  71.             }
  72.             // ... 省略相同的回传逻辑
  73.         }
  74.     }
  75.     // --- 工具具体实现 ---
  76.     private static Path safePath(String p) throws IOException {
  77.         Path path = WORKDIR.resolve(p).normalize();
  78.         if (!path.startsWith(WORKDIR)) {
  79.             throw new IOException("Path escapes workspace: " + p);
  80.         }
  81.         return path;
  82.     }
  83.     // ... 省略与之前相同的 runBash 实现
  84.     private static String runRead(String pathStr, Integer limit) throws IOException {
  85.         Path path = safePath(pathStr);
  86.         String content = Files.readString(path);
  87.         if (limit != null && limit < content.length()) {
  88.             return content.substring(0, limit) + "... (truncated)";
  89.         }
  90.         return content;
  91.     }
  92.     private static String runWrite(String pathStr, String content) throws IOException {
  93.         Path path = safePath(pathStr);
  94.         Files.createDirectories(path.getParent());
  95.         Files.writeString(path, content);
  96.         return "Wrote " + content.length() + " bytes to " + pathStr;
  97.     }
  98.     private static String runEdit(String pathStr, String oldText, String newText) throws IOException {
  99.         Path path = safePath(pathStr);
  100.         String content = Files.readString(path);
  101.         if (!content.contains(oldText)) {
  102.             return "Error: Text not found in " + pathStr;
  103.         }
  104.         String newContent = content.replace(oldText, newText);
  105.         Files.writeString(path, newContent);
  106.         return "Edited " + pathStr;
  107.     }
  108. }
复制代码
这段代码相比 s01,最大的进步在于本领的扩展安全边界。说白了就是,你可以像搭积木一样给 Agent 塞入各种工具函数,让它的本领边界随插件无穷延伸。
这段代码应该已经很清晰,我这里就不多表明确
工具抽象框架(战略模式)

焦点头脑:从"硬编码工具"升级为"可插拔架构",实现工具与主循环的解耦
  1. // 工具枚举 - 集中定义所有可用工具
  2. public enum ToolType {
  3.     BASH("bash", "Run a shell command."),
  4.     READ_FILE("read_file", "Read file contents."),
  5.     WRITE_FILE("write_file", "Write content to file.");
  6.     // 枚举定义:工具名 + 描述
  7.     // 为LLM提供工具列表时使用
  8. }
复制代码
  1. // 工具执行接口 - 统一调用契约
  2. @FunctionalInterface
  3. interface ToolExecutor {
  4.     String execute(Map<String, Object> args) throws Exception;
  5.     // 统一接口:所有工具都实现此方法
  6.     // 参数和返回值标准化
  7. }
复制代码
  1. // 工具注册表 - 动态路由
  2. private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
  3. static {
  4.     TOOL_HANDLERS.put("bash", args -> {
  5.         // 工具实现1
  6.     });
  7.     TOOL_HANDLERS.put("read_file", args -> {
  8.         // 工具实现2
  9.     });
  10.     // 注册中心:工具名 -> 实现函数
  11.     // 新增工具只需在这里注册
  12. }
复制代码

  • 开闭原则:不修改主循环就能添加新工具
  • 同一管理:全部工具注册、调用逻辑同等
  • 范例安全:通过罗列界说工具,制止硬编码字符串
文件操纵工具集

焦点头脑:为Agent提供文件体系读写本领,使其能像人类开辟者一样操纵文件。
  1. private static Path safePath(String p) throws IOException {
  2.     Path path = WORKDIR.resolve(p).normalize();
  3.     if (!path.startsWith(WORKDIR)) {
  4.         throw new IOException("Path escapes workspace: " + p);
  5.     }
  6.     return path;
  7.     // 安全沙箱:确保工具只能操作工作目录内的文件
  8.     // 防止路径逃逸攻击
  9. }
复制代码
  1. private static String runRead(String pathStr, Integer limit) throws IOException {
  2.     Path path = safePath(pathStr);
  3.     String content = Files.readString(path);
  4.     if (limit != null && limit < content.length()) {
  5.         return content.substring(0, limit) + "... (truncated)";
  6.     }
  7.     return content;
  8.     // 带限制的读取:防止大文件内存溢出
  9.     // 自动截断,返回友好提示
  10. }
复制代码
  1. private static String runWrite(String pathStr, String content) throws IOException {
  2.     Path path = safePath(pathStr);
  3.     Files.createDirectories(path.getParent());  // 自动创建父目录
  4.     Files.writeString(path, content);
  5.     return "Wrote " + content.length() + " bytes to " + pathStr;
  6.     // 自动创建目录:用户体验优化
  7.     // 明确的结果反馈
  8. }
复制代码
  1. private static String runEdit(String pathStr, String oldText, String newText) throws IOException {
  2.     Path path = safePath(pathStr);
  3.     String content = Files.readString(path);
  4.     if (!content.contains(oldText)) {
  5.         return "Error: Text not found in " + pathStr;  // 错误处理
  6.     }
  7.     String newContent = content.replace(oldText, newText);
  8.     Files.writeString(path, newContent);
  9.     return "Edited " + pathStr;
  10.     // 简单的文件编辑:文本查找替换
  11.     // 先验证后操作,避免损坏文件
  12. }
复制代码

  • 沙箱安全:全部文件操纵都颠末safePath查抄
  • 渐进式反馈:读操纵支持截断,制止相应过大
  • 容错处置惩罚:编辑前查抄文本是否存在
  • 自动化:写文件时自动创建父目次
动态工具路由
  1. // 在agentLoop中
  2. String toolName = (String) block.get("name");  // 从LLM响应中提取工具名
  3. Map<String, Object> inputArgs = (Map<String, Object>) block.get("input");
  4. // 根据工具名查找处理器
  5. ToolExecutor handler = TOOL_HANDLERS.get(toolName);
  6. String output;
  7. try {
  8.     if (handler != null) {
  9.         output = handler.execute(inputArgs);  // 动态调用
  10.     } else {
  11.         output = "Error: Unknown tool " + toolName;
  12.     }
  13. } catch (Exception e) {
  14.     output = "Error: " + e.getMessage();  // 统一错误处理
  15. }
复制代码

  • 动态分派:根据LLM选择的工具名调用对应实现
  • 同一错误处置惩罚:未知工具、实验非常都有同一格式的返回
  • 解耦:主循环不必要知道具体工具的实现细节
架构对比与代价

从AgentLoop到AgentWithTools的演进
维度AgentLoopAgentWithTools工具数目1个(Bash)4+个(可扩展)架构筹划硬编码战略模式添加新工具修改主代码注册表添加文件操纵无读写编辑安全性下令查抄沙箱路径代码复用低高焦点代价

  • 可扩展性:添加新工具只需在注册表中添加一行
  • 维护性:工具实现与主循环分离
  • 安全性:同一的路径和权限控制
  • 专业性:为开辟任务优化的专用工具集

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表