首页
/ Cline SDK @cline/agents 包详解:构建可移植的工具调用型 LLM Agent 循环

Cline SDK @cline/agents 包详解:构建可移植的工具调用型 LLM Agent 循环

2026-09-06 17:26:27作者:钟日瑜

@cline/agents 是 Cline SDK 中的运行时无关(runtime-agnostic)agent 循环包:它提供运行和延续"会用工具的 LLM 对话"所需的核心原语,却不捆绑会话存储、Hub 传输或宿主默认工具。读完本文,你将理解该包在 Cline SDK 分层架构中的定位、两种配置形态(Provider 形式与 Model 形式)、工具/事件/钩子/插件四大扩展点的用法与底层实现,并能在 Node、浏览器或自定义宿主环境中独立搭起一个工具调用型 Agent。

一、包定位:只有循环,没有宿主

@cline/agents 的自我定位可以用一句话概括:它是 agent 循环本身,而不是一个完整的应用运行时。根据 READMEpackage.json

  • 包描述为 "Browser-safe agent runtime for the next-generation Cline SDK",type: module,要求 node >= 22,许可证 Apache-2.0;
  • 运行时依赖只有三个:@cline/llms@cline/sharednanoid(用于生成 agentId 等标识符),这正是"轻依赖、可移植"的直接体现;
  • 不包含:文件系统/shell/网页抓取等默认宿主工具、会话持久化与有状态编排、共享 Hub 运行时/会话传输(@cline/core/hub)、子 agent 与团队协作原语——这些全部位于 @cline/core

这种切分的意义在于:当你想在 Node、浏览器或某个自定义宿主里,自备工具与运行时策略地跑一个 agent 循环时,@cline/agents 是唯一需要引入的循环层。从源码结构看,整个包只有一个实现文件 agent-runtime.ts(约 2100 行),其余为入口与测试,边界非常收敛。

对外导出的 API 面

包入口 index.ts 的导出面与文档完全一致:

导出 说明
Agent / AgentRuntime 同一个类的两个名字(agent-runtime.ts#L2183export const Agent = AgentRuntime;)。供 provider/model ID 时用 Agent,供预构建 AgentModel 时用 AgentRuntime
createAgent / createAgentRuntime 工厂函数等价物
AgentRuntimeAbortError 中止时携带的错误类型
AgentRuntimeConfig 及其两个变体 判别式配置联合类型
createTool @cline/shared 再导出,便于编写工具
一批类型 AgentToolAgentMessageAgentRuntimeEventAgentRuntimeHooksAgentRunResultAgentRuntimeStateSnapshot 等,均从 @cline/shared 再导出

README 建议:共享类型直接从 @cline/shared 导入,本包只是便利性地再导出。

二、安装与快速上手

安装

npm install @cline/agents @cline/shared @cline/llms

三个包都要装的原因:@cline/agents 内部依赖 @cline/llms(构建模型网关)与 @cline/shared(全部共享类型),而用户代码通常也会直接引用 AgentTool 等共享类型。

Quick Start

下面的示例完整继承自 README 的 Quick Start 一节,展示一个最小可运行的天气查询 agent:

import { Agent } from "@cline/agents";
import type { AgentTool } from "@cline/shared";

const getWeather: AgentTool<{ city: string }, { forecast: string }> = {
	name: "get_weather",
	description: "Return the current weather for a city.",
	inputSchema: {
		type: "object",
		properties: { city: { type: "string" } },
		required: ["city"],
	},
	async execute({ city }) {
		return { forecast: `sunny in ${city}` };
	},
};

const agent = new Agent({
	providerId: "anthropic",
	modelId: "claude-sonnet-4-6",
	apiKey: process.env.ANTHROPIC_API_KEY,
	systemPrompt: "You are a concise assistant.",
	tools: [getWeather],
});

const result = await agent.run("What's the weather in San Francisco?");
console.log(result.outputText);

这里有几个值得注意的行为,均可在源码中找到对应:

  • agentId 自动生成:构造函数中若未提供 agentId,会用 createUID("agent")agent_ 前缀 + 8 位 nanoid)生成,见 agent-runtime.ts#L518-L534
  • 工具以 name 为键注册进 Map,重名工具会被后注册者覆盖(initialize()this.tools.set(tool.name, tool),见 agent-runtime.ts#L619-L635);
  • toolExecution 默认 "sequential":构造函数会把未显式给出的 toolExecution 填充为顺序执行,即同一轮内的多个工具调用默认串行执行。

三、两种配置形态:Provider 形式 vs Model 形式

Agent / AgentRuntime 接受一个判别式配置联合 AgentRuntimeConfigagent-runtime.ts#L101-L139),对应两种用法。

Provider 形式(友好入口)

提供 providerId / modelId 与凭据,运行时通过 @cline/llms 替你构建 AgentModel

new Agent({
	providerId: "openai",
	modelId: "gpt-5",
	apiKey: process.env.OPENAI_API_KEY,
	// baseUrl, headers also supported
	tools: [/* ... */],
});

AgentRuntimeConfigWithProvider 在源码中的完整字段为(agent-runtime.ts#L115-L129):providerId: string(必填)、modelId: string(必填)、apiKey?baseUrl?(自定义 API 地址)、headers?(额外请求头)、options?(provider 专属网关选项,类型为 GatewayProviderSettings["options"])。

源码印证:resolveRuntimeConfig()agent-runtime.ts#L147-L168)在检测到没有预构建 model 时,执行 createGateway({ providerConfigs: [{ providerId, apiKey, baseUrl, headers, options }] }) 再调用 gateway.createAgentModel({ providerId, modelId }),并自动为 assistant 消息打上 messageModelInfo: { id: modelId, provider: providerId } 标记(除非调用者已显式提供)。也就是说,Provider 形式本质上是 Model 形式的一层语法糖

Model 形式(高级)

宿主已经自己拥有网关时(@cline/core 内部就是这么用的),直接传入预构建的 AgentModel

import { createGateway } from "@cline/llms";

const gateway = createGateway({ providerConfigs: [/* ... */] });
const model = gateway.createAgentModel({ providerId, modelId });

new Agent({
	model,
	tools: [/* ... */],
});

AgentModel 的契约本身非常薄(shared/src/agent.ts#L329-L333):只有一个 stream(request: AgentModelRequest) 方法,返回 AgentModelEvent 的异步可迭代流。这意味着你甚至可以完全绕过 @cline/llms,用自定义实现接入任意后端——只要事件流遵循 text-delta / reasoning-delta / tool-call-delta / usage / finish 等事件协议。

四、核心概念 1:工具(AgentTool)

工具遵循 @cline/sharedAgentTool<TInput, TOutput> 接口。README 给出的摘要式示例:

import type { AgentTool } from "@cline/shared";

const summarize: AgentTool<{ text: string }, { summary: string }> = {
	name: "summarize_text",
	description: "Summarize text into a short preview.",
	inputSchema: {
		type: "object",
		properties: { text: { type: "string" } },
		required: ["text"],
	},
	async execute({ text }, context) {
		// context.signal — aborts when the run is cancelled
		// context.emitUpdate(...) — stream progress as `tool-updated` events
		return { summary: text.slice(0, 120) };
	},
};

结合源码(shared/src/agent.ts#L195-L211),可以把接口拆得更细:

  • AgentTool 继承 AgentToolDefinition(即 name + description + inputSchema 三元组,inputSchema 为 JSON Schema);
  • 另有三个可选执行策略字段:timeoutMs?(超时)、retryable?(是否可重试)、maxRetries?(最大重试次数);
  • execute(input, context) 直接返回工具输出。context: AgentToolContext 提供 toolCallId?signal?(run 被取消时触发 abort)、metadata?snapshot?(当前运行时状态快照)、emitUpdate?(流式上报进度,运行时将其转成 tool-updated 事件)。

两条失败语义(来自 README,并被运行时的工具执行实现印证):

  1. 抛错即失败execute(...)throw 会把该次工具调用标记为失败(isError: true);
  2. 包装信封可改写:成功输出会被运行时包进内部的 AgentToolResult 信封,可用 afterTool 钩子对该信封做转换。

另外,配置里的 toolPolicies 支持按工具名配置策略,且 "*" 通配项与具体工具项会合并(agent-runtime.ts#L170-L178);若宿主需要交互式审批,还可以传入 requestToolApproval 回调(见下文配置总表)。

五、核心概念 2:事件流(AgentRuntimeEvent)

订阅运行时事件有两种方式:

// 1. 构造后挂载监听器,返回一个取消订阅函数。
const unsubscribe = agent.subscribe((event) => {
	if (event.type === "assistant-text-delta") {
		process.stdout.write(event.text);
	}
});

// 2. 构造时注册 onEvent 钩子。
new Agent({
	providerId,
	modelId,
	apiKey,
	hooks: {
		onEvent(event) {
			// fires for every runtime event
		},
	},
});

subscribe() 的实现极简(agent-runtime.ts#L562-L567):把监听器放入 Set,返回移除闭包。

AgentRuntimeEvent 是一个联合类型(shared/src/agent.ts#L563 起),覆盖 run/turn 边界、assistant 文本与推理增量、工具生命周期、用量更新和 run 完成/失败。从源码可见的事件成员包括:

事件 关键字段
run-started snapshot
message-added messagesnapshot
turn-started iterationsnapshot
assistant-text-delta textaccumulatedTextiteration
assistant-reasoning-delta textaccumulatedTextredacted?
assistant-media media(生成式媒体)
assistant-message messagefinishReason
tool-started / 后续工具生命周期事件 工具名、入参/输出、错误标志等

每个事件都携带 snapshot(当前运行时状态快照),因此监听器无需单独维护状态即可获得一致的运行时视图。

六、核心概念 3:会话控制 API

README 列出的六个会话控制点,全部可以在 AgentRuntime 类中找到对应实现:

  • agent.run(input) — 启动一次 run。input 可以是字符串、单个 AgentMessage 或消息数组(AgentRunInput = string | AgentMessage | readonly AgentMessage[]);传 undefined 表示不新增用户轮次、直接延续。实现上 runcontinue 都委托给同一个私有 execute()agent-runtime.ts#L536-L542);
  • agent.continue(input?)run(input?) 的便捷别名,语义是"延续对话";
  • agent.abort(reason?) — 取消当前 run。实现为(agent-runtime.ts#L544-L560):把 reason 归一为 AgentRuntimeAbortError,记录 lastError、发出 TASK_CANCELLED 生命周期遥测,再触发内部 AbortController.run() 会以 status: "aborted" 正常 resolve,而不是 reject;
  • agent.snapshot() — 返回当前 AgentRuntimeStateSnapshot 的不可变视图(agent-runtime.ts#L597-L612),字段包括 agentIdagentRoleparentAgentIdconversationIdrunIdstatusiterationmessages(深拷贝)、pendingToolCallsusagelastErrorlastErrorClass
  • agent.restore(messages) — 用持久化的消息数组替换整个会话(agent-runtime.ts#L577-L595)。源码注释明确了它"丢弃进行中的 run 与用量状态,但保留 model、tools、hooks、plugins、active event subscribers 与 agent 身份"——这对在外部自行持久化会话、需要重新灌入运行时而不重建订阅者的宿主非常关键;
  • initialMessages(构造参数)— 在启动时给会话播种初始消息,构造函数中通过 cloneMessages() 拷贝进内部状态。

七、核心概念 4:钩子(AgentRuntimeHooks)

在构造函数里传入 hooks 对象即可观察或影响 agent 循环。AgentRuntimeHooks 接口定义了 7 个挂载点(shared/src/agent.ts#L420-L449):

钩子 时机 可返回的控制信号
beforeRun run 开始前 AgentStopControl(可停止)
afterRun run 结束(含 result 无(仅观察)
beforeModel 每次模型调用前,可改写 request 中的消息/工具/选项 AgentBeforeModelResult,如 { options: { temperature: 0.2 } }
afterModel 每次模型调用后 AgentStopControl
beforeTool 每次工具执行前 AgentBeforeToolResult,如 { skip: true, reason } 可拦截调用
afterTool 每次工具执行后 AgentAfterToolResult,可改写结果信封
onEvent 每个运行时事件

所有钩子都可以是异步的;任一可停止钩子返回 { stop: true, reason } 都会以 aborted 状态终止本次 run。README 给出的组合示例(策略拦截 + 采样参数注入 + 用量日志):

new Agent({
	providerId,
	modelId,
	apiKey,
	tools: [/* ... */],
	hooks: {
		beforeModel({ request }) {
			// mutate messages/tools/options before the model call
			return { options: { temperature: 0.2 } };
		},
		beforeTool({ tool, input }) {
			// block a tool call based on policy
			if (tool.name === "get_weather" && !(input as { city?: string }).city) {
				return { skip: true, reason: "city required" };
			}
			return undefined;
		},
		afterRun({ result }) {
			console.log("done", result.usage);
		},
	},
});

注册机制值得注意:钩子不是"最后一个覆盖前一个",而是按数组追加registerHooks 对每个挂载点 pushagent-runtime.ts#L637-L648),因此构造参数里的 hooks 与插件贡献的 hooks 会叠加生效。若需要更强的宿主侧钩子编排——15 阶段 HookEngine、子进程钩子、MCP 扩展——README 明确指回 @cline/core

八、prepareTurn:只改写请求、不改写转录

prepareTurn 在消息发往 provider 之前运行,可以改写下一次请求的消息或系统提示词。README 给出的数据流非常清晰:

saved transcript
        |
        | turn preparation
        v
prepareTurn
        |
        v
prepared provider request
prepareTurn returns prepared messages
        |
        +--> provider request: yes
        +--> saved transcript: no
        +--> AgentRunResult.messages: no

这是有意的语义边界prepareTurn 的返回值只影响当前这次模型调用的 provider 请求,不会替换保存的历史转录,也不会出现在 AgentRunResult.messages 里。需要做持久化脱敏、归一化或策略过滤的宿主,必须在消息进入转录之前自行完成。

从源码看(shared/src/agent.ts#L227-L256),钩子收到的上下文 AgentRuntimePrepareTurnContext 包含 agentIdconversationIdparentAgentIditeration、当前 messages / systemPrompt / toolsmodel 信息、signal,以及一个特别字段 overflowRecovery?——当上一次请求因超出上下文窗口被拒时会置位,提示 prepare-turn 管线强制压缩而不是信任 token 估算。返回值是 { messages?, systemPrompt? }。运行时内部也配合了"每个 run 仅一次自动溢出恢复"的机制(agent-runtime.ts#L511-L512),并对"无可压缩历史"与"压缩后仍溢出"两种终端失败分别定义了可读的终止文案。

九、插件(AgentRuntimePlugin)

插件可以在启动(setup)阶段贡献工具与钩子,适合把横切能力(审计、限流、日志)打包复用:

import type { AgentRuntimePlugin } from "@cline/shared";

const loggingPlugin: AgentRuntimePlugin = {
	name: "logging",
	setup({ agentId }) {
		return {
			hooks: {
				afterTool({ tool, result }) {
					console.log(agentId, tool.name, result.isError);
					return undefined; // hook may return an AgentAfterToolResult
				},
			},
		};
	},
};

new Agent({
	providerId,
	modelId,
	apiKey,
	plugins: [loggingPlugin],
});

类型定义(shared/src/agent.ts#L455-L475):插件由 name: string 和可选的 setup(context) 组成;context 提供 agentIdagentRole?systemPrompt?setup 可以返回 undefined{ tools?, hooks? },且允许是异步的。

运行时在首次 run 前的惰性初始化(ensureInitialized()initialize()agent-runtime.ts#L619-L635)中依次完成:注册构造参数里的钩子 → 注册构造参数里的工具 → 依次 await 每个插件的 setup,把返回的 tools 并入工具 Map、hooks 并入钩子注册表。从源码结构看,插件初始化只发生一次(由 this.initialization Promise 缓存),因此插件内可以安全地做一次性资源准备。

十、完整配置参数速查

把 README 各节与 shared/src/agent.ts#L481-L557AgentRuntimeConfig 接口对照,完整构造参数如下(Provider/Model 二选一必填):

参数 类型/取值 说明
modelproviderId+modelId 二选一 见"两种配置形态";Provider 形式另有 apiKey?/baseUrl?/headers?/options?
systemPrompt string 系统提示词
tools AgentTool[] 本地执行的工具列表
hooks AgentRuntimeHooks 7 个生命周期挂载点
plugins AgentRuntimePlugin[] 启动时贡献工具/钩子
initialMessages AgentMessage[] 启动时播种会话
toolExecution "sequential" | "parallel" 同轮工具执行策略,默认 "sequential"
toolPolicies Record<string, ToolPolicy> 按工具名配置策略,支持 "*" 通配
toolContextMetadata Record<string, unknown> 注入工具 context.metadata
requestToolApproval (request) => ToolApprovalResult 宿主侧交互式审批回调
prepareTurn 见第八节 请求前投影钩子
consumePendingUserMessage () => string | undefined 交互式会话在循环轮次间注入排队中的用户消息
maxIterations number 循环迭代上限
completionPolicy { requireCompletionTool?, completionGuard? } 完成判定策略
agentId / conversationId / sessionId string 标识符三件套:agent 身份、转录 ID、宿主生命周期会话 ID
parentAgentId / agentRole 团队/子 agent 场景的身份标记
messageModelInfo { id, provider } assistant 消息的模型标记
modelOptions / modelTools 模型选项与 provider 侧执行的工具
telemetry / logger / distinctId / clientName / clientVersion / clineCoreVersion 遥测与客户端元数据

十一、Teams 与 Spawn:多 agent 去哪找

@cline/agents 刻意不提供多 agent 协调原语。README 指向 @cline/core 的四个入口:

import {
	createSpawnAgentTool,
	AgentTeamsRuntime,
	createAgentTeamsTools,
	bootstrapAgentTeams,
} from "@cline/core";

它们提供委派 run、邮箱(mailbox)、任务管理与结果收敛等协调原语。也就是说,架构上的分工是:@cline/agents 负责"一个 agent 如何转起来",@cline/core 负责"多个 agent 与宿主如何组织起来"。

十二、入口点、浏览器安全与相关包

  • 单一入口 + 浏览器条件@cline/agents 只有一个包入口。package.jsonexports 映射为 "." 下依次声明 browsertypesimport 条件,均指向 dist/index.js —— bundler 解析到 browser 条件时会自动获得浏览器安全 bundle。这与 README 的 Entry Point 一节一致;
  • 相关包分工
    • @cline/shared:共享类型(AgentToolAgentMessageAgentRuntimeEventAgentRuntimeHooks 等),位于 sdk/packages/shared/src/agent.ts
    • @cline/llms:provider 配置、模型目录、网关与 handler 创建(createGateway 即出自此包);
    • @cline/core:有状态运行时组装、存储、默认工具、子进程钩子、Hub 传输与 MCP 集成。

十三、测试与更多示例

小结

@cline/agents 的价值在于把"agent 循环"从宿主环境中彻底解耦:一个类、两种配置形态、工具/事件/钩子/插件四个扩展点,外加一套语义明确的会话控制 API(run / continue / abort / snapshot / restore)。当你只需要循环本身而自备工具与策略时,它是最小的引入面;当你需要会话存储、默认宿主工具或团队协作时,沿 @cline/core 向上一层组装即可。

登录后查看全文
热门项目推荐
相关项目推荐