← Vytvářejte agenty na Cloudflare / agents / harnesses / think
Hooky životního cyklu
Think vlastní streamText volání a poskytuje hooky v každé fázi kroku konverzace. Hooky se spouštějí při každém kroku bez ohledu na vstupní cestu: WebSocket chat, dílčí agent chat(), saveMessages(), trvalé submitMessages() provedení, continueLastTurn(), a automatické pokračování po výsledcích nástrojů.
Souhrn hooku
| Hook | Když se spustí | Návrat | Asynchronní |
|---|---|---|---|
configureSession(session) |
Jednou během onStart |
Session |
ano |
beforeTurn(ctx) |
Před streamText |
TurnConfig nebo void |
ano |
beforeStep(ctx) |
Před každým krokem modelu | StepConfig nebo void |
ano |
beforeToolCall(ctx) |
Před spuštěním serverového nástroje | ToolCallDecision nebo void |
ano |
afterToolCall(ctx) |
Jakmile je znám výsledek nástroje | void | ano |
onStepFinish(ctx) |
Po dokončení každého kroku | void | ano |
onChunk(ctx) |
Na jeden chunk streamu | void | ano |
onChatResponse(result) |
Po dokončení kola a uložení zprávy | void | ano |
onChatError(error, ctx?) |
Při chybě během kola | chyba k propagaci | ne |
classifyChatError(error, ctx?) |
Při chybě kola, když contextOverflow.reactive je zapnuto |
ChatErrorClassification nebo void |
ne |
Pořadí vykonávání
Pro kolo se dvěma voláními nástrojů:
flowchart TD
cfg["configureSession() — once at startup, not per-turn"] --> bt["beforeTurn() — inspect context, override model/tools/prompt"]
bt --> bs
subgraph loop ["streamText (repeats per step)"]
bs["beforeStep()"] --> chunk["onChunk() — per streaming chunk"]
chunk --> btc["beforeToolCall()"]
btc --> exec["tool executes"]
exec --> atc["afterToolCall()"]
atc --> sf["onStepFinish()"]
sf -->|"more steps"| bs
end
sf -->|"turn complete"| ocr["onChatResponse() — message persisted, turn lock released"]
beforeTurn
Volá se před streamText. Přijímá kompletně sestavený kontext: systémový prompt, převedené zprávy, sloučené nástroje a model. Vraťte TurnConfig k přepsání libovolné části, nebo void pro přijetí výchozích hodnot.
beforeTurn(ctx: TurnContext): TurnConfig | void | Promise<TurnConfig | void>TurnContext
| Pole | Typ | Popis |
|---|---|---|
system |
string |
Sestavený systémový prompt (z kontextových bloků nebo getSystemPrompt()) |
messages |
ModelMessage[] |
Sestavené zprávy modelu (zkrácené, ořezané) |
tools |
ToolSet |
Sloučená sada nástrojů (workspace + getTools + session + extensions + MCP + client) |
model |
LanguageModel |
Model z getModel() |
continuation |
boolean |
Zda jde o navazující kolo (automatické pokračování po výsledku nástroje) |
body |
Record<string, unknown> |
Vlastní pole těla z klientského požadavku |
TurnConfig
Všechna pole jsou volitelná. Vraťte pouze to, co chcete změnit.
| Pole | Typ | Popis |
|---|---|---|
model |
LanguageModel |
Přepište model pro tento turn |
system |
string |
Přepište systémový prompt |
messages |
ModelMessage[] |
Přepište sestavené zprávy |
tools |
ToolSet |
Další nástroje ke sloučení (přídavné) |
activeTools |
string[] |
Omezte, které nástroje může model volat |
toolChoice |
ToolChoice |
Vynucení konkrétního volání nástroje |
maxSteps |
number |
Přepsat maxSteps pro toto kolo |
sendReasoning |
boolean |
Odeslat části uvažování pro toto kolo |
chatStreamStallTimeoutMs |
number |
Přepište watchdog zaseknutí streamu pro tento turn (0 ji vypne); po kole se automaticky obnoví. Užitečné pro kolo se známě pomalým nástrojem, viz Trvalé obnovení |
output |
Output |
Vyžádat strukturovaný výstup pro toto kolo |
providerOptions |
Record<string, unknown> |
Možnosti specifické pro poskytovatele |
experimental_telemetry |
object |
nastavení telemetrie AI SDK pro toto kolo |
experimental_transform |
StreamTextTransform | StreamTextTransform[] |
transformace streamu AI SDK pro toto kolo: kontrola nebo přepis částí streamu (například vyslání source části odvozené z výsledků nástrojů). Uplatní se v daném pořadí. |
Příklady
Přepněte na levnější model pro navazující kola:
beforeTurn(ctx: TurnContext) {
if (ctx.continuation) {
return { model: this.cheapModel };
}
}Omezte, které nástroje může model volat:
beforeTurn(ctx: TurnContext) {
return { activeTools: ["read", "write", "getWeather"] };
}Přidejte kontext pro každé kolo z těla klientského požadavku:
beforeTurn(ctx: TurnContext) {
if (ctx.body?.selectedFile) {
return {
system: ctx.system + `\n\nUser is editing: ${ctx.body.selectedFile}`,
};
}
}Skrytí uvažování u interních pokračovacích kol:
beforeTurn(ctx: TurnContext) {
if (ctx.continuation) {
return { sendReasoning: false };
}
}Vynuťte strukturovaný výstup pro dané kolo:
import { Output } from "ai";
import { z } from "zod";
const ResultSchema = z.object({ severity: z.enum(["low", "high"]) });
beforeTurn(ctx: TurnContext) {
if (ctx.body?.mode === "structured-answer") {
return {
output: Output.object({ schema: ResultSchema }),
activeTools: [],
};
}
}output je nastavení pouze na úrovni kola. V AI SDK prepareStep nepřijímá output override, takže beforeStep nemůže přepínat strukturovaný výstup v rámci jednoho kroku.
beforeStep
Volá se před každým krokem AI SDK v agentní smyčce. Think tento hook předává do streamText jako prepareStep, takže dostává celý kontext prepare-step z AI SDK a může vracet přepisy pro jednotlivé kroky. Použijte beforeTurn pro sestavení napříč celým kolem a beforeStep když rozhodnutí závisí na čísle kroku nebo výsledcích předchozích kroků.
beforeStep(ctx: PrepareStepContext): StepConfig | void {
if (ctx.stepNumber > 0) {
return { activeTools: [] };
}
}beforeToolCall
Volá se před spuštěním funkce serverového nástroje execute funkce spustí. Think obalí každý nástroj na straně serveru tak, aby hook mohl volání povolit, upravit, zablokovat nebo nahradit ještě předtím, než model obdrží výsledek nástroje.
beforeToolCall(ctx: ToolCallContext): ToolCallDecision | void {
if (ctx.toolName === "delete" && this.isReadOnlyMode) {
return { action: "block", reason: "delete is disabled in read-only mode" };
}
if (ctx.toolName === "weather") {
const cached = this.weatherCache.get(JSON.stringify(ctx.input));
if (cached) return { action: "substitute", output: cached };
}
}| Pole | Typ | Popis |
|---|---|---|
toolName |
string |
Název volaného nástroje |
input |
unknown |
Vstup poskytnutý modelem |
toolCallId |
string |
ID pro toto volání nástroje |
messages |
ModelMessage[] |
Zprávy viditelné v době provádění nástroje |
abortSignal |
AbortSignal | undefined |
Signál, který se aktivuje při zrušení kola |
Vrátí ToolCallDecision pro řízení běhu:
| Rozhodnutí | Chování |
|---|---|
void nebo { action: "allow" } |
Spuštění původního nástroje s původním vstupem |
{ action: "allow", input } |
Spuštění původního nástroje s upraveným vstupem |
{ action: "block", reason } |
Přeskočte původní nástroj a vraťte reason jako výsledek nástroje |
{ action: "substitute", output } |
Přeskočte původní nástroj a vraťte output jako výsledek nástroje |
Pokud obalený nástroj vrátí AsyncIterable pro předběžné výsledky nástrojů. Think sbalí iterovatelný objekt na jeho poslední vrácenou hodnotu po beforeToolCall se spustí. Pokud od tohoto nástroje potřebujete opravdové předběžné streamování, nezachytávejte ho pomocí beforeToolCall.
afterToolCall
Volá se poté, co je znám výsledek nástroje. Zahrnuje to skutečná spuštění, blokovaná volání, nahrazená volání a vyvolané chyby nástroje.
afterToolCall(ctx: ToolCallResultContext) {
if (!ctx.success) return;
this.env.ANALYTICS.writeDataPoint({
blobs: [ctx.toolName],
doubles: [JSON.stringify(ctx.output).length],
});
}| Pole | Typ | Popis |
|---|---|---|
toolName |
string |
Název nástroje, který byl zavolán |
input |
unknown |
Vstup poskytnutý modelem |
toolCallId |
string |
ID pro toto volání nástroje |
messages |
ModelMessage[] |
Zprávy viditelné v době provádění nástroje |
durationMs |
number |
Doba běhu nástroje v milisekundách |
success |
boolean |
Zda model obdržel úspěšný výsledek nástroje |
output |
unknown |
Přítomné, když success je true |
error |
unknown |
Přítomné, když success je false |
Pro blokovaná a nahrazená volání nástrojů success je true protože model obdrží platný výsledek nástroje. Pouze chyby vyvolané (thrown) při původním spuštění nástroje se projeví jako success: false.
onStepFinish
Volá se po dokončení každého kroku v agentní smyčce. StepContext je událost step-finish z AI SDK, takže obsahuje kompletní záznam kroku: vygenerovaný text, uvažování, soubory, zdroje, typovaná volání nástrojů a jejich výsledky, využití, upozornění, metadata požadavku a odpovědi a metadata poskytovatele.
onStepFinish(ctx: StepContext) {
console.log(
`Step ${ctx.stepNumber} (${ctx.finishReason}): ` +
`${ctx.usage.inputTokens}in/${ctx.usage.outputTokens}out`,
);
}| Pole | Popis |
|---|---|
stepNumber |
Index kroku počítaný od nuly |
text |
Text vygenerovaný v tomto kroku |
reasoning |
Části uvažování vydané modelem |
files |
Soubory vygenerované během kroku |
sources |
Citace nebo zdroje použité modelem |
toolCalls |
Typovaná volání nástrojů provedená v tomto kroku |
toolResults |
Typované výsledky nástrojů přijaté v tomto kroku |
finishReason |
Proč krok skončil |
usage |
Využití tokenů včetně tokenů z cache a tokenů pro reasoning |
providerMetadata |
Metadata specifická pro poskytovatele |
onChunk
Volá se pro každý streamovaný chunk: vysoká frekvence, spouští se pro každý token. Použijte pro analýzu streamování, ukazatele průběhu nebo počítání tokenů. Pouze pro pozorování.
onChatResponse
Volá se poté, co kolo chatu vytvoří a uloží zprávu asistenta. Zámek kola se uvolní ještě před spuštěním tohoto hooku, takže je bezpečné zavolat saveMessages nebo jiné metody zevnitř.
Spouští se u všech průběhů kola, které ukládají zprávu asistenta: WebSocket, RPC subagenta, saveMessages, a automatické pokračování. Pokud kolo selže dříve, než vytvoří jakoukoli část odpovědi asistenta, onChatError místo toho zpracuje chybu.
onChatResponse(result: ChatResponseResult) {
if (result.status === "completed") {
console.log(`Turn ${result.requestId}: ${result.message.parts.length} parts`);
}
}| Pole | Typ | Popis |
|---|---|---|
message |
UIMessage |
Persistovaná zpráva asistenta |
requestId |
string |
Jedinečné ID tohoto kola |
continuation |
boolean |
Zda šlo o navazující kolo |
status |
"completed" | "error" | "aborted" |
Jak kolo skončilo |
error |
string? |
Chybová zpráva (když status je "error") |
onChatError
Volá se, když během kola chatu dojde k chybě. Chybu vraťte, pokud ji chcete propagovat dál, nebo vraťte jinou chybu. Volitelný kontext popisuje, kde k selhání došlo a zda už byly zprávy uživatele uloženy. Částečná zpráva asistenta (pokud existuje) se uloží ještě před spuštěním tohoto hooku.
onChatError(error: unknown, ctx?: ChatErrorContext): unknownChatErrorContext zahrnuje:
| Pole | Typ | Popis |
|---|---|---|
requestId |
string | undefined |
ID požadavku chatu, pokud je k dispozici |
stage |
"parse" | "persist" | "turn" | "stream" | "recovery" | "transcript" |
Fáze selhání |
messagesPersisted |
boolean |
Zda byly příchozí uživatelské zprávy již uloženy |
classification |
ChatErrorClassification | undefined |
Nastavte na "context_overflow" na koncovém onChatError když se přetečení kontextu nepodařilo obnovit (viz classifyChatError); undefined jinak |
Think také vyvolává chat:request:failed na agents:chat kanál pozorovatelnosti se stejnými informacemi o fázi a perzistenci.
onChatError(error: unknown, ctx?: ChatErrorContext) {
console.error("Chat turn failed:", ctx?.stage, error);
if (ctx?.classification === "context_overflow") {
return new Error("This conversation is too long to continue. Please start a new one.");
}
return new Error("Something went wrong. Please try again.");
}classifyChatError
Volá se, když během kola dojde k chybě, před onChatError. Přemapuje surovou chybu poskytovatele na kategorii nezávislou na poskytovateli, díky čemuž Think může reagovat, aniž by musel mít v frameworku napevno zadané řetězce specifické pro poskytovatele: stejné rozdělení jako u tokenCounter které předáváte do compactAfter(). Mapování spravuje aplikace, protože ví, se kterým poskytovatelem a modelem komunikuje.
classifyChatError(error: unknown, ctx?: ChatErrorContext): ChatErrorClassification | voidChatErrorClassification je "context_overflow" | "rate_limit" | "transient" | "fatal" | "unknown". Dnes tento hook řídí pouze zotavení po přetečení kontextu. Think ho volá, když v kole dojde k chybě a contextOverflow.reactive je zapnuto. Pokud je reactive vypnuté, nevolá se.
Vracení "context_overflow" spouští záložní mechanismus zhuštění a opakování (viz Obnovení po přetečení kontextového okna). Pokud obnova kolo nezachrání, tato klasifikace se projeví na konečném onChatError volání přes ChatErrorContext.classification.
Ostatní kategorie jsou vyhrazeny pro budoucí použití. Vrácení některé z nich dnes nemá žádný účinek a nepředává se do onChatError. Vrácení void (výchozí) zachovává stávající chování při ukončení.
Argumentem může být Error, AI SDK APICallError (s statusCode/responseBody), nebo, v případě chyb poskytovatele uvnitř streamu, které se projeví jako chybová část streamu a ne jako vyvolaná výjimka, řetězec s chybovou zprávou. Podle toho zúžete typ. Chyby poskytovatele způsobené přetečením kontextu přicházejí jako chybové části uvnitř streamu, takže je tento hook dostává jako řetězec, ne jako vyvolanou výjimku.
Druhý argument je ChatErrorContext. Během obnovy po přetečení je { stage: "stream", requestId }, takže klasifikátor může chybu spojit s právě probíhajícím tahem, například zavolat cancelChat(requestId) a předčasně ukončí zotavování.
Příklad
V běžném případě přiřaďte sdružený defaultContextOverflowClassifier, což odpovídá chybám přetečení kontextu u Anthropic, OpenAI, Google, Bedrock a dalších:
import { Think, defaultContextOverflowClassifier } from "@cloudflare/think";
export class MyAgent extends Think {
classifyChatError = defaultContextOverflowClassifier;
}import { Think, defaultContextOverflowClassifier } from "@cloudflare/think";
export class MyAgent extends Think<Env> {
override classifyChatError = defaultContextOverflowClassifier;
}Nebo si napište vlastní, případně s delegováním na vestavěný klasifikátor:
import { Think, defaultContextOverflowClassifier } from "@cloudflare/think";
export class MyAgent extends Think {
classifyChatError(error) {
if (error instanceof Error && /rate.?limit/i.test(error.message)) {
return "rate_limit";
}
return defaultContextOverflowClassifier(error);
}
}import type { ChatErrorClassification } from "@cloudflare/think";
import { Think, defaultContextOverflowClassifier } from "@cloudflare/think";
export class MyAgent extends Think<Env> {
override classifyChatError(error: unknown): ChatErrorClassification | void {
if (error instanceof Error && /rate.?limit/i.test(error.message)) {
return "rate_limit";
}
return defaultContextOverflowClassifier(error);
}
}