从0到1构建一个ClaudeAgent】工具与执行-Agent循环

张开发
2026/4/11 8:50:35 15 分钟阅读

分享文章

从0到1构建一个ClaudeAgent】工具与执行-Agent循环
while True: response client.messages.create(messagesmessages, toolstools) if response.stop_reason ! tool_use: break for tool_call in response.content: result execute_tool(tool_call.name, tool_call.input) messages.append(result)这段代码人工翻译一下就是调模型给指令执行工具读写文件、跑命令回传结果告诉模型干了啥继续迭代直到任务干完就这就这。剩下的全是围绕这个循环的各种优化和补丁。用 Java 来写核心循环大概是这样while (true) { MessageResponse response client.messagesCreate(messages, tools); if (!tool_use.equals(response.getStopReason())) { break; } for (ToolCall toolCall : response.getContent()) { ToolResult result executeTool(toolCall.getName(), toolCall.getInput()); messages.add(result); } }正是考虑到我们的读者多数是Java同学因此我决定用Java来和大家一块学习下渐进式学习路径网站将这 12 个阶段s01-s12归纳为五个核心能力的进阶看看一个成熟的 Agent 系统是如何一步步被做出来的S01 Agent 循环最小可用的 Agent 内核仅仅需要一个 while 循环 一个工具。说白了就是Agent 最本质的“大脑-手脚”循环Java实现代码public class AgentLoop { // 模拟 Anthropic API 客户端 private static final String API_KEY System.getenv(ANTHROPIC_API_KEY); private static final String MODEL_ID System.getenv(MODEL_ID); private static final HttpClient client HttpClient.newHttpClient(); // 核心循环 public static void agentLoop(ListMapString, Object messages) { while (true) { // 1. 调用 LLM System.out.println( 正在思考...); MapString, Object response callLLM(messages); // 2. 将助手回复加入历史 messages.add(response); // 3. 检查停止原因 // 注意这里简化了逻辑实际需解析 JSON 中的 stop_reason String stopReason (String) response.get(stop_reason); if (!tool_use.equals(stopReason)) { return; // 任务完成退出循环 } // 4. 执行工具 ListMapString, Object toolResults new ArrayList(); ListMapString, Object content (ListMapString, Object) response.get(content); for (MapString, Object block : content) { if (tool_use.equals(block.get(type))) { MapString, Object input (MapString, Object) block.get(input); String command (String) input.get(command); String toolId (String) block.get(id); System.out.println(\033[33m$ command \033[0m); // 黄色输出命令 // 执行 Bash String output runBash(command); System.out.println(output.length() 200 ? output.substring(0, 200) ... : output); // 构造工具结果 MapString, Object result new HashMap(); result.put(type, tool_result); result.put(tool_use_id, toolId); result.put(content, output); toolResults.add(result); } } // 5. 将工具结果作为用户输入再次加入历史 MapString, Object userTurn new HashMap(); userTurn.put(role, user); userTurn.put(content, toolResults); messages.add(userTurn); } } // 模拟 LLM 调用 (实际需替换为 SDK 调用) private static MapString, Object callLLM(ListMapString, Object messages) { // 这里是一个占位符实际应发送 HTTP 请求给 Anthropic API // 返回结构需匹配 API 响应 return new HashMap(); } // 执行 Shell 命令 private static String runBash(String command) { // 安全检查 if (command.contains(rm -rf /) || command.contains(sudo)) { return Error: Dangerous command blocked; } try { ProcessBuilder pb new ProcessBuilder(bash, -c, command); pb.redirectErrorStream(true); Process p pb.start(); // 读取输出 BufferedReader reader new BufferedReader(new InputStreamReader(p.getInputStream())); StringBuilder output new StringBuilder(); String line; while ((line reader.readLine()) ! null) { output.append(line).append(\n); } // 等待完成 (带超时) if (!p.waitFor(120, TimeUnit.SECONDS)) { p.destroyForcibly(); return Error: Timeout (120s); } String result output.toString().trim(); return result.isEmpty() ? (no output) : result.substring(0, Math.min(result.length(), 50000)); } catch (IOException | InterruptedException e) { return Error: e.getMessage(); } } public static void main(String[] args) { ListMapString, Object history new ArrayList(); Scanner scanner new Scanner(System.in); System.out.println(Agent 已启动 (输入 q 退出)); while (true) { System.out.print(\033[36ms01 \033[0m); String query scanner.nextLine(); if (query.trim().equalsIgnoreCase(q) || query.isEmpty()) { break; } MapString, Object userMsg new HashMap(); userMsg.put(role, user); userMsg.put(content, query); history.add(userMsg); agentLoop(history); // 打印最终回复 System.out.println(Agent 执行完毕。); } } }这段代码包含了所有 AI Agent 的灵魂。核心模式ReAct 循环代码中的while循环是 Agent 的心脏。它的逻辑是思考问 LLM 该做什么行动如果 LLM 说要调工具比如写代码、运行命令代码就去执行这个工具观察把工具执行的结果输出、报错再次告诉 LLM。循环LLM 根据结果决定是继续干还是说“搞定了”。while (true) { // 1. 调用 LLM MapString, Object response callLLM(messages); messages.add(response); // 2. 检查是否结束 String stopReason (String) response.get(stop_reason); if (!tool_use.equals(stopReason)) { return; // 任务完成 } // 3. 执行工具 ListMapString, Object toolResults executeTools(response); // 4. 将结果返回给 LLM messages.add(createUserTurn(toolResults)); }状态管理messages列表。它不仅仅是聊天记录它是 Agent 的短期记忆。每次循环我们都要把新的对话无论是人的指令还是工具的执行结果append进去。如果不把工具结果放回去LLM 就不知道自己刚才执行的命令成功了没有也就无法进行下一步。工具定义的标准化在 代码的TOOLS变量里我们会定义了工具长什么样名字、参数。// 遍历响应中的工具调用块 for (MapString, Object block : content) { if (tool_use.equals(block.get(type))) { // 提取命令 MapString, Object input (MapString, Object) block.get(input); String command (String) input.get(command); String toolId (String) block.get(id); // 执行 Bash String output runBash(command); // 构建工具结果 MapString, Object result new HashMap(); result.put(type, tool_result); result.put(tool_use_id, toolId); result.put(content, output); toolResults.add(result); } }关键点LLM 不会真的“运行”代码它只是输出一个符合这个格式的 JSON比如{name: bash, arguments: {command: ls}}。而代码才是负责解析这个 JSON 并真的去执行Runtime.exec()。安全围栏代码里的run_bash函数不仅仅是执行命令它还充当了防火墙。private static String runBash(String command) { // 安全检查 if (command.contains(rm -rf /) || command.contains(sudo)) { return Error: Dangerous command blocked; } // 带超时的命令执行 if (!p.waitFor(120, TimeUnit.SECONDS)) { p.destroyForcibly(); return Error: Timeout (120s); } // 限制输出长度 return result.substring(0, Math.min(result.length(), 50000)); }重要永远不要让 LLM 直接拥有无限制的 Shell 权限。虽然这里的检查很简单黑名单但在生产环境中可能会需要沙箱环境Docker来运行这些命令。文章转载自Seven原文链接https://www.cnblogs.com/sevencoding/p/19821012体验地址http://www.jnpfsoft.com/?from410

更多文章