跳转到内容

03 SDK 分层架构设计

学习目标

本章要解决什么问题:Cline SDK 为什么分四层?每层承担什么职责?依赖关系如何保证可替换性?


项目实践

四层依赖链

依赖方向:上层依赖下层,下层不依赖上层。这使得每层可以独立替换。

各层职责

职责关键组件
@cline/shared底层类型、工具函数、Hook 契约消息类型、错误定义、工具接口
@cline/llms提供商集成、模型目录、API 调用AnthropicHandler、OpenAiHandler、ModelInfo
@cline/agents无状态 Agent Loop、Hook 系统AgentRuntime、Hook 生命周期
@cline/core有状态编排、Session 生命周期、存储任务管理、上下文管理、持久化

AgentRuntime 无状态循环

AgentRuntimesdk/packages/agents/src/agent-runtime.ts)是 SDK 的核心:

# 伪代码:AgentRuntime 循环
class AgentRuntime {
async run(input: AgentRunInput): Promise<AgentRunResult> {
while (!abort) {
// 1. 调用模型
const response = await this.llmProvider.complete(messages)
// 2. 解析工具调用
const toolCalls = parseToolCalls(response)
// 3. 执行工具
for (const call of toolCalls) {
const result = await this.executeTool(call)
messages.push({ role: "tool", result })
}
// 4. 检查完成条件
if (isComplete(response)) break
}
return { messages, status: "completed" }
}
}

无状态的含义AgentRuntime 本身不保存任何对话历史。每次 run() 调用传入完整的消息列表,返回完整的消息列表。Session 生命周期由 @cline/core 层管理。

Hook 系统

AgentRuntime 支持 Hook 系统,允许外部逻辑介入 Agent 循环:

Hook触发时机用途
beforeRun开始执行前初始化资源、验证输入
beforeModel调用模型前修改消息、切换模型
beforeTool执行工具前审批检查、工具拦截
afterTool执行工具后审计日志、结果修改

Hook 实现:Hook 是函数数组,按注册顺序依次调用。每个 Hook 可以返回修改后的数据或抛出异常中断流程。

从 SDK 到 CLI 的集成

CLI 应用(sdk/apps/cli/)基于上述四层 SDK 构建:

  1. @cline/shared 提供类型定义
  2. @cline/llms 提供商集成
  3. @cline/agents 提供 AgentRuntime
  4. @cline/core 管理 Session 和存储

问题与规避

陷阱场景对策
循环依赖core 依赖 agents,agents 又依赖 core严格分层:agents 不依赖 core,core 依赖 agents
版本不一致四层包版本不同步npm workspace 统一管理版本
Hook 异常中断Hook 抛出未捕获异常AgentRuntime 的 catch 块统一处理 Hook 异常

设计取舍

无状态 Agent Loop vs 有状态

方案优势代价
无状态易于测试、可并行、可重入需要外部管理状态
有状态实现简单、状态内置测试困难、状态管理复杂

Cline SDK 选择无状态 Agent Loop + 有状态编排AgentRuntime 本身无状态,由 @cline/core 层负责状态管理。这样可以在不同场景下复用 Agent Loop(如 CLI、Webview、CI/CD),每种场景的状态管理策略可以不同。

分层 vs 一体化

方案优势代价
分层每层可独立替换、测试、发布依赖管理复杂、理解成本高
一体化实现简单、无依赖问题替换任何组件需要重构整体

Cline 选择分层,因为目标是在 VS Code 之外也支持 CLI、JetBrains 等多宿主。


参考来源

  • Cline 源码:sdk/ARCHITECTURE.md
  • Cline 源码:sdk/packages/agents/src/agent-runtime.ts
  • Cline 源码:sdk/packages/shared/src/