标签JavaloopMcpAgentExecutorMCP filesystem自我纠错代码生成JUnit实时编译前置阅读三 Agent 并行调研concurrent 节点构建并发-汇聚式旅游规划助手适合人群已掌握McpAgentExecutor与loop()基础希望构建真实双 Agent 纠错流程的 Java 开发者一、问题单 Agent 写→测→改的局限用AgentExecutor让 LLM 写代码并自动修复是自然的想法但整个循环隐藏在 ReAct 黑盒里无法区分这一轮是写代码还是修复代码无法在执行测试这一步单独挂监控或回调无法用不同模型分别负责写和测无法把执行测试替换成真实的 CI 环境loop()节点让你把这个循环显式画在 flow 图里本篇更进一步把写和测分别封装成两个独立的McpAgentExecutor通过 MCP filesystem 完成真实文件读写通过javax.tools.JavaCompiler完成真实编译和 JUnit 执行。二、整体架构用户需求实现 isPalindrome忽略大小写和非字母数字字符 ↓ TranslateHandler保存需求到 ContextBus transmitMap ↓ ┌─────────────────────────────────────────────────────────────────┐ │ loop(condition, writeNode, testNode) 最多 3 轮 │ │ │ │ writeNode (Write Agent) │ │ McpAgentExecutor MCP filesystem │ │ 第 1 轮LLM 编写 Palindrome.java → write_file → /tmp/gen/ │ │ 第 2 轮起LLM 读取旧代码 → 修复 → write_file 覆写 │ │ │ │ testNode (Test Agent) │ │ McpAgentExecutor MCP filesystem compile_and_run 工具 │ │ 第 1 轮read_file → 理解实现 → write_file 写测试 → 编译运行 │ │ 第 2 轮起直接调 compile_and_run 重跑同一套测试 │ └─────────────────────────────────────────────────────────────────┘ ↓ TranslateHandler格式化最终输出 ↓ 实现文件 测试文件 PASS/FAIL 状态两个 Agent 各司其职Write Agent 只负责写代码Test Agent 只负责验证正确性。loop()是协调者ContextBus.transmitMap是它们的共享黑板。三、compile_and_run 工具真实编译与执行Test Agent 除了 MCP filesystem 工具还额外注入了一个compile_and_run工具。它做三件事staticclassJavaTestRunner{AgentTool(编译Java实现类和测试类用JUnit 4运行所有测试返回PASS/FAIL及详情)publicStringcompileAndRun(Param(实现类Java文件绝对路径)StringimplFile,Param(测试类Java文件绝对路径)StringtestFile){StringresultdoCompileAndRun(implFile,testFile);ContextBus.get().putTransmit(test_result,result);// 写入共享状态returnresult;}privateStringdoCompileAndRun(StringimplFile,StringtestFile){// 1. 调用 javax.tools.JavaCompiler 进程内编译classpath 含 JUnit JARJavaCompilercompilerToolProvider.getSystemJavaCompiler();StringclasspathSystem.getProperty(java.class.path);// ... 编译到 GEN_DIR ...// 2. 每次新建 URLClassLoader避免 JVM 缓存旧版 .classtry(URLClassLoaderloadernewURLClassLoader(newURL[]{newFile(GEN_DIR).toURI().toURL()},Thread.currentThread().getContextClassLoader())){Class?testClassloader.loadClass(PalindromeTest);ResultresultnewJUnitCore().run(testClass);// 3. 返回 PASS/FAIL 详情returnresult.wasSuccessful()?PASS: All result.getRunCount() tests passed ✅:buildFailReport(result);}}}关键设计机制说明javax.tools.JavaCompilerJDK 内置编译器进程内完成无需Runtime.execSystem.getProperty(java.class.path)把当前 classpath含 JUnit JAR传给javac -cpURLClassLoader每次新建多轮修复后总能加载最新.class避免缓存旧版本ContextBus.get().putTransmit(test_result, result)工具直接写共享状态loop condition 无需解析 LLM 的自然语言输出四、两个 Agent 的配置Write AgentMcpAgentExecutorwriteAgentMcpAgentExecutor.builder(chainActor).llm(ChatAliyun.builder().model(qwen3.6-plus).temperature(0f).build()).tools(mcpClient,filesystem)// read_file / write_file / ....systemPrompt(You are a Java implementation expert.\nTask: write or fix a Java class and save it to the specified file path.\nFollow these steps exactly, in order:\n 1. Do NOT include any package declaration.\n 2. The class must be public and have a public method matching the required signature.\n 3. Call write_file ONCE to save the file.\n 4. After write_file succeeds, call list_directory on the parent directory to confirm the file is listed.\n 5. Once you see the file in the listing, output one confirmation line and stop. Make NO further tool calls.).maxIterations(5).build();Write Agent 只有 MCP filesystem 工具职责单一把代码写到文件。写完后调用list_directory确认文件存在给 LLM 一个明确的收尾动作避免反复重写。Test AgentMcpAgentExecutortestAgentMcpAgentExecutor.builder(chainActor).llm(ChatAliyun.builder().model(qwen3.6-plus).temperature(0f).build()).tools(mcpClient,filesystem)// read_file / write_file / ....tools(runner)// compile_and_run 工具.systemPrompt(You are a Java testing expert. You will be given EXACTLY three tasks to execute in sequence.\nCRITICAL: Call each tool exactly once, in this order, then stop:\n CALL 1 — read_file: read the implementation file. Pass ONLY the path parameter, no other parameters.\n CALL 2 — write_file: write a JUnit 4 test class to the test file path.\n CALL 3 — compile_and_run: pass the implementation file path and test file path.\nAfter compile_and_run returns, output its result verbatim and make NO further tool calls.).maxIterations(12).build();Test Agent 有 MCP filesystem compile_and_run两类工具职责写测试、编译、运行、汇报结果。McpAgentExecutor.Builder支持链式调用多次.tools()MCP 工具和自定义工具可以共存。五、loop 驱动逻辑.loop(// conditiontest_result 为 FAIL 则继续最多 3 轮i-{StringtestResultContextBus.get().getTransmit(test_result);booleanfailedtestResult!nulltestResult.startsWith(FAIL);return(i0||failed)i3;},// 节点AWrite Agent 写/修复实现(Objectinput)-{StringreqContextBus.get().getTransmit(requirement);StringtestResultContextBus.get().getTransmit(test_result);StringprompttestResultnull?Write Palindrome.java ... save to IMPL_FILE:Fix IMPL_FILE to pass failures: testResult;returnwriteAgent.invoke(prompt);},// 节点B第 1 轮 Test Agent 写测试执行第 2 轮起直接调 runner 跳过 LLM(ObjectwriteResult)-{StringtestsWrittenContextBus.get().getTransmit(tests_written);if(testsWrittennull){ContextBus.get().putTransmit(tests_written,true);returntestAgent.invoke(read_file, write tests to TEST_FILE, compile_and_run);}else{returnrunner.compileAndRun(IMPL_FILE,TEST_FILE);// 直接执行跳过 LLM}})transmitMap 中的共享状态key写入方读取方用途requirement前置 TranslateHandlerWrite Agent / Test Agent每轮保持原始需求多轮均可访问test_resultcompile_and_run工具loop conditionstartsWith(FAIL)决定是否继续tests_writtentestNode lambdatestNode lambda下一轮标记测试已写后续轮次仅重跑不重写测试只写一次设计Test Agent 第 1 轮写测试文件第 2 轮起直接调用runner.compileAndRun()跳过 LLM避免测试用例随轮次漂移。六、执行流程示例 双Agent自我纠错代码生成 需求判断字符串是否为回文忽略大小写和非字母数字字符 --- 步骤1Write Agent 编写初始实现 --- [WriteAgent] tool call: write_file {path:/private/tmp/gen/Palindrome.java, ...} [WriteAgent 完成] I have written the Palindrome class to /private/tmp/gen/Palindrome.java... --- 步骤2Test Agent 编写测试并执行 --- [TestAgent] tool call: read_file {path:/private/tmp/gen/Palindrome.java} [TestAgent] tool call: write_file {path:/private/tmp/gen/PalindromeTest.java, ...} [TestAgent] tool call: compile_and_run {implFile:/private/tmp/gen/Palindrome.java,testFile:/private/tmp/gen/PalindromeTest.java} [TestAgent] observation: PASS: All 4 tests passed ✅ [TestAgent 完成] PASS: All 4 tests passed ✅ --- Loop 条件检查第2轮failedfalse继续false --- 最终结果 PASS: All 4 tests passed ✅ 测试状态PASS: All 4 tests passed ✅ 生成文件/private/tmp/gen/Palindrome.java /private/tmp/gen/PalindromeTest.java 如果第 1 轮测试失败Write Agent 在第 2 轮读取失败信息并修复实现Test Agent 直接重跑同一套测试直至通过。七、运行前置条件JDK非 JREcompile_and_run使用javax.tools.JavaCompilerJRE 中不含编译器Node.jsMCP filesystem 服务器通过npx启动mcp.server.config.jsonfilesystem服务器 args 配置为/private/tmpmacOS或/tmpLinuxALIYUN_KEY环境变量示例使用qwen3.6-plus八、总结本篇展示了 j-langchain 中双 Agent 自我纠错的完整模式职责分离Write Agent 只写代码Test Agent 只验证——每个McpAgentExecutor只暴露与职责匹配的工具真实闭环LLM 生成 → MCP 写文件 →javac编译 → JUnit 执行全链路无 mock状态透明compile_and_run工具直接写 ContextBusloop condition 读状态两个 Agent 无需互相感知测试稳定tests_written标志保证测试用例只写一次多轮验证的是实现本身这种两个专家 Agent loop 协调的模式可推广到任何生成→验证→修复场景SQL 生成执行验证、配置文件生成语法检查、接口代码生成集成测试。 相关资源完整代码Article20TwoAgentSelfCorrect.java方法twoAgentSelfCorrect()j-langchain GitHubhttps://github.com/flower-trees/j-langchain运行环境JDK 17、Node.js、ALIYUN_KEYqwen3.6-plus