Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sub-agents

将子 agent 作为同址(co-located)的 Durable Object 派生出来,它们各自拥有独立的 SQLite 存储。父 agent 会得到一个类型化的 RPC stub,用于调用子 agent 的方法 —— 子类的每一个公开方法都可以作为远程过程调用,返回值会被包装为 Promise。

快速开始

JavaScript


import { Agent } from "agents";


export class Orchestrator extends Agent {

  async delegateWork() {

    const researcher = await this.subAgent(Researcher, "research-1");

    const findings = await researcher.search("cloudflare agents sdk");

    return findings;

  }

}


export class Researcher extends Agent {

  async search(query) {

    const results = await fetch(`https://api.example.com/search?q=${query}`);

    return results.json();

  }

}


Explain Code

TypeScript


import { Agent } from "agents";


export class Orchestrator extends Agent {

  async delegateWork() {

    const researcher = await this.subAgent(Researcher, "research-1");

    const findings = await researcher.search("cloudflare agents sdk");

    return findings;

  }

}


export class Researcher extends Agent {

  async search(query: string) {

    const results = await fetch(`https://api.example.com/search?q=${query}`);

    return results.json();

  }

}


Explain Code

两个类都必须从 worker 的入口点导出。无需为子类单独配置 Durable Object binding —— 它们会通过 ctx.exports 被自动发现。

JSONC


{

  "$schema": "./node_modules/wrangler/config-schema.json",

  // Set this to today's date

  "compatibility_date": "2026-04-29",

  "compatibility_flags": [

    "nodejs_compat",

    "experimental"

  ],

  "durable_objects": {

    "bindings": [

      {

        "class_name": "Orchestrator",

        "name": "Orchestrator"

      }

    ]

  },

  "migrations": [

    {

      "new_sqlite_classes": [

        "Orchestrator"

      ],

      "tag": "v1"

    }

  ]

}


Explain Code

TOML


# Set this to today's date

compatibility_date = "2026-04-29"

compatibility_flags = ["nodejs_compat", "experimental"]


[[durable_objects.bindings]]

class_name = "Orchestrator"

name = "Orchestrator"


[[migrations]]

new_sqlite_classes = ["Orchestrator"]

tag = "v1"


Explain Code

只有父 agent 需要 Durable Object binding 与 migration。子 agent 是父 agent 的 facet —— 它们与父 agent 共享同一台机器,但拥有完全独立的 SQLite 存储。

subAgent

获取或创建一个具名的子 agent。第一次以某个名称调用时,会触发该子 agent 的 onStart()。后续调用会返回已有的实例。

JavaScript


class Agent {}


TypeScript


class Agent {

  async subAgent<T extends Agent>(

    cls: SubAgentClass<T>,

    name: string,

  ): Promise<SubAgentStub<T>>;

}


参数类型描述
clsSubAgentClass<T>Agent 的子类。必须从 worker 入口点导出,且导出名必须与类名一致。
namestring该子实例的唯一名称。同一名称始终返回同一个子 agent。

返回一个 SubAgentStub<T> —— 一个类型化的 RPC stub,其中 T 上每一个用户自定义方法都可作为返回 Promise 的远程调用。

SubAgentStub

stub 暴露子类上所有用户自定义的公开实例方法。从 Agent 继承而来的方法(生命周期钩子、setStatebroadcastsql 等)不会出现在 stub 上 —— 只有你的自定义方法会出现。

返回类型若不是 Promise,会被自动包装为 Promise:

JavaScript


class MyChild extends Agent {

  greet(name) {

    return `Hello, ${name}`;

  }

  async fetchData(url) {

    return fetch(url).then((r) => r.json());

  }

}


// On the stub:

// greet(name: string) => Promise<string>       (sync → wrapped)

// fetchData(url: string) => Promise<unknown>   (already async → unchanged)


Explain Code

TypeScript


class MyChild extends Agent {

  greet(name: string): string {

    return `Hello, ${name}`;

  }

  async fetchData(url: string): Promise<unknown> {

    return fetch(url).then((r) => r.json());

  }

}


// On the stub:

// greet(name: string) => Promise<string>       (sync → wrapped)

// fetchData(url: string) => Promise<unknown>   (already async → unchanged)


Explain Code

要求

  • 子类必须继承 Agent
  • 子类必须从 worker 入口点导出(export class MyChild extends Agent)
  • 导出名必须与类名一致 —— export { Foo as Bar } 不被支持

abortSubAgent

强制停止一个正在运行的子 agent。子 agent 会立即停止执行,并在下一次 subAgent() 调用时重新启动。存储会被保留 —— 只是杀掉运行中的实例。

JavaScript


class Agent {}


TypeScript


class Agent {

  abortSubAgent(cls: SubAgentClass, name: string, reason?: unknown): void;

}


参数类型描述
clsSubAgentClass创建该子 agent 时使用的 Agent 子类
namestring要中止的子 agent 名称
reasonunknown抛给所有正在或后续调用 RPC 的调用者的错误

中止具有传递性 —— 如果该子 agent 自己也有子 agent,它们也会被中止。

deleteSubAgent

中止子 agent(若运行中)并永久清除其存储。下一次 subAgent() 调用会创建一个 SQLite 为空的全新实例。

JavaScript


class Agent {}


TypeScript


class Agent {

  deleteSubAgent(cls: SubAgentClass, name: string): void;

}


参数类型描述
clsSubAgentClass创建该子 agent 时使用的 Agent 子类
namestring要删除的子 agent 名称

删除具有传递性 —— 该子 agent 自己的子 agent 也会被删除。

存储隔离

每个子 agent 都有自己的 SQLite 数据库,与父 agent 以及其他子 agent 完全隔离。父 agent 写入 this.sql 与子 agent 写入 this.sql 操作的是不同的数据库:

JavaScript


export class Parent extends Agent {

  async demonstrate() {

    this.sql`INSERT INTO parent_data (key, value) VALUES ('color', 'blue')`;


    const child = await this.subAgent(Child, "child-1");

    await child.increment("clicks");


    // Parent's SQL and child's SQL are completely separate

  }

}


export class Child extends Agent {

  async increment(key) {

    this

      .sql`CREATE TABLE IF NOT EXISTS counters (key TEXT PRIMARY KEY, value INTEGER DEFAULT 0)`;

    this

      .sql`INSERT INTO counters (key, value) VALUES (${key}, 1) ON CONFLICT(key) DO UPDATE SET value = value + 1`;

    const row = this.sql`SELECT value FROM counters WHERE key = ${key}`.one();

    return row?.value ?? 0;

  }

}


Explain Code

TypeScript


export class Parent extends Agent {

  async demonstrate() {

    this.sql`INSERT INTO parent_data (key, value) VALUES ('color', 'blue')`;


    const child = await this.subAgent(Child, "child-1");

    await child.increment("clicks");


    // Parent's SQL and child's SQL are completely separate

  }

}


export class Child extends Agent {

  async increment(key: string): Promise<number> {

    this

      .sql`CREATE TABLE IF NOT EXISTS counters (key TEXT PRIMARY KEY, value INTEGER DEFAULT 0)`;

    this

      .sql`INSERT INTO counters (key, value) VALUES (${key}, 1) ON CONFLICT(key) DO UPDATE SET value = value + 1`;

    const row = this.sql<{

      value: number;

    }>`SELECT value FROM counters WHERE key = ${key}`.one();

    return row?.value ?? 0;

  }

}


Explain Code

命名与身份

两个不同的类可以共用同一个对外名称 —— 它们各自独立解析。内部 key 是类名与 facet 名的组合:

JavaScript


const counter = await this.subAgent(Counter, "shared-name");

const logger = await this.subAgent(Logger, "shared-name");

// These are two separate sub-agents with separate storage


TypeScript


const counter = await this.subAgent(Counter, "shared-name");

const logger = await this.subAgent(Logger, "shared-name");

// These are two separate sub-agents with separate storage


子 agent 的 this.name 属性返回的是 facet 名(不是父 agent 的名字):

JavaScript


export class Child extends Agent {

  getName() {

    return this.name; // Returns "shared-name", not the parent's ID

  }

}


TypeScript


export class Child extends Agent {

  getName(): string {

    return this.name; // Returns "shared-name", not the parent's ID

  }

}


模式

并行子 agent

并发运行多个子 agent:

JavaScript


export class Orchestrator extends Agent {

  async runAll(queries) {

    const results = await Promise.all(

      queries.map(async (query, i) => {

        const worker = await this.subAgent(Researcher, `research-${i}`);

        return worker.search(query);

      }),

    );

    return results;

  }

}


Explain Code

TypeScript


export class Orchestrator extends Agent {

  async runAll(queries: string[]) {

    const results = await Promise.all(

      queries.map(async (query, i) => {

        const worker = await this.subAgent(Researcher, `research-${i}`);

        return worker.search(query);

      }),

    );

    return results;

  }

}


Explain Code

嵌套子 agent

子 agent 可以派生自己的子 agent,形成一棵树:

JavaScript


export class Manager extends Agent {

  async delegate(task) {

    const team = await this.subAgent(TeamLead, "team-a");

    return team.assign(task);

  }

}


export class TeamLead extends Agent {

  async assign(task) {

    const worker = await this.subAgent(Worker, "worker-1");

    return worker.execute(task);

  }

}


export class Worker extends Agent {

  async execute(task) {

    return { completed: task };

  }

}


Explain Code

TypeScript


export class Manager extends Agent {

  async delegate(task: string) {

    const team = await this.subAgent(TeamLead, "team-a");

    return team.assign(task);

  }

}


export class TeamLead extends Agent {

  async assign(task: string) {

    const worker = await this.subAgent(Worker, "worker-1");

    return worker.execute(task);

  }

}


export class Worker extends Agent {

  async execute(task: string) {

    return { completed: task };

  }

}


Explain Code

回调流式传输

将一个 RpcTarget 回调传给子 agent,可以将结果流式返回给父 agent:

JavaScript


import { RpcTarget } from "cloudflare:workers";


class StreamCollector extends RpcTarget {

  chunks = [];

  onChunk(text) {

    this.chunks.push(text);

  }

}


export class Parent extends Agent {

  async streamFromChild() {

    const child = await this.subAgent(Streamer, "streamer-1");

    const collector = new StreamCollector();

    await child.generate("Write a poem", collector);

    return collector.chunks;

  }

}


export class Streamer extends Agent {

  async generate(prompt, callback) {

    const chunks = ["Once ", "upon ", "a ", "time..."];

    for (const chunk of chunks) {

      callback.onChunk(chunk);

    }

  }

}


Explain Code

TypeScript


import { RpcTarget } from "cloudflare:workers";


class StreamCollector extends RpcTarget {

  chunks: string[] = [];

  onChunk(text: string) {

    this.chunks.push(text);

  }

}


export class Parent extends Agent {

  async streamFromChild() {

    const child = await this.subAgent(Streamer, "streamer-1");

    const collector = new StreamCollector();

    await child.generate("Write a poem", collector);

    return collector.chunks;

  }

}


export class Streamer extends Agent {

  async generate(prompt: string, callback: StreamCollector) {

    const chunks = ["Once ", "upon ", "a ", "time..."];

    for (const chunk of chunks) {

      callback.onChunk(chunk);

    }

  }

}


Explain Code

限制

子 agent 作为父 Durable Object 的 facet 运行,部分 Agent 方法在子 agent 中不可用:

方法在子 agent 中的行为
schedule()抛出 “not supported in sub-agents”
cancelSchedule()抛出 “not supported in sub-agents”
keepAlive()抛出 “not supported in sub-agents”
setState()正常工作(写入子 agent 自己的存储)
this.sql正常工作(子 agent 自己的 SQLite)
subAgent()可用 —— 子 agent 可以派生自己的子 agent

对于需要调度的工作,可以由父 agent 调度任务,在调度触发时再委派给子 agent。

相关

  • Think —— chat() 方法,通过子 agent 流式进行 AI 回合
  • Long-running agents —— 在数周生命周期的 agent 场景下进行子 agent 委派
  • Callable methods —— 通过 @callable 与 service binding 进行 RPC
  • Chat agents —— 用于进程内 AI SDK 子调用的 ToolLoopAgent