S09: Agent Teams (智能体团队)
“任务太大一个人干不完, 要能分给队友” – 持久化队友 + JSONL 邮箱。
“任务太大一个人干不完, 要能分给队友” – 持久化队友 + JSONL 邮箱。
问题
子智能体 (s04) 是一次性的: 生成、干活、返回摘要、消亡。没有身份, 没有跨调用的记忆。后台任务 (s08) 能跑 shell 命令, 但做不了 LLM 引导的决策。
真正的团队协作需要三样东西:
(1) 能跨多轮对话存活的持久智能体;
(2) 身份和生命周期管理;
(3) 智能体之间的通信通道。
解决方案

工作原理
TeammateManager 通过 config.json 维护团队名册。
1 | `public TeammateManagerS09(Path teamDir, MessageBus messageBus) {``this.dir = teamDir;``this.messageBus = messageBus;``this.configPath = dir.resolve("config.json");``this.objectMapper = new ObjectMapper();``this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);``this.executorService = Executors.newFixedThreadPool(10);````try {``Files.createDirectories(dir);``this.config = loadConfig();``} catch (IOException e) {``throw new RuntimeException("Failed to initialize TeammateManager", e);``}````toolHandlers = Map.of(``"bash", new TeammateToolWrapper<>(EnhancedBashExecutor::runBash, EnhancedBashExecutor.BashCommand.class, this),``"readFile", new TeammateToolWrapper<>(PathUtils::runRead, PathUtils.ReadCommand.class, this),``"writeFile", new TeammateToolWrapper<>(PathUtils::runWrite, PathUtils.WriteCommand.class, this),``"editFile", new TeammateToolWrapper<>(PathUtils::runEdit, PathUtils.EditCommand.class, this),``"sendMessage", new TeammateToolWrapper<>(messageBus::send, MessageBus.SendCommand.class, this),``"readInbox", new TeammateToolWrapper<>(arg -> {``try {``return objectMapper.writeValueAsString(messageBus.readInbox(arg));``} catch (JsonProcessingException e) {``return "{}";``}``}, MessageBus.ReadInboxCommand.class, this)``);``}` |
spawn() 创建队友并在线程中启动 agent loop。
1 | `public String spawn(SpawnCommand command) {``var memberOptional = this.findMember(command.name());``if (memberOptional.isPresent()) {``var member = memberOptional.get();``if (!member.getStatus().equals("idle") && !member.getStatus().equals("shutdown")) {``return String.format("Error: '%s' is currently %s", member.getName(), member.getStatus());``}``member.setStatus("working");``member.setRole(command.role());``} else {``var member = new Member(command.name(), command.role(), "working");``this.config.members().add(member);``}``this.saveConfig();``this.executorService.execute(() -> this.teammateLoop(command));``return String.format("Spawned '%s' (role: %s)", command.name(), command.role());``}` |
MessageBus: append-only 的 JSONL 收件箱。
send()追加一行;read_inbox()读取全部并清空。
1 | `public MessageBus(Path inboxDir) {``this.dir = inboxDir;``try {``Files.createDirectories(dir);``} catch (IOException e) {``throw new RuntimeException(e);``}``this.objectMapper = new ObjectMapper();``}````/**``* Send a message to a specific teammate``*/``public String send(SendCommand command) {``if (!VALID_MSG_TYPES.contains(command.getMsgType())) {``return String.format("Error: Invalid type '%s'. Valid: %s", command.getMsgType(), VALID_MSG_TYPES);``}````Map<String, Object> message = new HashMap<>();``message.put("type", command.getMsgType());``message.put("from", command.getSender());``message.put("content", command.getContent());``message.put("timestamp", Instant.now().toEpochMilli() / 1000);````if (command.getExtra() != null) {``message.putAll(command.getExtra());``}````Path inboxPath = dir.resolve(command.getTo() + ".jsonl");````try (BufferedWriter writer = Files.newBufferedWriter(inboxPath,``StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {``writer.write(objectMapper.writeValueAsString(message));``writer.newLine();``return String.format("Sent %s to %s", command.getMsgType(), command.getTo());``} catch (IOException e) {``return String.format("Error sending message: %s", e.getMessage());``}``}``````/**``* Read all messages from a teammate's inbox``*/``public List<Map<String, Object>> readInbox(ReadInboxCommand command) {``Path inboxPath = dir.resolve(command.getSender() + ".jsonl");````if (!Files.exists(inboxPath)) {``return new ArrayList<>();``}````List<Map<String, Object>> messages = new ArrayList<>();````try {``List<String> lines = Files.readAllLines(inboxPath);````for (String line : lines) {``if (!line.trim().isEmpty()) {``try {``var message = objectMapper.readValue(line, new TypeReference<Map<String, Object>>() {``});``messages.add(message);``} catch (Exception e) {``System.err.println("Error parsing message: " + e.getMessage());``}``}``}````Files.write(inboxPath, new byte[0], StandardOpenOption.TRUNCATE_EXISTING);````} catch (IOException e) {``System.err.println("Error reading inbox: " + e.getMessage());``}````return messages;``}` |
每个队友在每次 LLM 调用前检查收件箱, 将消息注入上下文。
1 | `public void teammateLoop(SpawnCommand command) {``var sysPrompt = String.format("You are '%s', role: %s, at %s. \nUse send_message to communicate. Complete your task.",``command.name(), command.role(), WORKDIR);``var messages = new ArrayList<MessageParam>();``messages.add(MessageParam.builder().role(MessageParam.Role.USER).content(command.prompt()).build());``for (int i = 0; i < 50; i++) {``var inbox = this.messageBus.readInbox(new MessageBus.ReadInboxCommand(command.name()));``try {``for (Map<String, Object> msg : inbox) {``messages.addLast(MessageParam.builder().role(MessageParam.Role.USER).content(objectMapper.writeValueAsString(msg)).build());``}``var paramsBuilder = MessageCreateParams.builder()``.model(MODEL).system(sysPrompt)``.messages(messages)``.tools(TOOLS).maxTokens(8000);````var response = CLIENT.messages().create(paramsBuilder.build());``addAssistants(messages, response);````// If the model didn't call a tool, we're done``if (!response.stopReason().orElse(StopReason.END_TURN).equals(StopReason.TOOL_USE)) {``break;``}````List<ContentBlockParam> results = new ArrayList<>();``for (ContentBlock contentBlock : response.content()) {``if (contentBlock.isToolUse()) {``var toolUseBlock = contentBlock.toolUse().orElseThrow();``var handler = toolHandlers.get(toolUseBlock.name());``var output = "";``if (null == handler) {``output = "Unknown tool: " + toolUseBlock.name();``} else {``output = handler.executeCommand(command.name(), toolUseBlock._input());``IO.println(String.format("> %s: %s}", toolUseBlock.name(), output.substring(0, Math.min(output.length(), 200)).trim()));``}``results.add(``ContentBlockParam.ofToolResult(``ToolResultBlockParam.builder()``.toolUseId(toolUseBlock.id())``.content(output)``.build()``)``);``}``}``messages.add(``MessageParam.builder()``.role(MessageParam.Role.USER)``.content(MessageParam.Content.ofBlockParams(results))``.build()``);````} catch (JsonProcessingException e) {``break;``}````}````var member = this.findMember(command.name());``if (member.isPresent() && !member.get().getStatus().equals("shutdown")) {``member.get().setStatus("idle");``this.saveConfig();``}``}` |
试一试
如何使用,请参考项目中的README中的链接进行配置。
https://github.com/EvanMi/learn-claude-code-java
使用JDK21以上运行S09AgentTeams即可。
试试这些 prompt (英文 prompt 对 LLM 效果更好, 也可以用中文):
Spawn alice (coder) and bob (tester). Have alice send bob a message.Broadcast "status update: phase 1 complete" to all teammatesCheck the lead inbox for any messages- 输入
/team查看团队名册和状态 - 输入
/inbox手动检查领导的收件箱
💬 本文评论区已开启,但暂无读者留言。
本文转载自微信公众号,如有侵权请联系删除。
- 标题: S09: Agent Teams (智能体团队)
- 作者: lxiol
- 创建于 : 2026-06-15 19:09:58
- 更新于 : 2026-07-07 19:48:16
- 链接: https://blog.lxiol.cn/2026/06/15/S09-Agent-Teams-智能体团队/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。