news 2026/7/2 4:56:16

【从0到1构建一个ClaudeAgent】规划与协调-子Agent

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【从0到1构建一个ClaudeAgent】规划与协调-子Agent

在 原作者的Python代码 里,run_subagent函数就像一个“虫洞”,把任务传送到一个新的平行宇宙(子线程/子上下文)去执行,执行完只带回结果。

在 Java 中,我们通常通过创建新的类实例来实现这种隔离。父 Agent 和子 Agent 拥有各自独立的messages列表,互不干扰。

Java 实现代码

java

public class AgentWithSubAgents { private static final Path WORKDIR = Paths.get(System.getProperty("user.dir")); // --- 1. 工具定义 --- public enum ToolType { BASH("bash"), READ_FILE("read_file"), WRITE_FILE("write_file"), EDIT_FILE("edit_file"), TASK("task"); // 新增 TASK 工具 public final String name; ToolType(String name) { this.name = name; } } // ... 省略相同的 ToolExecutor 接口和基础工具注册 // --- 2. 子 Agent 类 (核心隔离单元) --- public static class SubAgent { private final List<Map<String, Object>> messages = new ArrayList<>(); // 独立上下文 private static final String SYSTEM_PROMPT = "You are a coding subagent. Complete the task and summarize findings."; // 子 Agent 只能使用基础工具,不能递归创建子 Agent private final List<Map<String, Object>> allowedTools = Arrays.asList( createToolSpec("bash", "Run shell command", "command"), createToolSpec("read_file", "Read file", "path"), createToolSpec("write_file", "Write file", "path", "content"), createToolSpec("edit_file", "Edit file", "path", "old_text", "new_text") ); // 执行子任务 public String executeTask(String prompt) { messages.clear(); messages.add(Map.of("role", "user", "content", prompt)); System.out.println(">>> [SubAgent] 启动子任务..."); // 安全循环限制,防止死循环 for (int i = 0; i < 30; i++) { Map<String, Object> response = callLLM(messages, SYSTEM_PROMPT, allowedTools); messages.add(response); if (!"tool_use".equals(response.get("stop_reason"))) { break; // 任务完成 } List<Map<String, Object>> results = new ArrayList<>(); List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content"); for (Map<String, Object> block : content) { if ("tool_use".equals(block.get("type"))) { String toolName = (String) block.get("name"); String toolId = (String) block.get("id"); Map<String, Object> inputArgs = (Map<String, Object>) block.get("input"); // 通过共享的 TOOL_HANDLERS 执行工具 ToolExecutor handler = TOOL_HANDLERS.get(toolName); String output = ""; try { output = handler != null ? handler.execute(inputArgs) : "Unknown tool"; } catch (Exception e) { output = "Error: " + e.getMessage(); } Map<String, Object> result = new HashMap<>(); result.put("type", "tool_result"); result.put("tool_use_id", toolId); result.put("content", output); results.add(result); } } messages.add(Map.of("role", "user", "content", results)); } // 提取最终文本结果 return extractText(response); } } // --- 3. 父 Agent 核心循环 --- // 父 Agent 拥有所有工具,包括 TASK private final List<Map<String, Object>> parentTools = new ArrayList<>(); { // 复制基础工具 parentTools.addAll(Arrays.asList( createToolSpec("bash", "Run shell command", "command"), createToolSpec("read_file", "Read file", "path"), createToolSpec("write_file", "Write file", "path", "content"), createToolSpec("edit_file", "Edit file", "path", "old_text", "new_text") )); // 添加 TASK 工具 parentTools.add(createTaskToolSpec()); } // 创建任务工具规格 private static Map<String, Object> createTaskToolSpec() { Map<String, Object> spec = new HashMap<>(); spec.put("name", "task"); spec.put("description", "Spawn a subagent with fresh context."); Map<String, Object> schema = new HashMap<>(); schema.put("type", "object"); Map<String, Object> props = new HashMap<>(); props.put("prompt", Map.of("type", "string", "description", "The task for the subagent")); props.put("description", Map.of("type", "string", "description", "Short description")); schema.put("properties", props); schema.put("required", Arrays.asList("prompt")); spec.put("input_schema", schema); return spec; } public void agentLoop(List<Map<String, Object>> messages) { SubAgent subAgent = new SubAgent(); // 需要时创建子 Agent while (true) { System.out.println(">>> [Parent] 思考中..."); Map<String, Object> response = callLLM(messages, "You are a manager. Use 'task' to delegate.", parentTools); messages.add(response); if (!"tool_use".equals(response.get("stop_reason"))) return; List<Map<String, Object>> results = new ArrayList<>(); List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content"); for (Map<String, Object> block : content) { if ("tool_use".equals(block.get("type"))) { String toolName = (String) block.get("name"); String toolId = (String) block.get("id"); Map<String, Object> inputArgs = (Map<String, Object>) block.get("input"); String output; if ("task".equals(toolName)) { // 委托给子 Agent String desc = (String) inputArgs.getOrDefault("description", "Subtask"); String prompt = (String) inputArgs.get("prompt"); System.out.println("> task (" + desc + "): " + prompt.substring(0, Math.min(prompt.length(), 50))); output = subAgent.executeTask(prompt); } else { // 自己执行基础工具 ToolExecutor handler = TOOL_HANDLERS.get(toolName); try { output = handler != null ? handler.execute(inputArgs) : "Unknown tool"; } catch (Exception e) { output = "Error: " + e.getMessage(); } } System.out.println(" Result: " + output.substring(0, Math.min(output.length(), 100))); Map<String, Object> result = new HashMap<>(); result.put("type", "tool_result"); result.put("tool_use_id", toolId); result.put("content", output); results.add(result); } } messages.add(Map.of("role", "user", "content", results)); } } // --- 辅助方法 --- private static String extractText(Map<String, Object> response) { List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content"); for (Map<String, Object> block : content) { if ("text".equals(block.get("type"))) return (String) block.get("text"); } return "(no summary)"; } }

subAgent

核心思想:引入分层架构和上下文隔离,让Agent能够分解复杂任务,分配给专门的"子Agent"处理,避免上下文污染。

java

// 子 Agent 类 - 独立的执行单元 public static class SubAgent { private final List<Map<String, Object>> messages = new ArrayList<>(); // 独立上下文 private static final String SYSTEM_PROMPT = "You are a coding subagent. Complete the task and summarize findings."; // 独立上下文:每个子任务有自己独立的消息历史 // 系统提示:为子任务定义专门的角色,如"编码专家" // 子 Agent 只能使用基础工具,不能递归创建子 Agent private final List<Map<String, Object>> allowedTools = Arrays.asList( createToolSpec("bash", "Run shell command", "command"), createToolSpec("read_file", "Read file", "path"), // 权限控制:限制工具集,防止无限递归 ); public String executeTask(String prompt) { messages.clear(); // 清空历史,从零开始 messages.add(Map.of("role", "user", "content", prompt)); // 每次调用都是全新的上下文,避免历史干扰 // 安全循环限制,防止死循环 for (int i = 0; i < 30; i++) { // 最多30轮 // ... 执行子任务 } } }
  • 上下文隔离:每个子任务在干净的环境中执行,不继承父任务的上下文污染
  • 角色专业化:可以通过不同的SYSTEM_PROMPT让子Agent专注特定领域
  • 防递归保护:子Agent不能调用task工具,防止无限递归
  • 资源限制:限制最大轮数,防止死循环

任务委派:TASK 工具

核心思想:将"委派子任务"抽象为一种工具,实现任务分解和并行化

java

// 父 Agent 的工具列表 private final List<Map<String, Object>> parentTools = new ArrayList<>(); { // 基础工具 parentTools.addAll(Arrays.asList( createToolSpec("bash", "Run shell command", "command"), // ... 其他基础工具 )); // 添加 TASK 工具 parentTools.add(createTaskToolSpec()); // 关键:父Agent独有的委派能力 } // 任务工具规格定义 private static Map<String, Object> createTaskToolSpec() { Map<String, Object> spec = new HashMap<>(); spec.put("name", "task"); spec.put("description", "Spawn a subagent with fresh context."); // 工具描述:明确这是创建子Agent的委派工具 Map<String, Object> schema = new HashMap<>(); schema.put("type", "object"); Map<String, Object> props = new HashMap<>(); props.put("prompt", Map.of("type", "string", "description", "The task for the subagent")); props.put("description", Map.of("type", "string", "description", "Short description")); schema.put("properties", props); schema.put("required", Arrays.asList("prompt")); spec.put("input_schema", schema); // 工具规格:定义输入参数 return spec; }
  • 委派抽象:将"让子Agent做某事"封装为一个工具调用
  • 权限控制:只有父Agent有这个工具,子Agent没有
  • 接口设计:明确的任务描述接口,便于LLM使用

父子协作流程

java

// 父 Agent 的核心循环 public void agentLoop(List<Map<String, Object>> messages) { SubAgent subAgent = new SubAgent(); // 创建可重用的子Agent实例 while (true) { // 父Agent思考,可以使用所有工具(包括task) Map<String, Object> response = callLLM(messages, "You are a manager. Use 'task' to delegate.", parentTools); for (Map<String, Object> block : content) { if ("tool_use".equals(block.get("type"))) { String toolName = (String) block.get("name"); Map<String, Object> inputArgs = (Map<String, Object>) block.get("input"); String output; if ("task".equals(toolName)) { // 委派给子 Agent String desc = (String) inputArgs.getOrDefault("description", "Subtask"); String prompt = (String) inputArgs.get("prompt"); System.out.println("> task (" + desc + "): " + prompt.substring(0, Math.min(prompt.length(), 50))); output = subAgent.executeTask(prompt); // 调用子Agent } else { // 自己执行基础工具 ToolExecutor handler = TOOL_HANDLERS.get(toolName); output = handler != null ? handler.execute(inputArgs) : "Unknown tool"; } // 将子Agent的结果返回给父Agent result.put("content", output); } } } }
  • 主从模式:父Agent是管理者,子Agent是工作者
  • 结果聚合:子Agent返回结果,父Agent继续处理
  • 上下文切换:子任务执行时,父Agent暂停等待
  • 可重用子Agent:一个子Agent实例可处理多个任务

架构演进与价值

从 AgentWithTodo 到 AgentWithSubAgents 的升级

维度AgentWithTodoAgentWithSubAgents
架构模式单例模式分层模式(父子)
上下文管理共享上下文上下文隔离
任务处理线性顺序并行/委派
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/2 4:55:42

从算卦到具身:一套跨越三千年的“不确定系统建模”抽象

将东方古典象数哲学与现代前沿物理/人工智能架构进行深度互译。从算卦到具身&#xff1a;一套跨越三千年的“不确定系统建模”抽象 在过去的三千年里&#xff0c;人类文明一直在用两种截然不同的语言&#xff0c;追问同一个问题&#xff1a; 在一个充满随机性、非线性和熵增的宇…

作者头像 李华
网站建设 2026/7/2 4:54:39

鸿蒙HarmonyOS应用架构实战 —— Navigation 外壳、悬浮 TabBar 与页面转场全解析

一、前言:应用架构的第一性原理 做鸿蒙应用开发,最容易被忽视但也最重要的事情是:先把应用外壳(AppShell)搭对。 很多开发者上来就写业务页面,结果发现: 标题栏风格不统一,有的手写、有的用系统 页面返回逻辑混乱,Router 和 Navigation 混用 Tab 切换和页面跳转的…

作者头像 李华
网站建设 2026/7/2 4:53:33

Attention Sinks and Compression Valleys in LLMs are Two Sides of the Same Coin

paper: https://arxiv.org/pdf/2510.06477 ICLR 2026 一句话概括 这篇论文想说明&#xff1a;LLM 中的 attention sink 和 compression valley 不是两个孤立现象&#xff0c;而是同一个底层机制的两种表现&#xff0c;这个机制就是 residual stream 中某些 token&#xff0c;尤…

作者头像 李华
网站建设 2026/7/2 4:52:02

射频实验室“三件套“到底怎么分工?别再把VNA当频谱仪用

很多刚入行的射频工程师&#xff0c;第一次进实验室面对矢量网络分析仪、频谱分析仪、信号发生器这三台"长得差不多"的大家伙&#xff0c;常常犯迷糊——都是射频测试&#xff0c;凭啥要三台&#xff1f;能不能省一台&#xff1f;答案是不能。三者的测试边界完全不同…

作者头像 李华
网站建设 2026/7/2 4:51:59

深入学习Redis(1):Redis内存模型

Redis是目前最火爆的内存数据库之一&#xff0c;通过在内存中读写数据&#xff0c;大大提高了读写速度&#xff0c;可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时&#xff0c;会接触Redis的5种对象类型&#xff08;字符串、哈希、列表、集合、有序集合&…

作者头像 李华
网站建设 2026/7/2 4:51:55

[Power节点]原理解析与实际应用

Power节点是Unity ShaderGraph中的核心数学工具&#xff0c;用于计算输入值A的B次幂&#xff08;即输出OutA^B&#xff09;。该节点通过指数运算实现非线性变换&#xff0c;能够以指数方式增强或减弱输入值&#xff0c;适用于需要动态调整强度或创建复杂效果的场景。例如&#…

作者头像 李华