← Создание агентов на Cloudflare / agents / tools / codemode
Используйте сервис OpenAPI с Code Mode
Используйте OpenApiConnector чтобы предоставить сервис OpenAPI внутри устойчивого runtime режима Code Mode. Коннектор создаёт один метод песочницы для каждой операции в документе OpenAPI.
Модель может обнаруживать методы с помощью codemode.search() и запрашивайте целевые типы ввода с помощью codemode.describe(). Полный документ OpenAPI не обязан попадать в контекст модели.
Эта страница описывает, как Agent использует службу OpenAPI. Чтобы опубликовать службу OpenAPI для внешних MCP-клиентов через search и execute, см. Создайте MCP-сервер типа найди и выполни.
Предварительные требования
Вам понадобится проект с устойчивый runtime Code Mode настроено. Настройка runtime предоставляет привязку Worker Loader и CodemodeRuntime экспорт, используемый в этом руководстве.
Создайте OpenAPI-коннектор
-
Добавьте документ OpenAPI в свой проект. Присвойте каждой операции уникальный
operationIdчтобы получить стабильное имя метода песочницы:src/orders-openapi.jsexport const ordersOpenApiSpec = { openapi: "3.1.0", info: { title: "Orders API", version: "1.0.0" }, paths: { "/orders/{orderId}": { get: { operationId: "get_order", summary: "Get an order by ID.", parameters: [ { name: "orderId", in: "path", required: true, schema: { type: "string" }, }, ], }, }, "/orders": { post: { operationId: "create_order", summary: "Create an order.", requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { productId: { type: "string" }, quantity: { type: "integer" }, }, required: ["productId", "quantity"], }, }, }, }, }, }, }, };src/orders-openapi.tsexport const ordersOpenApiSpec = { openapi: "3.1.0", info: { title: "Orders API", version: "1.0.0" }, paths: { "/orders/{orderId}": { get: { operationId: "get_order", summary: "Get an order by ID.", parameters: [ { name: "orderId", in: "path", required: true, schema: { type: "string" }, }, ], }, }, "/orders": { post: { operationId: "create_order", summary: "Create an order.", requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { productId: { type: "string" }, quantity: { type: "integer" }, }, required: ["productId", "quantity"], }, }, }, }, }, }, }, } as const; -
Создайте коннектор. Реализуйте
spec()чтобы вернуть документ иrequest()чтобы выполнять аутентифицированные запросы на стороне хоста:src/orders-connector.jsimport { OpenApiConnector } from "@cloudflare/codemode"; import { ordersOpenApiSpec } from "./orders-openapi"; const API_ORIGIN = "https://api.example.com"; export class OrdersConnector extends OpenApiConnector { name() { return "orders"; } instructions() { return "Use for reading and creating orders."; } spec() { return ordersOpenApiSpec; } async request(options) { if (!options.path.startsWith("/")) { throw new Error("Orders API path must start with a slash"); } const url = new URL(options.path, API_ORIGIN); for (const [key, value] of Object.entries(options.params ?? {})) { if (value !== undefined) { url.searchParams.set(key, String(value)); } } const response = await fetch(url, { method: options.method ?? "GET", headers: { ...(options.body !== undefined ? { "Content-Type": "application/json" } : {}), ...options.headers, Authorization: `Bearer ${this.env.ORDERS_API_TOKEN}`, }, body: options.body === undefined ? undefined : JSON.stringify(options.body), }); if (!response.ok) { throw new Error(`Orders API request failed: ${response.status}`); } if (response.status === 204) return null; return response.json(); } tool(name, tool) { if (name === "create_order") { return { ...tool, requiresApproval: true }; } return tool; } }src/orders-connector.tsimport { OpenApiConnector, type ConnectorTool, type OpenApiRequestOptions, } from "@cloudflare/codemode"; import { ordersOpenApiSpec } from "./orders-openapi"; const API_ORIGIN = "https://api.example.com"; export class OrdersConnector extends OpenApiConnector<Env> { override name() { return "orders"; } protected override instructions() { return "Use for reading and creating orders."; } protected override spec() { return ordersOpenApiSpec; } protected override async request(options: OpenApiRequestOptions) { if (!options.path.startsWith("/")) { throw new Error("Orders API path must start with a slash"); } const url = new URL(options.path, API_ORIGIN); for (const [key, value] of Object.entries(options.params ?? {})) { if (value !== undefined) { url.searchParams.set(key, String(value)); } } const response = await fetch(url, { method: options.method ?? "GET", headers: { ...(options.body !== undefined ? { "Content-Type": "application/json" } : {}), ...options.headers, Authorization: `Bearer ${this.env.ORDERS_API_TOKEN}`, }, body: options.body === undefined ? undefined : JSON.stringify(options.body), }); if (!response.ok) { throw new Error(`Orders API request failed: ${response.status}`); } if (response.status === 204) return null; return response.json(); } protected override tool(name: string, tool: ConnectorTool): ConnectorTool { if (name === "create_order") { return { ...tool, requiresApproval: true }; } return tool; } }Учётные данные остаются в хостовом Worker. Код, написанный моделью, получает методы коннектора и их результаты, а не
ORDERS_API_TOKEN.tool()хук декорирует производные операции. В этом примере требуется подтверждение передcreate_orderвыполняется. С помощью хука можно также добавить логику повтора или отката. -
Импортируйте коннектор и добавьте его в рантайм:
src/server.jsimport { AIChatAgent } from "@cloudflare/ai-chat"; import { createCodemodeRuntime, DynamicWorkerExecutor, } from "@cloudflare/codemode"; import { OrdersConnector } from "./orders-connector"; export class Chat extends AIChatAgent { #runtime() { return createCodemodeRuntime({ ctx: this.ctx, executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }), connectors: [new OrdersConnector(this.ctx, this.env)], }); } async onChatMessage() { const tools = { codemode: this.#runtime().tool() }; // Pass tools to your model call. } }src/server.tsimport { AIChatAgent } from "@cloudflare/ai-chat"; import { createCodemodeRuntime, DynamicWorkerExecutor, } from "@cloudflare/codemode"; import { OrdersConnector } from "./orders-connector"; export class Chat extends AIChatAgent<Env> { #runtime() { return createCodemodeRuntime({ ctx: this.ctx, executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }), connectors: [new OrdersConnector(this.ctx, this.env)], }); } async onChatMessage() { const tools = { codemode: this.#runtime().tool() }; // Pass tools to your model call. } } -
Позвольте модели обнаруживать операции и вызывать сгенерированные методы коннектора:
async () => { const matches = await codemode.search("get an order by ID"); const docs = await codemode.describe(matches.results[0].path); const order = await orders.get_order({ orderId: "order-123" }); return { docs, order }; };
Поведение производного метода
OpenApiConnector использует очищенную operationId в качестве имени метода. Если у операции нет operationId, он выводит имя из метода HTTP и пути. Определяйте уникальные ID операций, чтобы имена методов оставались стабильными и не возникало коллизий.
Каждый сгенерированный метод принимает один объект:
- Параметры пути, запроса и заголовков отображаются как поля верхнего уровня.
- Тело JSON-запроса появляется под
body. - Обязательные параметры OpenAPI становятся обязательными полями TypeScript.
- Локальный
$refзначения во входных схемах разрешаются до генерации типов.
Коннектор подставляет параметры пути и передаёт нормализованный { path, method, params, body, headers } объект на request().
Текущий коннектор выводит типы входных данных, но не выводит типы ответов из схем ответов OpenAPI. Поэтому сгенерированные методы возвращают unknown если только ваше приложение не предоставляет более конкретные декларации через другую реализацию коннектора.
Лазейка для запросов
Каждый OpenAPI-коннектор также предоставляет низкоуровневый request() метод sandbox. Используйте его, когда документ OpenAPI не описывает операцию, необходимую модели:
const result = await orders.request({
path: "/orders",
method: "GET",
params: { status: "processing" },
});Отдавайте предпочтение методам производных операций, если они доступны. Они предоставляют обнаруживаемые описания и сгенерированные типы входных данных.
exposeSpec() возвращает false по умолчанию. Переопределите его, чтобы возвращать true только когда написанному моделью коду нужен доступ к необработанному документу OpenAPI. Большие документы могут порождать большие результаты и объёмные записи в устойчивом журнале.