INTEGRITY Документация

Клиентские инструменты

Think поддерживает инструменты, которые выполняются в браузере. Клиент отправляет сериализуемые схемы инструментов в теле запроса чата, Think объединяет их с серверными инструментами, и когда LLM вызывает клиентский инструмент, вызов направляется клиенту для выполнения.

Определение клиентских инструментов

Для динамических инструментов на стороне клиента передайте tools к useAgentChat. Инструменты с execute функции регистрируются на сервере как инструменты, выполняемые на стороне клиента:

const { messages, sendMessage } = useAgentChat({
	agent,
	tools: {
		getUserTimezone: {
			description: "Get the user's timezone from their browser",
			parameters: {},
			execute: async () => {
				return Intl.DateTimeFormat().resolvedOptions().timeZone;
			},
		},
		getClipboard: {
			description: "Read text from the user's clipboard",
			parameters: {},
			execute: async () => {
				return navigator.clipboard.readText();
			},
		},
	},
});
const { messages, sendMessage } = useAgentChat({
	agent,
	tools: {
		getUserTimezone: {
			description: "Get the user's timezone from their browser",
			parameters: {},
			execute: async () => {
				return Intl.DateTimeFormat().resolvedOptions().timeZone;
			},
		},
		getClipboard: {
			description: "Read text from the user's clipboard",
			parameters: {},
			execute: async () => {
				return navigator.clipboard.readText();
			},
		},
	},
});

Клиентские инструменты: это инструменты без execute функцию на сервере, у них есть только схема. Когда LLM формирует вызов инструмента для одного из них, Think направляет его клиенту.

В большинстве приложений лучше определять инструменты на сервере и использовать onToolCall для выполнения только в браузере. tools опция наиболее полезна для SDK или платформ, где браузер сам определяет доступный набор инструментов во время выполнения.

Клиентские инструменты через RPC дочернего агента chat() путь

Когда родительский агент делегирует задачу дочернему агенту Think через RPC с chat() (а не браузерный WebSocket), нет WebSocket для передачи clientTools или для отправки результатов инструментов обратно. Передавайте их через ChatOptions вместо этого:

await child.chat(message, callback, {
	signal,
	clientTools: [
		{
			name: "get_user_timezone",
			description: "Get the caller's timezone",
			parameters: { type: "object" },
		},
	],
	onClientToolCall: async ({ toolName, input }) => {
		// Run the client tool wherever the parent can — return its output.
		return runClientTool(toolName, input);
	},
});
await child.chat(message, callback, {
	signal,
	clientTools: [
		{
			name: "get_user_timezone",
			description: "Get the caller's timezone",
			parameters: { type: "object" },
		},
	],
	onClientToolCall: async ({ toolName, input }) => {
		// Run the client tool wherever the parent can — return its output.
		return runClientTool(toolName, input);
	},
});

Если вы не укажете onClientToolCall, инструменты регистрируются, но не имеют результата: вызов модели передаётся через колбэк потока, и ход завершается с незавершённым вызовом инструмента (у колбэка RPC-потока нет собственного канала для входящего результата). Укажите onClientToolCall каждый раз, когда должен завершиться полный цикл запроса и ответа.

Замечания о поведении

Поток подтверждения

Обрабатывайте выполнение инструментов на стороне браузера в клиенте с помощью onToolCall:

useAgentChat({
	agent,
	onToolCall: async ({ toolCall, addToolOutput }) => {
		if (toolCall.toolName === "read") {
			const result = await readFromBrowser(toolCall.input);
			addToolOutput({
				toolCallId: toolCall.toolCallId,
				output: result,
			});
		}
	},
});
useAgentChat({
	agent,
	onToolCall: async ({ toolCall, addToolOutput }) => {
		if (toolCall.toolName === "read") {
			const result = await readFromBrowser(toolCall.input);
			addToolOutput({
				toolCallId: toolCall.toolCallId,
				output: result,
			});
		}
	},
});

Автопродолжение

После получения результата клиентского инструмента Think автоматически продолжает разговор без нового сообщения пользователя. У хода продолжения есть continuation: true в TurnContext, которую вы можете использовать в beforeTurn чтобы настроить выбор модели или инструментов.

Когда за один ход выполняется сразу несколько клиентских вызовов инструментов, Think ожидает all их результатов перед запуском единого продолжения, вместо того чтобы запускать по одному продолжению на каждый результат. Немедленный запрос на возобновление, поступивший в тот момент, когда продолжение уже ожидает выполнения, присоединяется к этому ожидающему продолжению, а не запускает дубликат, и на стороне сервера needsApproval продолжения надёжно возобновляются, как только подтверждение зафиксировано.

Переживает перезапуски в ожидании участия человека

Durable Object может быть вытеснен в любой момент, в том числе пока ход приостановлен на запросе подтверждения или клиентском вызове инструмента. Think устойчивое восстановление всегда включён. SDK считает такой ход ожидающим человека, а не зависшим. Он приостанавливает ход вместо того, чтобы завершить его с ошибкой. Итоговое подтверждение пользователя или результат инструмента возобновляет разговор.

Какие взаимодействия не учитываются в бюджетах восстановления, см. в Ходы, ожидающие человека, не опечатываются.

Параллелизм сообщений

messageConcurrency свойство определяет, как ведут себя перекрывающиеся отправки пользователя, когда ход чата уже активен.

Стратегия Поведение
"queue" Ставить в очередь каждую отправку и обрабатывать их по порядку. По умолчанию.
"latest" Сохраняется только последняя из перекрывающихся отправок; вытесненные отправки по прежнему сохраняют свои сообщения пользователя, но не запускают ход модели
"merge" Ставить в очередь параллельные отправки, а затем объединять их последние сообщения пользователя в один общий ход перед запуском последнего хода из очереди
"drop" Полностью игнорируйте перекрывающиеся отправки. Сообщения не сохраняются.
{ strategy: "debounce", debounceMs?: number } Trailing-edge latest с окном ожидания (по умолчанию 750 мс).
import { Think } from "@cloudflare/think";

export class SearchAgent extends Think {
	messageConcurrency = "latest";
	getModel() {
		/* ... */
	}
}
import { Think } from "@cloudflare/think";
import type { MessageConcurrency } from "@cloudflare/think";

export class SearchAgent extends Think<Env> {
	override messageConcurrency: MessageConcurrency = "latest";
	getModel() {
		/* ... */
	}
}

Трансляция между вкладками

Think рассылает потоковые ответы всем подключённым клиентам WebSocket. Если к одному и тому же агенту подключено несколько вкладок браузера, все вкладки видят потоковый ответ в реальном времени. Состояния вызовов инструментов (ожидание, результат, подтверждение) рассылаются во все вкладки.

Программный chat() ходов и clearMessages() также транслируют обновления сообщений подключённым useAgentChat клиентов, поэтому клиенты в браузере остаются синхронизированными без переподключения.