Appearance
AgentScope CodingAgent 启动指南(初学者版)
本文档面向初学者,详细拆解 agentscope-codingagent 项目从源码构建到应用就绪的每一步, 帮助你理解「为什么这样设计」和「每个组件在做什么」。
目录
- 项目概览
- 两种启动模式
- 构建过程详解
- 启动过程详解(Webhook 服务模式)
- 启动过程详解(CLI REPL 模式)
- CodingBootstrap 核心架构详解
- 双 Agent 体系:Coding + Reviewer
- 模型选择与 FallbackModel
- 中间件栈详解
- SqliteBaseStore 数据持久化
- GitHub Webhook 处理流程
- Session 会话管理体系
- HarnessGateway 路由机制
- Workspace 脚手架与技能模板
- Token 加密与安全
- 可观测性:Micrometer + Prometheus
- Channel 体系详解
- 配置参数速查表
- ClawHome 目录结构详解
- 常见启动问题与解决
- 关键术语解释
- 启动流程时序总结
1. 项目概览
定位
agentscope-codingagent 是一个代码工程 Agent,核心能力:
- 接收 GitHub Webhook 事件(Issue 评论、PR 评论、Review Request)→ 自动处理编码任务
- CLI REPL 交互→ 本地开发与冒烟测试
- 双 Agent 协作:Coding Agent(执行代码修改)+ Reviewer Agent(审查 PR 并发布 Findings)
- OpenSWE 模式:自治规划(todo_write)、验证循环、安全编辑、Docker 沙箱隔离
技术栈
| 层面 | 技术 |
|---|---|
| 后端框架 | Spring Boot + WebFlux(Reactor Netty) |
| Agent 核心 | agentscope-harness(HarnessAgent) |
| 模型提供商 | DashScope / OpenAI / Anthropic(三选一 + Fallback) |
| 数据持久化 | SQLite(SqliteBaseStore) |
| Token 加密 | Google Tink AEAD(AES-256-GCM) |
| 可观测性 | Micrometer + Prometheus + OTel |
| IM 通道 | ChatUIChannel / DingTalk / Feishu |
| 沙箱 | Docker(可选,默认无沙箱) |
与 paw/builder 的关键差异
| 特性 | codingagent | paw | builder |
|---|---|---|---|
| 入口模式 | Webhook 服务 + CLI REPL | Web 服务 + 前端 SPA | Web 服务 + 前端 SPA |
| 数据库 | SQLite | H2(JPA) | H2(JPA) |
| Security | 无(Webhook 服务) | Spring Security + JWT | Spring Security + JWT |
| 前端 | 无 | React SPA | React SPA |
| Agent 数量 | 双 Agent(coding + reviewer) | 单 Agent(multi-turn) | Builder |
| GitHub 集成 | ✅ Webhook + GitHub API Tool | ❌ | ❌ |
| 沙箱隔离 | Docker 可选 | ❌ | ❌ |
| 模型 Fallback | ✅ FallbackModel | ❌ | ❌ |
| 中间件栈 | OpenSWE 三件套 | ToolEventBus | ❌ |
2. 两种启动模式
codingagent 有两个独立的入口类,对应两种运行模式:
模式 A:Webhook 服务模式(生产部署)
入口类: CodingAgentApplication.java
运行方式: mvn spring-boot:run 或 java -jar codingagent.jar
用途: 接收 GitHub Webhook,自动处理 Issue/PR启动后 Spring Boot 在 8080 端口暴露:
POST /webhooks/github— 接收 GitHub 事件GET /health— 健康检查/actuator/prometheus— 指标端点/actuator/health— 详细健康状态
模式 B:CLI REPL 模式(本地开发)
入口类: CodingChatCli.java
运行方式: mvn exec:java 或 start-codingagent.bat
用途: 本地交互式对话、冒烟测试启动后在终端输出 REPL 循环:
- 输入消息 → Coding Agent 回复
review <pr_url>→ 触发 Reviewer Agent/exit→ 退出
3. 构建过程详解
为什么必须从根目录构建?
codingagent 依赖 agentscope-harness、agentscope-extensions-model-* 等多个模块, 这些模块的版本由根 pom.xml 的 ${revision} 统一管理。单独构建 codingagent 子模块 会遇到 BOM 版本找不到的问题。
正确的构建命令:
bash
# 从项目根目录构建
cd agentscope-java
mvn -pl agentscope-examples/agents/agentscope-codingagent -am compile
# 或打包为 Fat JAR
mvn -pl agentscope-examples/agents/agentscope-codingagent -am package -DskipTests-am(also-make)会先构建所有依赖模块,-pl 指定只构建 codingagent。
pom.xml 关键依赖
agentscope-harness ← Agent 核心运行时
agentscope-extensions-model-openai ← OpenAI 模型实现
agentscope-extensions-model-anthropic ← Anthropic 模型实现
agentscope-extensions-model-dashscope ← DashScope 模型实现(默认)
agentscope-extensions-channel-dingtalk ← 钉钉通道
agentscope-extensions-channel-feishu ← 飞书通道
spring-boot-starter-webflux ← WebFlux + Netty(Webhook 服务)
spring-boot-starter-actuator ← Actuator + Prometheus
sqlite-jdbc ← SQLite 数据库
tink ← Google Tink 加密
okhttp ← HTTP 工具底层
jjwt ← GitHub App JWT 认证
micrometer-registry-prometheus ← Prometheus 指标
micrometer-tracing-bridge-otel ← OTel 链路追踪Build 插件
xml
<!-- Spring Boot 打包插件(Webhook 服务入口) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<mainClass>io.agentscope.harness.coding.CodingAgentApplication</mainClass>
</plugin>
<!-- exec-maven-plugin(CLI 入口) -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<mainClass>io.agentscope.harness.coding.CodingChatCli</mainClass>
</plugin>4. 启动过程详解(Webhook 服务模式)
从 JVM 启动到应用就绪,完整经历以下 6 个阶段:
阶段 1:JVM + Spring Boot 自动配置
JVM 加载 CodingAgentApplication.class
→ SpringApplication.run() 启动
→ @SpringBootApplication 触发自动配置
→ Spring Boot WebFlux 自动配置启动 Netty 服务器
→ Actuator 自动注册 /actuator/prometheus, /actuator/health
→ Micrometer MeterRegistry 自动注入application.yml 配置:
yaml
server:
port: ${PORT:8080} # 默认 8080,可通过 PORT 环境变量覆盖
management:
endpoints:
web:
exposure:
include: health,prometheus,info,metrics # 暴露哪些 Actuator 端点
tracing:
sampling:
probability: ${TRACING_SAMPLE_RATE:0.1} # 10% 链路采样率初学者注意:codingagent 没有 Security 配置!因为它是一个 Webhook 接收服务, GitHub Webhook 通过 HMAC 签名验证,而非 Spring Security 的 JWT 认证。
阶段 2:SqliteBaseStore Bean 创建
java
@Bean
public SqliteBaseStore sqliteBaseStore() throws SQLException {
String dbPath = System.getProperty("agentscope.codingagent.db", ".agentscope/codingagent.db");
return new SqliteBaseStore(dbPath);
}执行细节:
- 使用 JDBC 连接 SQLite 文件
{cwd}/.agentscope/codingagent.db - 如果文件不存在,SQLite JDBC 自动创建
init()方法执行CREATE TABLE IF NOT EXISTS store,建表包含:namespace TEXT— 命名空间(如threads/abc)key TEXT— 键value TEXT— JSON 值version INTEGER— 版本号(乐观锁)- PRIMARY KEY = (namespace, key)
这是 codingagent 与 paw/builder 的最大差异之一:paw 使用 H2 + JPA + Spring Data, codingagent 使用 SQLite + 手写 SQL。原因:codingagent 不需要 ORM,只需要 KV 存储。
阶段 3:CodingAgentMetrics Bean 创建
java
@Bean
public CodingAgentMetrics codingAgentMetrics(MeterRegistry registry) {
return new CodingAgentMetrics(registry);
}在 Micrometer MeterRegistry 上注册 8 个指标:
coding_agent.webhook.received— 接收的 Webhook 数coding_agent.webhook.duplicate— 去重后跳过的 Webhook 数coding_agent.dispatch.total— Agent 调度总数coding_agent.dispatch.errors— 调度失败数coding_agent.model.calls— LLM 调用数coding_agent.findings.added— Reviewer Findings 新增数coding_agent.review.published— PR Review 发布数coding_agent.dispatch.duration— 调度延迟(Timer)
阶段 4:两个 Toolkit Bean 创建
java
@Bean
public Toolkit codingToolkit(ObjectProvider<RunDispatcher> dispatcherProvider) {
Toolkit tk = new Toolkit();
tk.registerTool(new HttpRequestTool()); // HTTP 请求工具
tk.registerTool(new FetchUrlTool()); // URL 内容抓取
tk.registerTool(new WebSearchTool()); // 网页搜索
tk.registerTool(new GitHubApiTool()); // GitHub REST API
tk.registerTool(new RequestPrReviewTool( // 触发 Reviewer
prUrl -> dispatcherProvider.getObject().dispatchReviewer(prUrl).subscribe()));
return tk;
}
@Bean
public Toolkit reviewerToolkit(ReviewerFindingsService findingsService, GitHubReviewPublisher publisher) {
Toolkit tk = new Toolkit();
tk.registerTool(new GitHubApiTool()); // GitHub REST API(只读)
tk.registerTool(new FetchUrlTool()); // URL 内容抓取
tk.registerTool(new AddFindingTool(findingsService)); // 添加 Finding
tk.registerTool(new UpdateFindingTool(findingsService)); // 更新 Finding
tk.registerTool(new ListFindingsTool(findingsService)); // 列出 Findings
tk.registerTool(new PublishReviewTool(findingsService, publisher)); // 发布 Review
return tk;
}关键设计点:
codingToolkit 使用
ObjectProvider<RunDispatcher>— 这是懒加载技巧! 因为RunDispatcher依赖CodingBootstrap,而CodingBootstrap依赖codingToolkit, 形成循环依赖。使用ObjectProvider在工具实际调用时才获取RunDispatcher,打破循环。reviewerToolkit 没有 execute/shell 工具 — Reviewer Agent 只读审查, 不能修改代码或执行命令。这是安全设计。
阶段 5:CodingBootstrap Bean 创建(核心!)
java
@Bean
public CodingBootstrap codingBootstrap(
Toolkit codingToolkit, Toolkit reviewerToolkit, SqliteBaseStore store) throws IOException {
Path cwd = Paths.get(System.getProperty("user.dir"));
return CodingBootstrap.builder()
.cwd(cwd)
.withDualCodingAgents(codingToolkit, reviewerToolkit, store)
.build();
}这是整个启动流程最复杂的部分,详见 第 6 章。
阶段 6:RunDispatcher + GitHubWebhookHandler 创建
java
@Bean
public RunDispatcher runDispatcher(CodingBootstrap bootstrap, SqliteBaseStore store, CodingAgentMetrics metrics) {
return new RunDispatcher(bootstrap.gateway(), store, metrics);
}
@Bean
public GitHubWebhookHandler gitHubWebhookHandler(
RunDispatcher dispatcher, SqliteBaseStore store, CodingAgentMetrics metrics) {
return new GitHubWebhookHandler(dispatcher, store, metrics);
}至此,所有 Bean 就绪:
- Spring Boot Netty 服务器监听 8080 端口
GitHubWebhookHandler注册为@RestControllerPOST /webhooks/github端点可接收 GitHub 事件RunDispatcher连接 Webhook → Gateway → Agent 的调度链路
5. 启动过程详解(CLI REPL 模式)
CLI 模式不经过 Spring Boot,纯 Java main 方法启动:
1. CodingChatCli.main()
2. printBanner() — 打印 ASCII art 横幅
3. buildModel() — 从环境变量构建模型(CodingAgentFactory.buildModel())
4. CodingBootstrap.builder()
.cwd(user.dir)
.model(model) ← 手动传入 Model(Webhook 模式由 Bootstrap 自己构建)
.skipConfigFile(true) ← 关键!跳过 ~/.agentscope/codingagent/agentscope.json
.withDualCodingAgents(codingToolkit, null) ← reviewerToolkit=null(CLI 模式没有 Findings 存储)
.build()
5. bootstrap.chatUiChannel() — 获取 ChatUiChannel
6. REPL 循环:reader.readLine() → chat.send(line).block()
7. 退出时 bootstrap.stop()为什么 skipConfigFile=true?
CLI 模式完全在代码中配置双 Agent(withDualCodingAgents),如果同时加载 ~/.agentscope/codingagent/agentscope.json,文件中的配置会和代码配置冲突覆盖。 所以 CLI 必须跳过配置文件。
6. CodingBootstrap 核心架构详解
CodingBootstrap.Builder.build() 是整个项目的组装中枢,分 3 个阶段:
Phase 1:构建共享会话基础设施
1. 加载 agentscope.json 配置文件
↓ 路径: ~/.agentscope/codingagent/agentscope.json(或 skipConfigFile=true 时跳过)
↓ 解析为 AgentscopeConfig 对象
2. 确定所有 Agent ID 集合
↓ 合并: fileConfig.agents.keySet() + prebuilt.keySet() + configurators.keySet()
↓ 默认: ["coding", "reviewer"]
3. 确定主 Agent ID
↓ 优先级: Builder.mainAgent > fileConfig.main > "default" > 集合中第一个
↓ 结果: "coding"
4. 解析主 Agent workspace 路径
↓ 优先级: fileConfig.workspace > DEFAULT_WORKSPACE_ROOT (~/.agentscope/codingagent/workspace)
↓ 结果: ~/.agentscope/codingagent/workspace 或配置中的路径
5. seedWorkspaceTemplates(mainWorkspace)
↓ 将 5 个模板文件复制到 workspace(如果不存在)
↓ skills/verify-changes/SKILL.md
↓ skills/git-checkpoint/SKILL.md
↓ skills/apply-patch/SKILL.md
↓ skills/code-search/SKILL.md
↓ subagents/general.md
↓ 策略: copy-if-absent(不覆盖用户修改)
6. 构建临时 HarnessAgent.Builder 提取 SubagentEntry
↓ applyFileEntry() — 应用文件配置到 Builder
↓ mainCustomizer.accept() — 应用编程式配置(withDualCodingAgents)
↓ buildSubagentEntries(workspace) — 扫描 workspace/subagents/ 目录
↓ 结果: List<SubagentEntry>(子 agent 定义列表)
7. 创建 WorkspaceManager
↓ WorkspaceManager(workspace, globalFileSystem) 或 WorkspaceManager(workspace)
8. 创建 DefaultAgentManager
↓ DefaultAgentManager(subagentEntries, wsManager)
↓ 负责创建和销毁子 agent 实例
9. 创建 SessionStore
↓ 路径: <workspace>/sessions.json
↓ sessionStore.load() — 从 JSON 文件恢复已有会话
10. 创建 SessionAgentManager
↓ SessionAgentManager(dam, config, subagentRunRegistry, sessionStore)
↓ 核心:管理所有会话的生命周期、并发控制、路由
11. 创建 ChannelManager
↓ ChannelManager() — 空的通道管理器
12. 创建 HarnessGateway
↓ HarnessGateway.create(sam, channelManager)
↓ 注册 AnnounceDispatcher = gateway::tryDispatchAnnounce
↓ 注册 SpawnInterceptor = gateway::onSpawn
↓ restorePersistedMainSessions() — 恢复已有 MAIN 会话的路由映射
13. 创建 WorkspaceTaskRepository
↓ WorkspaceTaskRepository(wsManager, mainAgentId)
↓ 管理 todo_write 任务持久化
14. 创建 SessionsTool
↓ SessionsTool(sam, taskRepo, null, 0)
↓ 提供 sessions_spawn / sessions_send / sessions_list / sessions_history 工具Phase 2:构建所有 Agent 实例
对每个 agent ID:
↓ 如果是 prebuilt(预构建的),直接使用
↓ 否则:
1. 创建 HarnessAgent.Builder
2. applyFileEntry() — 从 agentscope.json 应用 workspace、name、description 等
3. seedWorkspaceTemplates() — 为该 agent 的 workspace 也播种模板
4. b.model(model) — 如果 Builder 统一指定了 model
5. b.abstractFilesystem(globalFileSystem) — 如果指定了全局文件系统
6. b.externalSubagentTool(sessionsTool) — 注入 SessionsTool!
7. c.accept(b) — 应用编程式配置器(withDualCodingAgents 的 lambda)
8. b.build() — 构建 HarnessAgent 实例withDualCodingAgents lambda 对每个 Agent 做了什么?
对 coding Agent:
java
b.model(CodingAgentFactory.buildModel()) // 构建模型(DashScope/OpenAI/Anthropic + Fallback)
b.toolkit(codingToolkit) // 注册 5 个工具
b.sysPrompt(CodingSystemPrompt.build(...)) // 16 段系统提示词
b.maxIters(50) // 最多 50 次推理迭代
b.enableTaskList() // 启用 todo_write 规划
configureCompaction(b) // 40 条触发压缩,保留 15 条
configureSandbox(b) // SANDBOX_TYPE=docker 时启用 Docker 沙箱
registerOpenSweHooks(b, store) // 注册 3 个中间件对 reviewer Agent:
java
b.model(CodingAgentFactory.buildModel()) // 使用相同的模型构建逻辑
b.toolkit(reviewerToolkit) // 注册 6 个审查专用工具
b.sysPrompt(ReviewerSystemPrompt.buildTemplate(...)) // Reviewer 系统提示词
b.maxIters(30) // 最多 30 次推理迭代
b.disableSubagents() // 禁止子 agent(Reviewer 不需要)
configureCompaction(b) // 同样的压缩配置
configureSandbox(b) // 同样的沙箱配置
registerOpenSweHooks(b, store) // 同样的 3 个中间件Phase 3:连接 Gateway + 注册所有 Agent
1. gateway.registerAgent(id, agent) — 对每个 agent 注册到 Gateway
2. gateway.bindMainAgent(mainAgent) — 绑定主 Agent 作为路由默认值
3. resolveChannels() — 合并编程式 Channel 和文件配置 Channel
↓ chatui → ChatUiChannel.create()
↓ dingtalk → DingTalkChannel.fromProperties()
↓ feishu → FeishuChannel.fromProperties()
↓ disabled 的 Channel 被移除
4. 输出日志:AgentBootstrap: cwd=..., config=..., main=coding, agents=[coding, reviewer], channels=[chatui]
5. 返回 CodingBootstrap 实例7. 双 Agent 体系:Coding + Reviewer
Coding Agent(编码执行者)
| 特性 | 值 |
|---|---|
| ID | coding |
| 名称 | OpenSWE-Coding |
| 工具 | http_request, fetch_url, web_search, github_api_request, request_pr_review + 内置工具 |
| 最大迭代 | 50 |
| 子 Agent | ✅ 启用(通过 SessionsTool) |
| 沙箱 | Docker(可选) |
| 系统提示 | 16 段拼装(详见 CodingSystemPrompt) |
系统提示词由 16 个段拼装:
- Working Environment — 沙箱环境说明
- Task Overview — 任务概述
- About You — 自我认知(OpenSWE-Coding)
- Repository Setup — Git 克隆与 AGENTS.md 读取
- File & Code Management — 文件管理规则
- Planning & Task Tracking — todo_write 规划流程
- Task Execution — 任务执行步骤
- Verification Loop — 编辑后必须验证
- Safe Editing & Recovery — Git checkpoint + 回滚
- Tool Usage — 各工具使用说明
- Tool Usage Best Practices — 搜索、依赖、历史
- Searching the Codebase — ripgrep 搜索技巧
- Coding Standards — 编码规范
- Core Behavior — 持久性、准确性、自治性
- Dependency Installation — 依赖安装规则
- Communication Guidelines — 交流规则
- External Untrusted Comments —
<UNTRUSTED_GITHUB_COMMENT>安全处理 - Custom Instructions — 从 default_prompt.md 加载的自定义指令
Reviewer Agent(代码审查者)
| 特性 | 值 |
|---|---|
| ID | reviewer |
| 名称 | OpenSWE-Reviewer |
| 工具 | github_api_request, fetch_url, add_finding, update_finding, list_findings, publish_review |
| 最大迭代 | 30 |
| 子 Agent | ❌ 禁用 |
| 沙箱 | Docker(可选) |
| 系统提示 | PR 审查专用模板 |
关键安全设计:Reviewer Agent 没有 execute/shell 工具,不能修改代码。 它只能读取 PR diff、记录 Findings、发布 Review 评论。
双 Agent 协作流程
GitHub Issue/PR 评论 → Webhook → RunDispatcher
↓ 根据 threadId + agentId 路由到 coding 或 reviewer
↓ coding Agent 执行代码修改 → 可调用 request_pr_review 触发 reviewer
↓ reviewer Agent 审查 PR → 使用 add_finding 记录问题 → publish_review 发布到 GitHub8. 模型选择与 FallbackModel
模型选择逻辑
CodingAgentFactory.buildModel() 的选择逻辑:
读取 CODING_MODEL_ID 环境变量
↓ 格式: "提供商:模型名"
↓ dashscope:qwen-max(默认)
↓ openai:gpt-4o(需设 OPENAI_API_KEY)
↓ anthropic:claude-3.5(需设 ANTHROPIC_API_KEY)
解析前缀:
↓ "openai:" → OpenAIChatModel(apiKey=OPENAI_API_KEY, baseUrl=OPENAI_BASE_URL)
↓ "anthropic:" → AnthropicChatModel(apiKey=ANTHROPIC_API_KEY, baseUrl=ANTHROPIC_BASE_URL)
↓ "dashscope:" 或无前缀 → DashScopeChatModel(apiKey=DASHSCOPE_API_KEY)
检查 FALLBACK_MODEL_ID:
↓ 如果设置了 → return new FallbackModel(primary, fallback)
↓ 如果没设置 → return primaryFallbackModel 装饰器
java
public class FallbackModel implements Model {
private final Model primary;
private final Model secondary;
// 调用 primary.stream()
// 如果遇到 retryable 错误 → 自动切换到 secondary.stream()
// retryable 错误: rate_limit, overloaded, 529, too_many_requests, capacity
}设计意图:生产环境中 DashScope/OpenAI 可能遇到限流,FallbackModel 让 Agent 透明地切换到备用模型,不中断任务执行。
9. 中间件栈详解
codingagent 在双 Agent 上注册了 3 个中间件(OpenSWE 模式):
MessageQueueMiddleware(消息队列中间件)
作用:当某个 thread 正忙时,新到达的消息被排入队列, 在下一个推理步骤开始时注入到 system prompt 中。
工作流程:
1. RunDispatcher.dispatch() 设置 CURRENT_THREAD_ID = threadId
2. gateway.run() → Agent 开始推理
3. MessageQueueMiddleware.onSystemPrompt() 被调用
4. 从 SqliteBaseStore 读取 namespace=["queue", threadId] 的所有条目
5. 将队列消息追加到 system prompt: "[Message Queue — injected before this reasoning step]"
6. 清除已注入的队列条目依赖:SqliteBaseStore(如果 store=null,中间件不注册)
ThreadBudgetMiddleware(线程预算中间件)
作用:限制每个 thread 的模型调用次数,防止无限循环消耗资源。
配置:
THREAD_MODEL_CALL_BUDGET 环境变量(默认 200)工作流程:
每次 Agent.onReasoning() 被调用时:
1. 从 CURRENT_THREAD_ID 获取 threadId
2. callCounts[threadId].incrementAndGet()
3. 如果 count > maxModelCallsPerThread → 抛出 RuntimeException 终止运行ModelCallLimitMiddleware(全局模型调用限制中间件)
作用:限制所有 thread 的总模型调用次数,防止资源耗尽。
配置:
GLOBAL_MODEL_CALL_LIMIT 环境变量(默认 5000)工作流程:
每次 Agent.onReasoning() 被调用时:
1. totalCalls.incrementAndGet()
2. 如果 totalCalls > globalLimit → 抛出 RuntimeException 终止运行10. SqliteBaseStore 数据持久化
表结构
sql
CREATE TABLE store (
namespace TEXT NOT NULL, -- 命名空间(如 "threads/abc", "queue/abc", "deliveries")
key TEXT NOT NULL, -- 键
value TEXT NOT NULL, -- JSON 值
version INTEGER NOT NULL DEFAULT 1, -- 乐观锁版本号
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
PRIMARY KEY (namespace, key)
);命名空间用途
| Namespace | 用途 |
|---|---|
threads/<threadId> | 线程元数据(加密 token、状态等) |
queue/<threadId> | 排队消息(MessageQueueMiddleware) |
deliveries | Webhook 投递去重(GitHubWebhookHandler) |
findings/<threadId> | Reviewer Findings 存储 |
关键操作
put(namespace, key, value)— UPSERT(插入或更新 + 版本号递增)get(namespace, key)— 查询search(namespace, limit, offset)— 按命名空间搜索delete(namespace, key)— 删除putIfVersion(namespace, key, value, expectedVersion)— 乐观锁 CAS
数据库路径
默认: .agentscope/codingagent.db(相对于 cwd)
覆盖: -Dagentscope.codingagent.db=/path/to/db11. GitHub Webhook 处理流程
端点
POST /webhooks/github处理步骤
1. HMAC SHA-256 签名验证
↓ 从 X-Hub-Signature-256 读取签名
↓ 使用 GITHUB_WEBHOOK_SECRET 环境变量计算期望签名
↓ 常量时间比较(防时序攻击)
↓ 如果签名不匹配 → 401 Unauthorized
2. Delivery 去重
↓ 从 X-GitHub-Delivery 读取投递 ID
↓ 在 SqliteBaseStore 的 ["deliveries"] namespace 查询
↓ 如果已存在 → 返回 "Already processed"
3. 事件过滤
↓ 只处理: issue_comment, pull_request, pull_request_review_comment, push
↓ 其他事件 → 返回 "Ignored event"
4. 事件路由
↓ issue_comment (created) → Coding Agent
→ 线程 ID = SHA-256(owner/repo/number) → UUID
→ 提示词 = "GitHub issue comment on owner/repo#N: <comment>"
→ 外部评论用 <UNTRUSTED_GITHUB_COMMENT> 标签包裹
↓ pull_request (review_requested) → Reviewer Agent
→ 线程 ID = SHA-256(owner/repo/prNumber:reviewer) → UUID
→ 提示词 = "Please review the following pull request: <prUrl>"
↓ pull_request_review_comment (created) → Coding Agent
→ 类似 issue_comment,但针对 PR
↓ push → 仅记录日志,不触发 Agent
5. 自身评论过滤
↓ 比较 commenter 与 GITHUB_BOT_LOGIN 环境变量
↓ 如果是自身评论 → 跳过(防止无限循环)
6. 调度到 RunDispatcher
↓ dispatcher.dispatch(threadId, agentId, prompt, githubToken, userId)
↓ → Gateway.run() → Agent.call() → LLM 推理12. Session 会话管理体系
核心类
SessionAgentManager — 管理所有会话的生命周期、并发控制、路由。
会话类型
| 类型 | 枚举值 | 说明 |
|---|---|---|
| 主会话 | MAIN | 每个 threadId 对应一个主会话 |
| 子 Agent 会话 | SUBAGENT | Coding Agent spawn 的子任务会话 |
| 审查会话 | REVIEWER | Reviewer Agent 的会话 |
会话重置策略
SessionResetPolicy 定义会话何时过期:
| 模式 | 说明 |
|---|---|
NEVER(默认) | 会话永不过期 |
DAILY | 每天在指定小时(默认 4 点)重置 |
IDLE | 空闲超过 N 分钟后重置 |
BOTH | 每日 + 空闲双重条件 |
会话持久化
路径: <workspace>/sessions.json
格式: JSON 数组,每个条目包含 sessionKey, kind, agentId, gateKey, userId, lastActivityMs
加载: SessionStore.load() — 启动时恢复
保存: SessionStore.save() — 每次会话变更时持久化并发控制
- 每个 gateKey 有一个
SessionTurnGate(公平锁)→ 保证同一 thread 的 turn 串行执行 - 每个 CommandLane(SUBAGENT/NESTED)有 Semaphore → 限制子 agent 并发数
- 最大 spawn 深度 = 3(
SessionConstants.MAX_SPAWN_DEPTH)
13. HarnessGateway 路由机制
核心职责
- 路由:根据
MsgContext.canonicalKey()找到或创建 MAIN 会话 - Turn 串行化:每个 gateKey 有
SessionTurnGate锁,同一 thread 同时只有一个 turn - Announce 调度:子 Agent 完成后,自动向父 Agent 发送完成通知
- Agent 注册:
registerAgent(id, agent)+bindMainAgent(agent)构建路由表
路由流程
gateway.run(context, messages, outboundAddress)
↓
1. 从 context.extra().get("agentId") 获取请求的 agentId
2. resolveAgent(agentId) → 从 agentRegistry 查找
↓ 如果找不到 → 使用 mainAgent 作为默认
3. resolveOrCreateMainSession(gateKey, agent, userId)
↓ 查找 contextKeyToSessionKey 映射
↓ 如果会话存在且新鲜 → 直接使用
↓ 如果会话过期 → 注册新的 MAIN 会话
4. 记录 lastRouteBySessionKey → outboundAddress
↓ 用于后续 Announce 回复投递
5. withGatedTurn(gateKey, () -> agent.call(messages, runtimeContext))
↓ acquire(gateKey) → 获取公平锁
↓ agent.call() → 执行推理
↓ release(gateKey) → 释放锁Announce(子 Agent 完成通知)
当子 Agent 完成运行后:
1. SessionAgentManager 生成 PendingCompletion
2. 调用 AnnounceDispatcher = gateway::tryDispatchAnnounce
3. gateway 构建 announce 消息: Msg(role=USER, name="subagent_announce")
4. 找到父 Agent 的 sessionKey → gateKey → agentId
5. withGatedTurn(gateKey, () -> parentAgent.call(...))
6. 回复通过 ChannelManager.deliver() 投递到原始 Channel14. Workspace 脚手架与技能模板
Workspace 根路径
默认: ~/.agentscope/codingagent/workspace
配置: agentscope.json 中 agents.coding.workspace 字段注意:codingagent 的 workspace 在
~/.agentscope/codingagent/子目录下, 与 paw/builder 的~/.agentscope/workspace/分开,防止不同应用互相冲突。
自动播种的模板文件
启动时 seedWorkspaceTemplates() 将以下文件从 JAR 复制到 workspace(如果不存在):
workspace/
├── skills/
│ ├── verify-changes/SKILL.md — 验证变更技能
│ ├── git-checkpoint/SKILL.md — Git checkpoint 技能
│ ├── apply-patch/SKILL.md — git apply 技能
│ └── code-search/SKILL.md — ripgrep 搜索技能
├── subagents/
│ └── general.md — 通用子 agent 声明
└── default_prompt.md — 自定义指令模板(从 classpath 加载)策略:copy-if-absent — 如果文件已存在(用户修改过),不覆盖。
子 Agent 扫描
buildSubagentEntries() 扫描 <workspace>/subagents/ 目录下的 .md 文件, 每个文件声明一个子 Agent 定义。这些定义会被 DefaultAgentManager 用于 创建子 Agent 实例。
15. Token 加密与安全
TokenEncryption
使用 Google Tink AEAD(AES-256-GCM)加密 GitHub Token:
加密流程:
plaintext → aead.encrypt(bytes, null) → Base64url 编码 → 存入 SqliteBaseStore
解密流程:
SqliteBaseStore 取出 → Base64url 解码 → aead.decrypt(bytes, null) → plaintext密钥管理
密钥来源(优先级从高到低):
TOKEN_ENC_KEYSET_PATH环境变量 → 密钥 JSON 文件路径TOKEN_ENC_KEYSET环境变量 → Base64 编码的密钥 JSON
生成密钥:
bash
tinkey create-keyset --key-template AES256_GCM --out keyset.jsonHMAC Webhook 签名验证
1. 从 X-Hub-Signature-256 读取 "sha256=<hex>"
2. 使用 GITHUB_WEBHOOK_SECRET 计算 HMAC-SHA256
3. 常量时间比较(constantTimeEquals)→ 防时序攻击16. 可观测性:Micrometer + Prometheus
指标端点
GET /actuator/prometheus → Prometheus 格式指标
GET /actuator/health → 详细健康状态
GET /actuator/info → 应用信息
GET /actuator/metrics → 单项指标查询关键指标
| 指标名 | 类型 | 说明 |
|---|---|---|
coding_agent.webhook.received | Counter | 接收的 GitHub Webhook 总数 |
coding_agent.webhook.duplicate | Counter | 去重跳过的 Webhook 数 |
coding_agent.dispatch.total | Counter | Agent 调度总数 |
coding_agent.dispatch.errors | Counter | 调度失败数 |
coding_agent.dispatch.duration | Timer | 调度延迟 |
coding_agent.model.calls | Counter | LLM 调用总数 |
coding_agent.findings.added | Counter | Reviewer Findings 新增数 |
coding_agent.review.published | Counter | PR Review 发布数 |
OTel 链路追踪
yaml
# application.yml
management:
tracing:
sampling:
probability: ${TRACING_SAMPLE_RATE:0.1} # 10% 采样率设置 OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 启用。
17. Channel 体系详解
支持的 Channel 类型
| ID | 类 | 说明 |
|---|---|---|
chatui | ChatUiChannel | 内置聊天 UI(CLI/Web 嵌入) |
dingtalk | DingTalkChannel | 钉钉机器人通道 |
feishu | FeishuChannel | 飞书机器人通道 |
Channel 创建流程
resolveChannels(builderChannels, fileConfig)
↓ 合并编程式注册和文件配置
↓ 文件配置中 disabled=true → 移除
↓ 文件配置中已存在编程式注册 → 保留编程式版本
↓ chatui → ChatUiChannel.create(config)
↓ dingtalk → DingTalkChannel.fromProperties(channelId, config, properties)
↓ feishu → FeishuChannel.fromProperties(channelId, config, properties)Channel 启动
bootstrap.start() 或 bootstrap.start(channels)
↓ channelManager.register(channel)
↓ channelManager.initAll(gateway) → 每个 channel.init(gateway)
↓ channelManager.startAll() → 每个 channel.start()18. 配置参数速查表
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
DASHSCOPE_API_KEY | — | DashScope API 密钥(必须) |
OPENAI_API_KEY | — | OpenAI API 密钥 |
OPENAI_BASE_URL | — | OpenAI 自定义 Base URL |
ANTHROPIC_API_KEY | — | Anthropic API 密钥 |
ANTHROPIC_BASE_URL | — | Anthropic 自定义 Base URL |
CODING_MODEL_ID | dashscope:qwen-max | 主模型 ID |
FALLBACK_MODEL_ID | — | 备用模型 ID |
PORT | 8080 | Webhook 服务端口 |
SANDBOX_TYPE | none | 沙箱类型(docker/none) |
SANDBOX_IMAGE | agentscope/coding-sandbox:latest | Docker 沙箱镜像 |
SANDBOX_WORK_DIR | /home/agentscope/workspace | 沙箱内工作目录 |
GITHUB_WEBHOOK_SECRET | — | Webhook HMAC 签名密钥 |
GITHUB_BOT_LOGIN | — | Bot GitHub 登录名(过滤自身评论) |
THREAD_MODEL_CALL_BUDGET | 200 | 每 thread 模型调用上限 |
GLOBAL_MODEL_CALL_LIMIT | 5000 | 全局模型调用上限 |
TOKEN_ENC_KEYSET_PATH | — | Tink 密钥文件路径 |
TOKEN_ENC_KEYSET | — | Tink 密钥(Base64) |
TRACING_SAMPLE_RATE | 0.1 | 链路追踪采样率 |
OTEL_EXPORTER_OTLP_ENDPOINT | — | OTel Collector 地址 |
JVM 参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-Dagentscope.codingagent.db | .agentscope/codingagent.db | SQLite 数据库路径 |
agentscope.json 结构
json
{
"$schema": "https://json.schemastore.org/agentscope.json",
"main": "coding",
"agents": {
"coding": {
"name": "OpenSWE-Coding",
"workspace": ".agentscope/coding-workspace",
"maxIters": 50,
"sysPrompt": "...",
"description": "...",
"skillRepository": { ... }
},
"reviewer": {
"name": "OpenSWE-Reviewer",
"workspace": ".agentscope/reviewer-workspace",
"maxIters": 30
}
},
"channels": {
"chatui": {
"defaultAgentId": "coding"
},
"dingtalk": {
"disabled": false,
"properties": { ... }
}
}
}19. ClawHome 目录结构详解
~/.agentscope/codingagent/ ← per-app home(与其他应用隔离)
├── agentscope.json ← 配置文件
├── codingagent.db ← SQLite 数据库
└── workspace/ ← 主 Agent workspace
├── sessions.json ← 会话持久化
├── skills/
│ ├── verify-changes/SKILL.md
│ ├── git-checkpoint/SKILL.md
│ ├── apply-patch/SKILL.md
│ └── code-search/SKILL.md
├── subagents/
│ └── general.md
├── AGENTS.md.template ← (从 classpath 播种)
└── tasks/ ← WorkspaceTaskRepository 持久化目录20. 常见启动问题与解决
问题 1:DASHSCOPE_API_KEY 未设置
错误: DashScopeChatModel 构建 失败,apiKey=null
解决: export DASHSCOPE_API_KEY=sk-xxx
或在 start-codingagent.bat 中设置问题 2:BOM 版本找不到
错误: Cannot resolve agentscope-harness:jar:xxx
解决: 必须从项目根目录构建,加 -am 参数
mvn -pl agentscope-examples/agents/agentscope-codingagent -am compile问题 3:端口 8080 被占用
错误: Netty started on port 8080 but failed to bind
解决: 设置 PORT 环境变量
PORT=9090 mvn spring-boot:run
或 application.yml 中 server.port=9090问题 4:SQLite 数据库损坏
错误: SqliteBaseStore init failed
解决: 删除数据库文件重新启动
rm .agentscope/codingagent.db问题 5:配置文件冲突(CLI 模式)
错误: Agent id 'xxx' is not among configured agents
解决: CLI 模式已经 skipConfigFile=true
如果手动使用 CodingBootstrap.builder(),记得 .skipConfigFile(true)问题 6:Docker 沙箱连接失败
错误: DockerFilesystem 无法创建容器
解决: 确保 Docker daemon 运行
设置 SANDBOX_TYPE=none 可禁用沙箱(本地开发)问题 7:Webhook 签名验证失败
错误: Invalid signature for delivery=xxx
解决: 在 GitHub Webhook 设置页配置相同的 Secret
设置 GITHUB_WEBHOOK_SECRET=xxx问题 8:Token 加密密钥缺失
错误: No token encryption keyset configured
解决: 生成 Tink 密钥并设置环境变量
tinkey create-keyset --key-template AES256_GCM --out keyset.json
export TOKEN_ENC_KEYSET_PATH=./keyset.json21. 关键术语解释
| 术语 | 解释 |
|---|---|
| HarnessAgent | AgentScope 的核心 Agent 类,封装模型调用 + 工具调用 + 记忆管理 |
| CodingBootstrap | 组装中枢,Builder 模式构建双 Agent + Gateway + ChannelManager |
| SessionAgentManager | 会话生命周期管理器,管理 MAIN/SUBAGENT/REVIEWER 会话 |
| HarnessGateway | 路由网关,将消息路由到正确的 Agent + Turn 串行化 + Announce 调度 |
| SessionTurnGate | 每个 threadId 的公平锁,保证同一 thread 的 turn 串行执行 |
| RunDispatcher | Webhook → Agent 调度器,空闲即派发 |
| SqliteBaseStore | SQLite KV 存储,用于线程元数据、消息队列、投递去重、Findings |
| FallbackModel | Model 装饰器,主模型限流/过载时透明切换到备模型 |
| MessageQueueMiddleware | 将排队消息注入 system prompt 的中间件 |
| ThreadBudgetMiddleware | 每 thread 模型调用上限中间件 |
| ModelCallLimitMiddleware | 全局模型调用上限中间件 |
| SessionsTool | 提供 sessions_spawn/send/list/history 工具 |
| WorkspaceManager | 管理 Agent workspace 目录 |
| DefaultAgentManager | 子 Agent 创建和销毁管理器 |
| ReviewerFindingsService | Findings CRUD 服务(SqliteBaseStore 后端) |
| GitHubReviewPublisher | 将 Findings 发布为 GitHub PR Review |
| TokenEncryption | Google Tink AEAD 加密/解密 GitHub Token |
| SessionResetPolicy | 会话过期策略:NEVER/DAILY/IDLE/BOTH |
| ChannelManager | Channel 生命周期管理 + outbound 消息投递 |
| OpenSWE | Open SWE 模式:自治规划 + 验证循环 + 安全编辑 |
22. 启动流程时序总结
Webhook 服务模式时序
JVM → CodingAgentApplication.main()
│
├─ Spring Boot 自动配置
│ ├─ Netty 服务器初始化(端口 8080)
│ ├─ Actuator 注册(/actuator/prometheus, /actuator/health)
│ ├─ Micrometer MeterRegistry 创建
│
├─ Bean 创建顺序(Spring 容器)
│ ├─ SqliteBaseStore(SQLite 连接 + 建表)
│ ├─ CodingAgentMetrics(8 个 Micrometer 指标)
│ ├─ codingToolkit(5 个 Coding 工具 + 懒加载 RunDispatcher)
│ ├─ reviewerToolkit(6 个 Reviewer 工具)
│ ├─ ReviewerFindingsService + GitHubReviewPublisher
│ ├─ CodingBootstrap(核心组装!)
│ │ ├─ 加载 ~/.agentscope/codingagent/agentscope.json
│ │ ├─ seedWorkspaceTemplates() → workspace 模板播种
│ │ ├─ buildSubagentEntries() → 扫描子 Agent 定义
│ │ ├─ 创建 DefaultAgentManager + SessionAgentManager
│ │ ├─ 创建 HarnessGateway + 注册 Announce/Spawn 回调
│ │ ├─ 创建 SessionsTool + WorkspaceTaskRepository
│ │ ├─ 构建 coding Agent(模型 + 工具 + 系统提示 + 中间件 + 沙箱)
│ │ ├─ 构建 reviewer Agent(模型 + 工具 + 系统提示 + 中间件 + 沙箱)
│ │ ├─ gateway.registerAgent() → 注册双 Agent
│ │ ├─ gateway.bindMainAgent() → 绑定 coding 为默认
│ │ └─ resolveChannels() → 创建 ChatUIChannel 等
│ ├─ RunDispatcher(Gateway + Store + Metrics)
│ ├─ GitHubWebhookHandler(Dispatcher + Store + Metrics)
│
├─ Netty 就绪 → 监听 8080
│
└─ 应用就绪!
├─ POST /webhooks/github 可接收 GitHub 事件
├─ GET /health 可检查健康状态
├─ /actuator/prometheus 可被 Prometheus 刮取CLI REPL 模式时序
JVM → CodingChatCli.main()
│
├─ printBanner()
├─ CodingAgentFactory.buildModel() → 构建 DashScope/OpenAI/Anthropic 模型
├─ CodingBootstrap.builder()
│ ├─ .cwd(user.dir)
│ ├─ .model(model) ← 手动传入
│ ├─ .skipConfigFile(true) ← 跳过配置文件
│ ├─ .withDualCodingAgents(codingToolkit, null) ← 没有 reviewerToolkit
│ └─ .build()
│ ├─ 不加载 agentscope.json
│ ├─ 构建 coding Agent(with OpenSWE middleware)
│ ├─ 构建 reviewer Agent(简化版,无 Findings)
│ ├─ 创建 SessionAgentManager + HarnessGateway
│ ├─ seedWorkspaceTemplates()
│ └─ resolveChannels() → 空列表
├─ bootstrap.chatUiChannel() → ChatUiChannel.create(gateway)
├─ REPL 循环: stdin → chat.send() → stdout
└─ /exit → bootstrap.stop()下一步建议:
- 设置
DASHSCOPE_API_KEY,运行start-codingagent.bat体验 CLI 模式- 在 GitHub 仓库设置 Webhook,指向你的服务地址,体验 Webhook 模式
- 阅读
CodingSystemPrompt.java了解 Agent 的 16 段系统提示词设计- 阅读
HarnessGateway.java理解多 Agent 路由和 Announce 机制