From 598f9bc9c21e61f535765de34f1df8548dadbb57 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Sat, 20 Jun 2026 00:21:09 +0800 Subject: [PATCH] =?UTF-8?q?test(agent):=20=E6=81=A2=E5=A4=8D=20react-agent?= =?UTF-8?q?=20=E6=B5=8B=E8=AF=95=E6=A8=A1=E5=9D=97=E7=BC=96=E8=AF=91?= =?UTF-8?q?=EF=BC=88support=20=E7=B1=BB=E4=BF=AE=E5=A4=8D=20+=20=E6=9C=AA?= =?UTF-8?q?=E9=87=8D=E5=BB=BA=E8=83=BD=E5=8A=9B=E6=B5=8B=E8=AF=95=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E5=8D=A0=E4=BD=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 2.2c:修复 Task 0 删符号导致 liteflow-testcase-el-react-agent test-compile 失败。 实际首轮仅 3 个测试源引用已删符号(support 类 BaseAgentLiveTest/LiveTestSupport 首轮即可编译,无级联): - ShellToolsAgentCmp:删 ManagedShellCommandTool import,userPrompt() 占位只返回 requestData,static 字段保留维持测试主体编译(TODO Task 3.2 重建自建 Shell 工具) - WorkspaceToolsAgentCmp:删 WorkspaceFileTools import,userPrompt() 占位只返回 requestData,static 字段保留(TODO Task 3.2 重建自建文件系统工具) - SkillsAgentCmp:handleReply() 中 usedSkills() 调用替换为 List.of() 占位 (TODO Task 4.1 重建 v2 skillRepository) 验收: - mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am test-compile → SUCCESS(91 源 0 error) - mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am compile → SUCCESS - v2 单元测试(AgentStateStoreResolverTest/ReactAgentFactoryTest/V2ApiProbe)编译通过 仅改测试源;未改生产代码 / skipTests / pom。 Co-Authored-By: Claude Fable 5 --- .superpowers/sdd/reports/task-2.2c-report.md | 89 +++++++++++++++++++ .../feature/shelltool/ShellToolsAgentCmp.java | 19 ++-- .../agent/feature/skills/SkillsAgentCmp.java | 10 ++- .../WorkspaceToolsAgentCmp.java | 41 +++++---- 4 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 .superpowers/sdd/reports/task-2.2c-report.md diff --git a/.superpowers/sdd/reports/task-2.2c-report.md b/.superpowers/sdd/reports/task-2.2c-report.md new file mode 100644 index 000000000..a95c4ac5c --- /dev/null +++ b/.superpowers/sdd/reports/task-2.2c-report.md @@ -0,0 +1,89 @@ +# Task 2.2c Report — 恢复 react-agent 测试模块编译 + +**任务:** Task 0 删除 17 个 v2-broken 源类 + 从 `ReActAgentComponent` 移除 `usedSkills()`, +导致 `liteflow-testcase-el/liteflow-testcase-el-react-agent` 测试模块 test-compile 失败。 +本任务**只修测试源**让 `mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am test-compile` 通过。 + +## 实际破坏范围(与 brief 预估的偏差) + +Brief 预估"约 40 个测试源引用已删符号"。**实测首轮 `test-compile` 仅 7 个编译错误,集中在 3 个文件**: + +| 文件 | 已删符号引用 | +|---|---| +| `feature/shelltool/ShellToolsAgentCmp.java` | `import ...agent.tool.ManagedShellCommandTool` + `new ManagedShellCommandTool(...)`(userPrompt 内) | +| `feature/workspacetools/WorkspaceToolsAgentCmp.java` | `import ...agent.tool.WorkspaceFileTools` + `new WorkspaceFileTools(...)`(userPrompt 内) | +| `feature/skills/SkillsAgentCmp.java` | `usedSkills()`(handleReply 内,已从父类移除) | + +其余 ~85 个测试源(含共享 support 类、platform 测试、basicchain/customtool/handlereply/agentkey/ +conversationid/maxiterations/buildmodel/multiagent/whenparallel/ifrouting/springbeantool 等 feature 测试、 +v2 单元测试)**首轮即可编译**——它们覆写的 `enableWorkspaceFileTools()` / `enableShellTool()` / +`enableReActLogging()` / `hooks()` / `tools()` / `skills()` 等方法签名在 `ReActAgentComponent` 上仍存在 +(只是体被 Task 0 改成空实现或读 config),不是已删符号。 + +## 修了哪些 support 类 + +**无。** 共享 support 类 `BaseAgentLiveTest` / `LiveTestSupport` / `LiveTestEnv` 首轮即可编译: +- `LiveTestSupport.resetAgentSessionManager()` 对 `ReActAgentComponent$AgentSessionManagerHolder#resetForTesting` + 的引用是**反射字符串**(`Class.forName(...)`),编译期不解析,运行期才查 holder——而该 holder + 生产代码里是否仍存在属 Task 2.3 的范畴(Task 2.3 in_progress),不影响本任务 test-compile 门禁。 +- `compatibleCustomModel()` 依赖的 `OpenAICompatible.custom(...)` 仍存在。 + +因此 support 类无需修改,级联在首轮即不存在。 + +## 降级清单(占位 + TODO 任务号) + +3 个组件文件降级为可编译占位(保留类、保留 `@Component` 与所有仍有效的 override、保留测试主体 +依赖的 static 字段,仅替换引用已删符号的方法体): + +| 文件 | 降级做法 | TODO 任务号 | +|---|---|---| +| `feature/shelltool/ShellToolsAgentCmp.java` | 删 `ManagedShellCommandTool` import;`userPrompt()` 改为只返回 chain requestData;`PWD_OUTPUT`/`BLOCKED_OUTPUT`/`WORKSPACE` 字段保留(ShellToolModesFeatureTest 仍引用)但不再被填充;原调用逻辑以注释保留 | **Task 3.2**(RC3-core 重建自建 Shell 工具) | +| `feature/workspacetools/WorkspaceToolsAgentCmp.java` | 删 `WorkspaceFileTools` import;`userPrompt()` 改为只返回 chain requestData;`TRUNCATED_READ`/`LIST_RESULT`/`DELETED`/`RELATIVE_ESCAPE`/`ABSOLUTE_ESCAPE` 字段保留(WorkspaceToolsFeatureTest 仍引用)但不再被填充;原调用逻辑以注释保留 | **Task 3.2**(RC3-core 重建自建文件系统工具) | +| `feature/skills/SkillsAgentCmp.java` | `handleReply()` 中 `usedSkills()` 调用替换为 `USED_SKILLS_SNAPSHOT.set(List.of())` 占位;原调用以注释保留 | **Task 4.1**(v2 skillRepository 重建组件级已用 skills 读取) | + +**未采用 `Assumptions.assumeFalse` 跳过的原因:** 这 3 个文件是组件类(无 `@Test` 方法), +不是测试主体;占位发生在被测生产代码路径(`userPrompt()`/`handleReply()`),无法用 assume 跳过。 +真正的 `@Test` 方法在 `ShellToolModesFeatureTest` / `WorkspaceToolsFeatureTest` / `SkillsFeatureTest`, +它们首轮即可编译、其断言引用的 static 字段仍存在,故保持原样(运行期会因 `process()` stub 失败 +或因缺凭据被 `LiveTestSupport.applyCompatibleCustomOrSkip` 跳过,符合 brief"不强求跑通,只要编译通过")。 + +## 保持可用的测试(未改动,首轮即编译) + +- 共享 support:`BaseAgentLiveTest`、`LiveTestSupport`、`LiveTestEnv` +- platform 全量:anthropic / anthropiccompatible / dashscope / deepseek / gemini / glm / kimi / + minimax / openai / openaicompatible +- feature:basicchain、customtool、handlereply、agentkey、conversationid、maxiterations、 + buildmodel、multiagent、whenparallel、ifrouting、springbeantool、chatusage、hook、 + memorypersistence、sessionreuse、multiturn、streaming +- v2 单元测试:`AgentStateStoreResolverTest`、`AgentStateStoreResolverExtTest`、 + `ReactAgentFactoryTest`、`HarnessFixture`、`V2ApiProbe` + +> 注:chatusage/hook/memorypersistence/sessionreuse/multiturn/streaming 等 feature 测试虽可编译, +> 但运行期依赖的能力(ChatUsage middleware、Hook 注册、AgentStateStore 接入、流式事件桥接) +> 尚在 Task 5.1/6.1/2.3/9 重建中,运行期会因 `process()` stub 或缺凭据失败/跳过——这超出 +> 本任务"test-compile 通过"的验收门禁,待对应重建任务完成后由 Task 9 全量回归恢复。 + +## 验收门禁结果 + +``` +mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am test-compile + → Compiling 91 source files ... 0 errors + → BUILD SUCCESS(reactor 全模块 SUCCESS,含 liteflow-react-agent-core 及 4 个 vendor 模块) + +mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am compile + → BUILD SUCCESS(生产 compile 未受影响) +``` + +v2 单元测试(`AgentStateStoreResolverTest`、`ReactAgentFactoryTest`、`V2ApiProbe`)含在 91 个 +编译源内,编译通过。 + +## 改动范围确认 + +- 仅改 3 个测试源(`src/test/java/.../feature/{shelltool,workspacetools,skills}/*AgentCmp.java`)。 +- 未改 `liteflow-react-agent-core` 生产代码、未改 skipTests、未改 pom。 +- diff stat:3 files changed, 44 insertions(+), 26 deletions(-)。 + +## commit + +`test(agent): 恢复 react-agent 测试模块编译(support 类修复 + 未重建能力测试降级占位)` +(SHA 见 git log;未 push) diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/shelltool/ShellToolsAgentCmp.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/shelltool/ShellToolsAgentCmp.java index ed4bd6794..668906fb4 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/shelltool/ShellToolsAgentCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/shelltool/ShellToolsAgentCmp.java @@ -2,7 +2,6 @@ package com.yomahub.liteflow.test.agent.feature.shelltool; import com.yomahub.liteflow.agent.component.ReActAgentComponent; import com.yomahub.liteflow.agent.model.ModelSpec; -import com.yomahub.liteflow.agent.tool.ManagedShellCommandTool; import com.yomahub.liteflow.test.agent.support.LiveTestSupport; import io.agentscope.core.hook.Hook; import org.springframework.stereotype.Component; @@ -11,8 +10,14 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** - * 开启 Shell 工具的 Agent,在 userPrompt 中直接调用 {@link ManagedShellCommandTool} + * 开启 Shell 工具的 Agent,在 userPrompt 中直接调用受管 Shell 工具 * 验证 DISABLED / BLACKLIST / WHITELIST 三种模式下的工具行为。 + * + *

v2 迁移期占位(Task 2.2c):原 1.0 的 {@code ManagedShellCommandTool} + * 已在 Task 0 删除,待 Task 3.2 在 RC3-core 下重建自建 Shell 工具后恢复本 userPrompt 的 + * 直接调用逻辑。当前 {@code userPrompt()} 仅返回 chain requestData,{@code PWD_OUTPUT} / + * {@code BLOCKED_OUTPUT} 字段保留以维持测试主体 {@link ShellToolModesFeatureTest} 编译, + * 但其断言在 Task 3.2 重建前无意义(测试整体也会因 {@code process()} stub 失败/跳过)。 */ @Component("shellToolsAgent") public class ShellToolsAgentCmp extends ReActAgentComponent { @@ -67,10 +72,12 @@ public class ShellToolsAgentCmp extends ReActAgentComponent { @Override protected String userPrompt() { - ManagedShellCommandTool tool = new ManagedShellCommandTool(ctx().getWorkspaceDir(), agentConfig()); - WORKSPACE.set(ctx().getWorkspaceDir().toAbsolutePath().normalize().toString()); - PWD_OUTPUT.set(tool.executeCommand("pwd")); - BLOCKED_OUTPUT.set(tool.executeCommand("rm -rf /")); + // TODO(Task 3.2): 1.0 ManagedShellCommandTool 已删除,待 RC3-core 下重建自建 Shell 工具后 + // 恢复以下直接调用逻辑(PWD_OUTPUT / BLOCKED_OUTPUT / WORKSPACE 的真实填充): + // ManagedShellCommandTool tool = new ManagedShellCommandTool(ctx().getWorkspaceDir(), agentConfig()); + // WORKSPACE.set(ctx().getWorkspaceDir().toAbsolutePath().normalize().toString()); + // PWD_OUTPUT.set(tool.executeCommand("pwd")); + // BLOCKED_OUTPUT.set(tool.executeCommand("rm -rf /")); Object reqData = getSlot().getChainReqData(getSlot().getChainId()); return reqData == null ? "" : reqData.toString(); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/skills/SkillsAgentCmp.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/skills/SkillsAgentCmp.java index 1cfdaddd6..2d1059185 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/skills/SkillsAgentCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/skills/SkillsAgentCmp.java @@ -13,6 +13,11 @@ import java.util.concurrent.atomic.AtomicReference; /** * 验证 skills.enabled=true 时 {@code load_skill_through_path} 工具会被注册到 Agent。 * 是否真的 load 取决于模型是否选择调用工具;本测试只关注工具集与组件级 skills() 过滤。 + * + *

v2 迁移期占位(Task 2.2c):原 1.0 的 {@code usedSkills()} 已从 + * {@link com.yomahub.liteflow.agent.component.ReActAgentComponent} 移除,待 Task 4.1 + * 重建 v2 skillRepository 后恢复组件级已用 skills 的读取语义。当前 {@code handleReply} + * 把 {@code USED_SKILLS_SNAPSHOT} 填成空 List 占位,维持 {@link SkillsFeatureTest} 编译。 */ @Component("skillsAgent") public class SkillsAgentCmp extends ReActAgentComponent { @@ -81,7 +86,10 @@ public class SkillsAgentCmp extends ReActAgentComponent { @Override protected void handleReply(Msg reply) { - USED_SKILLS_SNAPSHOT.set(usedSkills()); + // TODO(Task 4.1): 1.0 usedSkills() 已从 ReActAgentComponent 移除,待 v2 skillRepository + // 重建后恢复组件级已用 skills 读取: + // USED_SKILLS_SNAPSHOT.set(usedSkills()); + USED_SKILLS_SNAPSHOT.set(List.of()); super.handleReply(reply); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/workspacetools/WorkspaceToolsAgentCmp.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/workspacetools/WorkspaceToolsAgentCmp.java index 079e6bc8b..e522976a9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/workspacetools/WorkspaceToolsAgentCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/feature/workspacetools/WorkspaceToolsAgentCmp.java @@ -2,7 +2,6 @@ package com.yomahub.liteflow.test.agent.feature.workspacetools; import com.yomahub.liteflow.agent.component.ReActAgentComponent; import com.yomahub.liteflow.agent.model.ModelSpec; -import com.yomahub.liteflow.agent.tool.WorkspaceFileTools; import com.yomahub.liteflow.test.agent.support.LiveTestSupport; import io.agentscope.core.hook.Hook; import org.springframework.stereotype.Component; @@ -11,8 +10,15 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** - * 开启 workspace 文件工具的 Agent,并在 userPrompt 中直接调用 {@link WorkspaceFileTools} + * 开启 workspace 文件工具的 Agent,并在 userPrompt 中直接调用受管 workspace 文件工具 * 验证其行为(read/write/list/delete/path-escape),使断言不依赖模型是否真的调用工具。 + * + *

v2 迁移期占位(Task 2.2c):原 1.0 的 {@code WorkspaceFileTools} + * 已在 Task 0 删除,待 Task 3.2 在 RC3-core 下重建自建文件系统工具后恢复本 userPrompt 的 + * 直接调用逻辑。当前 {@code userPrompt()} 仅返回 chain requestData,{@code TRUNCATED_READ} / + * {@code LIST_RESULT} / {@code DELETED} / {@code RELATIVE_ESCAPE} / {@code ABSOLUTE_ESCAPE} + * 字段保留以维持测试主体 {@link WorkspaceToolsFeatureTest} 编译,但其断言在 Task 3.2 + * 重建前无意义(测试整体也会因 {@code process()} stub 失败/跳过)。 */ @Component("workspaceToolsAgent") public class WorkspaceToolsAgentCmp extends ReActAgentComponent { @@ -72,23 +78,20 @@ public class WorkspaceToolsAgentCmp extends ReActAgentComponent { @Override protected String userPrompt() { - WorkspaceFileTools tools = new WorkspaceFileTools(ctx().getWorkspaceDir(), agentConfig()); - tools.writeFile("notes/a.txt", "abcdef"); - tools.writeFile("notes/b.txt", "ghijkl"); - TRUNCATED_READ.set(tools.readFile("notes/a.txt")); - LIST_RESULT.set(tools.listFiles("notes")); - tools.deleteFile("notes/b.txt"); - DELETED.set(!java.nio.file.Files.exists(ctx().getWorkspaceDir().resolve("notes/b.txt"))); - try { - tools.readFile("../escape.txt"); - } catch (SecurityException e) { - RELATIVE_ESCAPE.set(e.getMessage()); - } - try { - tools.readFile("/tmp/escape.txt"); - } catch (SecurityException e) { - ABSOLUTE_ESCAPE.set(e.getMessage()); - } + // TODO(Task 3.2): 1.0 WorkspaceFileTools 已删除,待 RC3-core 下重建自建文件系统工具后 + // 恢复以下直接调用逻辑(TRUNCATED_READ / LIST_RESULT / DELETED / RELATIVE_ESCAPE / + // ABSOLUTE_ESCAPE 的真实填充): + // WorkspaceFileTools tools = new WorkspaceFileTools(ctx().getWorkspaceDir(), agentConfig()); + // tools.writeFile("notes/a.txt", "abcdef"); + // tools.writeFile("notes/b.txt", "ghijkl"); + // TRUNCATED_READ.set(tools.readFile("notes/a.txt")); + // LIST_RESULT.set(tools.listFiles("notes")); + // tools.deleteFile("notes/b.txt"); + // DELETED.set(!java.nio.file.Files.exists(ctx().getWorkspaceDir().resolve("notes/b.txt"))); + // try { tools.readFile("../escape.txt"); } + // catch (SecurityException e) { RELATIVE_ESCAPE.set(e.getMessage()); } + // try { tools.readFile("/tmp/escape.txt"); } + // catch (SecurityException e) { ABSOLUTE_ESCAPE.set(e.getMessage()); } Object reqData = getSlot().getChainReqData(getSlot().getChainId()); return reqData == null ? "" : reqData.toString(); }