线程池这个问题,平时写业务时好像没什么存在感,很多代码里随手就是一个:
ExecutorService executor = Executors.newFixedThreadPool(10);
线程池这个问题,平时写业务时好像没什么存在感,很多代码里随手就是一个:
ExecutorService executor = Executors.newFixedThreadPool(10);
最小可用的 Agent 内核仅仅需要:一个 while 循环 + 一个工具。
说白了就是Agent 最本质的“大脑-手脚”循环
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(List<Map<String, Object>> messages) {
while (true) {
// 1. 调用 LLM
System.out.println(">>> 正在思考...");
Map<String, 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. 执行工具
List<Map<String, Object>> toolResults = 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"))) {
Map<String, Object> input = (Map<String, 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);
// 构造工具结果
Map<String, Object> result = new HashMap<>();
result.put("type", "tool_result");
result.put("tool_use_id", toolId);
result.put("content", output);
toolResults.add(result);
}
}
// 5. 将工具结果作为用户输入再次加入历史
Map<String, Object> userTurn = new HashMap<>();
userTurn.put("role", "user");
userTurn.put("content", toolResults);
messages.add(userTurn);
}
}
// 模拟 LLM 调用 (实际需替换为 SDK 调用)
private static Map<String, Object> callLLM(List<Map<String, 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) {
List<Map<String, 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;
}
Map<String, Object> userMsg = new HashMap<>();
userMsg.put("role", "user");
userMsg.put("content", query);
history.add(userMsg);
agentLoop(history);
// 打印最终回复
System.out.println("Agent 执行完毕。");
}
}
}
这是 Agent 进化的关键一步:从“只会说话”变成了“真正干活”。
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<String, Object> args) throws Exception;
}
// 3. 注册工具处理逻辑
private static final Map<String, ToolExecutor> 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<Map<String, Object>> messages) {
while (true) {
// ... 省略相同的 LLM 调用、消息追加逻辑
// 4. 执行工具
List<Map<String, Object>> toolResults = 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");
// 路由分发
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 < content.length()) {
return content.substring(0, limit) + "... (truncated)";
}
return content;
}
private static String runWrite(String pathStr, String content) throws IOException {
Path path = safePath(pathStr);
Files.createDirectories(path.getParent());
Files.writeString(path, content);
return "Wrote " + content.length() + " bytes to " + pathStr;
}
private static String runEdit(String pathStr, String oldText, String newText) throws IOException {
Path path = safePath(pathStr);
String content = Files.readString(path);
if (!content.contains(oldText)) {
return "Error: Text not found in " + pathStr;
}
String newContent = content.replace(oldText, newText);
Files.writeString(path, newContent);
return "Edited " + pathStr;
}
}
这段代码引入了一个非常关键的概念:“自我反思与状态管理”。
之前的 Agent 只是单纯的“听指令 -> 干活”,容易干着干着就忘了初衷,或者在复杂的任务中迷失方向。TodoManager 就像是给 Agent 装了一个“记事本”和“监工”。
public class AgentWithTodo {
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
// --- 1. 状态管理:TodoManager ---
// 任务状态枚举
public enum TaskStatus {
PENDING("pending"), IN_PROGRESS("in_progress"), COMPLETED("completed");
public final String label;
TaskStatus(String label) { this.label = label; }
public static TaskStatus fromLabel(String s) {
for (TaskStatus ts : values()) if (ts.label.equals(s)) return ts;
return PENDING;
}
}
// 任务实体
public static class TodoItem {
public String id;
public String text;
public TaskStatus status;
public TodoItem(String id, String text, String status) {
this.id = id; this.text = text; this.status = TaskStatus.fromLabel(status);
}
}
// 管理器类
public static class TodoManager {
private List<TodoItem> items = new ArrayList<>();
public String update(List<Map<String, Object>> newItems) throws Exception {
if (newItems.size() > 20) throw new Exception("Max 20 todos allowed");
List<TodoItem> validated = new ArrayList<>();
int inProgressCount = 0;
for (int i = 0; i < newItems.size(); i++) {
Map<String, Object> item = newItems.get(i);
String text = (String) item.getOrDefault("text", "");
String statusStr = (String) item.getOrDefault("status", "pending");
String id = String.valueOf(item.getOrDefault("id", String.valueOf(i + 1)));
if (text.trim().isEmpty()) throw new Exception("Item " + id + ": text required");
TaskStatus status = TaskStatus.fromLabel(statusStr.toLowerCase());
if (status == TaskStatus.IN_PROGRESS) inProgressCount++;
validated.add(new TodoItem(id, text.trim(), status.label));
}
if (inProgressCount > 1) throw new Exception("Only one task can be in_progress at a time");
this.items = validated;
return render();
}
public String render() {
if (items.isEmpty()) return "No todos.";
StringBuilder sb = new StringBuilder();
for (TodoItem item : items) {
String marker = item.status == TaskStatus.PENDING ? "[ ]" :
item.status == TaskStatus.IN_PROGRESS ? "[>]" : "[x]";
sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text));
}
long done = items.stream().filter(i -> i.status == TaskStatus.COMPLETED).count();
sb.append(String.format("%n(%d/%d completed)", done, items.size()));
return sb.toString();
}
}
private static final TodoManager TODO_MANAGER = new TodoManager();
// --- 2. 工具定义与分发 ---
public enum ToolType {
BASH("bash"), READ_FILE("read_file"), WRITE_FILE("write_file"),
EDIT_FILE("edit_file"), TODO("todo"); // 新增 todo 工具
public final String name;
ToolType(String name) { this.name = name; }
}
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略已有的工具注册
// 注册 Todo 工具
TOOL_HANDLERS.put(ToolType.TODO.name, args -> {
@SuppressWarnings("unchecked")
List<Map<String, Object>> items = (List<Map<String, Object>>) args.get("items");
return TODO_MANAGER.update(items);
});
}
// --- 3. 核心循环 ---
public static void agentLoop(List<Map<String, Object>> messages) {
int roundsSinceTodo = 0; // 新增:跟踪轮数
while (true) {
// ... 省略相同的 LLM 调用、消息追加、停止检查逻辑
// 3. 执行工具
List<Map<String, Object>> toolResults = new ArrayList<>();
List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content");
boolean usedTodo = false; // 新增:标记是否使用了 todo 工具
for (Map<String, Object> block : content) {
if ("tool_use".equals(block.get("type"))) {
// ... 省略相同的工具调用逻辑
String toolName = (String) block.get("name");
// ... 执行工具
if (toolName.equals("todo")) usedTodo = true; // 标记 todo 使用
}
}
// 4. 监工逻辑 (Nag Reminder)
roundsSinceTodo = usedTodo ? 0 : roundsSinceTodo + 1;
if (roundsSinceTodo >= 3) { // 关键:超过3轮没更新就提醒
Map<String, Object> nag = new HashMap<>();
nag.put("type", "text");
nag.put("text", "<reminder>Update your todos.</reminder>");
toolResults.add(0, nag); // 插入到结果列表最前面
System.out.println(">>> 监工提醒:更新待办列表!");
}
// 5. 回传结果
// ... 省略相同的回传逻辑
}
}
// --- 4. 工具实现 (简化版) ---
// ... 省略已有的工具实现
}
子任务污染主对话上下文怎么办?
因此这里主要展示了如何构建一个多智能体系统。
在 原作者的Python代码 里,run_subagent 函数就像一个“虫洞”,把任务传送到一个新的平行宇宙(子线程/子上下文)去执行,执行完只带回结果。
在 Java 中,我们通常通过创建新的类实例来实现这种隔离。父 Agent 和子 Agent 拥有各自独立的 messages 列表,互不干扰。
这里解决了 Agent 开发中的一个核心痛点:上下文窗口限制与知识广度的矛盾。
public class AgentWithSkills {
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
private static final Path SKILLS_DIR = WORKDIR.resolve("skills"); // 新增技能目录
// --- 1. 技能管理:SkillLoader ---
// 技能实体
public static class Skill {
public String name;
public String description;
public String tags;
public String body; // 技能的具体指令内容
public Path path;
}
// 管理器类
public static class SkillLoader {
private final Map<String, Skill> skills = new HashMap<>();
public SkillLoader(Path skillsDir) {
if (Files.exists(skillsDir)) {
loadAll(skillsDir);
}
}
// 扫描所有技能
private void loadAll(Path dir) {
try (var stream = Files.walk(dir)) {
stream.filter(p -> p.getFileName().toString().equals("SKILL.md"))
.forEach(this::parseSkillFile);
} catch (IOException e) {
System.err.println("Error loading skills: " + e.getMessage());
}
}
// 解析单个技能文件
private void parseSkillFile(Path path) {
try {
String content = Files.readString(path);
// 解析 Frontmatter (--- ... ---)
Matcher matcher = Pattern.compile("^---\\n(.*?)\\n---\\n(.*)", Pattern.DOTALL).matcher(content);
Skill skill = new Skill();
skill.path = path;
skill.name = path.getParent().getFileName().toString(); // 默认名称
if (matcher.matches()) {
String metaBlock = matcher.group(1);
skill.body = matcher.group(2).trim();
// 解析 YAML
for (String line : metaBlock.split("\\n")) {
if (line.contains(":")) {
String[] parts = line.split(":", 2);
String key = parts[0].trim();
String val = parts[1].trim();
if ("name".equals(key)) skill.name = val;
if ("description".equals(key)) skill.description = val;
if ("tags".equals(key)) skill.tags = val;
}
}
} else {
skill.body = content.trim(); // 没有 frontmatter
skill.description = "No description";
}
skills.put(skill.name, skill);
} catch (IOException e) {
System.err.println("Failed to parse skill: " + path);
}
}
// Layer 1: 获取简短描述列表
public String getDescriptions() {
if (skills.isEmpty()) return "(no skills available)";
return skills.values().stream()
.map(s -> String.format(" - %s: %s [%s]", s.name, s.description, s.tags != null ? s.tags : ""))
.collect(Collectors.joining("\n"));
}
// Layer 2: 获取完整技能内容
public String getContent(String name) {
Skill skill = skills.get(name);
if (skill == null) {
return "Error: Unknown skill '" + name + "'. Available: " + String.join(", ", skills.keySet());
}
return String.format("<skill name=\"%s\">\n%s\n</skill>", skill.name, skill.body);
}
}
private static final SkillLoader SKILL_LOADER = new SkillLoader(SKILLS_DIR);
// --- 2. 工具定义与分发 ---
public enum ToolType {
BASH("bash"), READ_FILE("read_file"), WRITE_FILE("write_file"),
EDIT_FILE("edit_file"), LOAD_SKILL("load_skill"); // 新增技能加载工具
public final String name;
ToolType(String name) { this.name = name; }
}
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略已有的工具注册
// 注册 load_skill 工具
TOOL_HANDLERS.put(ToolType.LOAD_SKILL.name, args -> SKILL_LOADER.getContent((String) args.get("name")));
}
// --- 3. 核心循环 ---
// Layer 1: 系统提示词注入技能列表
private static final String SYSTEM_PROMPT = String.format(
"You are a coding agent at %s.\n" +
"Use load_skill to access specialized knowledge.\n" +
"Skills available:\n%s",
WORKDIR, SKILL_LOADER.getDescriptions() // 动态注入技能列表
);
public static void agentLoop(List<Map<String, Object>> messages) {
// ... 省略相同的主循环逻辑,但注意 SYSTEM_PROMPT 包含了技能列表
}
// 辅助方法:构建工具定义 JSON
private static List<Map<String, Object>> getToolSpecs() {
List<Map<String, Object>> tools = new ArrayList<>();
// ... 添加基础工具定义
// 添加 load_skill 定义
Map<String, Object> skillTool = new HashMap<>();
skillTool.put("name", "load_skill");
skillTool.put("description", "Load specialized knowledge by name.");
Map<String, Object> schema = new HashMap<>();
schema.put("type", "object");
schema.put("properties", Map.of("name", Map.of("type", "string", "description", "Skill name")));
schema.put("required", Arrays.asList("name"));
skillTool.put("input_schema", schema);
tools.add(skillTool);
return tools;
}
}
对话一长,Token 烧得肉疼。那怎么办,做压缩
public class ContextCompactSystem {
// --- 配置 ---
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
private static final Path TRANSCRIPT_DIR = WORKDIR.resolve(".transcripts"); // 新增:对话存档目录
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
// 压缩参数
private static final int THRESHOLD_TOKENS = 50000; // 触发自动压缩的 token 阈值
private static final int KEEP_RECENT = 3; // 保留的最近工具结果数量
// --- 工具枚举 ---
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."),
COMPACT("compact", "Trigger manual conversation compression."); // 新增:手动压缩工具
public final String name;
public final String description;
ToolType(String name, String description) { this.name = name; this.description = description; }
}
// ... 省略相同的 ToolExecutor 接口和基础工具实现
// --- 三层次压缩系统 ---
/**
* Layer 1: 微观压缩 - 静默替换旧的工具结果
*/
private static List<Map<String, Object>> microCompact(List<Map<String, Object>> messages) {
// 收集所有的 tool_result 条目
List<ToolResultInfo> toolResults = new ArrayList<>();
for (int msgIdx = 0; msgIdx < messages.size(); msgIdx++) {
Map<String, Object> msg = messages.get(msgIdx);
if ("user".equals(msg.get("role"))) {
Object content = msg.get("content");
if (content instanceof List) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> contentList = (List<Map<String, Object>>) content;
for (int partIdx = 0; partIdx < contentList.size(); partIdx++) {
Map<String, Object> part = contentList.get(partIdx);
if ("tool_result".equals(part.get("type"))) {
toolResults.add(new ToolResultInfo(msgIdx, partIdx, part));
}
}
}
}
}
if (toolResults.size() <= KEEP_RECENT) {
return messages;
}
// 从先前的 assistant 消息中映射 tool_use_id 到 tool_name
Map<String, String> toolNameMap = new HashMap<>();
for (Map<String, Object> msg : messages) {
if ("assistant".equals(msg.get("role"))) {
Object content = msg.get("content");
if (content instanceof List) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> contentList = (List<Map<String, Object>>) content;
for (Map<String, Object> block : contentList) {
if ("tool_use".equals(block.get("type"))) {
String toolId = (String) block.get("id");
String toolName = (String) block.get("name");
toolNameMap.put(toolId, toolName);
}
}
}
}
}
// 清除旧的结果(保留最近的 KEEP_RECENT 个)
List<ToolResultInfo> toClear = toolResults.subList(0, toolResults.size() - KEEP_RECENT);
for (ToolResultInfo info : toClear) {
Map<String, Object> result = info.result;
Object content = result.get("content");
if (content instanceof String && ((String) content).length() > 100) {
String toolId = (String) result.get("tool_use_id");
String toolName = toolNameMap.getOrDefault(toolId, "unknown");
result.put("content", "[Previous: used " + toolName + "]"); // 静默替换
}
}
return messages;
}
/**
* Layer 2: 自动压缩 - 保存完整对话并生成摘要
*/
private static List<Map<String, Object>> autoCompact(List<Map<String, Object>> messages) throws IOException {
// 保存完整对话到磁盘
Files.createDirectories(TRANSCRIPT_DIR);
Path transcriptPath = TRANSCRIPT_DIR.resolve("transcript_" + System.currentTimeMillis() + ".jsonl");
try (BufferedWriter writer = Files.newBufferedWriter(transcriptPath)) {
for (Map<String, Object> msg : messages) {
writer.write(gson.toJson(msg));
writer.newLine();
}
}
System.out.println("[transcript saved: " + transcriptPath + "]");
// 调用 LLM 生成摘要
String conversationText = gson.toJson(messages);
if (conversationText.length() > 80000) {
conversationText = conversationText.substring(0, 80000);
}
String summary = simulateLLMSummary(conversationText);
// 用摘要替换整个对话历史
List<Map<String, Object>> compressedMessages = new ArrayList<>();
compressedMessages.add(Map.of(
"role", "user",
"content", "[Conversation compressed. Transcript: " + transcriptPath + "]\n\n" + summary
));
compressedMessages.add(Map.of(
"role", "assistant",
"content", "Understood. I have the context from the summary. Continuing."
));
return compressedMessages;
}
/**
* Layer 3: 手动压缩工具
* 当 Agent 主动调用 compact 工具时触发
*/
private static String handleCompactTool(Map<String, Object> args) {
String focus = (String) args.get("focus");
String focusMsg = focus != null ? " Focus: " + focus : "";
return "Manual compression requested." + focusMsg;
}
/**
* 估算 token 数量
* 简单实现:约 4 个字符对应 1 个 token
*/
private static int estimateTokens(List<Map<String, Object>> messages) {
String messagesStr = gson.toJson(messages);
return messagesStr.length() / 4;
}
// --- 工具处理器映射 ---
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略基础工具注册
TOOL_HANDLERS.put(ToolType.COMPACT.name, ContextCompactSystem::handleCompactTool);
}
// --- Agent 主循环(集成了三层压缩)---
public static void agentLoop(List<Map<String, Object>> messages) {
while (true) {
try {
// Layer 1: 每次调用前进行微观压缩
messages = microCompact(messages);
// Layer 2: 如果 token 数超过阈值,自动压缩
if (estimateTokens(messages) > THRESHOLD_TOKENS) {
System.out.println("[auto_compact triggered]");
messages = autoCompact(messages);
}
// ... 省略相同的 LLM 调用逻辑
boolean manualCompact = false;
for (Map<String, Object> block : content) {
if ("tool_use".equals(block.get("type"))) {
String toolName = (String) block.get("name");
// 检查是否是 compact 工具
if (ToolType.COMPACT.name.equals(toolName)) {
manualCompact = true; // 标记手动压缩
}
// ... 执行工具
}
}
// Layer 3: 如果调用了 compact 工具,执行手动压缩
if (manualCompact) {
System.out.println("[manual compact]");
messages = autoCompact(messages);
}
} catch (Exception e) {
System.err.println("Error in agent loop: " + e.getMessage());
e.printStackTrace();
return;
}
}
}
// --- 辅助类和方法 ---
private static class ToolResultInfo {
int msgIndex;
int partIndex;
Map<String, Object> result;
ToolResultInfo(int msgIndex, int partIndex, Map<String, Object> result) {
this.msgIndex = msgIndex;
this.partIndex = partIndex;
this.result = result;
}
}
}
多个任务之间有依赖关系怎么搞?
public class TaskSystem {
// --- 配置 ---
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
private static final Path TASKS_DIR = WORKDIR.resolve(".tasks"); // 任务存储目录
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
// --- 工具枚举---
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."),
TASK_CREATE("task_create", "Create a new task."), // 新增:创建任务
TASK_GET("task_get", "Get full details of a task by ID."), // 新增:获取任务详情
TASK_UPDATE("task_update", "Update a task's status or dependencies."), // 新增:更新任务
TASK_LIST("task_list", "List all tasks with status summary."); // 新增:列出任务
public final String name;
public final String description;
ToolType(String name, String description) { this.name = name; this.description = description; }
}
// ... 省略相同的 ToolExecutor 接口
// --- 任务管理器 ---
static class TaskManager {
private final Path tasksDir;
private int nextId = 1;
public TaskManager(Path tasksDir) throws IOException {
this.tasksDir = tasksDir;
Files.createDirectories(tasksDir);
this.nextId = getMaxId() + 1; // 自动计算下一个ID
}
private int getMaxId() {
// 扫描已有任务文件,找到最大ID
try {
return Files.list(tasksDir)
.filter(p -> p.getFileName().toString().startsWith("task_"))
.map(p -> {
try {
String name = p.getFileName().toString();
return Integer.parseInt(name.substring(5, name.length() - 5)); // task_xxx.json
} catch (NumberFormatException e) {
return 0;
}
})
.max(Integer::compare)
.orElse(0);
} catch (IOException e) {
return 0;
}
}
private Map<String, Object> loadTask(int taskId) throws IOException {
Path path = tasksDir.resolve("task_" + taskId + ".json");
if (!Files.exists(path)) {
throw new IllegalArgumentException("Task " + taskId + " not found");
}
String content = Files.readString(path);
Type type = new TypeToken<Map<String, Object>>(){}.getType();
return gson.fromJson(content, type);
}
private void saveTask(Map<String, Object> task) throws IOException {
int id = ((Double) task.get("id")).intValue();
Path path = tasksDir.resolve("task_" + id + ".json");
Files.writeString(path, gson.toJson(task)); // JSON格式存储
}
public String createTask(String subject, String description) throws IOException {
Map<String, Object> task = new LinkedHashMap<>();
task.put("id", nextId);
task.put("subject", subject);
task.put("description", description != null ? description : "");
task.put("status", "pending"); // 默认状态
task.put("blockedBy", new ArrayList<Integer>()); // 被哪些任务阻塞
task.put("blocks", new ArrayList<Integer>()); // 阻塞哪些任务
task.put("owner", ""); // 任务负责人
saveTask(task);
nextId++;
return gson.toJson(task);
}
public String getTask(int taskId) throws IOException {
return gson.toJson(loadTask(taskId));
}
public String updateTask(int taskId, String status,
List<Integer> addBlockedBy, List<Integer> addBlocks) throws IOException {
Map<String, Object> task = loadTask(taskId);
if (status != null) {
if (!Arrays.asList("pending", "in_progress", "completed").contains(status)) {
throw new IllegalArgumentException("Invalid status: " + status);
}
task.put("status", status);
// 任务完成时,从其他任务的 blockedBy 中移除
if ("completed".equals(status)) {
clearDependency(taskId);
}
}
if (addBlockedBy != null && !addBlockedBy.isEmpty()) {
@SuppressWarnings("unchecked")
List<Integer> currentBlockedBy = (List<Integer>) task.get("blockedBy");
List<Integer> newList = new ArrayList<>(currentBlockedBy);
newList.addAll(addBlockedBy);
task.put("blockedBy", newList.stream().distinct().collect(Collectors.toList()));
}
if (addBlocks != null && !addBlocks.isEmpty()) {
@SuppressWarnings("unchecked")
List<Integer> currentBlocks = (List<Integer>) task.get("blocks");
List<Integer> newList = new ArrayList<>(currentBlocks);
newList.addAll(addBlocks);
List<Integer> distinctBlocks = newList.stream().distinct().collect(Collectors.toList());
task.put("blocks", distinctBlocks);
// 双向更新:更新被阻塞任务的 blockedBy 列表
for (int blockedId : distinctBlocks) {
try {
Map<String, Object> blockedTask = loadTask(blockedId);
@SuppressWarnings("unchecked")
List<Integer> blockedByList = (List<Integer>) blockedTask.get("blockedBy");
if (!blockedByList.contains(taskId)) {
blockedByList.add(taskId);
saveTask(blockedTask);
}
} catch (Exception e) {
// 忽略不存在的任务
}
}
}
saveTask(task);
return gson.toJson(task);
}
private void clearDependency(int completedId) throws IOException {
Files.list(tasksDir)
.filter(p -> p.getFileName().toString().endsWith(".json"))
.forEach(p -> {
try {
String content = Files.readString(p);
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> task = gson.fromJson(content, type);
@SuppressWarnings("unchecked")
List<Integer> blockedBy = (List<Integer>) task.get("blockedBy");
if (blockedBy != null && blockedBy.contains(completedId)) {
blockedBy.remove(Integer.valueOf(completedId));
Files.writeString(p, gson.toJson(task));
}
} catch (IOException e) {
// 忽略读取错误
}
});
}
public String listAllTasks() throws IOException {
List<Map<String, Object>> tasks = new ArrayList<>();
Files.list(tasksDir)
.filter(p -> p.getFileName().toString().endsWith(".json"))
.sorted()
.forEach(p -> {
try {
String content = Files.readString(p);
Type type = new TypeToken<Map<String, Object>>(){}.getType();
tasks.add(gson.fromJson(content, type));
} catch (IOException e) {
// 忽略错误
}
});
if (tasks.isEmpty()) {
return "No tasks.";
}
StringBuilder sb = new StringBuilder();
for (Map<String, Object> task : tasks) {
String status = (String) task.get("status");
String marker = switch(status) {
case "pending" -> "[ ]";
case "in_progress" -> "[>]";
case "completed" -> "[x]";
default -> "[?]";
};
int id = ((Double) task.get("id")).intValue();
String subject = (String) task.get("subject");
@SuppressWarnings("unchecked")
List<Integer> blockedBy = (List<Integer>) task.get("blockedBy");
String blockedStr = (blockedBy != null && !blockedBy.isEmpty())
? " (blocked by: " + blockedBy + ")"
: "";
sb.append(String.format("%s #%d: %s%s\n", marker, id, subject, blockedStr));
}
return sb.toString().trim();
}
}
// --- 工具处理器映射 ---
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// 初始化任务管理器
TaskManager taskManager;
try {
taskManager = new TaskManager(TASKS_DIR);
} catch (IOException e) {
throw new RuntimeException("Failed to initialize task manager", e);
}
// ... 省略基础工具注册
// 注册 Task Create 工具
TOOL_HANDLERS.put(ToolType.TASK_CREATE.name, args -> {
String subject = (String) args.get("subject");
String description = (String) args.get("description");
return taskManager.createTask(subject, description);
});
// 注册 Task Get 工具
TOOL_HANDLERS.put(ToolType.TASK_GET.name, args -> {
int taskId = ((Number) args.get("task_id")).intValue();
return taskManager.getTask(taskId);
});
// 注册 Task Update 工具
TOOL_HANDLERS.put(ToolType.TASK_UPDATE.name, args -> {
int taskId = ((Number) args.get("task_id")).intValue();
String status = (String) args.get("status");
@SuppressWarnings("unchecked")
List<Integer> addBlockedBy = (List<Integer>) args.get("addBlockedBy");
@SuppressWarnings("unchecked")
List<Integer> addBlocks = (List<Integer>) args.get("addBlocks");
return taskManager.updateTask(taskId, status, addBlockedBy, addBlocks);
});
// 注册 Task List 工具
TOOL_HANDLERS.put(ToolType.TASK_LIST.name, args -> {
return taskManager.listAllTasks();
});
}
// ... 省略相同的工具实现和主循环
}
有些操作很慢,Agent 不能干等着。例如长时间编译/构建:make, mvn compile, gradle build 或 大数据处理:hadoop, spark-submit 等的一些工作
public class BackgroundTasksSystem {
// --- 配置 ---
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
// --- 后台任务管理器 ---
static class BackgroundManager {
// 任务存储
private final Map<String, TaskInfo> tasks = new ConcurrentHashMap<>();
// 通知队列
private final Queue<TaskNotification> notificationQueue = new ConcurrentLinkedQueue<>();
// 任务 ID 生成器
private final AtomicInteger taskIdCounter = new AtomicInteger(1);
// 锁
private final Object lock = new Object();
static class TaskInfo {
String taskId;
String status; // running, completed, timeout, error
String result;
String command;
long startTime;
Thread thread; // 关联的执行线程
}
static class TaskNotification {
String taskId;
String status;
String command;
String result;
}
/**
* 启动后台任务
* 立即返回任务 ID,不等待命令完成
*/
public String run(String command) {
String taskId = "task_" + taskIdCounter.getAndIncrement();
TaskInfo task = new TaskInfo(taskId, command);
tasks.put(taskId, task);
// 创建并启动后台线程
Thread thread = new Thread(() -> executeTask(task), "BackgroundTask-" + taskId);
thread.setDaemon(true);
task.thread = thread;
thread.start(); // 立即返回,不阻塞
return String.format("Background task %s started: %s",
taskId, command.substring(0, Math.min(command.length(), 80)));
}
/**
* 线程目标:执行子进程,捕获输出,推送结果到队列
*/
private void executeTask(TaskInfo task) {
String output;
String status;
try {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", task.command);
pb.directory(WORKDIR.toFile());
pb.redirectErrorStream(true);
Process process = pb.start();
boolean finished = process.waitFor(300, TimeUnit.SECONDS); // 5分钟超时
if (!finished) {
process.destroy();
output = "Error: Timeout (300s)";
status = "timeout";
} else {
output = new String(process.getInputStream().readAllBytes()).trim();
status = "completed";
}
} catch (Exception e) {
output = "Error: " + e.getMessage();
status = "error";
}
// 更新任务状态
task.status = status;
task.result = output.isEmpty() ? "(no output)" :
output.substring(0, Math.min(output.length(), 50000));
// 添加通知到队列
synchronized (lock) {
notificationQueue.offer(new TaskNotification(
task.taskId,
status,
task.command.substring(0, Math.min(task.command.length(), 80)),
task.result.substring(0, Math.min(task.result.length(), 500))
));
}
}
/**
* 检查任务状态
* 如果指定 taskId,检查单个任务;否则列出所有任务
*/
public String check(String taskId) {
if (taskId != null && !taskId.isEmpty()) {
TaskInfo task = tasks.get(taskId);
if (task == null) {
return "Error: Unknown task " + taskId;
}
return String.format("[%s] %s\n%s",
task.status,
task.command.substring(0, Math.min(task.command.length(), 60)),
task.result != null ? task.result : "(running)");
} else {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, TaskInfo> entry : tasks.entrySet()) {
TaskInfo task = entry.getValue();
sb.append(String.format("%s: [%s] %s\n",
task.taskId,
task.status,
task.command.substring(0, Math.min(task.command.length(), 60))));
}
return sb.length() > 0 ? sb.toString().trim() : "No background tasks.";
}
}
/**
* 清空通知队列并返回所有待处理的通知
*/
public List<TaskNotification> drainNotifications() {
synchronized (lock) {
List<TaskNotification> notifications = new ArrayList<>();
while (!notificationQueue.isEmpty()) {
notifications.add(notificationQueue.poll());
}
return notifications;
}
}
/**
* 获取所有任务
*/
public Map<String, TaskInfo> getAllTasks() {
return new HashMap<>(tasks);
}
}
// 初始化后台管理器
private static final BackgroundManager BG_MANAGER = new BackgroundManager();
// --- 工具枚举 ---
public enum ToolType {
BASH("bash", "Run a shell command (blocking)."),
READ_FILE("read_file", "Read file contents."),
WRITE_FILE("write_file", "Write content to file."),
EDIT_FILE("edit_file", "Replace exact text in file."),
BACKGROUND_RUN("background_run", "Run command in background thread. Returns task_id immediately."), // 新增
CHECK_BACKGROUND("check_background", "Check background task status. Omit task_id to list all."); // 新增
public final String name;
public final String description;
ToolType(String name, String description) { this.name = name; this.description = description; }
}
// --- 工具处理器映射 ---
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略基础工具注册
// 后台任务工具
TOOL_HANDLERS.put(ToolType.BACKGROUND_RUN.name, args -> {
String command = (String) args.get("command");
return BG_MANAGER.run(command);
});
TOOL_HANDLERS.put(ToolType.CHECK_BACKGROUND.name, args -> {
String taskId = (String) args.get("task_id");
return BG_MANAGER.check(taskId);
});
}
// ... 省略相同的工具实现
// --- Agent 主循环(集成后台任务通知)---
public static void agentLoop(List<Map<String, Object>> messages) {
while (true) {
try {
// 在 LLM 调用前检查后台通知
List<BackgroundManager.TaskNotification> notifications = BG_MANAGER.drainNotifications();
if (!notifications.isEmpty() && !messages.isEmpty()) {
StringBuilder notifText = new StringBuilder();
notifText.append("<background-results>\n");
for (BackgroundManager.TaskNotification notif : notifications) {
notifText.append(String.format("[bg:%s] %s: %s\n",
notif.taskId, notif.status, notif.result));
}
notifText.append("</background-results>");
messages.add(Map.of(
"role", "user",
"content", notifText.toString()
));
messages.add(Map.of(
"role", "assistant",
"content", "Noted background results."
));
// 异步结果注入:将后台任务结果插入到对话中
// 结构化格式:用XML标签包裹,便于LLM解析
}
// 显示当前活动任务
Map<String, BackgroundManager.TaskInfo> activeTasks = BG_MANAGER.getAllTasks();
int runningTasks = (int) activeTasks.values().stream()
.filter(t -> "running".equals(t.status))
.count();
if (runningTasks > 0) {
System.out.printf("[Active background tasks: %d]\n", runningTasks);
}
// ... 省略相同的 LLM 调用和工具执行逻辑
} catch (Exception e) {
System.err.println("Error in agent loop: " + e.getMessage());
e.printStackTrace();
return;
}
}
}
}
一个 Agent 干不完怎么办?
public class AgentTeamsSystem {
// --- 配置 ---
private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
private static final Path TEAM_DIR = WORKDIR.resolve(".team");
private static final Path INBOX_DIR = TEAM_DIR.resolve("inbox");
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
// 有效消息类型
private static final Set<String> VALID_MSG_TYPES = Set.of(
"message", "broadcast", "shutdown_request",
"shutdown_response", "plan_approval_response"
);
// --- 消息系统(MessageBus)---
static class MessageBus {
private final Path inboxDir;
public MessageBus(Path inboxDir) {
this.inboxDir = inboxDir;
try {
Files.createDirectories(inboxDir);
} catch (IOException e) {
throw new RuntimeException("Failed to create inbox directory", e);
}
}
/**
* 发送消息到指定智能体
*/
public String send(String sender, String to, String content,
String msgType, Map<String, Object> extra) {
if (!VALID_MSG_TYPES.contains(msgType)) {
return String.format("Error: Invalid type '%s'. Valid: %s",
msgType, String.join(", ", VALID_MSG_TYPES));
}
Map<String, Object> message = new LinkedHashMap<>();
message.put("type", msgType);
message.put("from", sender);
message.put("content", content);
message.put("timestamp", System.currentTimeMillis() / 1000.0);
if (extra != null) {
message.putAll(extra);
}
Path inboxPath = inboxDir.resolve(to + ".jsonl");
try {
String jsonLine = gson.toJson(message) + "\n";
Files.writeString(inboxPath, jsonLine,
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
return String.format("Sent %s to %s", msgType, to);
} catch (IOException e) {
return "Error: " + e.getMessage();
}
}
/**
* 读取并清空邮箱
*/
public List<Map<String, Object>> readInbox(String name) {
Path inboxPath = inboxDir.resolve(name + ".jsonl");
if (!Files.exists(inboxPath)) {
return new ArrayList<>();
}
try {
List<Map<String, Object>> messages = new ArrayList<>();
List<String> lines = Files.readAllLines(inboxPath);
for (String line : lines) {
if (!line.trim().isEmpty()) {
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> message = gson.fromJson(line, type);
messages.add(message);
}
}
// 清空邮箱(消费模式)
Files.writeString(inboxPath, "");
return messages;
} catch (IOException e) {
return new ArrayList<>();
}
}
/**
* 广播消息到所有队友
*/
public String broadcast(String sender, String content, List<String> teammates) {
int count = 0;
for (String name : teammates) {
if (!name.equals(sender)) {
send(sender, name, content, "broadcast");
count++;
}
}
return String.format("Broadcast to %d teammates", count);
}
}
// 初始化消息总线
private static final MessageBus BUS = new MessageBus(INBOX_DIR);
// --- 智能体管理器(TeammateManager)---
static class TeammateManager {
private final Path teamDir;
private final Path configPath;
private Map<String, Object> config;
private final Map<String, Thread> threads = new ConcurrentHashMap<>();
private final Map<String, AtomicBoolean> stopFlags = new ConcurrentHashMap<>();
public TeammateManager(Path teamDir) {
this.teamDir = teamDir;
this.configPath = teamDir.resolve("config.json");
loadConfig();
}
@SuppressWarnings("unchecked")
private void loadConfig() {
try {
if (Files.exists(configPath)) {
String content = Files.readString(configPath);
Type type = new TypeToken<Map<String, Object>>(){}.getType();
this.config = gson.fromJson(content, type);
} else {
this.config = new HashMap<>();
config.put("team_name", "default");
config.put("members", new ArrayList<Map<String, Object>>());
saveConfig();
}
} catch (IOException e) {
throw new RuntimeException("Failed to load team config", e);
}
}
@SuppressWarnings("unchecked")
public String spawn(String name, String role, String prompt) {
Map<String, Object> member = findMember(name);
if (member != null) {
String status = (String) member.get("status");
if (!"idle".equals(status) && !"shutdown".equals(status)) {
return String.format("Error: '%s' is currently %s", name, status);
}
member.put("status", "working");
member.put("role", role);
} else {
member = new LinkedHashMap<>();
member.put("name", name);
member.put("role", role);
member.put("status", "working");
((List<Map<String, Object>>) config.get("members")).add(member);
}
saveConfig();
// 停止之前的线程(如果存在)
if (threads.containsKey(name)) {
stopFlags.get(name).set(true);
try {
threads.get(name).join(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 创建新的停止标志
AtomicBoolean stopFlag = new AtomicBoolean(false);
stopFlags.put(name, stopFlag);
// 创建并启动新线程
Thread thread = new Thread(() -> teammateLoop(name, role, prompt, stopFlag),
"Teammate-" + name);
thread.setDaemon(true);
threads.put(name, thread);
thread.start();
return String.format("Spawned '%s' (role: %s)", name, role);
}
private void teammateLoop(String name, String role, String prompt, AtomicBoolean stopFlag) {
String systemPrompt = String.format(
"You are '%s', role: %s, at %s. " +
"Use send_message to communicate. Complete your task.",
name, role, WORKDIR
);
List<Map<String, Object>> messages = new ArrayList<>();
messages.add(Map.of("role", "user", "content", prompt));
// 最大迭代次数限制
for (int i = 0; i < 50 && !stopFlag.get(); i++) {
try {
// 检查邮箱
List<Map<String, Object>> inbox = BUS.readInbox(name);
for (Map<String, Object> msg : inbox) {
messages.add(Map.of("role", "user", "content", gson.toJson(msg)));
}
// 模拟调用 LLM
Map<String, Object> response = simulateTeammateLLMCall(systemPrompt, messages, name);
if (response == null || "end_turn".equals(response.get("stop_reason"))) {
break;
}
// ... 执行工具调用
// 短暂休眠,避免 CPU 过度使用
Thread.sleep(100);
} catch (Exception e) {
System.err.printf("[%s] Error: %s%n", name, e.getMessage());
break;
}
}
// 更新状态
@SuppressWarnings("unchecked")
Map<String, Object> member = findMember(name);
if (member != null && !"shutdown".equals(member.get("status"))) {
member.put("status", "idle");
saveConfig();
}
threads.remove(name);
stopFlags.remove(name);
}
@SuppressWarnings("unchecked")
public String listAll() {
List<Map<String, Object>> members = (List<Map<String, Object>>) config.get("members");
if (members.isEmpty()) {
return "No teammates.";
}
StringBuilder sb = new StringBuilder();
sb.append("Team: ").append(config.get("team_name")).append("\n");
for (Map<String, Object> member : members) {
sb.append(String.format(" %s (%s): %s%n",
member.get("name"),
member.get("role"),
member.get("status")
));
}
return sb.toString().trim();
}
@SuppressWarnings("unchecked")
public List<String> memberNames() {
List<Map<String, Object>> members = (List<Map<String, Object>>) config.get("members");
return members.stream()
.map(m -> (String) m.get("name"))
.collect(Collectors.toList());
}
/**
* 获取活动成员数量
*/
@SuppressWarnings("unchecked")
public int getActiveCount() {
List<Map<String, Object>> members = (List<Map<String, Object>>) config.get("members");
int count = 0;
for (Map<String, Object> member : members) {
if ("working".equals(member.get("status"))) {
count++;
}
}
return count;
}
}
// 初始化智能体管理器
private static final TeammateManager TEAM_MANAGER = new TeammateManager(TEAM_DIR);
// --- 工具枚举 ---
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."),
SPAWN_TEAMMATE("spawn_teammate", "Spawn a persistent teammate that runs in its own thread."), // 新增
LIST_TEAMMATES("list_teammates", "List all teammates with name, role, status."), // 新增
SEND_MESSAGE("send_message", "Send a message to a teammate's inbox."), // 新增
READ_INBOX("read_inbox", "Read and drain the lead's inbox."), // 新增
BROADCAST("broadcast", "Send a message to all teammates."); // 新增
public final String name;
public final String description;
ToolType(String name, String description) { this.name = name; this.description = description; }
}
// --- 工具处理器映射 ---
private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略基础工具注册
// 团队管理工具
TOOL_HANDLERS.put(ToolType.SPAWN_TEAMMATE.name, args -> {
String name = (String) args.get("name");
String role = (String) args.get("role");
String prompt = (String) args.get("prompt");
return TEAM_MANAGER.spawn(name, role, prompt);
});
TOOL_HANDLERS.put(ToolType.LIST_TEAMMATES.name, args -> {
return TEAM_MANAGER.listAll();
});
TOOL_HANDLERS.put(ToolType.SEND_MESSAGE.name, args -> {
String to = (String) args.get("to");
String content = (String) args.get("content");
String msgType = (String) args.get("msg_type");
if (msgType == null) msgType = "message";
return BUS.send("lead", to, content, msgType);
});
TOOL_HANDLERS.put(ToolType.READ_INBOX.name, args -> {
List<Map<String, Object>> inbox = BUS.readInbox("lead");
return gson.toJson(inbox);
});
TOOL_HANDLERS.put(ToolType.BROADCAST.name, args -> {
String content = (String) args.get("content");
return BUS.broadcast("lead", content, TEAM_MANAGER.memberNames());
});
}
// --- Agent 主循环(领导智能体)---
public static void agentLoop(List<Map<String, Object>> messages) {
while (true) {
try {
// 检查领导邮箱
List<Map<String, Object>> inbox = BUS.readInbox("lead");
if (!inbox.isEmpty()) {
String inboxJson = gson.toJson(inbox);
messages.add(Map.of(
"role", "user",
"content", "<inbox>" + inboxJson + "</inbox>"
));
messages.add(Map.of(
"role", "assistant",
"content", "Noted inbox messages."
));
// 邮箱自动注入:自动检查并注入收到的消息
// 结构化格式:用XML标签包裹,便于LLM解析
}
// 显示团队状态
int activeCount = TEAM_MANAGER.getActiveCount();
if (activeCount > 0) {
System.out.printf("[Active teammates: %d]%n", activeCount);
}
// ... 省略相同的 LLM 调用和工具执行逻辑
} catch (Exception e) {
System.err.println("Error in agent loop: " + e.getMessage());
e.printStackTrace();
return;
}
}
}
}