From 8380280bb15501590b7a15ab840c57f0bbc0821f Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Sat, 20 Jun 2026 12:17:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(agent):=20=E6=9B=B4=E6=96=B0=20react-agent?= =?UTF-8?q?=20=E6=8C=87=E5=8D=97=EF=BC=881.0=E2=86=922.0=20RC3=20=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=EF=BC=89+=20=E6=BA=90=E7=A0=81=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E5=86=92=E7=83=9F=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 §1.5「从 1.0 升级到 agentscope 2.0(RC3)」章节(6 子节):版本与范围 (RC3-core 迁移 + HarnessAgent 全家桶 GA-deferred)、核心架构变化(无状态 agent 单例 + AgentStateStore + RuntimeContext)、配置键映射表(保留映射 / @Deprecated 启动告警 / 新增)、行为变化(workspace 单根、shell→Permission、 skill skillId 陷阱、Hook→Middleware、streamEvents)、业务侧零改动兼容、 指向 v2-api-findings。 按「保留既有内容 + 冲突处加 2.0(RC3) 变化注记」原则,在 §3/§5.1/§5.4/§5.6 加注记(process() 重写、session 包删除、热 Session 缓存失效、AgentSessionFactory SPI 删除)。 新增 BackCompatSmokeTest:镜像 guide §2.3 范式的 ReActAgentComponent 子类 (model()/systemPrompt()/userPrompt(),model() 返回 mock ModelSpec 产出 CannedReplyModel,避免真实凭据),最简 chain THEN(agent),execute2Resp, 断言 success + responseData 非空。把「业务子类核心用法零改动可运行」做成 回归测试。 校验:mvn -pl liteflow-testcase-el/liteflow-testcase-el-react-agent -am -Dtest=BackCompatSmokeTest -Dmaven.test.skip=false -Dsurefire.failIfNoSpecifiedTests=false test → BUILD SUCCESS,1/1 绿, core/test 编译 SUCCESS。未改 skipTests。 Co-Authored-By: Claude Fable 5 --- docs/liteflow-react-agent-guide.md | 129 ++++++++++++++++++ .../test/agent/v2/BackCompatAgentCmp.java | 63 +++++++++ .../test/agent/v2/BackCompatSmokeTest.java | 87 ++++++++++++ .../feature/backcompat/application.properties | 12 ++ .../resources/feature/backcompat/flow.el.xml | 6 + 5 files changed, 297 insertions(+) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatAgentCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatSmokeTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/flow.el.xml diff --git a/docs/liteflow-react-agent-guide.md b/docs/liteflow-react-agent-guide.md index 4d9a668a5..dc11b4257 100644 --- a/docs/liteflow-react-agent-guide.md +++ b/docs/liteflow-react-agent-guide.md @@ -13,6 +13,8 @@ > 当前仓库根版本:`2.16.0`。 > > 当前源码中 `liteflow-react-agent` 聚合模块的 `maven.compiler.source` / `target` 为 `17`,根 `compile-17+` profile 会在 JDK 17 及以上激活该模块。实际运行时还需要满足 agentscope-java 及具体模型 SDK 的运行要求。 +> +> **agentscope 运行时版本:`2.0.0-RC3`,要求 JDK 21+。** 从 `2.16.0` 起,`liteflow-react-agent` 底层已从 agentscope 1.0.12 迁移到 2.0.0-RC3。如果你是从 agentscope 1.0 升级上来的老用户,请先读 [§ 1.5 从 1.0 升级到 agentscope 2.0(RC3)](#15-从-10-升级到-agentscope-20rc3);新用户可直接跳到 [§ 2 快速开始](#2-快速开始)。 --- @@ -32,6 +34,120 @@ --- +## 1.5 从 1.0 升级到 agentscope 2.0(RC3) + +本节面向**已经基于 agentscope 1.0.12 用过 `liteflow-react-agent` 的老用户**,说明从 1.0 升级到 2.0.0-RC3 时的版本范围、核心架构变化、配置键映射、行为变化以及业务侧兼容性。新用户可跳过本节,直接看 [§ 2 快速开始](#2-快速开始)。 + +> **RC3 API 权威说明**:本节所有 API / 行为描述均以 `docs/superpowers/specs/v2-api-findings.md`(针对 agentscope `2.0.0-RC3` 真实 jar 实测的探针笔记)为准,与设计文档 `docs/superpowers/specs/2026-06-19-agentscope-v2-migration-design.md` 配套阅读。若本指南与 findings 冲突,以 findings 为准。 + +### 1.5.1 版本与范围 + +- **目标运行时**:agentscope `2.0.0-RC3`,要求 **JDK 21+**(react-agent 模块 `maven.compiler.target=17` 不变,但 agentscope 2.0 运行期需要 21)。 +- **RC3-core 迁移范围(本版本已落地)**:会话 / 状态层、middleware、permission、skill 仓库、流式事件(`streamEvents`)、TaskList、per-session interrupt。 +- **推迟到 GA follow-up**:agentscope 官方规划中基于 `HarnessAgent` 全家桶的增强能力——workspace 持久累积语义、隔离沙箱(Docker/K8s/E2E)、context compaction、Plan Mode、subagent 委派、self-learning skill loop——**当前 RC3 迁移版本未引入**,等 agentscope GA 后再 re-spike。本节凡是提到这些能力,都指“未来 GA follow-up”,不是 RC3 现状。 + +> **重要更正**:设计文档早期基于“v2 引入 `HarnessAgent`”的推测,但 RC3 实测**核心 agent 类仍是 `io.agentscope.core.ReActAgent`**(`io.agentscope.core.HarnessAgent` 在 RC3 中不存在)。RC3 把 v2 能力(`.stateStore(...)`、`.middleware(...)`、`.permissionContext(...)`、`.skillRepository(...)`、`.enableTaskList(...)` 等)**直接挂在 `ReActAgent.builder()` 上**。本指南凡描述底层 agent,均指 `ReActAgent`。 + +### 1.5.2 核心架构变化 + +| 维度 | 1.0(agentscope 1.0.12) | 2.0(RC3) | +| --- | --- | --- | +| agent 实例模型 | 为每个 `(conversationId, agentKey)` 缓存一个独立 `ReActAgent` 实例 + 一把 `ReentrantLock`,JVM 内 LRU/超时清理 | 每个 `ReActAgentComponent` 子类构建**一个无状态 `ReActAgent` 单例**,所有 `(conversationId, agentKey)` 调用复用同一实例,靠 `RuntimeContext` 路由;串行/并行/隔离由 v2 保证 | +| 会话 / 状态层 | 自建 `AgentSessionManager` + `AgentSession` + `AgentSessionFactory` SPI + 5 个 factory(`InMemory/LocalFile/Redis/Mysql/None`),返回 v1 `Session` | **自建 session 层整体删除**。改用 v2 `AgentStateStore`(`InMemory`/`JsonFile`/`Redis`/`MySQL`,可选 `OSS`)按 `(userId, sessionId)` 分桶;`agent.call(...)` 入口自动 load、出口自动 save | +| 标识映射 | `conversationId`(业务/对话,决定 workspace 子目录)+ `agentKey`(默认 `nodeId`,区分 agent 实例与记忆) | `conversationId` → v2 `RuntimeContext.userId`;`agentKey` → v2 `RuntimeContext.sessionId`;`nodeId` → `agent.name()`。两层标识语义保留 | +| workspace 布局 | `workspace.root//`(每会话一目录) | RC3-core 下 workspace 工具绑定 `workspace.root` 单根,conversationId 用作 `userId` 维度命名;HarnessAgent 持久累积语义随 GA follow-up 落地 | +| LRU 清理 | `maxSessions`/`idleTimeout`/`cleanupInterval` 控制热 session 淘汰 | agent 无实例缓存,**不再有 LRU 淘汰**;上述三键标 `@Deprecated`(启动告警),见 1.5.3 | + +`ReActAgentComponent#process()` 仍是 `final`,但内部大幅瘦身:配置读取、conversation/agentKey 解析、`RuntimeContext` 构造、agent 单例获取、`call(...)`、`handleReply(...)` 由框架统一完成;**业务侧不再需要、也无法**手动 load/save memory 或加锁——v2 在 `call()` 入口/出口自动处理。 + +### 1.5.3 配置键映射表 + +老 `liteflow.agent.*` 配置键**全部保留并映射**到 v2 概念,老用户改配置即可升级。分三类: + +**(a) 保留映射(语义保留,行为等价或近似)** + +| 老键 | v2 去向 | 备注 | +| --- | --- | --- | +| `workspace.root` | `ReActAgent.builder()` workspace 绑定 | 必填不变 | +| `workspace.auto-create` / `cleanup-*` / `max-file-bytes` / `max-list-size` | workspace / 文件工具配置 | 保留;`cleanup-*` 语义随 workspace 持久累积(GA follow-up)微调 | +| `openai.*` / `anthropic.*` / `gemini.*` / `dashscope.*`(含 `openai-compatible..*` 与 `anthropic-compatible..*`) | `ModelSpec` 凭据 → `agent.model(...)` | **完全保留**,凭据解析规则不变 | +| `session.memory.mode` = `NONE` / `JVM` / `LOCAL_FILE` / `REDIS` / `MYSQL` | `AgentStateStoreResolver` → `agent.stateStore(...)` | 保留;`NONE` = 不调用 `.stateStore(...)`(builder 默认 `null` 等价不落盘);`JVM` = `InMemoryAgentStateStore`;`LOCAL_FILE` = `JsonFileAgentStateStore`;`REDIS`/`MYSQL` 从对应 optional 扩展反射构造 | +| `session.memory.redis.*` / `mysql.*` | 对应 store 构造参数 | 保留(`bean-name`/`client-type`/`key-prefix`/`data-source-bean-name`/`database-name`/`table-name`/`create-if-not-exist`) | +| `shell.*`(`mode`/`whitelist`/`blacklist`/`timeout`/`max-output-bytes`) | v2 `PermissionEngine` + `ManagedShellCommandTool`(`ToolBase` 子类 + `matchRule`) | 保留键,语义重映射为命令级 Permission 规则(见 1.5.4) | +| `skills.*`(`enabled` / `path` / `strict`) | `agent.skillRepository(...)` + workspace `skills/` + `DynamicSkillMiddleware` | 保留(见 1.5.4 skill 陷阱) | +| `defaults.max-iterations` | `agent.maxIters(...)` | 保留 | +| `logging.react-enabled` | logging middleware 开关 | 保留 | + +**(b) `@Deprecated`(v2 已取代,启动时打印 WARN 告警,给一个版本缓冲)** + +| 老键 | 状态 | 说明 | +| --- | --- | --- | +| `session.max-sessions` | `@Deprecated` 启动 WARN | v2 agent 无实例缓存,LRU 上限无意义 | +| `session.idle-timeout` | `@Deprecated` 启动 WARN | 同上,热 session 超时清理不再发生 | +| `session.cleanup-interval` | `@Deprecated` 启动 WARN | 同上,清理线程不再存在 | +| `session.memory.load-on-first-use` | `@Deprecated` 启动 WARN | v2 每次 `call()` 自动 load,开关失效 | +| `session.memory.save-after-call` | `@Deprecated` 启动 WARN | v2 每次 `call()` 出口自动 save | +| `session.memory.save-on-error` | `@Deprecated` 启动 WARN | v2 异常路径也会 save | + +> 告警只在“用户把上述键设为非默认值”时打印一次(`AgentConfigDeprecationLogger#warnOnce`),全默认配置不告警,既有 agent 不受影响。详见 [§ 5.4 JVM 热 Session 配置](#54-jvm-热-session-配置) 的“2.0(RC3) 变化”注记。 + +**(c) 新增(RC3 起出现,默认值已对齐既有行为)** + +| 新键 | 默认 | 说明 | +| --- | --- | --- | +| `task-list.enabled` | `false` | `true` 时 `agent.enableTaskList(true)`,注册内置 `todo_write` 工具 + `TaskReminderMiddleware`(每个 reasoning step 前把任务列表注入提示词) | +| `filesystem.mode` | `LOCAL`(预留) | RC3 仅 `LOCAL` 等价(host shell);`SANDBOX_*` / `REMOTE` 等 HarnessAgent 沙箱模式推迟到 GA follow-up | + +### 1.5.4 行为变化(升级时需注意) + +**workspace(单根)**:RC3-core 下内置文件工具绑定 `workspace.root`(单根),不再像 1.0 那样为每个 conversation 创建独立子目录;conversationId 作为 v2 `userId` 维度参与命名。1.0 的“每会话一目录”物理布局**不自动迁移**——老数据无法复用,升级后等同全新 workspace。HarnessAgent 的 workspace 持久累积语义(`MEMORY.md` 累积、`agents//sessions/` 只增、`plans/` 跨 call 保留)随 GA follow-up 落地。 + +**Shell 工具**:`ManagedShellCommandTool` 从 1.0 的注解式 `@Tool` 改为 v2 `ToolBase` 子类,并覆写 `matchRule(content, input)`(取命令首 token 与 `content` `equals` 比对)。命令级白/黑名单由 v2 `PermissionEngine` 裁决,`PermissionConfigMapper` 把 `shell.*` 配置翻译成 `PermissionRule`: + +- `shell.mode=DISABLED` → `PermissionMode.DEFAULT` + 单条 catch-all `DENY` 规则,禁用整个 `execute_shell_command` 工具; +- `shell.mode=WHITELIST`(默认)→ `PermissionMode.DONT_ASK` + 每条白名单命令一条 `ALLOW` 规则(`ruleContent` = 裸首 token,如 `"ls"`);未命中白名单的命令经 default path + `DONT_ASK` 转 `DENY`(注意:不能用 catch-all `DENY` 兜底,否则会先于 allow 吞掉白名单——`DONT_ASK` 是 RC3 下表达“白名单”语义的正确方式); +- `shell.mode=BLACKLIST` → `PermissionMode.DEFAULT` + 每条黑名单命令一条 `DENY` 规则 + 一条 catch-all `ALLOW` 兜底(deny 先于 allow 评估,黑名单硬拒、其余放行)。 + +对外行为(白名单/黑名单/禁用/超时/输出截断)与 1.0 等价,配置键不变。 + +**skill 陷阱(重要)**:`SkillBox` → `agent.skillRepository(...)`(典型用 `FileSystemSkillRepository`,指向含 `/SKILL.md` 的目录)。`skills()` allow-list 映射到 v2 `SkillFilter`。**RC3 `SkillFilter` 按 `skillId` 过滤,不是裸 `name`**——`AgentSkill.getSkillId()` = `name + "_" + source`(如 `research_filesystem-...skills`)。框架在 resolver 层会先把你在 `skills()` 里写的裸 name 遍历解析成 repo 里真实的 skillId 列表,再 `SkillFilter.only(skillIds)`;**但如果你自己直接调 v2 `SkillFilter.only("research")`,会永不命中**。这是升级到 RC3 时最容易踩的坑。 + +**Hook → Middleware**:1.0 的三个内置 Hook 改为 v2 `MiddlewareBase`(洋葱型 4 stage:`onAgent`/`onReasoning`/`onActing`/`onModelCall` + 管道型 `onSystemPrompt`): + +- `ReActLoggingHook` → `LoggingMiddleware`(reason/act/error 日志,开关仍是 `logging.react-enabled`); +- `ChatUsageTrackingHook` → `ChatUsageMiddleware`(在 `onModelCall` 后读 message usage 累加,**`ctx().getChatUsage()` API 保留**); +- `SkillTrackingHook` → `SkillTrackingMiddleware`(在 `onActing` 监听 `load_skill_through_path` 工具调用,记录 `usedSkills()`)。 + +业务侧 `hooks()` 返回 `List` 的写法**保留兼容**(内部经 `HookMiddlewareAdapter` 转 middleware),但方法标 `@Deprecated(forRemoval)`;**推荐改用 `middlewares()` 返回 `List`**。1.0 的 `Hook`(`io.agentscope.core.hook.Hook`)在 v2 软弃用但仍可编译运行。 + +**`ctx().getChatUsage()` 的线程模型**:真实 vendor 模型下,`onModelCall` 在 `boundedElastic` 调度线程上被调用(不在 `process()` 的 HTTP 线程上)。ChatUsage 累加器走 **reactor `Context` 向上游传播**(`process()` 在订阅前 `contextWrite` 把累加器塞进 Context),保证任意调度线程上的 middleware 都能读到;同时保留 ThreadLocal 回退使无 reactor Context 的单元测试继续可用。1.0 的单线程 ThreadLocal 假设在 v2 不成立——若你自定义 middleware 累加 token,请走同样的 reactor Context 方案。 + +**流式**:`agent.stream(...)` → `agent.streamEvents(...)`(`Flux`,28 个类型化事件)。`AgentEventBridge` 把 v2 事件映射到 LiteFlow 对外 4 类 `FlowEvent` type(**type 字符串不变**,保下游兼容):`agent.reasoning` / `agent.tool_result` / `agent.summary` / `agent.result`,并可选透传 HITL 事件。没有注册 `ExecuteOption.eventListener(...)` 时仍走阻塞 `call()` 路径。详见 [§ 2.6 流式输出](#26-流式输出)。 + +### 1.5.5 业务侧兼容性(绝大多数组件零改动) + +**源码零改动即可在 v2 编译运行**的写法(覆盖绝大多数业务组件): + +- 继承 `ReActAgentComponent`,覆写 `model()` / `systemPrompt()` / `userPrompt()`([§ 2.3](#23-编写-agent-组件) 范式); +- 覆写 `tools()` / `skills()` / `enableSkills()` / `resolveConversationId()` / `agentKey()` / `maxIterations()` / `handleReply(Msg)` / `buildModel()` / `ctx()` / `getSlot()`; +- 覆写 `enableShellTool()` / `enableWorkspaceFileTools()` / `enableReActLogging()`(签名保留,语义微调为“是否启用对应工具组 / middleware”)。 + +**需要少量调整的写法**(少数代码): + +- 直接 `import` 并引用已重构的内部类(`AgentSessionManager` / `AgentSessionFactory` / 旧 `Hook` 三实现 / `SkillBoxFactory` / `SkillToolResolver`)——这些类已删除或重写; +- 自定义 `hooks()` 返回 `List`——保留兼容但标 `@Deprecated`,建议迁 `middlewares()`; +- 依赖 workspace“每会话一目录”物理布局(自定义工具按 `/` 约定读写文件)——RC3 单根布局下需调整路径约定。 + +> **源码兼容回归测试**:`liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatSmokeTest.java` 把“业务子类核心用法零改动可编译运行”做成回归用例——一个严格镜像 [§ 2.3](#23-编写-agent-组件) 范式的 `ReActAgentComponent` 子类(`model()` 返回 mock `ModelSpec` 产出 `CannedReplyModel`,避免真实凭据),最简 chain `THEN(agent)`,`execute2Resp` 执行,断言 success + `responseData` 非空。每次升级 agentscope 版本都应保证此测试绿。 + +### 1.5.6 进一步阅读 + +- RC3 API 权威探针笔记:`docs/superpowers/specs/v2-api-findings.md` +- 迁移设计文档:`docs/superpowers/specs/2026-06-19-agentscope-v2-migration-design.md` +- agentscope 2.0 官方变更日志:https://java.agentscope.io/v2/zh/docs/change-log.html + +--- + ## 2. 快速开始 ### 2.1 引入依赖 @@ -252,6 +368,13 @@ if (response.isSuccess()) { `ReActAgentComponent#process()` 是 `final`。框架在其中统一完成配置读取、conversation 解析、`agentKey` 解析、Session 获取、加锁、Agent 懒构建、调用、回复处理和 memory 保存。业务侧通过覆写受保护方法定制行为。 +> **2.0(RC3) 变化**:`process()` 内部已重写——不再手动 Session 获取/加锁/懒构建/memory 保存,改为构建无状态 `ReActAgent` 单例 + `RuntimeContext(userId=conversationId, sessionId=agentKey)` + `agent.call(...)`,由 v2 管 load/save/串行。受保护方法签名(下表)**保持不变**,仅以下几项语义微调: +> - `hooks()`:1.0 注册 agentscope `Hook`;RC3 标 `@Deprecated(forRemoval)`,内部经 `HookMiddlewareAdapter` 转 v2 `MiddlewareBase`。**推荐改用 `middlewares()` 返回 `List`**。 +> - `enableShellTool()` / `enableWorkspaceFileTools()`:签名保留,语义改为“是否在 `ReActAgent` 上启用对应工具组”(与 1.0 行为等价,shell 仍与 `shell.mode` 取逻辑与)。 +> - `enableReActLogging()`:改为控制 `LoggingMiddleware` 是否安装(开关仍是 `logging.react-enabled`)。 +> +> 详见 [§ 1.5](#15-从-10-升级到-agentscope-20rc3)。 + | 方法 | 是否必须 | 默认行为 | 说明 | | --- | --- | --- | --- | | `model()` | 是 | 无 | 返回 `ModelSpec`,由框架从 `AgentConfig` 解析凭据并构造 agentscope `Model` | @@ -531,6 +654,8 @@ protected Model buildModel() { - 同一把 `ReentrantLock`; - 同一个持久化 key,格式为安全化后的 `conversationId + "__" + agentKey`。 +> **2.0(RC3) 变化**:`AgentSessionManager` / `AgentSession` / `AgentSessionFactory` SPI 整体删除。改用无状态 `ReActAgent` 单例 + v2 `AgentStateStore`(按 `(userId=conversationId, sessionId=agentKey)` 分桶),`call(...)` 入口/出口自动 load/save,串行/并行隔离由 v2 保证。标识映射(`conversationId`→`userId`、`agentKey`→`sessionId`)与“同 conversation 共享 workspace、不同 agentKey 隔离记忆”的语义保留。详见 [§ 1.5.2](#152-核心架构变化)。 + 同一个 `conversationId` 下的不同 `agentKey` 默认拥有独立 Agent 和独立 memory,但共享同一个 workspace 子目录,便于多个 Agent 通过文件协作。 因此,同一个 `(conversationId, agentKey)` 下的调用会串行执行,避免多线程同时修改同一份 memory。不同 `agentKey` 可以并行执行,但如果共享 workspace,需要由业务自行避免写同名文件造成冲突。 @@ -624,6 +749,8 @@ protected String agentKey() { 注意:`AgentSessionManager` 在首次执行 Agent 组件时按当前 `liteflow.agent.*` 配置懒创建,已有热 Session 中的 Agent 也会被复用。运行中修改 memory、skills、模型、工具或 hook 配置后,已有 `(conversationId, agentKey)` 不一定立即体现新配置;通常需要使用新的 conversation / agentKey,或重启应用。 +> **2.0(RC3) 变化**:本节描述的热 Session 缓存模型(`max-sessions`/`idle-timeout`/`cleanup-interval` + LRU 淘汰 + `liteflow-agent-session-cleaner` 清理线程)**在 RC3 已失效**——agent 不再为每个 `(conversationId, agentKey)` 缓存独立实例,而是单例无状态,复用同一 `ReActAgent`。这三个键保留但标 `@Deprecated`,启动时若设为非默认值会打印一次 WARN(见 [§ 1.5.3](#153-配置键映射表))。键本身仍可写,但不再产生任何运行期效果。 + ### 5.5 memory 持久化模式 `liteflow.agent.session.memory.*` 控制 Agent memory 保存在哪里。它和热 Session 缓存是两件事:热缓存决定当前 JVM 里保留多久,memory 持久化决定重启或重新加载后能否恢复对话历史。 @@ -678,6 +805,8 @@ LiteFlow 不创建 JDBC 连接池,`DataSource` 也需要由业务应用提供 ### 5.6 自定义持久化后端(SPI) +> **2.0(RC3) 变化**:本节描述的 `AgentSessionFactory` SPI(及 `AgentSessionFactoryRegistry`、`META-INF/services/...AgentSessionFactory` 注册文件)**已在 RC3 删除**——它依赖 v1 已删的 `Session` 类型。RC3 下持久化后端改由 v2 `AgentStateStore`(`InMemory`/`JsonFile`/`Redis`/`MySQL`,可选 `OSS`)承担,由 `AgentStateStoreResolver` 按 `session.memory.mode` 映射。要接入 PostgreSQL、对象存储、加密 JSON 等其它后端,实现 `io.agentscope.core.state.AgentStateStore` 接口并在 `AgentStateStoreResolver` 中按自定义 mode 路由即可(见 [§ 1.5.2](#152-核心架构变化))。下方 1.0 写法仅作历史参考。 + 除内置的五种 `memory.mode` 外,框架还通过 `AgentSessionFactory` SPI 开放了持久化后端扩展点。如果要接入 PostgreSQL、对象存储、加密 JSON 等其它后端,可以实现该接口并在 `META-INF/services/com.yomahub.liteflow.agent.session.factory.AgentSessionFactory` 中注册: ```java diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatAgentCmp.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatAgentCmp.java new file mode 100644 index 000000000..f12efaaa5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatAgentCmp.java @@ -0,0 +1,63 @@ +package com.yomahub.liteflow.test.agent.v2; + +import com.yomahub.liteflow.agent.component.ReActAgentComponent; +import com.yomahub.liteflow.agent.model.ModelSpec; +import com.yomahub.liteflow.property.agent.AgentConfig; +import io.agentscope.core.model.Model; +import org.springframework.stereotype.Component; + +/** + * 业务子类源码兼容冒烟测试用的 {@link ReActAgentComponent} 子类, + * 严格镜像 {@code docs/liteflow-react-agent-guide.md} §2.3 范式: + * 只覆写 {@code model() / systemPrompt() / userPrompt()} 三个无参方法, + * 不使用 {@code buildModel()} 逃生舱、不依赖任何真实凭据。 + * + *

{@code model()} 返回一个 mock {@link ModelSpec}——其 {@code resolve(AgentConfig)} + * 直接产出 {@link CannedReplyModel}(确定性罐头回复)。这样既能验证「业务侧只覆写 + * {@code model()} 返回 {@link ModelSpec} 即可在 v2 上跑通」(即 guide §2.3 主路径), + * 又能避免任何网络 / 真实 LLM 调用与凭据依赖。 + * + *

关闭 shell / workspace 工具与 ReAct 内部日志,最小化 toolkit,让冒烟测试聚焦于 + * 「核心用法零改动可编译运行」这一回归目标。 + */ +@Component("backCompatAgent") +public class BackCompatAgentCmp extends ReActAgentComponent { + + /** 仅满足 {@code model()} 抽象签名;{@code resolve} 直接返回 mock 模型。 */ + @Override + @SuppressWarnings("rawtypes") + protected ModelSpec model() { + return new ModelSpec() { + @Override + public Model resolve(AgentConfig c) { + return new CannedReplyModel("backcompat"); + } + }; + } + + @Override + protected String systemPrompt() { + return "你是 LiteFlow ReAct Agent 的兼容冒烟测试助手,请用一句简短中文回答。"; + } + + @Override + protected String userPrompt() { + Object reqData = getSlot().getChainReqData(getSlot().getChainId()); + return reqData == null ? "" : reqData.toString(); + } + + @Override + protected boolean enableShellTool() { + return false; + } + + @Override + protected boolean enableWorkspaceFileTools() { + return false; + } + + @Override + protected boolean enableReActLogging() { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatSmokeTest.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatSmokeTest.java new file mode 100644 index 000000000..ac039c539 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/test/agent/v2/BackCompatSmokeTest.java @@ -0,0 +1,87 @@ +package com.yomahub.liteflow.test.agent.v2; + +import com.yomahub.liteflow.agent.component.ReActAgentComponent; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.agent.support.LiveTestSupport; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; + +import javax.annotation.Resource; + +/** + * Task 8.2 源码兼容冒烟测试:把「业务子类核心用法零改动可编译运行」固化成回归用例。 + * + *

测试目标:一个严格镜像 {@code docs/liteflow-react-agent-guide.md} §2.3 范式的 + * {@link ReActAgentComponent} 子类({@link BackCompatAgentCmp},只覆写 + * {@code model() / systemPrompt() / userPrompt()},{@code model()} 返回 mock {@link ModelSpec} + * 产出 {@link CannedReplyModel}),在最简 EL chain {@code THEN(backCompatAgent)} 下能 + * 经 {@link FlowExecutor#execute2Resp} 完整跑通——验证从 agentscope 1.0 升级到 + * 2.0.0-RC3 后,业务侧继承 {@link ReActAgentComponent} 的核心写法源码零改动仍可运行。 + * + *

无真实 LLM:mock 模型固定 emit 罐头回复,整个测试不需要任何 apikey / baseUrl, + * 不会因缺失凭据被跳过。 + * + *

断言: + *

    + *
  • {@code response.isSuccess()} 为 true;
  • + *
  • {@code slot.getResponseData()} 非空(默认 {@code handleReply} 写回路径正常);
  • + *
  • responseData 即 {@link CannedReplyModel#CANNED_REPLY},证明 model() → resolve → + * ReActAgent.call → handleReply 链路完整。
  • + *
+ */ +@TestPropertySource("classpath:/feature/backcompat/application.properties") +@SpringBootTest(classes = BackCompatSmokeTest.class) +@EnableAutoConfiguration +@ComponentScan("com.yomahub.liteflow.test.agent.v2") +public class BackCompatSmokeTest { + + @Resource + private FlowExecutor flowExecutor; + + @BeforeEach + public void resetRuntime() { + // 清空 ReactAgentFactory 进程内单例缓存,保证本测试以本类 cfg 重新构建 agent。 + LiveTestSupport.resetAgentSessionManager(); + } + + @Test + public void testBackCompatAgentCoreUsageRunsEndToEnd() { + String prompt = "用一句话介绍 LiteFlow ReAct Agent。"; + LiteflowResponse response = flowExecutor.execute2Resp("backCompatChain", prompt); + + Assertions.assertTrue(response.isSuccess(), + "chain failed: " + (response.getCause() == null + ? "" + : toString(response.getCause()))); + + // 默认 handleReply 把 reply.getTextContent() 写入 slot.responseData,必须非空。 + Object responseData = response.getSlot().getResponseData(); + Assertions.assertNotNull(responseData, + "agent reply must be propagated to slot.responseData"); + Assertions.assertEquals(CannedReplyModel.CANNED_REPLY, responseData.toString(), + "responseData must equal the canned reply (model() → resolve → call → handleReply)"); + + // conversationId 由 process() 写回 slot 后经 LiteflowResponse 透出。 + Assertions.assertNotNull(response.getConversationId(), + "conversationId must be propagated to LiteflowResponse"); + } + + private static String toString(Throwable t) { + StringBuilder sb = new StringBuilder(); + Throwable cur = t; + while (cur != null) { + sb.append(cur.getClass().getSimpleName()).append(": ").append(cur.getMessage()); + cur = cur.getCause(); + if (cur != null) { + sb.append(" || caused by: "); + } + } + return sb.toString(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/application.properties b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/application.properties new file mode 100644 index 000000000..0b9916724 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/application.properties @@ -0,0 +1,12 @@ +liteflow.rule-source=feature/backcompat/flow.el.xml +liteflow.print-banner=false + +# 最小 agent 配置:workspace 指向 tmp(CannedReplyModel 不实际写盘);shell=disabled; +# skills 关闭。本冒烟测试不接触真实 LLM,验证业务子类核心用法在 agentscope 2.0.0-RC3 下 +# 源码零改动可编译运行。 +liteflow.agent.workspace.root=target/wk/v2_backcompat_smoke +liteflow.agent.workspace.auto-create=true +liteflow.agent.shell.mode=disabled +liteflow.agent.defaults.max-iterations=3 +liteflow.agent.logging.react-enabled=false +liteflow.agent.skills.enabled=false diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/flow.el.xml new file mode 100644 index 000000000..de6ca115c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/resources/feature/backcompat/flow.el.xml @@ -0,0 +1,6 @@ + + + + THEN(backCompatAgent); + +