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

Интеграция браузера

Используйте @cloudflare/codemode/browser когда браузер владеет инструментами, которые должна оркестровать модель. Например, эти инструменты могут считывать состояние страницы, обращаться к API браузера или обновлять данные, хранящиеся в вашем приложении.

Code Mode полезен, когда модели нужно вызывать несколько клиентских инструментов с циклами, условиями или промежуточными результатами. Для отдельного действия в браузере вместо этого используйте обычный инструмент на стороне клиента.

Code Mode предоставляет эти инструменты модели в виде типизированных функций. Модель пишет одну асинхронную arrow-функцию JavaScript, которая может вызывать несколько инструментов, объединять их результаты и управлять потоком выполнения. IframeSandboxExecutor выполняет этот сгенерированный код в изолированном iframe на странице.

Эта интеграция не даёт агенту контроль над удалённым браузером. Для просмотра веб-страниц, создания снимков экрана или автоматизации страниц с помощью Chrome DevTools Protocol (CDP) обратитесь к Инструменты браузера.

Установка Code Mode

Установите пакет в своём клиентском приложении:

npm i @cloudflare/codemode

@cloudflare/codemode/browser точка входа использует JSON Schema и API браузера. Она не требует peer-зависимостей AI SDK или Zod, которые использует @cloudflare/codemode/ai.

Добавьте Code Mode в чат-интерфейс Agent

Браузер создаёт инструмент Code Mode и регистрирует его как динамический клиентский инструмент. Agent получает схему инструмента, но сама реализация инструмента остаётся в браузере.

  1. Определите инструменты, принадлежащие браузеру, с помощью JSON Schema и execute функцию.

    src/browser-tools.js
    export const browserTools = {
    	getPageInfo: {
    		description: "Get information about the current browser page",
    		inputSchema: {
    			type: "object",
    			properties: {},
    			required: [],
    		},
    		execute: async () => ({
    			title: document.title,
    			url: window.location.href,
    		}),
    	},
    	getSelectionText: {
    		description: "Get the user's current text selection",
    		inputSchema: {
    			type: "object",
    			properties: {},
    			required: [],
    		},
    		execute: async () => ({
    			text: window.getSelection()?.toString() ?? "",
    		}),
    	},
    };
    src/browser-tools.ts
    import type { JsonSchemaExecutableToolDescriptors } from "@cloudflare/codemode/browser";
    
    export const browserTools: JsonSchemaExecutableToolDescriptors = {
      getPageInfo: {
        description: "Get information about the current browser page",
        inputSchema: {
          type: "object",
          properties: {},
          required: []
        },
        execute: async () => ({
          title: document.title,
          url: window.location.href
        })
      },
      getSelectionText: {
        description: "Get the user's current text selection",
        inputSchema: {
          type: "object",
          properties: {},
          required: []
        },
        execute: async () => ({
          text: window.getSelection()?.toString() ?? ""
        })
      }
    };

    JSON Schema задаёт типы, которые видит модель. createBrowserCodeTool() не использует схему для проверки аргументов во время выполнения. Проверяйте ненадёжные входные данные внутри каждого execute функцию при необходимости.

  2. Создайте дескриптор Code Mode с исполнителем iframe.

    src/codemode-tool.js
    import {
    	IframeSandboxExecutor,
    	createBrowserCodeTool,
    } from "@cloudflare/codemode/browser";
    import { browserTools } from "./browser-tools";
    
    export const codemodeTool = createBrowserCodeTool({
    	tools: browserTools,
    	executor: new IframeSandboxExecutor(),
    });
    src/codemode-tool.ts
    import {
      IframeSandboxExecutor,
      createBrowserCodeTool
    } from "@cloudflare/codemode/browser";
    import { browserTools } from "./browser-tools";
    
    export const codemodeTool = createBrowserCodeTool({
      tools: browserTools,
      executor: new IframeSandboxExecutor()
    });

    createBrowserCodeTool() возвращает простой дескриптор с именем codemode. Его описание включает сгенерированные определения TypeScript для инструментов браузера. Его входные данные содержат сгенерированный моделью JavaScript в code свойство.

    executor опция необязательна. Если её не указать, createBrowserCodeTool() создаёт IframeSandboxExecutor с настройками по умолчанию.

  3. Зарегистрируйте дескриптор с помощью useAgentChat() и выполняет вызовы клиентских инструментов.

    src/client.jsx
    import { useAgentChat } from "@cloudflare/ai-chat/react";
    import { useAgent } from "agents/react";
    import { useMemo } from "react";
    import { codemodeTool } from "./codemode-tool";
    
    function BrowserCodeModeChat() {
    	const agent = useAgent({ agent: "browser-codemode" });
    
    	const tools = useMemo(
    		() => ({
    			codemode: {
    				description: codemodeTool.description,
    				parameters: codemodeTool.inputSchema,
    				execute: (input) => codemodeTool.execute(input),
    			},
    		}),
    		[],
    	);
    
    	const { messages, sendMessage } = useAgentChat({
    		agent,
    		tools,
    		onToolCall: async ({ toolCall, addToolOutput }) => {
    			const tool = tools[toolCall.toolName];
    			if (!tool?.execute) return;
    
    			try {
    				const output = await tool.execute(toolCall.input);
    				addToolOutput({
    					toolCallId: toolCall.toolCallId,
    					output,
    				});
    			} catch (error) {
    				addToolOutput({
    					toolCallId: toolCall.toolCallId,
    					state: "output-error",
    					errorText: error instanceof Error ? error.message : String(error),
    				});
    			}
    		},
    	});
    
    	// Render messages and call sendMessage() from your chat UI.
    }
    src/client.tsx
    import { useAgentChat, type AITool } from "@cloudflare/ai-chat/react";
    import { useAgent } from "agents/react";
    import { useMemo } from "react";
    import { codemodeTool } from "./codemode-tool";
    
    function BrowserCodeModeChat() {
      const agent = useAgent({ agent: "browser-codemode" });
    
      const tools = useMemo<Record<string, AITool>>(
        () => ({
          codemode: {
            description: codemodeTool.description,
            parameters: codemodeTool.inputSchema,
            execute: (input) =>
              codemodeTool.execute(input as { code: string })
          }
        }),
        []
      );
    
      const { messages, sendMessage } = useAgentChat({
        agent,
        tools,
        onToolCall: async ({ toolCall, addToolOutput }) => {
          const tool = tools[toolCall.toolName];
          if (!tool?.execute) return;
    
          try {
            const output = await tool.execute(toolCall.input);
            addToolOutput({
              toolCallId: toolCall.toolCallId,
              output
            });
          } catch (error) {
            addToolOutput({
              toolCallId: toolCall.toolCallId,
              state: "output-error",
              errorText: error instanceof Error ? error.message : String(error)
            });
          }
        }
      });
    
      // Render messages and call sendMessage() from your chat UI.
    }

    useAgentChat() отправляет схему зарегистрированного клиентского инструмента в Agent. Когда модель вызывает codemode, onToolCall выполняет дескриптор в браузере и добавляет его результат в разговор.

  4. В Agent преобразуйте клиентские схемы в инструменты модели.

    src/server.js
    import { AIChatAgent, createToolsFromClientSchemas } from "@cloudflare/ai-chat";
    import { convertToModelMessages, stepCountIs, streamText } from "ai";
    import { createWorkersAI } from "workers-ai-provider";
    
    export class BrowserCodemode extends AIChatAgent {
    	async onChatMessage(_onFinish, options) {
    		const workersai = createWorkersAI({ binding: this.env.AI });
    
    		const result = streamText({
    			model: workersai("@cf/moonshotai/kimi-k2.7-code"),
    			system:
    				"Use the codemode tool to write JavaScript that calls browser-provided tools.",
    			messages: await convertToModelMessages(this.messages),
    			tools: createToolsFromClientSchemas(options?.clientTools),
    			stopWhen: stepCountIs(10),
    		});
    
    		return result.toUIMessageStreamResponse();
    	}
    }
    src/server.ts
    import { AIChatAgent, createToolsFromClientSchemas } from "@cloudflare/ai-chat";
    import { convertToModelMessages, stepCountIs, streamText } from "ai";
    import { createWorkersAI } from "workers-ai-provider";
    
    export class BrowserCodemode extends AIChatAgent<Env> {
      async onChatMessage(
        _onFinish?: unknown,
        options?: {
          clientTools?: Parameters<typeof createToolsFromClientSchemas>[0];
        }
      ) {
        const workersai = createWorkersAI({ binding: this.env.AI });
    
        const result = streamText({
          model: workersai("@cf/moonshotai/kimi-k2.7-code"),
          system:
            "Use the codemode tool to write JavaScript that calls browser-provided tools.",
          messages: await convertToModelMessages(this.messages),
          tools: createToolsFromClientSchemas(options?.clientTools),
          stopWhen: stepCountIs(10)
        });
    
        return result.toUIMessageStreamResponse();
      }
    }

    Agent сообщает модели схему, предоставленную клиентом. Он не выполняет сгенерированный код и не запускает реализации инструментов браузера.

Если набор инструментов браузера изменяется во время выполнения, создайте новый дескриптор Code Mode и зарегистрируйте обновлённый дескриптор в клиентском слое инструментов.

Выполнение iframe и безопасность

IframeSandboxExecutor создаёт скрытый iframe для каждого выполнения. Этот iframe использует sandbox="allow-scripts" и получает сгенерированный код через postMessage. Вызовы инструментов возвращаются на родительскую страницу, которая запускает соответствующий execute функцию.

Сообщения ограничены текущим iframe и nonce выполнения. После завершения, ошибки или тайм-аута executor удаляет iframe и слушатель сообщений.

Executor принимает следующие параметры:

Опция Тип По умолчанию Поведение
timeout number 30000 Завершает выполнение по истечении указанного количества миллисекунд.
csp string default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval'; Устанавливает Content Security Policy (CSP) для документа iframe.

Политика CSP по умолчанию блокирует все ресурсы, кроме встроенных и выполняемых через eval скриптов, необходимых для запуска сгенерированного кода. Передавайте собственную политику только если сгенерированному коду требуются дополнительные возможности iframe.

Ослабление директив, таких как connect-src, img-src, или form-action может позволить сгенерированному коду iframe взаимодействовать с внешними системами. Такой код способен раскрыть значения, возвращённые инструментами браузера. Ограничивайте набор допустимых адресов для исходящих запросов и не помещайте секреты в результаты инструментов. Инструменты, принадлежащие браузеру, выполняются отдельно, на родительской странице, с теми возможностями, которые предоставляет их реализация.

Ограничения подтверждения

createBrowserCodeTool() исключает любой инструмент, чей needsApproval значение равно true или функцию. Code Mode не приостанавливает выполнение iframe, чтобы запросить подтверждение для этих инструментов.

Держите действия, требующие подтверждения, вне дескриптора Code Mode. Регистрируйте их как обычные инструменты и используйте useAgentChat() поток подтверждения взамен.