Skip to content
页面导航
精简

AgentScope CodingAgent 启动指南(初学者版)

本文档面向初学者,详细拆解 agentscope-codingagent 项目从源码构建到应用就绪的每一步, 帮助你理解「为什么这样设计」和「每个组件在做什么」。


目录

  1. 项目概览
  2. 两种启动模式
  3. 构建过程详解
  4. 启动过程详解(Webhook 服务模式)
  5. 启动过程详解(CLI REPL 模式)
  6. CodingBootstrap 核心架构详解
  7. 双 Agent 体系:Coding + Reviewer
  8. 模型选择与 FallbackModel
  9. 中间件栈详解
  10. SqliteBaseStore 数据持久化
  11. GitHub Webhook 处理流程
  12. Session 会话管理体系
  13. HarnessGateway 路由机制
  14. Workspace 脚手架与技能模板
  15. Token 加密与安全
  16. 可观测性:Micrometer + Prometheus
  17. Channel 体系详解
  18. 配置参数速查表
  19. ClawHome 目录结构详解
  20. 常见启动问题与解决
  21. 关键术语解释
  22. 启动流程时序总结

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 的关键差异

特性codingagentpawbuilder
入口模式Webhook 服务 + CLI REPLWeb 服务 + 前端 SPAWeb 服务 + 前端 SPA
数据库SQLiteH2(JPA)H2(JPA)
Security无(Webhook 服务)Spring Security + JWTSpring Security + JWT
前端React SPAReact 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-harnessagentscope-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;
}

关键设计点:

  1. codingToolkit 使用 ObjectProvider<RunDispatcher> — 这是懒加载技巧! 因为 RunDispatcher 依赖 CodingBootstrap,而 CodingBootstrap 依赖 codingToolkit, 形成循环依赖。使用 ObjectProvider 在工具实际调用时才获取 RunDispatcher,打破循环。

  2. 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 注册为 @RestController
  • POST /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(编码执行者)

特性
IDcoding
名称OpenSWE-Coding
工具http_request, fetch_url, web_search, github_api_request, request_pr_review + 内置工具
最大迭代50
子 Agent✅ 启用(通过 SessionsTool)
沙箱Docker(可选)
系统提示16 段拼装(详见 CodingSystemPrompt)

系统提示词由 16 个段拼装:

  1. Working Environment — 沙箱环境说明
  2. Task Overview — 任务概述
  3. About You — 自我认知(OpenSWE-Coding)
  4. Repository Setup — Git 克隆与 AGENTS.md 读取
  5. File & Code Management — 文件管理规则
  6. Planning & Task Tracking — todo_write 规划流程
  7. Task Execution — 任务执行步骤
  8. Verification Loop — 编辑后必须验证
  9. Safe Editing & Recovery — Git checkpoint + 回滚
  10. Tool Usage — 各工具使用说明
  11. Tool Usage Best Practices — 搜索、依赖、历史
  12. Searching the Codebase — ripgrep 搜索技巧
  13. Coding Standards — 编码规范
  14. Core Behavior — 持久性、准确性、自治性
  15. Dependency Installation — 依赖安装规则
  16. Communication Guidelines — 交流规则
  17. External Untrusted Comments — <UNTRUSTED_GITHUB_COMMENT> 安全处理
  18. Custom Instructions — 从 default_prompt.md 加载的自定义指令

Reviewer Agent(代码审查者)

特性
IDreviewer
名称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 发布到 GitHub

8. 模型选择与 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 primary

FallbackModel 装饰器

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)
deliveriesWebhook 投递去重(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/db

11. 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 会话SUBAGENTCoding Agent spawn 的子任务会话
审查会话REVIEWERReviewer 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 路由机制

核心职责

  1. 路由:根据 MsgContext.canonicalKey() 找到或创建 MAIN 会话
  2. Turn 串行化:每个 gateKey 有 SessionTurnGate 锁,同一 thread 同时只有一个 turn
  3. Announce 调度:子 Agent 完成后,自动向父 Agent 发送完成通知
  4. 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() 投递到原始 Channel

14. 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

密钥管理

密钥来源(优先级从高到低):

  1. TOKEN_ENC_KEYSET_PATH 环境变量 → 密钥 JSON 文件路径
  2. TOKEN_ENC_KEYSET 环境变量 → Base64 编码的密钥 JSON

生成密钥:

bash
tinkey create-keyset --key-template AES256_GCM --out keyset.json

HMAC 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.receivedCounter接收的 GitHub Webhook 总数
coding_agent.webhook.duplicateCounter去重跳过的 Webhook 数
coding_agent.dispatch.totalCounterAgent 调度总数
coding_agent.dispatch.errorsCounter调度失败数
coding_agent.dispatch.durationTimer调度延迟
coding_agent.model.callsCounterLLM 调用总数
coding_agent.findings.addedCounterReviewer Findings 新增数
coding_agent.review.publishedCounterPR 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说明
chatuiChatUiChannel内置聊天 UI(CLI/Web 嵌入)
dingtalkDingTalkChannel钉钉机器人通道
feishuFeishuChannel飞书机器人通道

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_KEYDashScope API 密钥(必须)
OPENAI_API_KEYOpenAI API 密钥
OPENAI_BASE_URLOpenAI 自定义 Base URL
ANTHROPIC_API_KEYAnthropic API 密钥
ANTHROPIC_BASE_URLAnthropic 自定义 Base URL
CODING_MODEL_IDdashscope:qwen-max主模型 ID
FALLBACK_MODEL_ID备用模型 ID
PORT8080Webhook 服务端口
SANDBOX_TYPEnone沙箱类型(docker/none)
SANDBOX_IMAGEagentscope/coding-sandbox:latestDocker 沙箱镜像
SANDBOX_WORK_DIR/home/agentscope/workspace沙箱内工作目录
GITHUB_WEBHOOK_SECRETWebhook HMAC 签名密钥
GITHUB_BOT_LOGINBot GitHub 登录名(过滤自身评论)
THREAD_MODEL_CALL_BUDGET200每 thread 模型调用上限
GLOBAL_MODEL_CALL_LIMIT5000全局模型调用上限
TOKEN_ENC_KEYSET_PATHTink 密钥文件路径
TOKEN_ENC_KEYSETTink 密钥(Base64)
TRACING_SAMPLE_RATE0.1链路追踪采样率
OTEL_EXPORTER_OTLP_ENDPOINTOTel Collector 地址

JVM 参数

参数默认值说明
-Dagentscope.codingagent.db.agentscope/codingagent.dbSQLite 数据库路径

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.json

21. 关键术语解释

术语解释
HarnessAgentAgentScope 的核心 Agent 类,封装模型调用 + 工具调用 + 记忆管理
CodingBootstrap组装中枢,Builder 模式构建双 Agent + Gateway + ChannelManager
SessionAgentManager会话生命周期管理器,管理 MAIN/SUBAGENT/REVIEWER 会话
HarnessGateway路由网关,将消息路由到正确的 Agent + Turn 串行化 + Announce 调度
SessionTurnGate每个 threadId 的公平锁,保证同一 thread 的 turn 串行执行
RunDispatcherWebhook → Agent 调度器,空闲即派发
SqliteBaseStoreSQLite KV 存储,用于线程元数据、消息队列、投递去重、Findings
FallbackModelModel 装饰器,主模型限流/过载时透明切换到备模型
MessageQueueMiddleware将排队消息注入 system prompt 的中间件
ThreadBudgetMiddleware每 thread 模型调用上限中间件
ModelCallLimitMiddleware全局模型调用上限中间件
SessionsTool提供 sessions_spawn/send/list/history 工具
WorkspaceManager管理 Agent workspace 目录
DefaultAgentManager子 Agent 创建和销毁管理器
ReviewerFindingsServiceFindings CRUD 服务(SqliteBaseStore 后端)
GitHubReviewPublisher将 Findings 发布为 GitHub PR Review
TokenEncryptionGoogle Tink AEAD 加密/解密 GitHub Token
SessionResetPolicy会话过期策略:NEVER/DAILY/IDLE/BOTH
ChannelManagerChannel 生命周期管理 + outbound 消息投递
OpenSWEOpen 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()

下一步建议

  1. 设置 DASHSCOPE_API_KEY,运行 start-codingagent.bat 体验 CLI 模式
  2. 在 GitHub 仓库设置 Webhook,指向你的服务地址,体验 Webhook 模式
  3. 阅读 CodingSystemPrompt.java 了解 Agent 的 16 段系统提示词设计
  4. 阅读 HarnessGateway.java 理解多 Agent 路由和 Announce 机制