← Vytvářejte agenty na Cloudflare / agents / runtime / execution
Agents jako nástroje
Agents jako nástroje umožňují jednomu chatovacímu agentovi pověřit prací dalšího podřízeného agenta schopného chatu. Potomek je skutečný podřízený agent s vlastním úložištěm Durable Object, zprávami, nástroji, obnovitelným streamem a URL pro podrobný náhled. Nadřazený agent si vede malý registr běhů, díky kterému mohou klienti vykreslit časovou osu potomka, přehrát ji po obnovení stránky a později ji vymazat.
Agents jako nástroje podporují @cloudflare/think agenti a AIChatAgent podtřídy. AIChatAgent potomci běží v headless režimu přes saveMessages(), takže by měly používat nástroje na straně serveru. Klientské nástroje poskytované prohlížečem nejsou během tahu agentního nástroje dostupné, pokud tuto interakci nemodelujete jako stav na straně serveru nebo jako samostatný workflow zprostředkovaný nadřazeným agentem.
Agents jako nástroje oproti sub-agent RPC
Použijte subAgent(...).chat() když nadřazený kód potřebuje přímé streamované RPC ke konkrétnímu podřízenému a váš kód sám řídí předávání, zrušení a politiku přehrávání.
Použijte agentTool() nebo runAgentTool() když nadřazený model nebo workflow deleguje práci na podřízeného agenta a chcete zachované běhy podřízených agentů, přehrávání událostí, přemostění přerušení a možnost proklikat se v UI k detailu. Volby kola specifické pro Think najdete v Zvolte API pro kola.
Použití agenta jako nástroje AI SDK
Použijte agentTool() když se nadřazený model má rozhodnout, kdy pomocníka zavolat.
import { Think } from "@cloudflare/think";
import { agentTool } from "agents/agent-tools";
import { z } from "zod";
export class Researcher extends Think {
getSystemPrompt() {
return "Research the user's topic and end with a concise summary.";
}
}
export class Assistant extends Think {
getTools() {
return {
research: agentTool(Researcher, {
description: "Research one topic in depth.",
displayName: "Researcher",
inputSchema: z.object({
query: z.string().min(3),
}),
}),
};
}
}import { Think } from "@cloudflare/think";
import { agentTool } from "agents/agent-tools";
import { z } from "zod";
export class Researcher extends Think<Env> {
getSystemPrompt() {
return "Research the user's topic and end with a concise summary.";
}
}
export class Assistant extends Think<Env> {
getTools() {
return {
research: agentTool(Researcher, {
description: "Research one topic in depth.",
displayName: "Researcher",
inputSchema: z.object({
query: z.string().min(3),
}),
}),
};
}
}Potomkem může být také AIChatAgent:
import { AIChatAgent } from "@cloudflare/ai-chat";
import { agentTool } from "agents/agent-tools";
import { convertToModelMessages, stepCountIs, streamText } from "ai";
import { z } from "zod";
export class Summarizer extends AIChatAgent {
formatAgentToolInput(input, request) {
return {
id: `agent-tool-${request.runId}-input`,
role: "user",
parts: [{ type: "text", text: `Summarize:\n\n${input.text}` }],
};
}
async onChatMessage() {
const result = streamText({
model: this.env.MODEL,
messages: await convertToModelMessages(this.messages),
});
return result.toUIMessageStreamResponse();
}
}
export class Assistant extends AIChatAgent {
async onChatMessage() {
const result = streamText({
model: this.env.MODEL,
messages: await convertToModelMessages(this.messages),
tools: {
summarize: agentTool(Summarizer, {
description: "Summarize long text in a separate retained agent.",
inputSchema: z.object({ text: z.string() }),
}),
},
stopWhen: stepCountIs(5),
});
return result.toUIMessageStreamResponse();
}
}import { AIChatAgent } from "@cloudflare/ai-chat";
import { agentTool } from "agents/agent-tools";
import { convertToModelMessages, stepCountIs, streamText } from "ai";
import { z } from "zod";
export class Summarizer extends AIChatAgent<Env> {
protected override formatAgentToolInput(input: { text: string }, request) {
return {
id: `agent-tool-${request.runId}-input`,
role: "user",
parts: [{ type: "text", text: `Summarize:\n\n${input.text}` }],
};
}
async onChatMessage() {
const result = streamText({
model: this.env.MODEL,
messages: await convertToModelMessages(this.messages),
});
return result.toUIMessageStreamResponse();
}
}
export class Assistant extends AIChatAgent<Env> {
async onChatMessage() {
const result = streamText({
model: this.env.MODEL,
messages: await convertToModelMessages(this.messages),
tools: {
summarize: agentTool(Summarizer, {
description: "Summarize long text in a separate retained agent.",
inputSchema: z.object({ text: z.string() }),
}),
},
stopWhen: stepCountIs(5),
});
return result.toUIMessageStreamResponse();
}
}Vygenerovaný nástroj volá this.runAgentTool(ChildAgent, ...), streamuje agent-tool-event frame na nadřazeném WebSocketu a vrátí souhrn potomka nadřazenému modelu. Pokud běh selže, je přerušen nebo zastaven, nástroj vrátí strukturovanou AgentToolFailure místo prázdné hodnoty úspěchu:
type AgentToolFailure = {
ok: false;
status: "error" | "aborted" | "interrupted";
error: string; // human-readable, safe to surface
retryable: boolean;
// Present only when `status` is "interrupted":
reason?: AgentToolInterruptedReason;
childStillRunning?: boolean;
};
type AgentToolInterruptedReason =
| "no-progress"
| "window-exceeded"
| "not-tailable"
| "inspect-timeout"
| "inspect-failed"
| "recovery-deadline"
| "budget-exceeded";retryable je true pouze pro interrupted běh: podřízený agent byl resetován nebo nahrazen nasazením či obnovením nadřazeného agenta a nikdy nedosáhl logického výsledku, takže opětovné odeslání stejného volání může uspět. Skutečný error nebo záměrné aborted je retryable: false. Díky tomu může konvence nadřazeného promptu nebo orchestrační vrstva přechodné přerušení znovu spustit, místo aby ho uživateli nahlásila jako konečné selhání. AgentToolFailure se exportuje z agents.
Při interrupted běh, reason poskytuje strojově čitelnou příčinu a childStillRunning hlásí, zda podřízený agent v okamžiku, kdy nadřazený agent přestal čekat, ještě pracoval (true) nebo od té doby byla zrušena (false). Rozlišujte podle nich, místo abyste parsovali error prózu, například znovu odeslat no-progress přerušení (potomek se přesto může sám zotavit), ale znovu se připojit nebo zobrazit window-exceeded jeden (podřízený prvek byl zrušen). Oba reason a childStillRunning se také zrcadlí do agent-tool-event wire frame a useAgentToolEvents() stav běhu.
Pro potomky Think, kteří provádějí práci ve stylu workflow bez textu asistenta viditelného pro uživatele, přepište getAgentToolOutput() a v případě potřeby getAgentToolSummary(). Text asistenta zůstává výchozím souhrnem, pokud existuje, ale spuštění agentního nástroje Think může úspěšně proběhnout i bez vygenerování textových chunků.
Jakýkoli strukturovaný výstup uložte před dokončením podřízeného kola, protože getAgentToolOutput() se přečte, jakmile saveMessages() se vyřeší. Ponechte getAgentToolSummary() stručný pro zobrazení; celá strukturovaná hodnota se ukládá zvlášť jako výstup nástroje.
export class Extractor extends Think {
getAgentToolOutput(runId) {
const rows = this.sql`
SELECT result_json FROM extraction_runs WHERE id = ${runId}
`;
return rows[0] ? JSON.parse(rows[0].result_json) : undefined;
}
getAgentToolSummary(_runId, output) {
return output ? "Extraction complete" : "";
}
}export class Extractor extends Think<Env> {
protected override getAgentToolOutput(runId: string) {
const rows = this.sql<{ result_json: string }>`
SELECT result_json FROM extraction_runs WHERE id = ${runId}
`;
return rows[0] ? JSON.parse(rows[0].result_json) : undefined;
}
protected override getAgentToolSummary(_runId: string, output: unknown) {
return output ? "Extraction complete" : "";
}
}Imperativní spuštění nástroje agenta
Použijte runAgentTool() pro deterministické workflow, naplánovanou práci, HTTP obslužné rutiny nebo kód pro rozvětvení (fan-out).
const [a, b] = await Promise.allSettled([
this.runAgentTool(Researcher, {
input: { query: "HTTP/3" },
parentToolCallId: toolCallId,
displayOrder: 0,
}),
this.runAgentTool(Researcher, {
input: { query: "gRPC" },
parentToolCallId: toolCallId,
displayOrder: 1,
}),
]);const [a, b] = await Promise.allSettled([
this.runAgentTool(Researcher, {
input: { query: "HTTP/3" },
parentToolCallId: toolCallId,
displayOrder: 0,
}),
this.runAgentTool(Researcher, {
input: { query: "gRPC" },
parentToolCallId: toolCallId,
displayOrder: 1,
}),
]);runAgentTool() je idempotentní podle runId. Předání stejného runId nikdy nespustí duplicitní dílčí tah. Dokončené, neúspěšné, zrušené a přerušené běhy zůstávají zachovány, dokud je explicitně nevymažete.
Odpojené běhy (na pozadí)
Standardně runAgentTool() čeká potomka do konečného stavu, než dojde k návratu. U dlouho běžících úloh, jako jsou velké importy, renderování videa nebo hloubkový výzkum, u kterých nechcete blokovat odesílající tah, předejte detached. Běh se odešle ke zpracování, aktuální kolo pokračuje a runAgentTool() vrátí handle okamžitě:
type DetachedRunAgentToolResult = {
runId: string;
agentType: string;
status: "running" | "error"; // "error" only if dispatch itself was rejected
};detached: true funguje na principu fire-and-forget: průběh sledujte přes agent-tool-event frame (stejné, které useAgentToolEvents() spotřebovává) a globální onAgentToolFinish() hook. Předáním objektu propojíte cílený, trvalý callback pro dokončení:
export class Importer extends Think {
async startImport(input) {
const { runId } = await this.runAgentTool(ImportAgent, {
input,
detached: { onFinish: "onImportDone", maxBudgetMs: 60 * 60 * 1000 },
});
return runId;
}
// Fires once, even if the Durable Object was evicted and rehydrated while the
// child ran. Referenced by METHOD NAME (like schedule()) — never a closure,
// which cannot survive eviction.
async onImportDone(run, result) {
switch (result.status) {
case "completed":
await this.markImportReady(run.runId, result.summary);
break;
case "error":
await this.markImportFailed(run.runId, result.error);
break;
case "interrupted":
// reason "budget-exceeded" ⇒ the run hit its maxBudgetMs ceiling.
// interrupted is soft: a child that finishes anyway re-fires this
// hook with "completed", so make the handler idempotent.
break;
}
}
}export class Importer extends Think<Env> {
async startImport(input: ImportInput) {
const { runId } = await this.runAgentTool(ImportAgent, {
input,
detached: { onFinish: "onImportDone", maxBudgetMs: 60 * 60 * 1000 },
});
return runId;
}
// Fires once, even if the Durable Object was evicted and rehydrated while the
// child ran. Referenced by METHOD NAME (like schedule()) — never a closure,
// which cannot survive eviction.
async onImportDone(run: AgentToolRunInfo, result: AgentToolLifecycleResult) {
switch (result.status) {
case "completed":
await this.markImportReady(run.runId, result.summary);
break;
case "error":
await this.markImportFailed(run.runId, result.error);
break;
case "interrupted":
// reason "budget-exceeded" ⇒ the run hit its maxBudgetMs ceiling.
// interrupted is soft: a child that finishes anyway re-fires this
// hook with "completed", so make the handler idempotent.
break;
}
}
}Klíčové chování:
- Trvalé dokončení. Doručení přežije uvolnění z paměti i nasazení: dokud izolát běží, zajišťuje rychlá cesta doručení s nízkou latencí a samo se plánující sladicí mechanismus dokončí vše, co rychlá cesta nestihla. Za běžných okolností se doručí přesně jednou, při pádu nejméně jednou, proto
onFinishhandlery musí být idempotentní. - Ukončení vzdáním a dokončení jsou na sobě nezávislé. Vzdání se rozpočtu se doručuje jako
status: "interrupted",reason: "budget-exceeded". Protožeinterruptedje měkký: podřízený úkol, který se dokončí až po vzdání se, přesto znovu vyvoláonFinishse skutečným výsledkem: předčasné vzdání se nikdy neskryje pozdější dokončení. - Ohraničené. Každý odpojený běh má absolutní
maxBudgetMsstrop (na jeden běh, nebodetachedMaxBudgetMsstatická možnost, výchozí je 24h). Po vypršení se nadřazený prvek vzdá sledování a zruší potomka, aby si opuštěný běh nemohl trvale podržetmaxConcurrentAgentToolsslot navždy. - Žádný zděděný signál. Odpojený běh musí přetrvat déle než kolo, ve kterém vznikl, proto ne dědí
options.signal. Zrušte ho explicitně:
await this.cancelAgentTool(runId); // idempotent; delivers onFinish "aborted"await this.cancelAgentTool(runId); // idempotent; delivers onFinish "aborted"Upozornit chat na dokončení (Think / AIChatAgent)
U chatovacího agenta (@cloudflare/think nebo AIChatAgent) obvykle chcete, aby model react k dokončenému běhu na pozadí. Namísto propojování onFinish ručně, předejte notify: true : jakmile běh skončí, agent vloží zprávu do chatu (idempotentně podle kombinace běh a stav, takže dokončení typu exactly-once se nikdy neduplikuje) a model provede své další kolo s výsledkem v kontextu:
await this.runAgentTool(ResearchAgent, { input, detached: { notify: true } });await this.runAgentTool(ResearchAgent, { input, detached: { notify: true } });Pokud vaše aplikace směruje nebo skrývá syntetické zprávy pomocí metadata.source, předejte vlastní zdroj:
await this.runAgentTool(ResearchAgent, {
input,
detached: { notify: { source: "research-background" } },
});await this.runAgentTool(ResearchAgent, {
input,
detached: { notify: { source: "research-background" } },
});Přepsat formatDetachedCompletion(run, result) pro úpravu vloženého textu, nebo vraťte prázdný řetězec pro potlačení notifikace u daného výsledku. Explicitní onFinish má přednost před notify.
inspectAgentToolRun kontrakt
Potomkův inspectAgentToolRun(runId) vrátí aktuální stavový snímek běhu, nebo null. null neznamená „selhalo" : znamená to, že potomek nemá o daném běhu žádný záznam zatím. To je běžné bezprostředně po odeslání (potomek může teprve ukládat svůj první řádek) a stejnou hodnotu vrací i čerstvě rehydratovaný potomek, dokud líným způsobem nesladí zastaralý running řádek. Volající, stejně jako vlastní mechanismus sesouhlasení frameworku, zacházejí s null jako "not terminal, keep watching within budget", nikdy jako trvalé selhání. Pouze ne-null kontrolu pomocí terminálu status (completed / error / aborted) dokončí běh.
Nahlaste průběh a milníky
Podřízený agent běžící jako nástroj agenta, ať už awaitovaný nebo odpojený, může hlásit průběh uprostřed běhu, aby nadřazený agent mohl zobrazovat živý stavový řádek, měřit běh na straně serveru nebo reagovat na pojmenovaný kontrolní bod ještě před dokončením běhu. Zavolejte reportProgress() zevnitř potomka (například z execute):
export class ImportAgent extends Think {
getTools() {
return {
ingest: tool({
inputSchema: z.object({ url: z.string() }),
execute: async ({ url }) => {
// Ephemeral progress: drives a generic bar / phase / status line.
await this.reportProgress({
fraction: 0.6,
phase: "ingesting",
message: "Ingested 40k/80k rows",
});
// ...
},
}),
};
}
}export class ImportAgent extends Think<Env> {
getTools() {
return {
ingest: tool({
inputSchema: z.object({ url: z.string() }),
execute: async ({ url }) => {
// Ephemeral progress: drives a generic bar / phase / status line.
await this.reportProgress({
fraction: 0.6,
phase: "ingesting",
message: "Ingested 40k/80k rows",
});
// ...
},
}),
};
}
}reportProgress() je k dispozici u chatovacích agentů (@cloudflare/think a AIChatAgent). Jde o no-op operaci s vývojovým varováním na základní Agent třída, i když je volána mimo aktivní běh agentního nástroje, takže stejný podřízený kód lze bezpečně spustit samostatně. Framework odvodí aktivní běh z aktuálního kola, ID běhu tedy nikdy nemusíte ručně předávat.
reportProgress<T>(
progress: {
fraction?: number; // 0..1 — drives a progress bar
message?: string; // human-readable status line
phase?: string; // coarse phase label, e.g. "ingesting"
milestone?: string; // present ⇒ a durable milestone (see below)
data?: T; // app-specific payload; live-only unless persisted
},
options?: { persist?: boolean },
): Promise<void>;Dočasné signály putují po vlastním streamu kola potomka jako přechodný data-agent-progress část, takže se znovu odešlou připojeným klientům nadřazeného prvku a zobrazí se na AgentToolRunState.progress přes useAgentToolEvents() : Panel běžících úloh na pozadí dokáže zobrazit živý pruh průběhu, fázi a stavový řádek bez nutnosti otevírat detail. Dávky se slučují (platí poslední hodnota, zpoždění fraction >= 1 frame se vždy odešle). data pole je dostupné jen naživo, pokud nepředáte { persist: true }.
Sledovat průběh na rodiči
Přepsat onProgress() k měření, řízení nebo zobrazování průběhu na straně serveru. Spouští se s nejlepším úsilím pokaždé, když je signál průběhu podřízeného prvku předán přes nadřazený prvek, a to jak u awaitovaných, tak u odpojených běhů:
export class Assistant extends Think {
async onProgress(run, progress) {
if (progress.milestone) {
// A durable milestone landed — branch on it.
}
console.log(run.runId, progress.phase, progress.fraction);
}
}export class Assistant extends Think<Env> {
override async onProgress(
run: AgentToolRunInfo,
progress: AgentToolProgressSnapshot,
) {
if (progress.milestone) {
// A durable milestone landed — branch on it.
}
console.log(run.runId, progress.phase, progress.fraction);
}
}onProgress() není trvalý: po vyřazení z paměti se poslední snímek odpojeného běhu rekonstruuje z inspectAgentToolRun().progress při sladění, místo opětovného vyvolání hooku. Nejnovější snímek se zároveň ukládá do řádku dílčího běhu, takže obnovený nadřazený proces dokáže odpovědět na otázku „kde se tento běh nachází“, aniž by musel sledovat živý stream.
Trvalé milníky
Pojmenování milestone povýší signál z dočasné vrstvy na trvalý jeden: stále existuje jen jedna metoda emit:
await this.reportProgress({
milestone: "sources-gathered",
data: { sources: 2 },
});await this.reportProgress({
milestone: "sources-gathered",
data: { sources: 2 },
});Milník se ukládá jako jeden řádek u potomka s monotónním čítačem pro daný běh sequence, a jede streamem jako persistováno data-agent-milestone část (na rozdíl od dočasného průběhu). Díky tomu přežije vyřazení z paměti, přehraje se při podrobném zobrazení a zobrazí se, deduplikovaná podle sequence : na AgentToolRunState.milestones a inspectAgentToolRun().milestones. onProgress() se spustí i u milníků, s progress.milestone nastavené, aby odběratel mohl rozlišovat mezi milníkem a dočasným průběhem.
Upozornit chat na milník (Think / AIChatAgent)
Pro odpojený běh chatovacího agenta detached: { onMilestones } zobrazí chatovou zprávu, jakmile je dosaženo nakonfigurovaného milníku, před běh dokončí. Každý (runId, name) se spustí nanejvýš jednou, ať už je zaznamenána naživo, nebo dohledána zpětně po odebrání z paměti, takže deterministické ID sjednocuje teplé i studené doručení do modelu nanejvýš jednou:
// "narrate" (default): inject a synthetic assistant status line — no model turn.
await this.runAgentTool(Researcher, {
input,
detached: { onMilestones: ["sources-gathered"] },
});
// "react": post a user-role turn so the model responds (steer, start dependent
// work). Costs a model turn.
await this.runAgentTool(Researcher, {
input,
detached: { onMilestones: { names: ["needs-approval"], mode: "react" } },
});// "narrate" (default): inject a synthetic assistant status line — no model turn.
await this.runAgentTool(Researcher, {
input,
detached: { onMilestones: ["sources-gathered"] },
});
// "react": post a user-role turn so the model responds (steer, start dependent
// work). Costs a model turn.
await this.runAgentTool(Researcher, {
input,
detached: { onMilestones: { names: ["needs-approval"], mode: "react" } },
});Přepsat formatDetachedMilestone(run, milestone) k přizpůsobení znění, případně vraťte prázdný řetězec pro potlačení daného milníku. Syntetické zprávy typu narrate obsahují metadata.source, takže je klienti mohou vykreslit jako událost agenta, nikoli jako tah člověka.
Resetování rozpočtu bez postupu pro odpojené běhy
Jakmile odpojený potomek nahlásí alespoň jeden signál, sladicí mechanismus se vzdá, pokud běh poté zůstane bez odezvy po dobu detachedNoProgressBudgetMs (výchozí 1 hodina, lze přepsat pro jednotlivé běhy pomocí detached: { noProgressBudgetMs }). Projeví se to jako status: "interrupted", reason: "no-progress". Potomek, který se nikdy nehlásí, je omezen pouze absolutní detachedMaxBudgetMs strop: běh se nikdy nevzdává jen proto, že je pomalý. Nastavte noProgressBudgetMs na 0 nebo Infinity k vypnutí resetovacího okna pro daný běh.
Vykreslujte časové osy potomků v Reactu
useAgentToolEvents() je headless hook. Přihlásí se k odběru existujícího nadřazeného připojení, odstraňuje duplicity při souběhu replay a živých dat a použije podřízený UIMessageChunk těla na části zprávy a seskupuje sourozenecké běhy podle ID nadřazeného volání nástroje. Stav každého běhu obsahuje progress a milestones, takže panel běhů na pozadí může zobrazit živý ukazatel průběhu, fázi a štítky milníků bez nutnosti se do nich zanořovat.
import { useAgent, useAgentToolEvents } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
const agent = useAgent({ agent: "Assistant", name: userId });
const { messages } = useAgentChat({ agent });
const agentTools = useAgentToolEvents({ agent });
for (const message of messages) {
for (const part of message.parts) {
if (part.type === "tool-call") {
const runs = agentTools.getRunsForToolCall(part.toolCallId);
// Render the child runs beside this tool call.
}
}
}import { useAgent, useAgentToolEvents } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
const agent = useAgent({ agent: "Assistant", name: userId });
const { messages } = useAgentChat({ agent });
const agentTools = useAgentToolEvents({ agent });
for (const message of messages) {
for (const part of message.parts) {
if (part.type === "tool-call") {
const runs = agentTools.getRunsForToolCall(part.toolCallId);
// Render the child runs beside this tool call.
}
}
}Imperativní běhy bez nadřazeného volání nástroje jsou dostupné jako agentTools.unboundRuns.
Prozkoumejte podrobnosti a omezte přístup
Agents jako nástroje jsou běžní podřízení agenti. K zachovanému potomkovi se připojte prostřednictvím trasy nadřazeného agenta:
useAgent({
agent: "Assistant",
name: userId,
sub: [{ agent: "Researcher", name: runId }],
});useAgent({
agent: "Assistant",
name: userId,
sub: [{ agent: "Researcher", name: runId }],
});Omezte externí přístup pomocí nadřazeného registru, aby uhodnutá ID běhů nemohla spouštět nové podřízené facety:
override async onBeforeSubAgent(_request, child) {
if (!this.hasAgentToolRun(child.className, child.name)) {
return new Response("Not found", { status: 404 });
}
}Vymazat zachované běhy
Běhy a podřízené facety jsou ve výchozím nastavení zachovávány pro obnovení, proklikávání a pozdější kontrolu. Smažte je explicitně při mazání historie chatu nebo při uplatňování vlastních zásad uchovávání dat:
await this.clearAgentToolRuns();
await this.clearAgentToolRuns({
status: ["completed", "error", "aborted", "interrupted"],
});
await this.clearAgentToolRuns({ olderThan: Date.now() - 7 * 24 * 60 * 60_000 });await this.clearAgentToolRuns();
await this.clearAgentToolRuns({
status: ["completed", "error", "aborted", "interrupted"],
});
await this.clearAgentToolRuns({ olderThan: Date.now() - 7 * 24 * 60 * 60_000 });Pokud je uchovaný běh stále starting nebo running, úklid zruší podřízeného agenta ještě před odstraněním jeho facetu.
Přerušené běhy a zotavení
Běhy nástrojů agenta se uchovávají u nadřazeného agenta. Pokud se nadřazený agent restartuje (nasazení nebo vyřazení z paměti) v době, kdy běh potomka stále starting nebo running, neopustí podřízeného agenta. Obnovení při startu se znovu připojí k živému podřízenému agentovi a jeho stream napojí až na koncový výsledek podřízeného agenta. Protože je podřízený agent sub-agentem s vlastním chatRecovery, sám se zotaví ze svého přerušeného tahu, zatímco nadřazený agent předává jeho výstup dál. Dokončený podřízený agent se finalizuje, aniž by se znovu spouštěla již hotová práce.
Čekání na opětovné připojení je klíčované podle průběhu, nikoli pevně daný reálný čas. Dvě statické options ho doladit:
| Možnost | Výchozí | Chování |
|---|---|---|
agentToolReattachNoProgressTimeoutMs |
120000 (2 min) |
Jak dlouho rodič čeká s ne postupu vpřed, než se operace vzdá. Resetuje se při každém předaném chunku, takže streamovaný potomek je sledován až do koncového stavu. |
agentToolReattachMaxWindowMs |
Infinity |
Volitelný pevný strop wall-clock času pro jedno opětovné připojení (re-attach). Ve výchozím nastavení bez omezení (odpovídá chat recovery maxRecoveryWork), takže zdravý, dlouho běžící podřízený proces nikdy není přerušen. Chcete-li nastavit horní limit, zadejte konečnou hodnotu. |
Výstupy ukončení vzdáním odpovídají AgentToolFailure pole:
- Potomek, který se odmlčí na celé okno bez postupu, se zapečetí
reason: "no-progress",childStillRunning: true. Toto zapečetění je měkké: potomek zůstává spuštěný, takže opětovné odeslání stejnéhorunIdse k němu může znovu připojit a převzít ho, pokud se sám zotaví. - Pokud nastavíte konečné
agentToolReattachMaxWindowMsa spustí se, běh je zapečetěnreason: "window-exceeded",childStillRunning: false, a podřízený proces se ukončí (měl celé své okno a považuje se za vyčerpaný). - Potomek, kterého nelze sledovat ani zkontrolovat, nebo který překročí celkovou lhůtu pro obnovení, se zapečetí s odpovídajícím
reasonaby nadřazené volání nástroje vrátilo strukturovanou chybu, místo aby se neomezeně dlouho zaseklo.
Zaseknutý potomek nemůže obnovení blokovat navždy. Rozpočet bez postupu omezuje mlčícího potomka. Neřízený nárůst obsahu je omezen vlastním chatRecovery (maxRecoveryWork a shouldKeepRecovering), ne časovačem určeným jen pro nadřazený objekt.
Sledujte sladění nadřazeného prvku prostřednictvím agentTool kanál pozorovatelnosti:
import { subscribe } from "agents/observability";
const unsubscribe = subscribe("agentTool", (event) => {
if (event.type === "agent_tool:recovery:row") {
console.log("Recovered agent-tool row", event.payload);
}
});import { subscribe } from "agents/observability";
const unsubscribe = subscribe("agentTool", (event) => {
if (event.type === "agent_tool:recovery:row") {
console.log("Recovered agent-tool row", event.payload);
}
});Surový diagnostics_channel odběratelé by měli použít název kanálu agents:agent_tool.