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

Используйте сервис 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-коннектор

  1. Добавьте документ OpenAPI в свой проект. Присвойте каждой операции уникальный operationId чтобы получить стабильное имя метода песочницы:

    src/orders-openapi.js
    export 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.ts
    export 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;
  2. Создайте коннектор. Реализуйте spec() чтобы вернуть документ и request() чтобы выполнять аутентифицированные запросы на стороне хоста:

    src/orders-connector.js
    import { 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.ts
    import {
    	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 выполняется. С помощью хука можно также добавить логику повтора или отката.

  3. Импортируйте коннектор и добавьте его в рантайм:

    src/server.js
    import { 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.ts
    import { 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.
    	}
    }
  4. Позвольте модели обнаруживать операции и вызывать сгенерированные методы коннектора:

    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 операций, чтобы имена методов оставались стабильными и не возникало коллизий.

Каждый сгенерированный метод принимает один объект:

Коннектор подставляет параметры пути и передаёт нормализованный { 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. Большие документы могут порождать большие результаты и объёмные записи в устойчивом журнале.