Appearance
AgentScope-PAW 启动原理详解
本文档面向初学者,从零开始拆解 agentscope-paw 项目从构建到运行的每一个环节。 PAW(内部代号 Claw2)是一个本地单用户 AI Agent 助手平台。
一、项目概览
1.1 PAW 是什么?
AgentScope-PAW 是一个 本地单用户 AI Agent 助手平台,提供可视化 Web UI 让用户与 Agent 对话、创建自定义 Agent、管理技能和会话。它是一个前后端一体项目:
| 层 | 技术 | 说明 |
|---|---|---|
| 后端 | Spring Boot WebFlux (响应式) | Netty 服务器、无认证(单用户本地使用)、SSE 流式对话 |
| 前端 | React + Vite SPA | 打包为静态资源嵌入后端 JAR |
| Agent 框架 | HarnessAgent + ClawBootstrap | 本地单 Agent + 自定义 Agent 懒加载 |
| 模型 | DashScope qwen-max (默认) | 支持 OpenAI / Anthropic 等自定义 Model Bean |
| 持久化 | JSON 文件 (无数据库) | agentscope.json、agents.json、sessions.json |
关键区别:与 Builder 不同,PAW 不使用数据库(没有 JPA/H2),所有持久化状态都存储在 JSON 文件中。也没有认证系统——设计为单用户本地部署。
1.2 核心技术栈
┌───────────────────────────────────────────────────────┐
│ 浏览器 → http://localhost:8080 │
│ ┌───────────────┐ ┌──────────────────────────────┐ │
│ │ React SPA │ │ Spring Boot WebFlux │ │
│ │ (前端页面) │←│ REST API + SSE 流式聊天 │ │
│ └───────────────┘ └──────────────────────────────┘ │
│ │ │
│ ├─ Security (全开放 permitAll) │
│ ├─ SPA Fallback (index.html) │
│ └─ BuilderConfig │
│ │ │
│ ├─ ClawBootstrap (核心) │
│ │ ├─ HarnessAgent (内置) │
│ │ ├─ SessionAgentManager │
│ │ ├─ HarnessGateway │
│ │ ├─ SessionsTool │
│ │ ├─ OutboundTool │
│ │ └─ ChatUiChannel │
│ ├─ DashScopeChatModel │
│ │ │ │
│ │ ↓ │
│ │ DashScope API (通义千问) │
│ ├─ ToolEventBus + Middleware │
│ └─ ChannelManager (IM 通道) │
│ ├─ DingTalk / Feishu / WeCom│
│ └─ GitHub / GitLab │
└───────────────────────────────────────────────────────┘1.3 与 Builder 的关键差异
| 特性 | Builder | PAW (Claw2) |
|---|---|---|
| 定位 | Agent 构建平台(多人协作) | 本地助手(单用户) |
| 认证 | JWT + 多用户 | 无认证(permitAll) |
| 数据库 | JPA + H2/MySQL | 纯 JSON 文件 |
| Bootstrap | BuilderBootstrap(3 Phase) | ClawBootstrap(Builder 模式) |
| 会话隔离 | DmScope.PER_PEER(每用户独立) | DmScope.MAIN(全局共享) |
| 自定义 Agent | 无(预定义) | 懒加载注册(首次对话才构建) |
| 主类包名 | io.agentscope.builder | io.agentscope.claw2 |
二、构建过程详解
2.1 Maven 多模块依赖链
PAW 是 agentscope-java 多模块项目的一个子模块。它的 Maven 构建依赖链:
agentscope-parent (根 POM)
├─ agentscope-dependencies-bom ← BOM(版本管理)
├─ agentscope-distribution/agentscope-bom ← 运行时 BOM
├─ agentscope-core ← 核心框架
├─ agentscope-harness ← Agent 运行时
├─ agentscope-extensions-model-dashscope ← DashScope 模型
├─ agentscope-extensions-channel-* ← IM 通道扩展
└─ agentscope-examples/agents/agentscope-paw ← 本项目2.2 为什么需要从根目录构建?
PAW 的 pom.xml 使用 ${revision} 引用父 POM 和 BOM,这些 SNAPSHOT 依赖在 Maven 本地仓库中不存在。如果直接在 PAW 子目录运行 mvn exec:java,Maven 会报错找不到 BOM。
解决方案:从项目根目录运行 Maven,利用 Reactor(Maven 多模块构建机制)自动在内存中解析所有模块间依赖,无需先 mvn install 到本地仓库。
bash
# ❌ 错误方式:在子目录单独运行
cd agentscope-paw && mvn exec:java
# ✅ 正确方式:从根目录指定模块
cd agentscope-java && mvn -pl agentscope-examples/agents/agentscope-paw -am exec:java
# ✅ 更好的方式:先打包成 fat JAR,再 java -jar
cd agentscope-java && mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-paw -am
java -jar agentscope-paw/target/agentscope-paw-2.0.0-SNAPSHOT.jar
-pl指定构建模块,-am(also-make)自动构建该模块的依赖模块。
2.3 Fat JAR 的形成
PAW 使用 spring-boot-maven-plugin 的 repackage goal 将编译产物打包成一个可独立运行的 Fat JAR:
agentscope-paw-2.0.0-SNAPSHOT.jar ← Fat JAR(约 50MB)
├─ BOOT-INF/classes/ ← Java 类文件 + 配置文件
│ ├─ io/agentscope/claw2/ ← 后端代码
│ ├─ static/ ← 前端打包产物(React SPA)
│ │ ├─ index.html
│ │ ├─ assets/*.js, *.css
│ │ └─ favicon.ico
│ ├─ scaffold/default/ ← Workspace 脚手架模板
│ └─ application.yml ← Spring Boot 配置
├─ BOOT-INF/lib/ ← 所有依赖 JAR(Spring、Netty、agentscope-core 等)
└─ META-INF/ ← MANIFEST.MF (Main-Class: spring-boot launcher)运行 java -jar agentscope-paw.jar 时,Spring Boot Launcher 自动解压依赖到临时目录,加载所有 JAR,然后调用 Claw2App.main()。
2.4 前端自动构建
frontend-maven-plugin 在 Maven 的 generate-resources 阶段自动构建前端:
- 安装 Node.js — 下载
v20.19.2到target/目录 - 安装 npm 依赖 — 在
frontend/目录执行npm install - 构建前端 — 执行
npm run build,Vite 将 React 项目编译为静态文件 - 输出到
classpath:/static/— Vite 配置outDir: ../src/main/resources/static,直接放入资源目录
最终 spring-boot-maven-plugin 将 static/ 目录打包进 Fat JAR,后端就能通过 Netty 服务器直接提供前端页面。
三、启动过程详解(7 个阶段)
阶段 1:JVM 启动 + Spring Boot 自动配置
java -jar agentscope-paw.jar
→ Spring Boot Launcher 解压 BOOT-INF/lib/ 中的 JAR
→ 创建 Spring ApplicationContext
→ 扫描 @SpringBootApplication → io.agentscope.claw2 包
→ 自动发现并注册所有 @Configuration、@Component、@RestControllerClaw2App.main() 是整个应用的入口点:
java
@SpringBootApplication
public class Claw2App {
public static void main(String[] args) {
SpringApplication.run(Claw2App.class, args);
}
}@SpringBootApplication 是一个组合注解,相当于:
@Configuration— 声明这是一个配置类@EnableAutoConfiguration— 让 Spring Boot 根据依赖自动配置@ComponentScan— 自动扫描当前包及子包下的所有组件
Spring Boot 自动配置的核心工作(WebFlux 版):
- 发现
spring-boot-starter-webflux→ 配置 Netty 作为 HTTP 服务器(不是 Tomcat) - 发现
spring-boot-starter-security→ 配置 SecurityWebFilterChain - 发现
spring-boot-starter-actuator→ 注册/actuator/health、/actuator/info - 加载
application.yml→ 设置端口8080(可通过CLAW_PORT环境变量覆盖)
阶段 2:Security 配置(全开放)
java
// SecurityConfig.java — 无认证,全开放
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.csrf(ServerHttpSecurity.CsrfSpec::disable) // 禁用 CSRF
.cors(cors -> cors.configurationSource(...)) // 允许 localhost CORS
.authorizeExchange(auth -> auth.anyExchange().permitAll()) // 全开放
.build();
}
}为什么不需要认证? PAW 设计为单用户本地部署应用,运行在个人电脑上,所有端点开放访问。CORS 仅允许 localhost:* 来源,用于支持 Vite 开发模式(http://localhost:5173)。
阶段 3:BuilderConfig — 核心 Bean 组装
BuilderConfig 是 PAW 的核心配置类,负责组装所有关键 Bean:
java
@Configuration
public class BuilderConfig {
// Bean 1: Model(条件创建)
@Bean
@ConditionalOnMissingBean(Model.class)
@ConditionalOnExpression("'${claw.dashscope.api-key:}' != ''")
public Model dashscopeModel() {
return DashScopeChatModel.builder()
.apiKey(dashscopeApiKey)
.modelName(dashscopeModelName) // 默认 qwen-max
.stream(dashscopeStream) // 默认 true
.build();
}
// Bean 2: ClawBootstrap(核心)
@Bean
public ClawBootstrap builderBootstrap(Optional<Model> modelOpt, ToolEventBus toolEventBus) {
// 1. 解析 clawHome 路径
// 2. 自动生成 agentscope.json(如果不存在)
// 3. 构建 ClawBootstrap → 组装所有 Agent 和 Channel
// 4. 启动 Channel → 开始监听消息
}
// Bean 3: ChatUiChannel
@Bean
public ChatUiChannel chatUiChannel(ClawBootstrap bootstrap) {
return bootstrap.channelManager().getChannel(ChatUiChannel.CHANNEL_ID)...;
}
}Model 创建优先级:
- 如果已有其他
@Configuration注册了ModelBean → 直接使用 - 如果
claw.dashscope.api-key不为空 → 创建DashScopeChatModel - 如果两者都没有 → 启动失败,抛出
IllegalStateException
阶段 4:ClawBootstrap 构建(Builder 模式,3 Phase)
ClawBootstrap 是整个 PAW Agent 运行时的核心。它使用 Builder 模式,内部分 3 个 Phase 构建:
Phase 1:Session 基础设施
BuilderConfig.builderBootstrap()
→ ClawBootstrap.builder().cwd(home).model(model).build()
→ Builder.build() 内部:
Phase 1: 构建 Session 基础设施
├─ 加载 agentscope.json → 获取所有 Agent 配置
├─ 构建 mainAgent Builder → 提取 SubagentEntry 列表
├─ 创建 WorkspaceManager(mainWorkspace)
├─ 创建 DefaultAgentManager(entries, wsManager) ← 子 Agent 创建工厂
├─ 创建 SessionStore(sessions.json) ← JSON 文件持久化
├─ 创建 SessionAgentManager(dam, config, registry, store) ← 会话管理核心
├─ 创建 ChannelManager() ← IM 通道注册表
├─ 创建 HarnessGateway(sam, channelMgr) ← 消息路由网关
├─ 创建 WorkspaceTaskRepository(wsManager, main)
├─ 创建 SessionsTool(sam, taskRepo) ← Agent 可调用的 sessions 工具
└─ 创建 OutboundTool(channelMgr) ← Agent 可调用的 outbound 工具Phase 2:Agent 构建
Phase 2: 构建 Agent 实例
for each agentId:
├─ 创建 HarnessAgent.Builder
├─ applyFileEntry() → 设置 name/sysPrompt/workspace/model 等属性
├─ 注入 SessionsTool(外部子 Agent 调用能力)
├─ 注入 OutboundTool(主动推送消息到 IM 通道能力)
├─ 应用 configureAllAgents → ToolNotificationMiddleware(工具调用事件推送)
└─ b.build() → 生成 HarnessAgent 实例Phase 3:Gateway 注册 + Channel 解析
Phase 3: 网关注册 + 通道解析
for each (agentId, HarnessAgent):
→ gateway.registerAgent(agentId, agent) ← 注册到路由表
→ gateway.bindMainAgent(mainAgent) ← 标记主 Agent
→ resolveChannels() → 从 agentscope.json 的 channels 配置创建 Channel
├─ ChannelTypeRegistry 静态注册了 6 种类型:
│ ├─ chatui → ChatUiChannel (Web SSE)
│ ├─ dingtalk → DingTalkChannel
│ ├─ wecom → WeComChannel
│ ├─ feishu → FeishuChannel
│ ├─ github → GitHubChannel
│ └─ gitlab → GitLabChannel
└─ 返回解析后的 Channel 列表阶段 5:ClawHome 初始化 + Workspace 脚手架
在 BuilderConfig.builderBootstrap() 中,首次启动时会自动生成配置文件和 Workspace:
java
private void ensureAgentscopeConfig(Path home) throws IOException {
Path configFile = home.resolve("agentscope.json");
if (Files.exists(configFile)) return; // 已存在则跳过
// 创建目录结构
Files.createDirectories(home);
Files.createDirectories(home.resolve("workspace"));
// 自动生成 agentscope.json
String agentsJson = """
{
"main": "default",
"agents": {
"default": {
"name": "claw", ← 来自 claw.agent.name 配置
"workspace": "workspace",
"sysPrompt": "You are a helpful local assistant..."
}
}
}
""";
Files.writeString(configFile, agentsJson);
// 脚手架:创建 Workspace 默认文件结构
WorkspaceScaffolder.scaffold(workspace, agentName, agentSysPrompt);
}WorkspaceScaffolder 创建的默认 Workspace 结构:
~/.agentscope/claw/workspace/
├─ AGENTS.md ← Agent 身份描述 + 系统提示词(从模板生成)
├─ tools.json ← 工具白名单/黑名单配置
├─ skills/
│ ├─ example-skill/
│ │ └─ SKILL.md ← 示例技能
│ └─ (空目录,用户可添加技能)
├─ subagents/
│ ├─ README.md ← 子 Agent 说明
│ └─ (空目录,用户可添加子 Agent)
└─ memory/
└─ .gitkeep ← 空标记文件(让 git 能跟踪空目录)AGENTS.md 模板:
markdown
# claw ← {{NAME}} 占位符替换为 agent 名称
You are a helpful local assistant... ← {{SYSPROMPT}} 占位符替换
## How this folder works
... (使用说明)重要:WorkspaceScaffolder 使用"写时跳过"语义(
writeIfMissing),已存在的文件不会被覆盖,所以重复启动不会破坏用户修改。
阶段 6:Channel 启动
在 BuilderConfig.builderBootstrap() 返回前,会启动所有 Channel:
java
// 创建 ChatUiChannel
ChannelConfig chatuiCfg = ChannelConfig.builder(ChatUiChannel.CHANNEL_ID)
.dmScope(DmScope.MAIN) ← 单用户共享模式
.build();
ChatUiChannel webChannel = ChatUiChannel.create(chatuiCfg);
// 注册文件配置中的 Channel(dingtalk/wecom 等)
for (Channel ch : bootstrap.registeredChannels()) {
if (!ChatUiChannel.CHANNEL_ID.equals(ch.channelId())) {
bootstrap.channelManager().register(ch);
}
}
// 启动所有 Channel
bootstrap.start(webChannel);DmScope.MAIN 是什么?
DmScope.MAIN— 所有对话共享一个会话(单用户模式)DmScope.PER_PEER— 每个用户独立会话(Builder 使用这种)- PAW 使用
MAIN因为它是单用户本地应用
阶段 7:Netty 服务器就绪
Spring Boot WebFlux 自动配置启动 Netty 服务器:
Netty Server started on port 8080
→ 监听所有 HTTP 请求
→ /api/* → REST Controller 处理
→ /actuator/* → Actuator 处理
→ 其他路径 → WebConfig SPA Fallback → 返回 index.html此时应用完全就绪,可以接受浏览器请求。
四、ClawBootstrap 核心架构详解
4.1 ClawBootstrap 的两个角色
ClawBootstrap 有两个运行阶段:
- Build 阶段(
Builder.build())— 静态组装,创建所有对象和依赖关系 - Runtime 阶段(
start()/chatUiChannel())— 动态运行,启动 Channel 开始监听
4.2 Session 管理体系
PAW 的会话管理是一个多层级体系:
SessionAgentManager (核心)
├─ sessionsByKey (ConcurrentHashMap) ← 会话注册表
│ sessionKey → SessionEntry (元数据)
├─ SessionStore (JSON 文件持久化)
│ ~/.agentscope/claw/agents/{agentId}/sessions.json
├─ SubagentRunRegistry ← 运行跟踪
├─ AnnounceDispatcher ← 子 Agent 完成通知
├─ SpawnInterceptor ← 子 Agent 创建拦截
└─ SessionTurnGate ← 会话级别公平锁(防止并发冲突)会话类型(SessionKind):
MAIN— 主 Agent 的对话会话SUBAGENT— 子 Agent 的临时会话
4.3 HarnessGateway 路由机制
Gateway 是所有消息的路由中心:
ChatController.stream()
→ resolveRoute(agentId, message) ← 通过 ChannelRouter 确定路由
→ resolveGatewayAgentId(agentId) ← 确定目标 Agent
├─ 内置 Agent → 直接返回 agentId
└─ 自定义 Agent → 懒加载:首次调用才构建 HarnessAgent 并注册到 Gateway
→ gateway.run(context, messages, outboundAddress)
→ resolveOrCreateMainSession(gateKey, ha) ← 创建或复用会话
→ withGatedTurn(gateKey, () -> ha.call(messages, runtimeContext))
→ SessionTurnGate.acquire(gateKey) ← 加锁(同一会话串行执行)
→ HarnessAgent.call(messages, ctx) ← Agent 执行
→ SessionTurnGate.release(gateKey) ← 释放锁4.4 自定义 Agent 懒加载机制
PAW 支持用户通过 UI 创建自定义 Agent。这些 Agent 不会在启动时构建,而是 首次对话时才构建并注册:
java
// AgentCatalogService.resolveGatewayAgentId()
public String resolveGatewayAgentId(String agentId) {
if (isBuiltin(agentId)) {
return agentId; // 内置 Agent 直接返回
}
// 自定义 Agent → 懒加载
return registeredCustomIds.computeIfAbsent(agentId, k -> buildAndRegisterCustom(entry));
}
private String buildAndRegisterCustom(StoredEntry entry) {
HarnessAgent.Builder b = HarnessAgent.builder();
b.name(name);
b.sysPrompt(sysPrompt);
b.workspace(workspace);
b.middleware(new ToolNotificationMiddleware(toolEventBus)); // 工具事件推送
if (entry.model() != null) b.model(entry.model());
else if (model != null) b.model(model); // 使用默认模型
HarnessAgent agent = b.build();
gateway.registerAgent(entry.id(), agent); // 注册到路由表
return entry.id();
}这种设计的好处:
- 启动快 — 不需要构建所有自定义 Agent
- 节省资源 — 不常用的 Agent 不占内存
- 动态更新 — 修改自定义 Agent 后只需清除缓存,下次对话自动重建
五、前端构建与 SPA 部署
5.1 前端构建流程
Maven generate-resources 阶段
→ frontend-maven-plugin
├─ install-node-and-npm → 下载 Node v20.19.2 + npm 10.9.2
├─ npm install → 安装 React/Vite 等依赖
└─ npm run build → Vite 编译
→ Vite 配置 outDir: ../src/main/resources/static
→ 生成 index.html + JS/CSS bundles
→ 输出到 src/main/resources/static/5.2 SPA Fallback 机制
WebConfig 配置了 SPA 路由回退:
java
@Bean
public RouterFunction<ServerResponse> spaFallback() {
return RouterFunctions.route()
.GET(
request -> !path.startsWith("/api") // 非 API
&& !path.contains(".") // 非静态资源(无文件扩展名)
&& !path.startsWith("/actuator"), // 非 Actuator
request -> ServerResponse.ok()
.contentType(MediaType.TEXT_HTML)
.bodyValue(new ClassPathResource("/static/index.html")))
.build();
}浏览器请求流程:
| 请求路径 | 处理方式 |
|---|---|
/ | → classpath:/static/index.html (React SPA) |
/agents/123 | → classpath:/static/index.html (React Router 处理) |
/api/agents | → AgentCatalogController (REST API) |
/api/agents/default/chat/stream | → ChatController (SSE 流式) |
/static/assets/app.js | → Spring 默认静态资源处理器 |
/actuator/health | → Spring Actuator |
六、聊天流程详解(端到端)
6.1 SSE 流式对话
浏览器发送: POST /api/agents/default/chat/stream
body: { "message": "你好", "sessionKey": null }
ChatController.stream(agentId, req)
├─ handleSlashCommand() → 检查是否 /new /reset 命令
├─ resolveGateKey(agentId) → 确定路由键
│ → resolveRoute(agentId, "__probe__")
│ → catalogService.resolveGatewayAgentId("default")
│ → ChannelRouter.resolveRoute(chatui.config(), inbound)
│ → RouteResult { context, outboundAddress }
├─ findSessionKeyByGate(gateKey) → 查找已有会话
├─ toolEventBus.subscribe(sessionKey) → 监听工具调用事件
├─ executeChat(agentId, message) → Agent 执行
│ → resolveRoute(agentId, message)
│ → gateway.run(context, messages, outboundAddress)
│ → resolveOrCreateMainSession(gateKey, ha) → 创建/复用会话
│ → withGatedTurn → SessionTurnGate.acquire(gateKey)
│ → HarnessAgent.call(messages, runtimeContext)
│ → ReActAgent 执行循环:
│ ├─ 调用 DashScope API → 生成回复或工具调用
│ ├─ ToolNotificationMiddleware.onActing() → 发布 TOOL_CALL 事件到 ToolEventBus
│ ├─ 执行工具 → 工具返回结果
│ └─ 重复直到生成最终回复或达到 maxIters
│ → SessionTurnGate.release(gateKey)
│ → 返回 Msg (最终回复)
└─ 合并 SSE 事件流:
Flux.merge(toolEvents, agentReply)
→ SSE 事件序列:
├─ event: tool_call data: {"type":"tool_call","toolName":"read_file",...}
├─ event: tool_result data: {"type":"tool_result","toolName":"read_file",...}
├─ event: token data: {"type":"token","data":"你好!我是..."}
└─ event: done data: {"type":"done","sessionKey":"..."}6.2 同步对话
浏览器发送: POST /api/agents/default/chat/send
body: { "message": "你好", "sessionKey": null }
ChatController.send(agentId, req)
→ executeChat(agentId, message) → 同上,但返回完整回复而非 SSE 流
→ ChatResponse { reply: "你好!...", sessionKey: "..." }6.3 Slash 命令
| 命令 | 效果 |
|---|---|
/new | 清除当前会话历史,下次消息重新开始 |
/reset | 同 /new |
七、ClawHome 目录结构详解
所有持久化状态都存储在 clawHome 目录下:
~/.agentscope/claw/ ← clawHome (可通过 CLAW_HOME 环境变量修改)
├─ agentscope.json ← 内置 Agent 配置(启动时自动生成)
│ {
│ "main": "default",
│ "agents": {
│ "default": {
│ "name": "claw",
│ "workspace": "workspace",
│ "sysPrompt": "..."
│ }
│ },
│ "channels": { ... }, ← IM 通道配置(可选)
│ "session": { ... } ← 会话生命周期配置(可选)
│ }
├─ agents.json ← 自定义 Agent 定义(用户通过 UI 创建)
├─ workspace/ ← 主 Agent 的 Workspace
│ ├─ AGENTS.md
│ ├─ tools.json
│ ├─ skills/
│ ├─ subagents/
│ └─ memory/
├─ agents/{agentId}/workspace/ ← 自定义 Agent 的 Workspace
├─ agents/{agentId}/sessions.json ← 每个Agent的会话持久化文件
└─ marketplace/ ← 技能市场缓存多应用隔离:每个 harness 应用有自己的 clawHome。Builder 使用
~/.agentscope/builder,DataAgent 使用~/.agentscope/dataagent,PAW 使用~/.agentscope/claw,互不干扰。
八、ToolEventBus + ToolNotificationMiddleware 详解
PAW 有一个独特的事件推送机制,用于在 SSE 流中实时展示 Agent 的工具调用过程:
ToolNotificationMiddleware (Agent Middleware)
→ onActing() → 在每个工具调用前发布 TOOL_CALL 事件
→ ToolEventBus (Spring @Component, 内存事件总线)
→ Sinks.Many<ToolEvent> (Reactor 多播)
→ publish(ToolEvent) → 推送事件
→ subscribe(sessionKey) → 按 sessionKey 过滤订阅
ChatController.stream()
→ toolEventBus.subscribe(sessionKey) → 订阅工具事件
→ toToolFrame(event) → 转换为 SSE frame
→ 合并到 SSE 流中这使浏览器能实时看到 Agent 正在调用什么工具,而不仅仅是最终文字回复。
九、Channel 体系详解
9.1 ChannelTypeRegistry
ChannelTypeRegistry 是 Channel 的工厂注册表,静态初始化了 6 种 Channel 类型:
java
static {
register("chatui", (id, routing, props) -> ChatUiChannel.create(routing));
register("dingtalk", DingTalkChannel::fromProperties);
register("wecom", WeComChannel::fromProperties);
register("feishu", FeishuChannel::fromProperties);
register("github", GitHubChannel::fromProperties);
register("gitlab", GitLabChannel::fromProperties);
}9.2 Channel 配置示例
在 agentscope.json 中可以配置 IM 通道:
json
{
"channels": {
"dingtalk": {
"type": "dingtalk",
"properties": {
"appId": "...",
"appSecret": "..."
}
},
"feishu": {
"type": "feishu",
"disabled": true,
"properties": { ... }
}
}
}"disabled": true→ 该 Channel 被跳过,不创建"type"→ 如果不指定,默认使用 channelId 作为 type(向后兼容)
十、配置参数速查表
10.1 YAML 配置(application.yml)
| 参数 | 默认值 | 说明 |
|---|---|---|
server.port | 8080 | HTTP 端口(CLAW_PORT 环境变量覆盖) |
spring.application.name | agentscope-claw | 应用名称 |
claw.home | ~/.agentscope/claw | 数据根目录(CLAW_HOME 环境变量覆盖) |
claw.dashscope.api-key | 空 | DashScope API Key(DASHSCOPE_API_KEY 环境变量覆盖) |
claw.dashscope.model-name | qwen-max | DashScope 模型名(CLAW_MODEL_NAME 环境变量覆盖) |
claw.dashscope.stream | true | 是否启用流式输出 |
claw.agent.name | claw | 默认 Agent 名称(CLAW_AGENT_NAME 环境变量覆盖) |
claw.agent.sys-prompt | You are a helpful local assistant... | 默认系统提示词 |
10.2 环境变量覆盖
| 环境变量 | 对应 YAML | 说明 |
|---|---|---|
CLAW_PORT | server.port | 修改端口 |
CLAW_HOME | claw.home | 修改数据目录 |
DASHSCOPE_API_KEY | claw.dashscope.api-key | DashScope API Key(启动必需) |
CLAW_MODEL_NAME | claw.dashscope.model-name | 模型名称 |
CLAW_AGENT_NAME | claw.agent.name | 默认 Agent 名称 |
10.3 agentscope.json 配置
| 字段 | 类型 | 说明 |
|---|---|---|
main | string | 主 Agent ID(默认 "default") |
agents | Map<string, AgentConfigEntry> | Agent 定义列表 |
agents.{id}.name | string | Agent 显示名称 |
agents.{id}.workspace | string | Workspace 路径(相对于 clawHome) |
agents.{id}.sysPrompt | string | 系统提示词 |
agents.{id}.model | string | 模型名称(覆盖默认) |
agents.{id}.maxIters | int | 最大推理轮次 |
agents.{id}.tools | object | 工具白名单/黑名单配置 |
agents.{id}.identity | object | Agent 身份配置(名称、头像) |
agents.{id}.groupChat | object | 群聊配置 |
agents.{id}.sandbox | object | Docker 沙箱配置 |
agents.{id}.skills | object | 技能白名单/黑名单 |
channels | Map<string, ChannelConfigEntry> | IM 通道配置 |
session | object | 会话生命周期配置 |
session.maintenance | object | 会话维护配置(清理策略) |
十一、常见启动问题与解决
11.1 "Cannot start without a model"
IllegalStateException: agentscope-claw cannot start without a model.原因:没有配置 DashScope API Key,也没有注册自定义 Model Bean。
解决:
bash
# 方式 1:设置环境变量
set DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx
# 方式 2:修改 application.yml
claw:
dashscope:
api-key: sk-xxxxxxxxxxxxxxxx
# 方式 3:自定义 Model Bean(如使用 OpenAI)
@Configuration
public class MyModelConfig {
@Bean
public Model model() {
return OpenAIChatModel.builder().apiKey("...").modelName("gpt-4o").build();
}
}11.2 "BOM not found" / SNAPSHOT 依赖缺失
Non-resolvable import POM: io.agentscope:agentscope-bom:pom:2.0.0-SNAPSHOT (absent)原因:在子目录单独运行 mvn exec:java,Maven 找不到本地仓库中的 SNAPSHOT 依赖。
解决:从根目录构建,利用 Maven Reactor:
bash
cd d:\InspurCode\micro\agentscope-java
mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-paw -am
java -jar agentscope-examples/agents/agentscope-paw/target/agentscope-paw-2.0.0-SNAPSHOT.jar11.3 端口冲突
Web server failed to start. Port 8080 was already in use.解决:
bash
# 方式 1:修改端口
set CLAW_PORT=8081
java -jar agentscope-paw.jar
# 方式 2:找到占用进程并关闭
netstat -ano | findstr :8080
taskkill /PID <pid> /F11.4 前端构建失败
npm ERR! code ERESOLVE ... (依赖冲突)解决:清除 Node 缓存重新构建:
bash
cd agentscope-paw/frontend
rm -rf node_modules package-lock.json
cd agentscope-java
mvn clean package -DskipTests -pl agentscope-examples/agents/agentscope-paw -am11.5 agentscope.json 自动生成问题
首次启动时 PAW 会自动生成 ~/.agentscope/claw/agentscope.json。如果你想重置:
bash
# 删除整个 clawHome(会清除所有会话和自定义 Agent)
rm -rf ~/.agentscope/claw
# 或仅删除配置文件
rm ~/.agentscope/claw/agentscope.json十二、关键术语解释
| 术语 | 解释 |
|---|---|
| ClawBootstrap | Agent 运行时的组装器,使用 Builder 模式构建所有核心组件(Agent、Gateway、Channel) |
| HarnessAgent | 带有工具调用、子 Agent、Workspace 的完整 Agent 实例 |
| HarnessGateway | 消息路由网关,将外部请求路由到正确的 Agent,管理会话和并发 |
| ChatUiChannel | Web UI 通道,处理 SSE 流式对话和会话管理 |
| ChannelManager | 通道生命周期管理器,负责注册、初始化、启动和消息投递 |
| ChannelTypeRegistry | Channel 工厂注册表,按 type 字符串查找对应的创建工厂 |
| SessionAgentManager | 会话管理核心,负责创建、追踪、维护、重置会话 |
| SessionTurnGate | 会话级别的公平锁,确保同一会话的对话串行执行 |
| SessionsTool | Agent 可调用的工具,用于创建和调用子 Agent 会话 |
| OutboundTool | Agent 可调用的工具,用于主动推送消息到 IM 通道 |
| ToolEventBus | 内存事件总线,将 Agent 的工具调用事件推送到前端 SSE 流 |
| ToolNotificationMiddleware | Agent Middleware,在每次工具调用前发布事件到 ToolEventBus |
| DmScope.MAIN | 会话作用域:所有对话共享一个会话(单用户模式) |
| clawHome | 数据根目录(默认 ~/.agentscope/claw),存储所有配置、Workspace、会话 |
| agentscope.json | 核心配置文件,定义内置 Agent、IM 通道、会话策略 |
| Workspace | Agent 的"文件系统",包含 AGENTS.md、tools.json、skills、subagents、memory |
| WorkspaceScaffolder | 首次启动时自动创建 Workspace 默认文件结构的工具 |
十三、启动流程总结(时序图)
时间线 →
[JVM 启动]
→ Spring Boot Launcher 解压依赖
→ 创建 ApplicationContext
→ 扫描 @SpringBootApplication → io.agentscope.claw2
[自动配置]
→ WebFlux AutoConfig → Netty 服务器 (port 8080)
→ Security AutoConfig → SecurityWebFilterChain (permitAll)
→ Actuator AutoConfig → /actuator/health, /actuator/info
[Bean 组装]
→ BuilderConfig.dashscopeModel() → DashScopeChatModel (条件创建)
→ BuilderConfig.builderBootstrap()
→ resolveClawHome() → ~/.agentscope/claw
→ ensureAgentscopeConfig() → 自动生成 agentscope.json + Workspace
→ ClawBootstrap.builder().cwd(home).model(model)
→ configureAllAgents → ToolNotificationMiddleware
→ .build()
→ [Phase 1] Session 基础设施
→ SessionStore + SessionAgentManager + HarnessGateway
→ SessionsTool + OutboundTool
→ [Phase 2] Agent 构建
→ 每个 agentId → HarnessAgent.Builder → build()
→ [Phase 3] Gateway 注册 + Channel 解析
→ gateway.registerAgent() × N
→ gateway.bindMainAgent()
→ resolveChannels() → ChannelTypeRegistry
→ ChatUiChannel.create(DmScope.MAIN)
→ 注册非 chatui Channel 到 ChannelManager
→ bootstrap.start(webChannel) → 启动所有 Channel
[Netty 就绪]
→ Netty Server started on port 8080
→ SPA Fallback → /static/index.html
→ REST API → /api/agents, /api/agents/{id}/chat/stream
[应用就绪] ← 浏览器访问 http://localhost:8080