Appearance
AgentScope-Builder 启动原理详解
本文档面向初学者,从零开始拆解 agentscope-builder 项目从构建到运行的每一个环节。
一、项目概览
1.1 Builder 是什么?
AgentScope-Builder 是一个 Agent 构建平台,提供可视化 Web UI 让用户创建、配置和管理 AI Agent。它是一个典型的前后端一体项目:
| 层 | 技术 | 说明 |
|---|---|---|
| 后端 | Spring Boot WebFlux (响应式) | Netty 服务器、JPA 数据持久化、JWT 认证 |
| 前端 | React + Vite SPA | 打包为静态资源嵌入后端 JAR |
| Agent 框架 | HarnessAgent | 多租户隔离的 Agent 运行时 |
| 模型 | DashScope qwen-max (默认) | 支持切换 OpenAI / Anthropic 等 |
| 数据库 | 嵌入式 H2 (默认) | 支持 MySQL / PostgreSQL |
1.2 核心技术栈
┌─────────────────────────────────────────────────┐
│ 浏览器 → http://localhost:8080 │
│ ┌───────────────┐ ┌────────────────────────┐ │
│ │ React SPA │ │ Spring Boot WebFlux │ │
│ │ (前端页面) │←│ REST API + SSE 流式 │ │
│ └───────────────┘ └────────────────────────┘ │
│ │ │
│ ├─ JWT 认证 (Security) │
│ ├─ JPA (H2/MySQL) │
│ └─ BuilderBootstrap │
│ │ │
│ ├─ HarnessAgent │
│ ├─ SessionsTool │
│ ├─ ChatUiChannel │
│ └─ DashScopeChatModel │
│ │ │
│ ↓ │
│ DashScope API │
└─────────────────────────────────────────────────┘二、构建过程详解
2.1 构建命令
bash
# 从项目根目录构建 builder(-am 表示同时构建依赖模块)
mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-builder -am2.2 Maven 构建流程
Builder 是一个 多模块项目,不能独立构建。-am(also-make)会让 Maven 自动先构建 builder 所依赖的上游模块:
构建顺序(Reactor 顺序):
1. agentscope-parent (根 POM,定义 BOM)
2. agentscope-core (核心框架)
3. agentscope-harness (HarnessAgent 运行时)
4. agentscope-extensions (扩展模块父 POM)
5. agentscope-extensions-model-dashscope (DashScope 模型)
6. agentscope-extensions-mysql (MySQL/JDBC 存储)
7. agentscope-extensions-channel-* (IM 频道:钉钉/飞书/企微/GitHub/GitLab)
8. agentscope-extensions-skill-git-repository (技能仓库)
9. agentscope-extensions-nacos-skill (Nacos 技能)
10. agentscope-builder ← 最终目标为什么不能单独构建? Builder 的 pom.xml 引用了
agentscope-bom和agentscope-dependencies-bom两个 BOM,它们定义了所有 agentscope 模块的版本号。这些是 SNAPSHOT 版本,只存在于项目内部(本地 Maven 仓库中没有),必须通过 Maven reactor 从根目录一起构建。
2.3 Fat JAR 与前端打包
Builder 使用 spring-boot-maven-plugin 生成 可执行 Fat JAR(与 codingagent 不同,codingagent 的 <skip>true</skip> 跳过了 repackage):
构建产物:
agentscope-builder-2.0.0-SNAPSHOT.jar ← Fat JAR(可直接 java -jar 运行)
agentscope-builder-2.0.0-SNAPSHOT.jar.original ← 原始 thin JAR(不含依赖)前端打包 通过 frontend-maven-plugin 在 Maven 的 generate-resources 阶段自动完成:
前端构建步骤:
1. install-node-and-npm → 安装 Node v20.19.2 + npm 10.9.2 到 target/ 目录
2. npm install → 安装前端依赖 (package.json)
3. npm run build → Vite 构建 → 输出到 frontend/dist/
→ Maven 将 frontend/dist/ 复制到 src/main/resources/static/
→ 最终打包进 Fat JAR 的 classpath:/static/ 中这意味着:你不需要手动安装 Node.js 或运行 npm!Maven 会自动下载 Node 并构建前端。生成的 Fat JAR 包含了前端所有静态资源。
三、启动过程详解
3.1 启动命令
bash
# 一键启动(端口 8080)
java -jar agentscope-builder-2.0.0-SNAPSHOT.jar
# 指定不同端口
java -jar agentscope-builder-2.0.0-SNAPSHOT.jar --server.port=8082
# 切换到 MySQL 数据库
java -jar agentscope-builder-2.0.0-SNAPSHOT.jar --spring.profiles.active=jdbc3.2 启动全景流程(7 个阶段)
JVM 启动
│
▼
┌─────────────────────────────────────────┐
│ 阶段 1: Spring Boot 自动配置 │
│ @SpringBootApplication 扫描组件 │
│ 加载 application.yml │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 2: JPA + 数据库初始化 │
│ H2 DataSource → Hibernate DDL → 种子 │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 3: Spring Bean 组装 │
│ BuilderConfig → Model → BaseStore │
│ → BuilderBootstrap → SecurityConfig │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 4: BuilderBootstrap 核心 3-Phase │
│ Phase1: Session 基础设施 │
│ Phase2: Agent 构建 + SessionsTool │
│ Phase3: Gateway + Channel 注册 │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 5: Workspace 脚手架 │
│ 自动生成 agentscope.json + scaffold │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 6: Security + Netty 服务器启动 │
│ JWT 过滤器 + CORS + SPA fallback │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 阶段 7: 应用就绪 │
│ http://localhost:8080 可访问 │
└─────────────────────────────────────────┘阶段 1: Spring Boot 自动配置
入口类:io.agentscope.builder.BuilderApp
java
@SpringBootApplication
public class BuilderApp {
public static void main(String[] args) {
SpringApplication.run(BuilderApp.class, args);
}
}@SpringBootApplication 是三个注解的组合:
@SpringBootConfiguration→ 标记为配置类@EnableAutoConfiguration→ 自动配置 Spring Boot 组件@ComponentScan→ 扫描io.agentscope.builder包下所有组件
配置文件加载顺序:
application.yml(基础配置,总是加载)application-{profile}.yml(仅当激活对应 profile 时加载)
阶段 2: JPA + 数据库初始化
2.1 DataSource 创建
默认使用嵌入式 H2 文件数据库:
数据库路径: ${user.home}/.agentscope-builder/db
JDBC URL: jdbc:h2:file:${user.home}/.agentscope-builder/db;AUTO_SERVER=TRUE;MODE=MYSQL
驱动: org.h2.Driver
用户: sa / 密码: 空为什么放在 user.home 而不是项目目录? 这是故意设计——数据库文件与工作空间分离,避免工作空间卷意外包含数据库表。
AUTO_SERVER=TRUE允许多个进程同时连接同一个 H2 文件。
2.2 Hibernate DDL 自动建表
yaml
spring.jpa.hibernate.ddl-auto: update # 默认值update 模式意味着:Hibernate 在启动时对比 Entity 类和数据库表结构,自动创建缺失的表和列。这适合开发环境。
主要表结构:
| 表名 | Entity 类 | 说明 |
|---|---|---|
builder_user | UserEntity | 用户表 (userId PK, username, passwordHash, rolesCsv) |
builder_agent | AgentEntity | Agent 定义表 (ownerId + agentId 唯一约束) |
builder_agent_share | AgentShareEntity | Agent 分享/ACL 表 |
builder_user_marketplace | UserMarketplaceEntity | 用户技能市场偏好 |
2.3 种子数据
yaml
spring.sql.init:
mode: always # 每次启动都运行
platform: h2 # 只运行 data-h2.sql两层种子机制:
data-h2.sql— 创建两个演示账户(仅 H2 模式下运行):bob / bob (role: user) alice / alice (role: user)使用
MERGE INTO ... KEY(user_id)保证幂等性(重复运行不会报错)。JpaUserStore.@PostConstruct— 如果admin用户不存在,自动创建:admin / admin (role: user,admin)
注意:
defer-datasource-initialization: true确保data-h2.sql在 Hibernate 建表之后执行,否则builder_user表还没建好就运行 SQL 会报错。
阶段 3: Spring Bean 组装
Builder 的核心 Bean 由 BuilderConfig 类组装,这是整个启动的心脏:
3.1 Model Bean(模型连线)
优先级规则:
① 已有 Model Bean → 直接使用(其他 @Configuration 提供的)
② builder.dashscope.api-key 已设置 → 自动创建 DashScopeChatModel
③ 两者都没有 → 无模型启动(agent 调用会失败)代码逻辑:
java
@Bean
@ConditionalOnMissingBean(Model.class) // ① 没有其他 Model Bean
@ConditionalOnExpression("'${builder.dashscope.api-key:}' != ''") // ② api-key 不为空
public Model dashscopeModel() {
return DashScopeChatModel.builder()
.apiKey(dashscopeApiKey)
.modelName("qwen-max")
.stream(true)
.build();
}关键设计:使用
@ConditionalOnMissingBean而不是硬编码创建,让 Operator 可以通过自定义@Configuration注入任何模型(OpenAI、Anthropic 等)。
3.2 BaseStore Bean(分布式文件存储)
java
@Bean
@ConditionalOnMissingBean(BaseStore.class)
public BaseStore baseStore(DataSource dataSource) {
return JdbcStore.builder(dataSource).initializeSchema(true).build();
}默认使用 JdbcStore(基于 Spring DataSource),自动创建存储 schema。Operator 可以覆盖为 Redis/SQLite 等实现。
3.3 BuilderBootstrap Bean(Agent 编排中心)
java
@Bean
public BuilderBootstrap builderBootstrap(
Optional<Model> modelOpt, // 模型(可能为空)
ToolEventBus toolEventBus, // 工具事件总线(SSE 流式推送)
BaseStore baseStore, // 分布式文件存储(必须提供)
Optional<AgentStateStore> sessionOpt) // Agent 状态存储(可选)为什么用
Optional<Model>而不是直接注入? 避免循环依赖——dashscopeModel()方法和builderBootstrap()方法在同一个BuilderConfig类中,如果builderBootstrap直接依赖Model,Spring 会在创建dashscopeModelBean 时发现builderBootstrap还没准备好,导致循环依赖错误。
阶段 4: BuilderBootstrap 核心 3-Phase 组装
BuilderBootstrap.Builder.build() 是整个 Agent 运行时的组装引擎,分为 3 个 Phase:
Phase 1: 构建 Session 基础设施
1. 读取 ~/.agentscope/builder/agentscope.json 配置
2. 解析 main agent 的 subagent entries
3. 创建 WorkspaceManager → 管理 workspace 文件系统
4. 创建 DefaultAgentManager → 管理子 agent 实例池
5. 创建 SessionStore (sessions.json) → 持久化 session 注册表
6. 创建 SessionAgentManager → 全量 session 管理器
7. 创建 HarnessGateway → 消息路由网关
8. 创建 TaskRepository → 任务持久化
9. 创建 SessionsTool → 注入到每个 Agent 的子 agent 调度工具
10. 创建 OutboundTool → 注入到每个 Agent 的主动推送工具Phase 2: 构建 Agent 实例
对于每个配置中的 agent(来自 agentscope.json 和程序化注册):
1. 创建 HarnessAgent.Builder
2. 应用 agentscope.json 中的配置 (name, sysPrompt, workspace, model, skills)
3. 注入共享 Model(如果有)
4. 注入 SessionsTool(子 agent 调度)
5. 注入 OutboundTool(频道推送)
6. 注入 ToolNotificationMiddleware(工具调用事件中间件)
7. 注入 AgentStateStore(状态持久化)
8. 配置 Filesystem(根据 store 类型选择 Local/Remote)
9. 调用 programmatic configurator(自定义配置)
10. b.build() → 生成 HarnessAgent 实例Phase 3: 连接 Gateway + 注册 Channel
1. 将所有 agent 注册到 HarnessGateway
2. 绑定 main agent 到 gateway
3. 解析 agentscope.json 中的频道配置(钉钉/飞书/企微/GitHub/GitLab)
4. 创建 ChatUiChannel(Web UI 交互频道)
5. 注册所有频道到 ChannelManager阶段 5: Workspace 脚手架
如果 ~/.agentscope/builder/agentscope.json 不存在,BuilderConfig 会 自动生成:
java
private void ensureAgentscopeConfig() throws IOException {
if (Files.exists(configFile)) return; // 已存在则跳过
// 自动生成最小配置
String agentsJson = """
{
"main": "default",
"agents": {
"default": {
"name": "builder-agent",
"sysPrompt": "You are a helpful assistant built with AgentScope Builder..."
}
}
}
""";
Files.writeString(configFile, agentsJson);
// 同时脚手架 workspace 目录
WorkspaceScaffolder.scaffold(workspaceRoot, agentName, agentSysPrompt);
}WorkspaceScaffolder 创建的默认文件结构:
~/.agentscope/builder/workspace/
├── AGENTS.md ← 从模板生成,含 {{NAME}}/{{SYSPROMPT}} 占位符
├── tools.json ← 默认工具配置
├── skills/
│ ├── example-skill/
│ │ └── SKILL.md ← 示例技能
│ └── ... ← 更多技能目录
├── subagents/
│ └── README.md ← 子 agent 说明
├── memory/
│ └── .gitkeep ← 空标记文件
└── knowledge/ ← 知识库目录关键:
writeIfMissing语义意味着——已有文件永远不会被覆盖,即使重新启动也不会破坏用户自定义的内容。
阶段 6: Security + Netty 服务器启动
6.1 SecurityConfig(JWT 认证)
java
@EnableWebFluxSecurity
public class SecurityConfig {
securityWebFilterChain:
/api/auth/login → permitAll (公开,登录接口)
/actuator/health → permitAll (公开,健康检查)
/api/** → authenticated (需 JWT Token)
/** → permitAll (静态资源,React SPA)
}JwtAuthFilter(自定义 WebFilter)的工作流程:
每个请求:
1. 检查 Authorization header
2. 提取 Bearer Token
3. JwtService.parse(token) → 解析 JWT Claims
4. 提取 userId + roles
5. 构建 UsernamePasswordAuthenticationToken
6. 写入 ReactiveSecurityContextHolder
→ 后续 Controller 通过 @Authentication 获取当前用户6.2 WebConfig(SPA Fallback)
java
@Bean
public RouterFunction<ServerResponse> spaFallback() {
// 对所有非 /api、非 /actuator、无文件扩展名的路径
// 返回 /static/index.html(React Router 在客户端处理路由)
return RouterFunctions.route()
.GET(request -> !path.startsWith("/api")
&& !path.contains(".")
&& !path.startsWith("/actuator"),
request -> ServerResponse.ok()
.contentType(TEXT_HTML)
.bodyValue(indexHtml))
.build();
}为什么需要 SPA Fallback? React 是单页应用(SPA),使用客户端路由(如
/agents/123/chat)。当浏览器直接访问这些路径时,服务器需要返回index.html,让 React Router 在前端处理路由,而不是返回 404。
6.3 Netty 服务器
Spring Boot WebFlux 使用 Netty(而非 Tomcat)作为 HTTP 服务器:
- 非阻塞、事件驱动
- 天然支持 SSE(Server-Sent Events)流式推送
- 适合响应式编程模型
阶段 7: 应用就绪
启动完成后,以下端点可用:
| 端点 | 方法 | 说明 |
|---|---|---|
http://localhost:8080 | GET | React SPA 前端页面 |
POST /api/auth/login | POST | JWT 登录认证 |
GET /api/auth/me | GET | 当前用户信息 |
POST /api/agents/{id}/chat/stream | POST | SSE 流式聊天 |
POST /api/agents/{id}/chat/send | POST | 同步聊天 |
GET /api/agents | GET | Agent 列表 |
GET /api/user/profile | GET | 用户资料 |
GET /actuator/health | GET | 健康检查 |
四、配置参数速查表
4.1 服务器配置
| 参数 | 默认值 | 环境变量 | 说明 |
|---|---|---|---|
server.port | 8080 | SERVER_PORT | HTTP 端口 |
4.2 数据库配置
| 参数 | 默认值 | 环境变量 | 说明 |
|---|---|---|---|
spring.datasource.url | jdbc:h2:file:~/.agentscope-builder/db | BUILDER_DB_URL | 数据库 URL |
spring.datasource.driver-class-name | org.h2.Driver | BUILDER_DB_DRIVER | JDBC 驱动 |
spring.datasource.username | sa | BUILDER_DB_USER | 数据库用户 |
spring.datasource.password | 空 | BUILDER_DB_PASSWORD | 数据库密码 |
spring.jpa.hibernate.ddl-auto | update | BUILDER_JPA_DDL_AUTO | DDL 模式 |
4.3 Agent 配置
| 参数 | 默认值 | 环境变量 | 说明 |
|---|---|---|---|
builder.dashscope.api-key | 空 | DASHSCOPE_API_KEY | DashScope API Key |
builder.dashscope.model-name | qwen-max | BUILDER_MODEL_NAME | 模型名称 |
builder.dashscope.stream | true | — | 流式输出 |
builder.agent.name | builder-agent | BUILDER_AGENT_NAME | Agent 名称 |
builder.agent.sys-prompt | You are a helpful assistant... | — | 系统提示词 |
builder.workspace | JVM 当前目录 | BUILDER_WORKSPACE | 工作目录 |
4.4 安全配置
| 参数 | 默认值 | 环境变量 | 说明 |
|---|---|---|---|
builder.jwt.secret | builder-default-dev-secret-change-in-production-32chars | BUILDER_JWT_SECRET | JWT 签名密钥(≥32字符) |
五、多租户架构详解
Builder 是一个 多租户 平台,每个用户拥有独立的 Agent 会话空间:
用户 bob 登录 → JWT Token 包含 userId="bob"
→ ChatController 接收请求
→ ChatUiChannel 使用 DmScope.PER_PEER
→ 每个用户获得独立的 (userId, agentId) 会话
→ HarnessGateway 路由消息到对应 agent 实例Filesystem 隔离:
CompositeFilesystem 结构:
├── LocalFilesystem (只读,共享模板)
│ └── AGENTS.md, skills/, subagents/, knowledge/
│
└── RemoteFilesystem (每个用户的写入空间)
└── [agents, <agentId>, memory/] ← 每用户独立
└── [agents, <agentId>, sessions/] ← 每用户独立
└── [agents, <agentId>, tasks/] ← 每用户独立
└── activity/ ← 共享(跨用户可见)IsolationScope.USER:每个用户看到自己的 memory、sessions、tasks,但共享模板和 activity。
六、前端构建与 SPA 部署详解
6.1 前端构建时机
前端在 Maven 构建阶段 就完成了打包(不是启动时):
Maven generate-resources 阶段:
frontend-maven-plugin:
1. install-node-and-npm → 下载 Node v20.19.2 到 target/
2. npm install → 安装依赖到 frontend/node_modules/
3. npm run build → Vite 打包 → frontend/dist/
→ dist/ 中的文件被复制到 src/main/resources/static/
→ 打包进 Fat JAR6.2 SPA 运行机制
浏览器请求流程:
1. 首次访问 http://localhost:8080
→ Spring 返回 /static/index.html
→ 浏览器加载 React SPA
2. SPA 内部路由跳转 (如 /agents/chat)
→ React Router 在客户端处理,不发新请求
3. SPA 直接刷新 (如 F5 刷新 /agents/chat)
→ 请求到达 Spring → WebConfig.spaFallback 检测到无文件扩展名
→ 返回 index.html → React Router 处理路由
4. API 请求 (如 /api/agents)
→ Spring Security JWT 过滤器验证 Token
→ ChatController/AgentController 处理七、常见启动问题与解决
7.1 Maven 构建失败:找不到 agentscope-bom
错误: Could not find artifact io.agentscope:agentscope-bom:pom:2.0.0-SNAPSHOT原因:在子目录单独运行 mvn,无法解析项目内部的 SNAPSHOT BOM。
解决:必须从项目根目录运行,使用 -pl 指定子模块:
bash
mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-builder -am7.2 端口冲突
错误: Web server failed to start. Port 8080 was already in use.解决:
bash
# 方案 1: 指定不同端口
java -jar builder.jar --server.port=8082
# 方案 2: 先停止占用 8080 的服务
stop-all.bat7.3 DashScope API Key 未配置
警告: No model configured. Set builder.dashscope.api-key...解决:
bash
# 方案 1: 环境变量
set DASHSCOPE_API_KEY=sk-xxx
# 方案 2: application.yml 中配置
builder.dashscope.api-key: sk-xxx7.4 H2 数据库锁
错误: Database may be already in use: "Locked by another process"原因:另一个 builder 实例正在使用同一个 H2 文件。
解决:停止之前的实例,或使用 AUTO_SERVER=TRUE(已默认配置)允许多进程访问。
7.5 前端构建失败(无 Node.js)
如果 frontend-maven-plugin 构建失败:
bash
# 手动构建前端
cd agentscope-builder/frontend
npm install
npm run build
# 然后回到项目根目录重新 Maven 构建
mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-builder -am八、启动脚本
已创建一键启动脚本 start-builder.bat:
bat
@echo off
chcp 65001 >nul
title AgentScope-Builder (port 8080)
set DASHSCOPE_API_KEY=sk-xxx ← 替换为你的 API Key
java -jar agentscope-builder-2.0.0-SNAPSHOT.jar --server.port=8080
pause九、关键术语解释
| 术语 | 解释 |
|---|---|
| Fat JAR | 包含所有依赖库的可执行 JAR,java -jar 直接运行 |
| Thin JAR | 只含项目代码不含依赖,需要classpath才能运行 |
| Spring Boot WebFlux | 响应式 Web 框架,使用 Netty 服务器,支持 SSE |
| SSE (Server-Sent Events) | 服务器向浏览器单向推送文本事件的协议,用于流式输出 |
| JWT (JSON Web Token) | 无状态认证令牌,包含 userId + roles,无需 session |
| H2 Database | 嵌入式 Java 数据库,无需安装,数据存储在文件中 |
| JPA (Java Persistence API) | Java 对象-关系映射标准,Hibernate 是其实现 |
| HarnessAgent | AgentScope 的核心 Agent 运行时,支持工具调用/子agent/频道 |
| BuilderBootstrap | Agent 编排引擎,负责读取配置→创建 Agent→启动频道 |
| ChatUiChannel | Web UI 交互频道,处理浏览器 → Agent 的消息路由 |
| HarnessGateway | 消息路由网关,将 inbound 消息分发到正确的 agent |
| SessionAgentManager | 全量会话管理器,跟踪每个 (userId, agentId) 的活跃会话 |
| CompositeFilesystem | 混合文件系统,只读本地层 + 可写远程层,实现多租户隔离 |
| BaseStore | 分布式文件存储接口,JDBC/Redis/SQLite 等实现 |
| DmScope.PER_PEER | 每个对话方独立会话的策略(多租户隔离) |
| WorkspaceScaffolder | 工作空间脚手架,首次启动时自动创建默认文件结构 |
| SPA Fallback | 将非 API 请求返回 index.html,让前端路由处理 |
| Reactor | Maven 的构建顺序计算引擎,解决多模块依赖关系 |