子任务污染主对话上下文怎么办?
因此这里主要展示了如何构建一个多智能体系统。
在 原作者的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 的升级:
| 维度 | AgentWithTodo | AgentWithSubAgents |
|---|---|---|
| 架构模式 | 单例模式 | 分层模式(父子) |
| 上下文管理 | 共享上下文 | 上下文隔离 |
| 任务处理 | 线性顺序 | 并行/委派 |
| 复杂性 | 状态管理 | 分治策略 |
| 可扩展性 | 有限 | 强大 |