docs: update docs.json and versions.json
This commit is contained in:
1 parent
2afa4712cb
commit
1ecef9dc3a
1 file changed
+91
-91
@@ -170,7 +170,7 @@
|
||||
"filename": "S04Subagent.java",
|
||||
"title": "Subagents",
|
||||
"subtitle": "Clean Context Per Subtask",
|
||||
"loc": 88,
|
||||
"loc": 92,
|
||||
"tools": [
|
||||
"task"
|
||||
],
|
||||
@@ -188,7 +188,7 @@
|
||||
{
|
||||
"name": "SubagentTool",
|
||||
"startLine": 101,
|
||||
"endLine": 150
|
||||
"endLine": 154
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -230,26 +230,26 @@
|
||||
{
|
||||
"name": "BashTool",
|
||||
"signature": "new BashTool(),",
|
||||
"startLine": 133
|
||||
"startLine": 135
|
||||
},
|
||||
{
|
||||
"name": "ReadFileTool",
|
||||
"signature": "new ReadFileTool(),",
|
||||
"startLine": 134
|
||||
"startLine": 136
|
||||
},
|
||||
{
|
||||
"name": "WriteFileTool",
|
||||
"signature": "new WriteFileTool(),",
|
||||
"startLine": 135
|
||||
"startLine": 137
|
||||
},
|
||||
{
|
||||
"name": "EditFileTool",
|
||||
"signature": "new EditFileTool()",
|
||||
"startLine": 136
|
||||
"startLine": 138
|
||||
}
|
||||
],
|
||||
"layer": "planning",
|
||||
"source": "// === S04Subagent.java ===\npackage io.mybatis.learn.s04;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n/**\r\n * S04 - 子 Agent:上下文隔离,保护主 Agent 的思维清晰。\r\n * <p>\r\n * 格言: \"大任务拆小, 每个小任务干净的上下文\"\r\n * <p>\r\n * 核心模式:\r\n * <pre>\r\n * Parent agent Subagent\r\n * +------------------+ +------------------+\r\n * | messages=[...] | | messages=[] | ← fresh\r\n * | | dispatch | |\r\n * | tool: task | --------> | ChatClient.call |\r\n * | prompt=\"...\" | | execute tools |\r\n * | | summary | |\r\n * | result = \"...\" | <-------- | return last text |\r\n * +------------------+ +------------------+\r\n * |\r\n * Parent context stays clean.\r\n * Subagent context is discarded.\r\n * </pre>\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s04_subagent.py}。\r\n * Python 版手动创建空的 messages 列表实现隔离。\r\n * Spring AI 通过创建独立的 {@link ChatClient} 实例实现相同效果 —— 更加自然。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S04Subagent implements CommandLineRunner {\r\n\r\n private final ChatClient chatClient;\r\n\r\n public S04Subagent(ChatModel chatModel) {\r\n this.chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(\"You are a coding agent at \" + System.getProperty(\"user.dir\")\r\n + \". Use the task tool to delegate exploration or subtasks.\")\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n new SubagentTool(chatModel) // 父 Agent 独有的 task 工具\r\n )\r\n .build();\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n AgentRunner.interactive(\"s04\", userMessage ->\r\n chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content()\r\n );\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S04Subagent.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === SubagentTool.java ===\npackage io.mybatis.learn.s04;\r\n\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\n/**\r\n * 子 Agent 工具 —— 生成具有独立上下文的子 Agent 执行任务。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s04_subagent.py} 中的 {@code run_subagent(prompt)} 函数。\r\n * Python 版创建 {@code sub_messages = []} 实现上下文隔离,\r\n * Spring AI 通过创建全新的 {@link ChatClient} 实例实现相同效果。\r\n * 子 Agent 获得基础工具但没有 task 工具(防止递归生成)。\r\n * 只有最终文本返回给父 Agent,子 Agent 的对话历史被完全丢弃。\r\n */\r\npublic class SubagentTool {\r\n private static final Logger log = LoggerFactory.getLogger(SubagentTool.class);\r\n\r\n private final ChatModel chatModel;\r\n private final String workDir;\r\n\r\n public SubagentTool(ChatModel chatModel) {\r\n this.chatModel = chatModel;\r\n this.workDir = System.getProperty(\"user.dir\");\r\n }\r\n\r\n /**\r\n * TIP: 对应 Python {@code run_subagent(prompt)}。\r\n * Python 版在独立线程中运行子 Agent 的 while 循环(最多30次迭代)。\r\n * Spring AI 的 ChatClient.call() 内部管理循环,无需手动限制迭代次数。\r\n */\r\n @Tool(description = \"Spawn a subagent with fresh context. \"\rLine truncated
|
||||
"source": "// === S04Subagent.java ===\npackage io.mybatis.learn.s04;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n/**\r\n * S04 - 子 Agent:上下文隔离,保护主 Agent 的思维清晰。\r\n * <p>\r\n * 格言: \"大任务拆小, 每个小任务干净的上下文\"\r\n * <p>\r\n * 核心模式:\r\n * <pre>\r\n * Parent agent Subagent\r\n * +------------------+ +------------------+\r\n * | messages=[...] | | messages=[] | ← fresh\r\n * | | dispatch | |\r\n * | tool: task | --------> | ChatClient.call |\r\n * | prompt=\"...\" | | execute tools |\r\n * | | summary | |\r\n * | result = \"...\" | <-------- | return last text |\r\n * +------------------+ +------------------+\r\n * |\r\n * Parent context stays clean.\r\n * Subagent context is discarded.\r\n * </pre>\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s04_subagent.py}。\r\n * Python 版手动创建空的 messages 列表实现隔离。\r\n * Spring AI 通过创建独立的 {@link ChatClient} 实例实现相同效果 —— 更加自然。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S04Subagent implements CommandLineRunner {\r\n\r\n private final ChatClient chatClient;\r\n\r\n public S04Subagent(ChatModel chatModel) {\r\n this.chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(\"You are a coding agent at \" + System.getProperty(\"user.dir\")\r\n + \". Use the task tool to delegate exploration or subtasks.\")\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n new SubagentTool(chatModel) // 父 Agent 独有的 task 工具\r\n )\r\n .build();\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n AgentRunner.interactive(\"s04\", userMessage ->\r\n chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content()\r\n );\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S04Subagent.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === SubagentTool.java ===\npackage io.mybatis.learn.s04;\r\n\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\n/**\r\n * 子 Agent 工具 —— 生成具有独立上下文的子 Agent 执行任务。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s04_subagent.py} 中的 {@code run_subagent(prompt)} 函数。\r\n * Python 版创建 {@code sub_messages = []} 实现上下文隔离,\r\n * Spring AI 通过创建全新的 {@link ChatClient} 实例实现相同效果。\r\n * 子 Agent 获得基础工具但没有 task 工具(防止递归生成)。\r\n * 只有最终文本返回给父 Agent,子 Agent 的对话历史被完全丢弃。\r\n */\r\npublic class SubagentTool {\r\n private static final Logger log = LoggerFactory.getLogger(SubagentTool.class);\r\n\r\n private final ChatModel chatModel;\r\n private final String workDir;\r\n\r\n public SubagentTool(ChatModel chatModel) {\r\n this.chatModel = chatModel;\r\n this.workDir = System.getProperty(\"user.dir\");\r\n }\r\n\r\n /**\r\n * TIP: 对应 Python {@code run_subagent(prompt)}。\r\n * Python 版在独立线程中运行子 Agent 的 while 循环(最多30次迭代)。\r\n * Spring AI 的 ChatClient.call() 内部管理循环,无需手动限制迭代次数。\r\n */\r\n @Tool(description = \"Spawn a subagent with fresh context. \"\rLine truncated
|
||||
},
|
||||
{
|
||||
"id": "s05",
|
||||
@@ -468,7 +468,7 @@
|
||||
"filename": "S07TaskSystem.java",
|
||||
"title": "Tasks",
|
||||
"subtitle": "Task Graph + Dependencies",
|
||||
"loc": 248,
|
||||
"loc": 266,
|
||||
"tools": [
|
||||
"taskCreate",
|
||||
"taskGet",
|
||||
@@ -492,7 +492,7 @@
|
||||
{
|
||||
"name": "TaskManager",
|
||||
"startLine": 99,
|
||||
"endLine": 316
|
||||
"endLine": 334
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -529,53 +529,53 @@
|
||||
{
|
||||
"name": "maxId",
|
||||
"signature": "private int maxId()",
|
||||
"startLine": 121
|
||||
"startLine": 123
|
||||
},
|
||||
{
|
||||
"name": "load",
|
||||
"signature": "private Map<String, Object> load(int taskId) throws IOException",
|
||||
"startLine": 134
|
||||
"startLine": 136
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"signature": "private void save(Map<String, Object> task) throws IOException",
|
||||
"startLine": 143
|
||||
"startLine": 145
|
||||
},
|
||||
{
|
||||
"name": "taskCreate",
|
||||
"signature": "public String taskCreate(",
|
||||
"startLine": 149
|
||||
"startLine": 151
|
||||
},
|
||||
{
|
||||
"name": "taskGet",
|
||||
"signature": "public String taskGet(@ToolParam(description = \"Task ID\") int taskId)",
|
||||
"startLine": 173
|
||||
"startLine": 179
|
||||
},
|
||||
{
|
||||
"name": "taskUpdate",
|
||||
"signature": "public String taskUpdate(",
|
||||
"startLine": 192
|
||||
"startLine": 200
|
||||
},
|
||||
{
|
||||
"name": "clearDependency",
|
||||
"signature": "private void clearDependency(int completedId)",
|
||||
"startLine": 253
|
||||
"startLine": 265
|
||||
},
|
||||
{
|
||||
"name": "taskList",
|
||||
"signature": "public String taskList()",
|
||||
"startLine": 275
|
||||
"startLine": 291
|
||||
}
|
||||
],
|
||||
"layer": "planning",
|
||||
"source": "// === S07TaskSystem.java ===\npackage io.mybatis.learn.s07;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\n\r\n/**\r\n * S07 - 任务系统:大目标拆成小任务,排好序,记在磁盘上。\r\n * <p>\r\n * 格言: \"大目标要拆成小任务, 排好序, 记在磁盘上\"\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s07_task_system.py}。\r\n * 核心洞察: 任务状态持久化在磁盘({@code .tasks/task_*.json}),\r\n * 不受上下文压缩影响 —— 因为它在对话之外。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S07TaskSystem implements CommandLineRunner {\r\n\r\n private final ChatClient chatClient;\r\n\r\n public S07TaskSystem(ChatModel chatModel) {\r\n Path tasksDir = Path.of(System.getProperty(\"user.dir\"), \".tasks\");\r\n TaskManager taskManager = new TaskManager(tasksDir);\r\n\r\n this.chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(\"You are a coding agent at \" + System.getProperty(\"user.dir\")\r\n + \". Use task tools to plan and track work.\")\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n taskManager\r\n )\r\n .build();\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n AgentRunner.interactive(\"s07\", userMessage ->\r\n chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content()\r\n );\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S07TaskSystem.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === TaskManager.java ===\npackage io.mybatis.learn.s07;\r\n\r\nimport com.fasterxml.jackson.core.type.TypeReference;\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.file.Files;\r\nimport java.nio.file.Path;\r\nimport java.util.*;\r\nimport java.util.stream.Stream;\r\n\r\n/**\r\n * 持久化任务管理器 —— 任务状态存储在磁盘上,不受上下文压缩影响。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s07_task_system.py} 中的 {@code TaskManager} 类。\r\n * Python 使用 {@code json.loads/json.dumps},Java 使用 Jackson {@code ObjectMapper}。\r\n * <pre>\r\n * .tasks/\r\n * task_1.json {\"id\":1, \"subject\":\"...\", \"status\":\"completed\", ...}\r\n * task_2.json {\"id\":2, \"blockedBy\":[1], \"status\":\"pending\", ...}\r\n *\r\n * 依赖解析:\r\n * task 1 (complete) --> task 2 (blocked) --> task 3 (blocked)\r\n * | ^\r\n * +--- completing task 1 removes it from task 2's blockedBy\r\n * </pre>\r\n */\r\npublic class TaskManager {\r\n private static final Logger log = LoggerFactory.getLogger(TaskManager.class);\r\n\r\n private static final ObjectMapper MAPPER = new ObjectMapper();\r\n private static final List<String> VALID_STATUSES = List.of(\"pending\", \"in_progress\", \"completed\");\r\n\r\n private final Path dir;\r\n private int nextId;\r\n\r\n public TaskManager(Path tasksDir) {\r\n this.dir = tasksDir;\r\n try {\r\n Files.createDirectories(dir);\r\n log.debug(\"任务目录已就绪: {}\", dir);\r\n } catch (IOException e) {\r\n log.error(\"创建任务目录失败: {}, error={}\", dir, e.getMessage());\r\n throw new RuntimeException(\"Cannot create tasks directory: \" + e.getMessage(), e);\r\n }\r\n this.nextId = maxId() + 1;\r\n log.info(\"TaskManager 初始化完成,nextId={}, dir={}\", nextId, dir);\r\n }\r\n\r\n private int maxId() {\r\n try (Stream<Path> files = Files.list(dir)) {\r\n return files.filter(f -> f.getFileName().toString().matches(\"task_\\\\d+\\\\.json\"))\r\n Line truncated
|
||||
"source": "// === S07TaskSystem.java ===\npackage io.mybatis.learn.s07;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\n\r\n/**\r\n * S07 - 任务系统:大目标拆成小任务,排好序,记在磁盘上。\r\n * <p>\r\n * 格言: \"大目标要拆成小任务, 排好序, 记在磁盘上\"\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s07_task_system.py}。\r\n * 核心洞察: 任务状态持久化在磁盘({@code .tasks/task_*.json}),\r\n * 不受上下文压缩影响 —— 因为它在对话之外。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S07TaskSystem implements CommandLineRunner {\r\n\r\n private final ChatClient chatClient;\r\n\r\n public S07TaskSystem(ChatModel chatModel) {\r\n Path tasksDir = Path.of(System.getProperty(\"user.dir\"), \".tasks\");\r\n TaskManager taskManager = new TaskManager(tasksDir);\r\n\r\n this.chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(\"You are a coding agent at \" + System.getProperty(\"user.dir\")\r\n + \". Use task tools to plan and track work.\")\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n taskManager\r\n )\r\n .build();\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n AgentRunner.interactive(\"s07\", userMessage ->\r\n chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content()\r\n );\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S07TaskSystem.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === TaskManager.java ===\npackage io.mybatis.learn.s07;\r\n\r\nimport com.fasterxml.jackson.core.type.TypeReference;\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.file.Files;\r\nimport java.nio.file.Path;\r\nimport java.util.*;\r\nimport java.util.stream.Stream;\r\n\r\n/**\r\n * 持久化任务管理器 —— 任务状态存储在磁盘上,不受上下文压缩影响。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s07_task_system.py} 中的 {@code TaskManager} 类。\r\n * Python 使用 {@code json.loads/json.dumps},Java 使用 Jackson {@code ObjectMapper}。\r\n * <pre>\r\n * .tasks/\r\n * task_1.json {\"id\":1, \"subject\":\"...\", \"status\":\"completed\", ...}\r\n * task_2.json {\"id\":2, \"blockedBy\":[1], \"status\":\"pending\", ...}\r\n *\r\n * 依赖解析:\r\n * task 1 (complete) --> task 2 (blocked) --> task 3 (blocked)\r\n * | ^\r\n * +--- completing task 1 removes it from task 2's blockedBy\r\n * </pre>\r\n */\r\npublic class TaskManager {\r\n private static final Logger log = LoggerFactory.getLogger(TaskManager.class);\r\n\r\n private static final ObjectMapper MAPPER = new ObjectMapper();\r\n private static final List<String> VALID_STATUSES = List.of(\"pending\", \"in_progress\", \"completed\");\r\n\r\n private final Path dir;\r\n private int nextId;\r\n\r\n public TaskManager(Path tasksDir) {\r\n this.dir = tasksDir;\r\n try {\r\n Files.createDirectories(dir);\r\n if (log.isDebugEnabled()) {\r\n System.out.printf(\"🚀 任务目录就绪: %s%n\", dir);\r\n }\r\n } catch (IOException e) {\r\n log.error(\"创建任务目录失败: {}, error={}\", dir, e.getMessage());\r\n throw new RuntimeException(\"Cannot create tasks directory: \" + e.getMessage(), e);\r\n }\r\n this.nextId = maxId() + 1;\r\n log.info(\"TaskManager 初始化完成,nextId={}, dir={}\", nextId, dir);\r\n }\r\n\r\n private int maxId() {\r\n try (Stream<Path> files = Files.list(dir)) {\r\n return files.filter(f -> f.getLine truncated
|
||||
},
|
||||
{
|
||||
"id": "s08",
|
||||
"filename": "S08BackgroundTasks.java",
|
||||
"title": "Background Tasks",
|
||||
"subtitle": "Background Threads + Notifications",
|
||||
"loc": 171,
|
||||
"loc": 179,
|
||||
"tools": [
|
||||
"backgroundRun",
|
||||
"checkBackground"
|
||||
@@ -595,7 +595,7 @@
|
||||
{
|
||||
"name": "BackgroundManager",
|
||||
"startLine": 121,
|
||||
"endLine": 236
|
||||
"endLine": 244
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -652,28 +652,28 @@
|
||||
{
|
||||
"name": "InputStreamReader",
|
||||
"signature": "new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)))",
|
||||
"startLine": 171
|
||||
"startLine": 173
|
||||
},
|
||||
{
|
||||
"name": "checkBackground",
|
||||
"signature": "public String checkBackground(",
|
||||
"startLine": 204
|
||||
"startLine": 208
|
||||
},
|
||||
{
|
||||
"name": "drainNotifications",
|
||||
"signature": "public List<Notification> drainNotifications()",
|
||||
"startLine": 227
|
||||
"startLine": 233
|
||||
}
|
||||
],
|
||||
"layer": "concurrency",
|
||||
"source": "// === S08BackgroundTasks.java ===\npackage io.mybatis.learn.s08;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.util.stream.Collectors;\r\n\r\n/**\r\n * S08 - 后台任务:慢操作丢后台,Agent 继续想下一步。\r\n * <p>\r\n * 格言: \"慢操作丢后台, agent 继续想下一步\"\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s08_background_tasks.py}。\r\n * Python 在每次 LLM 调用前 drain 通知队列并注入 {@code <background-results>}。\r\n * Spring AI 的 ChatClient 管理内部循环,因此改为在每次用户输入时\r\n * drain 通知并注入系统提示。核心概念不变: fire and forget。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S08BackgroundTasks implements CommandLineRunner {\r\n\r\n private final ChatModel chatModel;\r\n\r\n public S08BackgroundTasks(ChatModel chatModel) {\r\n this.chatModel = chatModel;\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n BackgroundManager bgManager = new BackgroundManager();\r\n String workDir = System.getProperty(\"user.dir\");\r\n\r\n AgentRunner.interactive(\"s08\", userMessage -> {\r\n // Drain 后台任务通知(对应 Python 中循环前的 drain_notifications)\r\n var notifs = bgManager.drainNotifications();\r\n String bgContext = \"\";\r\n if (!notifs.isEmpty()) {\r\n String notifText = notifs.stream()\r\n .map(n -> \"[bg:\" + n.taskId() + \"] \" + n.status() + \": \" + n.result())\r\n .collect(Collectors.joining(\"\\n\"));\r\n bgContext = \"\\n\\n<background-results>\\n\" + notifText + \"\\n</background-results>\";\r\n System.out.println(\"[Background tasks completed: \" + notifs.size() + \"]\");\r\n }\r\n\r\n String system = \"You are a coding agent at \" + workDir\r\n + \". Use backgroundRun for long-running commands.\" + bgContext;\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(system)\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n bgManager\r\n )\r\n .build();\r\n\r\n return chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content();\r\n });\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S08BackgroundTasks.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === BackgroundManager.java ===\npackage io.mybatis.learn.s08;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStreamReader;\r\nimport java.nio.charset.StandardCharsets;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.UUID;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.CopyOnWriteArrayList;\r\nimport java.util.concurrent.ExecutorService;\r\nimport java.util.concurrent.Executors;\r\nimport java.util.stream.Collectors;\r\n\r\n/**\r\n * 后台任务管理器 —— 慢操作丢后台,Agent 继续想下一步。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s08_background_tasks.py} 中的 {@code BackgroundManager} 类。\r\n * Python 使用 {@code threading.Thread(daemon=True)},\r\n * Java 使用 {@link ExecutorService} + 虚拟线程(Java 21)。\r\n * <pre>\r\n * Main thread Background thread\r\n * +-----------------+ +-----------------+\r\n * | agent loop | | task executes |\r\n * | ... | | ... |\r\n * | [LLM call] <--+------- | notify(result) |\r\n * | ^drain queue | +-----------------+\r\n * +-----------------+\r\n * </pre>\r\n */\r\npublic cLine truncated
|
||||
"source": "// === S08BackgroundTasks.java ===\npackage io.mybatis.learn.s08;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.BashTool;\r\nimport io.mybatis.learn.core.tools.EditFileTool;\r\nimport io.mybatis.learn.core.tools.ReadFileTool;\r\nimport io.mybatis.learn.core.tools.WriteFileTool;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.WebApplicationType;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.util.stream.Collectors;\r\n\r\n/**\r\n * S08 - 后台任务:慢操作丢后台,Agent 继续想下一步。\r\n * <p>\r\n * 格言: \"慢操作丢后台, agent 继续想下一步\"\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s08_background_tasks.py}。\r\n * Python 在每次 LLM 调用前 drain 通知队列并注入 {@code <background-results>}。\r\n * Spring AI 的 ChatClient 管理内部循环,因此改为在每次用户输入时\r\n * drain 通知并注入系统提示。核心概念不变: fire and forget。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S08BackgroundTasks implements CommandLineRunner {\r\n\r\n private final ChatModel chatModel;\r\n\r\n public S08BackgroundTasks(ChatModel chatModel) {\r\n this.chatModel = chatModel;\r\n }\r\n\r\n @Override\r\n public void run(String... args) {\r\n BackgroundManager bgManager = new BackgroundManager();\r\n String workDir = System.getProperty(\"user.dir\");\r\n\r\n AgentRunner.interactive(\"s08\", userMessage -> {\r\n // Drain 后台任务通知(对应 Python 中循环前的 drain_notifications)\r\n var notifs = bgManager.drainNotifications();\r\n String bgContext = \"\";\r\n if (!notifs.isEmpty()) {\r\n String notifText = notifs.stream()\r\n .map(n -> \"[bg:\" + n.taskId() + \"] \" + n.status() + \": \" + n.result())\r\n .collect(Collectors.joining(\"\\n\"));\r\n bgContext = \"\\n\\n<background-results>\\n\" + notifText + \"\\n</background-results>\";\r\n System.out.println(\"[Background tasks completed: \" + notifs.size() + \"]\");\r\n }\r\n\r\n String system = \"You are a coding agent at \" + workDir\r\n + \". Use backgroundRun for long-running commands.\" + bgContext;\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(system)\r\n .defaultTools(\r\n new BashTool(),\r\n new ReadFileTool(),\r\n new WriteFileTool(),\r\n new EditFileTool(),\r\n bgManager\r\n )\r\n .build();\r\n\r\n return chatClient.prompt()\r\n .user(userMessage)\r\n .call()\r\n .content();\r\n });\r\n }\r\n\r\n public static void main(String[] args) {\r\n SpringApplication app = new SpringApplication(S08BackgroundTasks.class);\r\n app.setWebApplicationType(WebApplicationType.NONE);\r\n app.run(args);\r\n }\r\n}\r\n\n\n// === BackgroundManager.java ===\npackage io.mybatis.learn.s08;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStreamReader;\r\nimport java.nio.charset.StandardCharsets;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.UUID;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.CopyOnWriteArrayList;\r\nimport java.util.concurrent.ExecutorService;\r\nimport java.util.concurrent.Executors;\r\nimport java.util.stream.Collectors;\r\n\r\n/**\r\n * 后台任务管理器 —— 慢操作丢后台,Agent 继续想下一步。\r\n * <p>\r\n * TIP: 对应 Python {@code agents/s08_background_tasks.py} 中的 {@code BackgroundManager} 类。\r\n * Python 使用 {@code threading.Thread(daemon=True)},\r\n * Java 使用 {@link ExecutorService} + 虚拟线程(Java 21)。\r\n * <pre>\r\n * Main thread Background thread\r\n * +-----------------+ +-----------------+\r\n * | agent loop | | task executes |\r\n * | ... | | ... |\r\n * | [LLM call] <--+------- | notify(result) |\r\n * | ^drain queue | +-----------------+\r\n * +-----------------+\r\n * </pre>\r\n */\r\npublic cLine truncated
|
||||
},
|
||||
{
|
||||
"id": "s09",
|
||||
"filename": "S09AgentTeams.java",
|
||||
"title": "Agent Teams",
|
||||
"subtitle": "Teammates + Mailboxes",
|
||||
"loc": 268,
|
||||
"loc": 282,
|
||||
"tools": [
|
||||
"spawnTeammate",
|
||||
"listTeammates",
|
||||
@@ -698,12 +698,12 @@
|
||||
{
|
||||
"name": "S09AgentTeams",
|
||||
"startLine": 35,
|
||||
"endLine": 169
|
||||
"endLine": 175
|
||||
},
|
||||
{
|
||||
"name": "TeammateManager",
|
||||
"startLine": 170,
|
||||
"endLine": 368
|
||||
"startLine": 176,
|
||||
"endLine": 382
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -755,73 +755,73 @@
|
||||
{
|
||||
"name": "loadConfig",
|
||||
"signature": "private Map<String, Object> loadConfig()",
|
||||
"startLine": 197
|
||||
"startLine": 203
|
||||
},
|
||||
{
|
||||
"name": "saveConfig",
|
||||
"signature": "private synchronized void saveConfig()",
|
||||
"startLine": 210
|
||||
"startLine": 218
|
||||
},
|
||||
{
|
||||
"name": "findMember",
|
||||
"signature": "private Map<String, Object> findMember(String name)",
|
||||
"startLine": 221
|
||||
"startLine": 231
|
||||
},
|
||||
{
|
||||
"name": "setStatus",
|
||||
"signature": "protected synchronized void setStatus(String name, String status)",
|
||||
"startLine": 229
|
||||
"startLine": 239
|
||||
},
|
||||
{
|
||||
"name": "spawn",
|
||||
"signature": "public synchronized String spawn(String name, String role, String prompt)",
|
||||
"startLine": 240
|
||||
"startLine": 250
|
||||
},
|
||||
{
|
||||
"name": "teammateLoop",
|
||||
"signature": "protected void teammateLoop(String name, String role, String initialPrompt)",
|
||||
"startLine": 273
|
||||
"startLine": 283
|
||||
},
|
||||
{
|
||||
"name": "WriteFileTool",
|
||||
"signature": "new WriteFileTool(), new EditFileTool(), messageTool)",
|
||||
"startLine": 285
|
||||
"startLine": 295
|
||||
},
|
||||
{
|
||||
"name": "listAll",
|
||||
"signature": "public String listAll()",
|
||||
"startLine": 317
|
||||
"startLine": 331
|
||||
},
|
||||
{
|
||||
"name": "memberNames",
|
||||
"signature": "public List<String> memberNames()",
|
||||
"startLine": 329
|
||||
"startLine": 343
|
||||
},
|
||||
{
|
||||
"name": "TeammateMessageTool",
|
||||
"signature": "public TeammateMessageTool(MessageBus bus, String name)",
|
||||
"startLine": 346
|
||||
"startLine": 360
|
||||
},
|
||||
{
|
||||
"name": "sendMessage",
|
||||
"signature": "public String sendMessage(",
|
||||
"startLine": 352
|
||||
"startLine": 366
|
||||
},
|
||||
{
|
||||
"name": "readInbox",
|
||||
"signature": "public String readInbox()",
|
||||
"startLine": 359
|
||||
"startLine": 373
|
||||
}
|
||||
],
|
||||
"layer": "collaboration",
|
||||
"source": "// === S09AgentTeams.java ===\npackage io.mybatis.learn.s09;\r\n\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.team.MessageBus;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\nimport java.util.List;\r\n\r\n/**\r\n * S09 - Agent Teams:持久化命名agent + JSONL收件箱通信\r\n *\r\n * TIPS: 对应Python s09_agent_teams.py。\r\n * Python用 TOOL_HANDLERS 字典 + 手动while循环分发9个工具;\r\n * Java用 ChatClient + @Tool 自动处理,Lead工具封装在 LeadTools 内部类。\r\n *\r\n * 核心概念:\r\n * Subagent (s04): spawn → execute → return summary → destroyed\r\n * Teammate (s09): spawn → work → idle → work → ... → shutdown\r\n *\r\n * 关键差异:Python队友在每次LLM调用前检查收件箱(s09第174行),\r\n * Java队友在每次完整工具链(call())之间检查。这是Spring AI的自然适配。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S09AgentTeams implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n Path teamDir = workDir.resolve(\".team\");\r\n\r\n MessageBus bus = new MessageBus(teamDir.resolve(\"inbox\"));\r\n TeammateManager team = new TeammateManager(chatModel, bus, teamDir);\r\n LeadTools leadTools = new LeadTools(bus, team);\r\n ObjectMapper mapper = new ObjectMapper();\r\n\r\n String systemPrompt = \"You are a team lead at \" + workDir\r\n + \". Spawn teammates and communicate via inboxes.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(), leadTools)\r\n .build();\r\n\r\n AgentRunner.interactive(\"s09\", input -> {\r\n if (\"/team\".equals(input)) return team.listAll();\r\n if (\"/inbox\".equals(input)) {\r\n try {\r\n return mapper.writeValueAsString(bus.readInbox(\"lead\"));\r\n } catch (Exception e) {\r\n return \"Error: \" + e.getMessage();\r\n }\r\n }\r\n\r\n // 每次LLM调用前检查lead收件箱\r\n var inbox = bus.readInbox(\"lead\");\r\n String fullInput = input;\r\n if (!inbox.isEmpty()) {\r\n try {\r\n fullInput = \"<inbox>\" + mapper.writeValueAsString(inbox)\r\n + \"</inbox>\\n\\n\" + input;\r\n } catch (Exception e) { /* ignore */ }\r\n }\r\n\r\n return chatClient.prompt(fullInput).call().content();\r\n });\r\n }\r\n\r\n /**\r\n * Lead专用工具集(5个团队管理工具)\r\n *\r\n * TIPS: 对应Python TOOL_HANDLERS中的5个lead工具(s09第310-319行)。\r\n * Python用lambda + 字典映射;Java用@Tool注解的方法。\r\n */\r\n public static class LeadTools {\r\n private final MessageBus bus;\r\n private final TeammateManager team;\r\n private final ObjectMapper mapper = new ObjectMapper();\r\n\r\n public LeadTools(MessageBus bus, TeammateManager team) {\r\n this.bus = bus;\r\n this.team = team;\r\n }\r\n\r\n @Tool(description = \"Spawn a persistent teammate that runs in its own thread\")\r\n public String spawnTeammate(\r\n @ToolParam(description = \"Teammate name\") String name,\r\n @ToolParam(description = \"Role description\") String role,\r\n @ToolParam(description = \"Initial task prompt\") String prompt) {\r\n return team.spawn(name, role, prompt);\r\n }\r\n\r\n @Tool(description = \"List all teammates with name, role, status\")\r\n public String listTeammates() {\r\n return team.listAll();\r\n }\r\n\r\n @Tool(description = \"Send a message to a teammate's inbox\")\r\n public String sendMessage(\r\n @ToolParam(description = \"Recipient teammate name\") String to,\r\n @ToolParam(deLine truncated
|
||||
"source": "// === S09AgentTeams.java ===\npackage io.mybatis.learn.s09;\r\n\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.team.MessageBus;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\nimport java.util.List;\r\n\r\n/**\r\n * S09 - Agent Teams:持久化命名agent + JSONL收件箱通信\r\n *\r\n * TIPS: 对应Python s09_agent_teams.py。\r\n * Python用 TOOL_HANDLERS 字典 + 手动while循环分发9个工具;\r\n * Java用 ChatClient + @Tool 自动处理,Lead工具封装在 LeadTools 内部类。\r\n *\r\n * 核心概念:\r\n * Subagent (s04): spawn → execute → return summary → destroyed\r\n * Teammate (s09): spawn → work → idle → work → ... → shutdown\r\n *\r\n * 关键差异:Python队友在每次LLM调用前检查收件箱(s09第174行),\r\n * Java队友在每次完整工具链(call())之间检查。这是Spring AI的自然适配。\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S09AgentTeams implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n Path teamDir = workDir.resolve(\".team\");\r\n\r\n MessageBus bus = new MessageBus(teamDir.resolve(\"inbox\"));\r\n TeammateManager team = new TeammateManager(chatModel, bus, teamDir);\r\n LeadTools leadTools = new LeadTools(bus, team);\r\n ObjectMapper mapper = new ObjectMapper();\r\n\r\n String systemPrompt = \"You are a team lead at \" + workDir\r\n + \". Spawn teammates and communicate via inboxes.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(), leadTools)\r\n .build();\r\n\r\n AgentRunner.interactive(\"s09\", input -> {\r\n if (\"/team\".equals(input)) return team.listAll();\r\n if (\"/inbox\".equals(input)) {\r\n try {\r\n return mapper.writeValueAsString(bus.readInbox(\"lead\"));\r\n } catch (Exception e) {\r\n return \"Error: \" + e.getMessage();\r\n }\r\n }\r\n\r\n // 每次LLM调用前检查lead收件箱\r\n var inbox = bus.readInbox(\"lead\");\r\n String fullInput = input;\r\n if (!inbox.isEmpty()) {\r\n try {\r\n fullInput = \"<inbox>\" + mapper.writeValueAsString(inbox)\r\n + \"</inbox>\\n\\n\" + input;\r\n } catch (Exception e) { /* ignore */ }\r\n }\r\n\r\n return chatClient.prompt(fullInput).call().content();\r\n });\r\n }\r\n\r\n /**\r\n * Lead专用工具集(5个团队管理工具)\r\n *\r\n * TIPS: 对应Python TOOL_HANDLERS中的5个lead工具(s09第310-319行)。\r\n * Python用lambda + 字典映射;Java用@Tool注解的方法。\r\n */\r\n public static class LeadTools {\r\n private final MessageBus bus;\r\n private final TeammateManager team;\r\n private final ObjectMapper mapper = new ObjectMapper();\r\n\r\n public LeadTools(MessageBus bus, TeammateManager team) {\r\n this.bus = bus;\r\n this.team = team;\r\n }\r\n\r\n @Tool(description = \"Spawn a persistent teammate that runs in its own thread\")\r\n public String spawnTeammate(\r\n @ToolParam(description = \"Teammate name\") String name,\r\n @ToolParam(description = \"Role description\") String role,\r\n @ToolParam(description = \"Initial task prompt\") String prompt) {\r\n return team.spawn(name, role, prompt);\r\n }\r\n\r\n @Tool(description = \"List all teammates with name, role, status\")\r\n public String listTeammates() {\r\n return team.listAll();\r\n }\r\n\r\n @Tool(description = \"Send a message to a teammate's inbox\")\r\n public String sendMessage(\r\n @ToolParam(description = \"Recipient teammate name\") String to,\r\n @ToolParam(deLine truncated
|
||||
},
|
||||
{
|
||||
"id": "s10",
|
||||
"filename": "S10TeamProtocols.java",
|
||||
"title": "Team Protocols",
|
||||
"subtitle": "Shared Communication Rules",
|
||||
"loc": 303,
|
||||
"loc": 305,
|
||||
"tools": [
|
||||
"shutdownResponse",
|
||||
"planApproval",
|
||||
@@ -852,7 +852,7 @@
|
||||
{
|
||||
"name": "ProtocolTracker",
|
||||
"startLine": 337,
|
||||
"endLine": 425
|
||||
"endLine": 427
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -989,21 +989,21 @@
|
||||
{
|
||||
"name": "respondToShutdown",
|
||||
"signature": "public String respondToShutdown(String sender, String requestId, boolean approve, String reason)",
|
||||
"startLine": 379
|
||||
"startLine": 381
|
||||
},
|
||||
{
|
||||
"name": "submitPlan",
|
||||
"signature": "public String submitPlan(String sender, String planText)",
|
||||
"startLine": 395
|
||||
"startLine": 397
|
||||
},
|
||||
{
|
||||
"name": "reviewPlan",
|
||||
"signature": "public String reviewPlan(String requestId, boolean approve, String feedback)",
|
||||
"startLine": 410
|
||||
"startLine": 412
|
||||
}
|
||||
],
|
||||
"layer": "collaboration",
|
||||
"source": "// === S10TeamProtocols.java ===\npackage io.mybatis.learn.s10;\r\n\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.team.MessageBus;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.file.Files;\r\nimport java.nio.file.Path;\r\nimport java.util.*;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\n/**\r\n * S10 - Team Protocols:关闭协议 + 计划审批协议\r\n *\r\n * TIPS: 对应Python s10_team_protocols.py。\r\n * 在S09基础上新增3个Lead工具和2个Teammate工具。\r\n *\r\n * 关闭协议FSM: pending → approved | rejected\r\n * Lead调用 shutdown_request → Teammate收到 shutdown_request 消息\r\n * → Teammate调用 shutdown_response → Lead收件箱收到响应\r\n *\r\n * 计划审批FSM: pending → approved | rejected\r\n * Teammate调用 plan_approval → Lead收件箱收到计划\r\n * → Lead调用 plan_approval(review) → Teammate收到审批结果\r\n *\r\n * 关键洞察:\"Same request_id correlation pattern, two domains.\"\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S10TeamProtocols implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n Path teamDir = workDir.resolve(\".team\");\r\n\r\n MessageBus bus = new MessageBus(teamDir.resolve(\"inbox\"));\r\n ProtocolTracker tracker = new ProtocolTracker(bus);\r\n S10TeammateManager team = new S10TeammateManager(chatModel, bus, tracker, teamDir);\r\n S10LeadTools leadTools = new S10LeadTools(bus, team, tracker);\r\n ObjectMapper mapper = new ObjectMapper();\r\n\r\n String systemPrompt = \"You are a team lead at \" + workDir\r\n + \". Manage teammates with shutdown and plan approval protocols.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(), leadTools)\r\n .build();\r\n\r\n AgentRunner.interactive(\"s10\", input -> {\r\n if (\"/team\".equals(input)) return team.listAll();\r\n if (\"/inbox\".equals(input)) {\r\n try { return mapper.writeValueAsString(bus.readInbox(\"lead\")); }\r\n catch (Exception e) { return \"Error: \" + e.getMessage(); }\r\n }\r\n\r\n var inbox = bus.readInbox(\"lead\");\r\n String fullInput = input;\r\n if (!inbox.isEmpty()) {\r\n try {\r\n fullInput = \"<inbox>\" + mapper.writeValueAsString(inbox)\r\n + \"</inbox>\\n\\n\" + input;\r\n } catch (Exception e) { /* ignore */ }\r\n }\r\n\r\n return chatClient.prompt(fullInput).call().content();\r\n });\r\n }\r\n\r\n // ---- 队友管理器(增加协议处理) ----\r\n\r\n /**\r\n * TIPS: 对应Python s10第134-290行的TeammateManager。\r\n * 相比S09增加了:\r\n * 1. should_exit标志 - 队友批准关闭后退出循环\r\n * 2. shutdown_response工具 - 更新tracker + 发送响应消息\r\n * 3. plan_approval工具 - 提交计划到lead收件箱\r\n */\r\n static class S10TeammateManager {\r\n private final ChatModel chatModel;\r\n private final MessageBus bus;\r\n private final ProtocolTracker tracker;\r\n private final Path configPath;\r\n private final ObjectMapper mapper = new ObjectMapper();\r\n private Map<String, Object> config;\r\n\r\n S10TeammateManager(ChatModel chatModel, MessageBus bus,\r\n ProtocolTracker tracker, Path teamDir) {\r\n this.chatModel = chatModel;\r\n this.bus = bus;\r\n this.tracker = tracker;\r\n this.configPath = teamDir.resolve(\"config.json\");\r\n try { Files.createDirectories(teamDir); } catch (IOException e) { throw new RuntimeException(e); }\r\n this.config = loadConfig();\r\n }\r\n\r\n @SuppressWarnings(\"unchecked\")\r\n private Map<String, Object> loadConfig() {\r\n Line truncated
|
||||
"source": "// === S10TeamProtocols.java ===\npackage io.mybatis.learn.s10;\r\n\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.team.MessageBus;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.file.Files;\r\nimport java.nio.file.Path;\r\nimport java.util.*;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\n/**\r\n * S10 - Team Protocols:关闭协议 + 计划审批协议\r\n *\r\n * TIPS: 对应Python s10_team_protocols.py。\r\n * 在S09基础上新增3个Lead工具和2个Teammate工具。\r\n *\r\n * 关闭协议FSM: pending → approved | rejected\r\n * Lead调用 shutdown_request → Teammate收到 shutdown_request 消息\r\n * → Teammate调用 shutdown_response → Lead收件箱收到响应\r\n *\r\n * 计划审批FSM: pending → approved | rejected\r\n * Teammate调用 plan_approval → Lead收件箱收到计划\r\n * → Lead调用 plan_approval(review) → Teammate收到审批结果\r\n *\r\n * 关键洞察:\"Same request_id correlation pattern, two domains.\"\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S10TeamProtocols implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n Path teamDir = workDir.resolve(\".team\");\r\n\r\n MessageBus bus = new MessageBus(teamDir.resolve(\"inbox\"));\r\n ProtocolTracker tracker = new ProtocolTracker(bus);\r\n S10TeammateManager team = new S10TeammateManager(chatModel, bus, tracker, teamDir);\r\n S10LeadTools leadTools = new S10LeadTools(bus, team, tracker);\r\n ObjectMapper mapper = new ObjectMapper();\r\n\r\n String systemPrompt = \"You are a team lead at \" + workDir\r\n + \". Manage teammates with shutdown and plan approval protocols.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(), leadTools)\r\n .build();\r\n\r\n AgentRunner.interactive(\"s10\", input -> {\r\n if (\"/team\".equals(input)) return team.listAll();\r\n if (\"/inbox\".equals(input)) {\r\n try { return mapper.writeValueAsString(bus.readInbox(\"lead\")); }\r\n catch (Exception e) { return \"Error: \" + e.getMessage(); }\r\n }\r\n\r\n var inbox = bus.readInbox(\"lead\");\r\n String fullInput = input;\r\n if (!inbox.isEmpty()) {\r\n try {\r\n fullInput = \"<inbox>\" + mapper.writeValueAsString(inbox)\r\n + \"</inbox>\\n\\n\" + input;\r\n } catch (Exception e) { /* ignore */ }\r\n }\r\n\r\n return chatClient.prompt(fullInput).call().content();\r\n });\r\n }\r\n\r\n // ---- 队友管理器(增加协议处理) ----\r\n\r\n /**\r\n * TIPS: 对应Python s10第134-290行的TeammateManager。\r\n * 相比S09增加了:\r\n * 1. should_exit标志 - 队友批准关闭后退出循环\r\n * 2. shutdown_response工具 - 更新tracker + 发送响应消息\r\n * 3. plan_approval工具 - 提交计划到lead收件箱\r\n */\r\n static class S10TeammateManager {\r\n private final ChatModel chatModel;\r\n private final MessageBus bus;\r\n private final ProtocolTracker tracker;\r\n private final Path configPath;\r\n private final ObjectMapper mapper = new ObjectMapper();\r\n private Map<String, Object> config;\r\n\r\n S10TeammateManager(ChatModel chatModel, MessageBus bus,\r\n ProtocolTracker tracker, Path teamDir) {\r\n this.chatModel = chatModel;\r\n this.bus = bus;\r\n this.tracker = tracker;\r\n this.configPath = teamDir.resolve(\"config.json\");\r\n try { Files.createDirectories(teamDir); } catch (IOException e) { throw new RuntimeException(e); }\r\n this.config = loadConfig();\r\n }\r\n\r\n @SuppressWarnings(\"unchecked\")\r\n private Map<String, Object> loadConfig() {\r\n Line truncated
|
||||
},
|
||||
{
|
||||
"id": "s11",
|
||||
@@ -1142,7 +1142,7 @@
|
||||
"filename": "S12WorktreeIsolation.java",
|
||||
"title": "Worktree + Task Isolation",
|
||||
"subtitle": "Isolate by Directory",
|
||||
"loc": 634,
|
||||
"loc": 664,
|
||||
"tools": [
|
||||
"taskCreate",
|
||||
"taskList",
|
||||
@@ -1177,22 +1177,22 @@
|
||||
{
|
||||
"name": "S12WorktreeIsolation",
|
||||
"startLine": 32,
|
||||
"endLine": 214
|
||||
"endLine": 217
|
||||
},
|
||||
{
|
||||
"name": "EventBus",
|
||||
"startLine": 215,
|
||||
"endLine": 304
|
||||
"startLine": 218,
|
||||
"endLine": 312
|
||||
},
|
||||
{
|
||||
"name": "WorktreeManager",
|
||||
"startLine": 305,
|
||||
"endLine": 633
|
||||
"startLine": 313,
|
||||
"endLine": 651
|
||||
},
|
||||
{
|
||||
"name": "WorktreeTaskManager",
|
||||
"startLine": 634,
|
||||
"endLine": 799
|
||||
"startLine": 652,
|
||||
"endLine": 829
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
@@ -1289,141 +1289,141 @@
|
||||
{
|
||||
"name": "emit",
|
||||
"signature": "public synchronized void emit(String event, Map<String, Object> task,",
|
||||
"startLine": 233
|
||||
"startLine": 238
|
||||
},
|
||||
{
|
||||
"name": "emit",
|
||||
"signature": "public void emit(String event)",
|
||||
"startLine": 252
|
||||
"startLine": 259
|
||||
},
|
||||
{
|
||||
"name": "listRecent",
|
||||
"signature": "public String listRecent(int limit)",
|
||||
"startLine": 257
|
||||
"startLine": 264
|
||||
},
|
||||
{
|
||||
"name": "isGitRepo",
|
||||
"signature": "private boolean isGitRepo()",
|
||||
"startLine": 338
|
||||
"startLine": 348
|
||||
},
|
||||
{
|
||||
"name": "runGit",
|
||||
"signature": "private String runGit(String... args) throws IOException",
|
||||
"startLine": 349
|
||||
"startLine": 359
|
||||
},
|
||||
{
|
||||
"name": "loadIndex",
|
||||
"signature": "private Map<String, Object> loadIndex() throws IOException",
|
||||
"startLine": 370
|
||||
"startLine": 382
|
||||
},
|
||||
{
|
||||
"name": "saveIndex",
|
||||
"signature": "private void saveIndex(Map<String, Object> index) throws IOException",
|
||||
"startLine": 374
|
||||
"startLine": 386
|
||||
},
|
||||
{
|
||||
"name": "findWorktree",
|
||||
"signature": "private Map<String, Object> findWorktree(String name) throws IOException",
|
||||
"startLine": 379
|
||||
"startLine": 391
|
||||
},
|
||||
{
|
||||
"name": "validateName",
|
||||
"signature": "private void validateName(String name)",
|
||||
"startLine": 387
|
||||
"startLine": 399
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"signature": "public String create(String name, Integer taskId, String baseRef)",
|
||||
"startLine": 397
|
||||
"startLine": 409
|
||||
},
|
||||
{
|
||||
"name": "listAll",
|
||||
"signature": "public String listAll()",
|
||||
"startLine": 447
|
||||
"startLine": 459
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"signature": "public String status(String name)",
|
||||
"startLine": 469
|
||||
"startLine": 483
|
||||
},
|
||||
{
|
||||
"name": "run",
|
||||
"signature": "public String run(String name, String command)",
|
||||
"startLine": 491
|
||||
"startLine": 507
|
||||
},
|
||||
{
|
||||
"name": "remove",
|
||||
"signature": "public String remove(String name, boolean force, boolean completeTask)",
|
||||
"startLine": 531
|
||||
"startLine": 549
|
||||
},
|
||||
{
|
||||
"name": "keep",
|
||||
"signature": "public String keep(String name)",
|
||||
"startLine": 580
|
||||
"startLine": 598
|
||||
},
|
||||
{
|
||||
"name": "isGitAvailable",
|
||||
"signature": "public boolean isGitAvailable() { return gitAvailable; }",
|
||||
"startLine": 611
|
||||
"startLine": 629
|
||||
},
|
||||
{
|
||||
"name": "maxId",
|
||||
"signature": "private int maxId()",
|
||||
"startLine": 651
|
||||
"startLine": 669
|
||||
},
|
||||
{
|
||||
"name": "taskPath",
|
||||
"signature": "private Path taskPath(int taskId)",
|
||||
"startLine": 662
|
||||
"startLine": 680
|
||||
},
|
||||
{
|
||||
"name": "load",
|
||||
"signature": "private Map<String, Object> load(int taskId) throws IOException",
|
||||
"startLine": 667
|
||||
"startLine": 685
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"signature": "private void save(Map<String, Object> task) throws IOException",
|
||||
"startLine": 673
|
||||
"startLine": 691
|
||||
},
|
||||
{
|
||||
"name": "exists",
|
||||
"signature": "public boolean exists(int taskId)",
|
||||
"startLine": 678
|
||||
"startLine": 696
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"signature": "public synchronized String create(String subject, String description)",
|
||||
"startLine": 682
|
||||
"startLine": 700
|
||||
},
|
||||
{
|
||||
"name": "get",
|
||||
"signature": "public String get(int taskId)",
|
||||
"startLine": 705
|
||||
"startLine": 727
|
||||
},
|
||||
{
|
||||
"name": "update",
|
||||
"signature": "public String update(int taskId, String status, String owner)",
|
||||
"startLine": 713
|
||||
"startLine": 735
|
||||
},
|
||||
{
|
||||
"name": "bindWorktree",
|
||||
"signature": "public String bindWorktree(int taskId, String worktree, String owner)",
|
||||
"startLine": 736
|
||||
"startLine": 762
|
||||
},
|
||||
{
|
||||
"name": "unbindWorktree",
|
||||
"signature": "public String unbindWorktree(int taskId)",
|
||||
"startLine": 752
|
||||
"startLine": 780
|
||||
},
|
||||
{
|
||||
"name": "listAll",
|
||||
"signature": "public String listAll()",
|
||||
"startLine": 767
|
||||
"startLine": 797
|
||||
}
|
||||
],
|
||||
"layer": "collaboration",
|
||||
"source": "// === S12WorktreeIsolation.java ===\npackage io.mybatis.learn.s12;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\n\r\n/**\r\n * S12 - Worktree + Task Isolation:目录级隔离的并行任务执行\r\n *\r\n * TIPS: 对应Python s12_worktree_task_isolation.py。\r\n * 与S09-S11的团队通信路线不同,S12走「目录隔离」路线:\r\n * 任务(控制平面)+ Worktree(执行平面)。\r\n *\r\n * S09-S11: 多个agent → 消息队列通信 → 协议协调\r\n * S12: 单个agent → 多个worktree → 按目录隔离 → 按task ID协调\r\n *\r\n * 17个工具:base(4) + task(5) + worktree(8)\r\n *\r\n * 关键洞察:\"Isolate by directory, coordinate by task ID.\"\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S12WorktreeIsolation implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n\r\n // 检测git仓库根目录\r\n Path repoRoot = detectRepoRoot(workDir);\r\n if (repoRoot == null) repoRoot = workDir;\r\n\r\n WorktreeTaskManager tasks = new WorktreeTaskManager(repoRoot.resolve(\".tasks\"));\r\n EventBus events = new EventBus(repoRoot.resolve(\".worktrees\").resolve(\"events.jsonl\"));\r\n WorktreeManager worktrees = new WorktreeManager(repoRoot, tasks, events);\r\n\r\n TaskTools taskTools = new TaskTools(tasks);\r\n WorktreeTools wtTools = new WorktreeTools(worktrees, events);\r\n\r\n String systemPrompt = \"You are a coding agent at \" + workDir + \". \"\r\n + \"Use task + worktree tools for multi-task work. \"\r\n + \"For parallel or risky changes: create tasks, allocate worktree lanes, \"\r\n + \"run commands in those lanes, then choose keep/remove for closeout. \"\r\n + \"Use worktree_events when you need lifecycle visibility.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(),\r\n taskTools, wtTools)\r\n .build();\r\n\r\n System.out.println(\"Repo root for s12: \" + repoRoot);\r\n if (!worktrees.isGitAvailable()) {\r\n System.out.println(\"Note: Not in a git repo. worktree_* tools will return errors.\");\r\n }\r\n\r\n AgentRunner.interactive(\"s12\", input ->\r\n chatClient.prompt(input).call().content());\r\n }\r\n\r\n private static Path detectRepoRoot(Path cwd) {\r\n try {\r\n ProcessBuilder pb = new ProcessBuilder(\"git\", \"rev-parse\", \"--show-toplevel\");\r\n pb.directory(cwd.toFile());\r\n pb.redirectErrorStream(true);\r\n Process p = pb.start();\r\n String out = new String(p.getInputStream().readAllBytes()).trim();\r\n if (p.waitFor() == 0 && !out.isEmpty()) {\r\n Path root = Path.of(out);\r\n return root.toFile().exists() ? root : null;\r\n }\r\n } catch (Exception e) { /* ignore */ }\r\n return null;\r\n }\r\n\r\n // ---- 任务工具集(5个) ----\r\n\r\n public static class TaskTools {\r\n private final WorktreeTaskManager tasks;\r\n\r\n public TaskTools(WorktreeTaskManager tasks) { this.tasks = tasks; }\r\n\r\n @Tool(description = \"Create a new task on the shared task board\")\r\n public String taskCreate(\r\n @ToolParam(description = \"Task subject\") String subject,\r\n @ToolParam(description = \"Task description\") String description) {\r\n return tasks.create(subject, description);\r\n }\r\n\r\n @Tool(description = \"List all tasks with status, owner, and worktree binding\")\r\n public String taskList() { return tasks.listAll(); }\r\n\r\n @Tool(description = \"Get task details by ID\")\r\n public String taskGet(@ToolParam(description = \"Task ID\") int taskId) {\r\n return tasks.get(taskId);\r\n }\r\n\r\n @Tool(description = \"Update task status or owner\")\r\n public StLine truncated
|
||||
"source": "// === S12WorktreeIsolation.java ===\npackage io.mybatis.learn.s12;\r\n\r\nimport io.mybatis.learn.core.AgentRunner;\r\nimport io.mybatis.learn.core.tools.*;\r\nimport org.springframework.ai.chat.client.ChatClient;\r\nimport org.springframework.ai.chat.model.ChatModel;\r\nimport org.springframework.ai.tool.annotation.Tool;\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.CommandLineRunner;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\nimport java.nio.file.Path;\r\n\r\n/**\r\n * S12 - Worktree + Task Isolation:目录级隔离的并行任务执行\r\n *\r\n * TIPS: 对应Python s12_worktree_task_isolation.py。\r\n * 与S09-S11的团队通信路线不同,S12走「目录隔离」路线:\r\n * 任务(控制平面)+ Worktree(执行平面)。\r\n *\r\n * S09-S11: 多个agent → 消息队列通信 → 协议协调\r\n * S12: 单个agent → 多个worktree → 按目录隔离 → 按task ID协调\r\n *\r\n * 17个工具:base(4) + task(5) + worktree(8)\r\n *\r\n * 关键洞察:\"Isolate by directory, coordinate by task ID.\"\r\n */\r\n@SpringBootApplication(scanBasePackages = \"io.mybatis.learn.core\")\r\npublic class S12WorktreeIsolation implements CommandLineRunner {\r\n\r\n @Autowired\r\n private ChatModel chatModel;\r\n\r\n @Override\r\n public void run(String... args) throws Exception {\r\n Path workDir = Path.of(System.getProperty(\"user.dir\"));\r\n\r\n // 检测git仓库根目录\r\n Path repoRoot = detectRepoRoot(workDir);\r\n if (repoRoot == null) repoRoot = workDir;\r\n\r\n WorktreeTaskManager tasks = new WorktreeTaskManager(repoRoot.resolve(\".tasks\"));\r\n EventBus events = new EventBus(repoRoot.resolve(\".worktrees\").resolve(\"events.jsonl\"));\r\n WorktreeManager worktrees = new WorktreeManager(repoRoot, tasks, events);\r\n\r\n TaskTools taskTools = new TaskTools(tasks);\r\n WorktreeTools wtTools = new WorktreeTools(worktrees, events);\r\n\r\n String systemPrompt = \"You are a coding agent at \" + workDir + \". \"\r\n + \"Use task + worktree tools for multi-task work. \"\r\n + \"For parallel or risky changes: create tasks, allocate worktree lanes, \"\r\n + \"run commands in those lanes, then choose keep/remove for closeout. \"\r\n + \"Use worktree_events when you need lifecycle visibility.\";\r\n\r\n ChatClient chatClient = ChatClient.builder(chatModel)\r\n .defaultSystem(systemPrompt)\r\n .defaultTools(new BashTool(), new ReadFileTool(),\r\n new WriteFileTool(), new EditFileTool(),\r\n taskTools, wtTools)\r\n .build();\r\n\r\n System.out.println(\"Repo root for s12: \" + repoRoot);\r\n if (!worktrees.isGitAvailable()) {\r\n System.out.println(\"Note: Not in a git repo. worktree_* tools will return errors.\");\r\n }\r\n\r\n AgentRunner.interactive(\"s12\", input ->\r\n chatClient.prompt(input).call().content());\r\n }\r\n\r\n private static Path detectRepoRoot(Path cwd) {\r\n try {\r\n ProcessBuilder pb = new ProcessBuilder(\"git\", \"rev-parse\", \"--show-toplevel\");\r\n pb.directory(cwd.toFile());\r\n pb.redirectErrorStream(true);\r\n Process p = pb.start();\r\n String out = new String(p.getInputStream().readAllBytes()).trim();\r\n if (p.waitFor() == 0 && !out.isEmpty()) {\r\n Path root = Path.of(out);\r\n return root.toFile().exists() ? root : null;\r\n }\r\n } catch (Exception e) { /* ignore */ }\r\n return null;\r\n }\r\n\r\n // ---- 任务工具集(5个) ----\r\n\r\n public static class TaskTools {\r\n private final WorktreeTaskManager tasks;\r\n\r\n public TaskTools(WorktreeTaskManager tasks) { this.tasks = tasks; }\r\n\r\n @Tool(description = \"Create a new task on the shared task board\")\r\n public String taskCreate(\r\n @ToolParam(description = \"Task subject\") String subject,\r\n @ToolParam(description = \"Task description\") String description) {\r\n return tasks.create(subject, description);\r\n }\r\n\r\n @Tool(description = \"List all tasks with status, owner, and worktree binding\")\r\n public String taskList() { return tasks.listAll(); }\r\n\r\n @Tool(description = \"Get task details by ID\")\r\n public String taskGet(@ToolParam(description = \"Task ID\") int taskId) {\r\n return tasks.get(taskId);\r\n }\r\n\r\n @Tool(description = \"Update task status or owner\")\r\n public StLine truncated
|
||||
},
|
||||
{
|
||||
"id": "full",
|
||||
@@ -1676,7 +1676,7 @@
|
||||
"newTools": [
|
||||
"task"
|
||||
],
|
||||
"locDelta": -28
|
||||
"locDelta": -24
|
||||
},
|
||||
{
|
||||
"from": "s04",
|
||||
@@ -1696,7 +1696,7 @@
|
||||
"newTools": [
|
||||
"loadSkill"
|
||||
],
|
||||
"locDelta": 38
|
||||
"locDelta": 34
|
||||
},
|
||||
{
|
||||
"from": "s05",
|
||||
@@ -1747,7 +1747,7 @@
|
||||
"taskUpdate",
|
||||
"taskList"
|
||||
],
|
||||
"locDelta": 80
|
||||
"locDelta": 98
|
||||
},
|
||||
{
|
||||
"from": "s07",
|
||||
@@ -1769,7 +1769,7 @@
|
||||
"backgroundRun",
|
||||
"checkBackground"
|
||||
],
|
||||
"locDelta": -77
|
||||
"locDelta": -87
|
||||
},
|
||||
{
|
||||
"from": "s08",
|
||||
@@ -1806,7 +1806,7 @@
|
||||
"sendMessage",
|
||||
"readInbox"
|
||||
],
|
||||
"locDelta": 97
|
||||
"locDelta": 103
|
||||
},
|
||||
{
|
||||
"from": "s09",
|
||||
@@ -1836,7 +1836,7 @@
|
||||
"shutdownResponse",
|
||||
"planApproval"
|
||||
],
|
||||
"locDelta": 35
|
||||
"locDelta": 23
|
||||
},
|
||||
{
|
||||
"from": "s10",
|
||||
@@ -1864,7 +1864,7 @@
|
||||
"main",
|
||||
"main"
|
||||
],
|
||||
"locDelta": 7
|
||||
"locDelta": 5
|
||||
},
|
||||
{
|
||||
"from": "s11",
|
||||
@@ -1930,7 +1930,7 @@
|
||||
"worktreeKeep",
|
||||
"worktreeEvents"
|
||||
],
|
||||
"locDelta": 324
|
||||
"locDelta": 354
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user