# agentscope-demo **Repository Path**: dddpeter/agentscope-demo ## Basic Information - **Project Name**: agentscope-demo - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-31 - **Last Updated**: 2026-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AgentScope Java 2.0 GA Demo > 基于 [AgentScope Java 2.0.0 GA](https://github.com/agentscope-ai/agentscope-java) 的真实可运行 Demo。 > 模型走 **HQAI 网关**(OpenAI 兼容端点),核心演示 **ReAct 范式 + 工具调用 + 订单退款场景**。 ## 项目概览 这是一个 Spring Boot 应用,使用 AgentScope Java SDK 构建多轮对话智能体系统。通过 ReAct(Reason-Act-Observe)框架,Agent 可以自主规划、调用工具、观察结果并生成自然语言回复。项目完整实现了: - ✅ **真实 ReAct 推理**:`ReActAgent` + `streamEvents()`,自主"思考-行动-观察"循环 - ✅ **动态工具调用**:`@Tool` 注解声明工具,LLM 自主决定何时触发(订单查询/退款/时间/计算器/天气) - ✅ **SSE 流式输出**:思考链、工具调用、文本增量实时流式返回前端 - ✅ **会话状态管理**:`JsonFileAgentStateStore` 按 sessionId 持久化多轮上下文 - ✅ **配置驱动 Agent**:`agents.yml` 定义多个 Agent,无需改代码即可增改 - ✅ **极简聊天页**:Thymeleaf 单页应用,开箱即用 ## 内置 Agent 对照表 | Agent ID | 名称 | 描述 | 关联工具 | |---|---|---|---| | `order-assistant` | 订单助手 | 订单查询与退款处理(核心业务场景) | `query_order_status`, `execute_refund` | | `chat-basic` | 基础对话 | 最简 ReAct,无工具纯对话 | — | | `tool-test` | 工具调用演示 | 演示 LLM 自主决定调用工具 | `get_current_time`, `calculate_sum`, `get_weather` | 详见 `src/main/resources/config/agents.yml`。 ## 快速开始 ### 前置要求 - JDK 17+ - Maven 3.8+ - `HQAI_API_KEY` 环境变量(见下方) ### 1. 设置 API Key(安全敏感) ```bash export HQAI_API_KEY=sk-your-hqai-gateway-key ``` > ⚠️ **安全提示**:API Key 必须通过环境变量传递,**绝不要写入代码或配置文件**中。`application.yml` 中使用 `${HQAI_API_KEY:}` 引用,留空则默认从 env 读取。 ### 2. 编译与运行 ```bash # 编译 mvn clean compile # 或用 Maven Spring Boot 插件直接运行 mvn spring-boot:run ``` 启动成功后控制台将显示: ``` ======================================== AgentScope Java Demo 已启动! 浏览器打开 http://localhost:9090 SSE 接口 POST http://localhost:9090/chat/send 先 export HQAI_API_KEY=... 再启动 ======================================== ``` ### 3. 访问界面 浏览器打开:**http://localhost:9090** - 顶部下拉选择 Agent(订单助手 / 基础对话 / 工具演示) - 输入框输入消息,Enter 发送(Shift+Enter 换行) - 右侧看到 SSE 流式输出:思考过程 → 工具调用标记 → 最终答案 ### 4. API 测试(cURL SSE) **订单查询**(会触发 `query_order_status` 工具调用): ```bash curl -N -X POST http://localhost:9090/chat/send \ -H 'Content-Type: application/json' \ -d '{"message":"帮我查询订单 ORD-001 的状态","agentId":"order-assistant"}' ``` **退款流程**(ReAct 自动先查订单再退款): ```bash curl -N -X POST http://localhost:9090/chat/send \ -H 'Content-Type: application/json' \ -d '{"message":"帮我退款 ORD-001,原因是商品损坏","agentId":"order-assistant"}' ``` **其他接口**: | 方法 | 路径 | 描述 | |---|---|---| | GET | `/` | 聊天页面 | | POST | `/chat/send` | SSE 流式对话(核心) | | GET | `/api/agents` | Agent 列表 | | GET | `/api/agents/{id}/messages` | 某 Agent 的对话历史 | | GET | `/api/sessions` | 会话列表 | | POST | `/api/sessions` | 新建会话(需 body `{ "agentId": "..." }`) | | DELETE | `/api/sessions/{sessionId}` | 删除会话 | | GET | `/health` | 健康检查 | **SSE 事件类型**:`text`(文本增量)、`thinking`(思考过程)、`tool_start`/`tool_end`(工具调用)、`error`、`done`。 ## 工程结构详解 ``` agentscope-demo/ ├── pom.xml # Maven 构建文件 + AgentScope 2.0 GA 依赖 ├── README.md # 本文档 ├── src/ │ ├── main/ │ │ ├── java/com/hqins/agentscope/demo/ │ │ │ ├── AgentScopeDemoApplication.java # Spring Boot 启动类 │ │ │ ├── agent/ # Agent 相关 │ │ │ │ ├── AgentConfig.java # Agent 配置 POJO │ │ │ │ ├── AgentConfigService.java # agents.yml 加载器 │ │ │ │ ├── AgentFactory.java # ⭐ ReActAgent.builder() 构建核心 │ │ │ │ └── SamplePrompt.java # Prompt 示例模板 │ │ │ ├── controller/ # REST/SSE 控制器 │ │ │ │ └── AgentController.java # 入口端点 + SSE 流式处理 │ │ │ ├── model/ # 数据模型 │ │ │ │ ├── ModelFactory.java # ⭐ HQAI 网关 OpenAIChatModel 创建 │ │ │ │ ├── ChatRequest.java # 请求 DTO │ │ │ │ ├── ChatMessage.java # 对话消息实体 │ │ │ │ ├── ChatEvent.java # 事件实体(数据库用) │ │ │ │ └── Order.java # 订单模拟实体 │ │ │ ├── runtime/ # 运行时编排 │ │ │ │ ├── AgentRuntime.java # ⭐ AgentEvent → SSE Map 转换 │ │ │ │ ├── AgentEventMapper.java # 事件映射 │ │ │ │ └── AgentRuntimeFactory.java # Runtime 工厂 │ │ │ ├── service/ # 业务服务层 │ │ │ │ ├── AgentService.java # ⭐ 编排(会话/无状态 + 日志记录) │ │ │ │ ├── SessionManagerService.java # 会话上下文管理 │ │ │ │ ├── ChatHistoryRepository.java # 抽象仓库接口 │ │ │ │ └── InMemoryChatHistoryRepository.java # InMemory 实现 │ │ │ │ ├── OrderService.java # 订单服务(模拟内存存储) │ │ │ │ └── RefundService.java # 退款服务(模拟) │ │ │ └── tool/ # 工具集 │ │ │ │ ├── OrderTools.java # ⭐ 订单工具 (@Tool) │ │ │ │ ├── SimpleTools.java # 演示工具 (@Tool) │ │ │ │ └── ToolRegistry.java # @Tool 自动扫描注册器 │ │ │ └── ... │ │ └── resources/ │ │ ├── application.yml # Server + AgentScope 模型配置 │ │ └── config/agents.yml # Agent 定义(3 个 Agent) │ │ └── templates/chat.html # Thymeleaf 聊天页 │ └── test/ │ └── java/com/hqins/agentscope/demo/ │ └── AgentScopeDemoApplicationTests.java # Spring Boot 测试骨架 ├── target/ # Maven 编译输出 ├── .vscode/ # VS Code 配置 └── .zcode/ # ZCode 配置 ``` ## 核心模块详解 ### 1. AgentFactory — ReActAgent 构建器 核心代码骨架: ```java ReActAgent agent = ReActAgent.builder() .name(config.getName()) .description(config.getDescription()) .sysPrompt(config.getSystemPrompt()) .model(modelFactory.createModel(config.getModelName(), config.isStreaming())) .stateStore(stateStore) // InMemory 或 JsonFile(持久化) .defaultSessionId(agentId) .toolkit(toolkit) // 注册 @Tool 工具实例 .maxIters(10) // ReAct 最大轮数 .enablePendingToolRecovery(true) // 恢复未完成的工具调用 .build(); ``` - **会话模式**:`createAgentForSession()` 使用 `JsonFileAgentStateStore`,会话上下文跨请求持久化到 `~/.agentscope/demo-sessions/` - **无状态模式**:`createAgent()` 使用 `InMemoryAgentStateStore`,每次独立 ### 2. ModelFactory — HQAI 网关接入 关键点:AgentScope 提供 OpenAI 兼容 SPI,通过 `baseUrl` 覆盖指向自定义网关: ```java OpenAIChatModel model = OpenAIChatModel.builder() .apiKey(apiKey) // 来自 ${HQAI_API_KEY} .baseUrl("https://hqai-gw.e-hqins.com/openai/v1") // HQAI 网关 .modelName("minimax-token-plan/MiniMax-M2.7-highspeed") .stream(true) .formatter(new OpenAIChatFormatter()) // 消息格式序列化 .build(); ``` **切换模型/网关**:修改 `application.yml` 中 `agentscope.model.openai.*` 项,或调整 `ModelFactory` 创建对应模型的实现(如 DashScopeModel、OllamaChatModel)。 ### 3. OrderTools — 业务工具示例 工具方法使用 AgentScope 真实的 `@Tool` 和 `@ToolParam` 注解,LLM 在 ReAct 循环中根据语义自主识别是否需要调用: ```java @Component public class OrderTools { @Autowired private OrderService orderService; @Autowired private RefundService refundService; @Tool(name = "query_order_status", description = "根据订单号查询订单的状态、金额和下单时间。订单号形如 ORD-001。") public String queryOrderStatus(@ToolParam(name = "order_id", required = true) String orderId) { Order order = orderService.findByOrderId(orderId); if (order == null) return "未找到订单:" + orderId; return String.format("订单号:%s,状态:%s,金额:%s元,下单时间:%s", order.getOrderId(), order.getStatus(), formatAmount(order.getAmount()), order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); } @Tool(name = "execute_refund", description = "为指定订单执行退款。仅当订单状态为 PAID 或 SHIPPED 时允许退款;REFUNDED 表示已退过。") public String executeRefund( @ToolParam(name = "order_id", required = true) String orderId, @ToolParam(name = "reason", required = true) String reason) { Order order = orderService.findByOrderId(orderId); if (order == null) return "退款失败:未找到订单 " + orderId; String status = order.getStatus(); if ("REFUNDED".equalsIgnoreCase(status)) { return "订单 " + orderId + " 已退款过,无需重复操作。"; } if (!"PAID".equalsIgnoreCase(status) && !"SHIPPED".equalsIgnoreCase(status)) { return "订单 " + orderId + " 当前状态为 " + status + ",不允许退款。"; } BigDecimal refundAmount = order.getAmount(); boolean success = refundService.processRefund(orderId, reason); if (success) { orderService.updateStatus(orderId, "REFUNDED"); return String.format("退款成功:订单 %s 已退款 %s元,原因:%s。", orderId, formatAmount(refundAmount), reason); } return "退款失败:支付网关处理失败,请稍后重试。"; } } ``` ### 4. AgentService — 流式编排 ```java public Flux> createStreamFlux(String agentId, String message, String sessionId) { return Flux.defer(() -> { Msg userMsg = Msg.builder() .role(MsgRole.USER) .content(TextBlock.builder().text(message).build()) .build(); Flux> stream; if (sessionId != null && !sessionId.isBlank()) { // 会话模式:复用上下文 SessionManagerService.SessionContext ctx = sessionManagerService.getOrCreateSession(sessionId, agentId); AgentRuntime runtime = new AgentRuntime(ctx.getAgent(), agentId); stream = runtime.stream(userMsg) .doFinally(signal -> sessionManagerService.saveSession(ctx.getSessionId())); } else { // 无状态模式:每次新建 Agent AgentRuntime runtime = runtimeFactory.createRuntime(agentId); stream = runtime.stream(userMsg); } return recordChatTranscript(agentId, message, stream); }); } ``` `recordChatTranscript()` 累积 assistant 文本,在 `done` 事件后写入 `ChatHistoryRepository`。 ### 5. AgentRuntime — Event 转换 将 AgentScope 原生 `AgentEvent` 流转换为前端可理解的 Map 结构: ```java agent.streamEvents(userMsg) .handle((AgentEvent event, SynchronousSink> sink) -> { Map map = eventMapper.apply(event); if (map != null && !map.isEmpty()) sink.next(map); }) .concatWith(Mono.just(Map.of("type", "done"))) // 结尾标记完成 .doOnError(e -> log.error(...)); ``` 前端(`chat.js`)监听事件类型:`text`(渲染)、`tool_start`(显示工具标签)、`error`(报错)、`done`(结束)。 ## Agents.yml 配置说明 YAML 配置驱动,支持动态扩展 Agent: ```yaml agents: - agentId: order-assistant name: 订单助手 category: single description: 订单查询与退款处理(支持多轮 ReAct 推理 + 工具调用) systemPrompt: | 你是一个专业的订单客服助手。你可以使用以下工具: - query_order_status:根据订单号查询订单状态和金额 - execute_refund:为订单执行退款(需先查清订单状态) ... modelName: minimax-token-plan/MiniMax-M2.7-highspeed streaming: true userTools: - query_order_status - execute_refund samplePrompts: - prompt: "帮我查询订单 ORD-001 的状态" expectedBehavior: "调用 query_order_status 返回订单状态和金额" - prompt: "帮我退款 ORD-001,原因是商品损坏" expectedBehavior: "先查询订单,确认可退后调用 execute_refund,反馈退款结果" - agentId: chat-basic name: 基础对话 systemPrompt: | 你是一个友好的 AI 助手。请保持简洁、清晰地回答用户的问题。用中文回答。 modelName: minimax-token-plan/MiniMax-M2.7-highspeed streaming: true # 无 userTools,纯对话 - agentId: tool-test name: 工具调用演示 systemPrompt: | 你是一个拥有多种工具的 AI 助手。当需要时,请使用合适的工具来准确回答问题: - get_current_time:获取指定时区的当前时间 - calculate_sum:计算两个数的和 - get_weather:获取城市天气 modelName: minimax-token-plan/MiniMax-M2.7-highspeed streaming: true userTools: - get_current_time - calculate_sum - get_weather ``` ## ReAct 工作流示例(订单退款) ``` 用户: "帮我退款 ORD-001,原因是商品损坏" ↓ (SSE: type=thinking) Agent 思考:"用户需要退款,应先查询订单确认状态和金额" ↓ (SSE: type=tool_start, tool=query_order_status) Agent 行动:调用 query_order_status("ORD-001") ↓ (SSE: type=tool_end) Agent 观察:返回"订单号:ORD-001,状态:PAID,金额:299.00元,下单时间:2026-01-15 10:30" ↓ (SSE: type=thinking) Agent 思考:"订单状态 PAID 可退,现在调用 execute_refund" ↓ (SSE: type=tool_start, tool=execute_refund) Agent 行动:调用 execute_refund("ORD-001", "商品损坏") ↓ (SSE: type=tool_end) Agent 观察:退款服务返回 success=true,订单状态更新为 REFUNDED ↓ (SSE: type=text) Agent 回复:"退款成功:订单 ORD-001 已退款 299.00 元,原因:商品损坏。原路退回需 3-5 个工作日到账。" ``` ## 技术栈 | 组件 | 版本/说明 | |---|---| | AgentScope Java | 2.0.0 GA(core / openai-extension / harness / spring-boot-starter) | | Spring Boot | 3.5.14 | | Java | 17+ | | Maven | 3.8+ | | Project Reactor | Flux/Mono,SSE 流式编码 | | Thymeleaf | 前端模板引擎 | | Lombok | 消除样板代码(@Data, @Component, @Autowired 等) | | HQAI Gateway | OpenAI 兼容端点 https://hqai-gw.e-hqins.com/openai/v1 | | MiniMax 模型 | minimax-token-plan/MiniMax-M2.7-highspeed | ## 参考文档与链接 - 📖 **AgentScope 官方文档**:https://java.agentscope.io - 📂 **GitHub 主库**:https://github.com/agentscope-ai/agentscope-java - 📝 **AgentsScope Java 2.0 GA 发布说明**:查看 GitHub Releases - 🔧 **ToolRegistry 实现**:扫描所有 `@Component` 中含 `@Tool` 方法的类,按 name 去重注册到 `Toolkit` - 🗄️ **数据存储**:对话历史 H2 内存数据库(`ChatHistoryRepository`),会话状态 JSON 文件(`~/.agentscope/demo-sessions/`) ## 开发建议 1. **新增 Agent**:在 `agents.yml` 中添加 entry,在 `tool/` 目录创建新的工具类(带 `@Component` + `@Tool`),在 `AgentConfigService` 中确保配置可被加载。 2. **新增工具**:创建 `@Component` 类,方法加 `@Tool(name, description)`,参数加 `@ToolParam(required=true/false)`。工具名需在 `agents.yml` 的 `userTools` 中列出。 3. **持久化会话**:将 `AgentFactory` 中的 `InMemoryAgentStateStore` 替换为 `JsonFileAgentStateStore`,指定存储目录。 4. **模型切换**:修改 `application.yml` 的 `agentscope.model.openai.*`;如需切换 provider(如 DashScope),修改 `ModelFactory` 创建对应 `*ChatModel`。 5. **调试技巧**:设置 `logging.level.io.agentscope=DEBUG` 可查看完整的 Agent 内部事件流。 --- **License**: 本项目为 Demo 用途,遵循 AgentScope 项目的 MIT 许可证(具体见 upstream 仓库)。