构建你的第一个 AI 驱动的 WordPress 插件

最近两次 WordPress 大版本发布,为在 WordPress 中探索 AI 的开发者带来了三个重要组成部分:

  • Abilities API:以标准方式注册一项功能,让 REST API、区块编辑器、其他开发者和 AI 代理都能发现并执行它。
  • WordPress AI Client:不绑定具体服务商的 PHP 库,用来与 AI 模型交互,插件无需围绕每家服务商分别编写业务代码。
  • MCP Adapter:将 Ability 暴露为 MCP 工具,供桌面或云端 AI 代理使用。

这些组件分别使用时已经很有价值,组合在一起才能形成完整的插件功能。如果你一直关注 WordPress Core AI 项目,却还不清楚这些部分如何在实际插件中配合,下面就逐步实现一个例子。

如果你是第一次接触这些概念,建议先学习Abilities API 入门、WordPress MCP Adapter 入门和使用 WordPress AI Client 构建图像生成插件,再回到本教程。本教程也曾用于 WordCamp Europe 2026 的工作坊;视频资料见原文中的 WordPress.tv 链接。

要构建的功能

插件名为 Photo to Post。输入一张图片的 URL 后,它会依次完成三件事:

  1. 使用 AI 视觉模型理解图片,生成文字描述。
  2. 根据描述生成文章标题和正文,并建立文章草稿。
  3. 更新草稿,将原始图片设为特色图片。

实现方式是本教程的重点。插件内部由三个可以组合使用的 WordPress Ability 构成:

Ability 作用 AI 能力
describe-image 图片 URL → 文字描述 视觉理解,接收图片输入
generate-post-from-description 文字描述 → 文章标题与正文 文本生成
create-post-from-photo 编排前两个 Ability,然后创建文章 本身不调用 AI,只负责组合其他 Ability

第三个 Ability 展示了能力组合:通过 WP_Ability::execute(),一个 Ability 可以调用另一个 Ability。与传统的 WordPress 功能开发方式相比,这是 Abilities API 一个值得关注的特点。

开始之前

运行教程代码,需要在本地电脑准备:

  • WordPress 7.0 或更高版本,PHP 8.1 或更高版本的本地开发环境。
  • Composer 和 Node.js。
  • 支持视觉模型的 AI 服务商及相应 API 凭据。

本教程基于官方起始插件开发,现有基础代码可以缩短搭建过程。原教程使用的是 1.1.1 版本;请在自己的本地开发环境安装并启用这个教程版本。

启用后,在“工具”菜单下会出现 WP AI Workshop Demo 页面。它使用 WordPress DataForm 渲染 Photo to Post 表单,目前还没有接入实际功能。在插件目录安装 Composer 和 npm 依赖:

composer install
npm install

打开主插件文件 wp-ai-workshop-demo.php,你会发现几个标有 TODO 的注释,例如注册钩子的位置:

// TODO: Register the ability category and the three Photo to Post ability hooks.

这些标记指示需要补上实现的位置。教程要求在某个文件中添加代码时,找到对应的 TODO 并替换它。

连接 AI 服务商

AI Client 需要先连接 AI 服务商,才能读取图片、生成描述和文章内容。WordPress 7.0 在“设置”菜单中提供了连接器页面。它基于 Connectors API,为站点管理员提供集中管理外部 API 密钥的界面。

过去,连接外部服务的插件往往各自提供密钥设置页,例如 Akismet、Mailchimp for WordPress、Google Site Kit,以及 Stripe、PayPal、Authorize.net 等 WooCommerce 支付扩展。如果有四个插件各自保存密钥,管理员就要在四个位置管理连接。

Connectors API 将这些连接集中到一处。开发者注册自己服务的 Connector,它就会显示在连接器页面中,供用户输入并保存 API 密钥。

AI 服务商已有 OpenAI、Anthropic 和 Google 的适配插件。可从连接器页面安装所选服务商插件,然后保存 API 密钥,启用 AI Client 的功能。

也可以选择 Ollama、OpenRouter 或 Mistral 等适配插件。本教程处理图片,所以务必选择支持视觉输入的模型。

实现插件功能

服务商连接完成后,就可以开始实现各个插件功能。

第一步:注册 Ability

Ability 是对插件功能的正式描述,包含名称标签、说明、输入结构、输出结构、权限检查,以及负责实际工作的回调函数。本例的 Ability 注册代码全部放在 includes/abilities.php 中。

先用 wp_register_ability_category() 注册分类,将相关 Ability 归到同一组。在 wp_ai_workshop_demo_register_ability_categories() 内添加:

wp_register_ability_category(
	'wp-ai-workshop-demo',
	array(
		'label'       => __( 'WP AI Workshop Demo', 'wp-ai-workshop-demo' ),
		'description' => __( 'Abilities for the WP AI Workshop Demo.', 'wp-ai-workshop-demo' ),
	)
);

接着注册第一个 Ability:describe-image。输入与输出 schema 会告诉调用者应该传入什么,以及会得到什么结果。在 wp_ai_workshop_demo_register_describe_image_ability() 内添加:

wp_register_ability(
	'wp-ai-workshop-demo/describe-image',
	array(
		'label'               => __( 'Describe an image via AI', 'wp-ai-workshop-demo' ),
		'description'         => __( 'Given an image URL, use AI vision to produce a detailed text description of the image.', 'wp-ai-workshop-demo' ),
		'category'            => 'wp-ai-workshop-demo',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'image_url' => array(
					'type'        => 'string',
					'description' => 'The URL of the image to describe.',
				),
			),
			'required'   => array( 'image_url' ),
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'description' => array(
					'type'        => 'string',
					'description' => 'A detailed description of the image.',
				),
			),
			'required'   => array( 'description' ),
		),
		'execute_callback'    => 'wp_ai_workshop_demo_describe_image',
		'permission_callback' => function () {
			return current_user_can( 'edit_posts' );
		},
		'meta'                => array(
			'show_in_rest' => true,
		),
	)
);

generate-post-from-description 和 create-post-from-photo 使用相同的注册模式,只需改变输入输出结构和执行回调。先实现 wp_ai_workshop_demo_register_generate_post_from_description_ability():

wp_register_ability(
	'wp-ai-workshop-demo/generate-post-from-description',
	array(
		'label'               => __( 'Generate post copy from a description', 'wp-ai-workshop-demo' ),
		'description'         => __( 'Given a description (and optional tone/angle), generate a WordPress post title and body content.', 'wp-ai-workshop-demo' ),
		'category'            => 'wp-ai-workshop-demo',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'description' => array(
					'type'        => 'string',
					'description' => 'The source description to base the post on.',
				),
				'prompt'      => array(
					'type'        => 'string',
					'description' => 'Optional tone, angle, or extra guidance for the post.',
				),
			),
			'required'   => array( 'description' ),
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'title'   => array(
					'type'        => 'string',
					'description' => 'The generated post title.',
				),
				'content' => array(
					'type'        => 'string',
					'description' => 'The generated post content in Block Editor markup.',
				),
			),
			'required'   => array( 'title', 'content' ),
		),
		'execute_callback'    => 'wp_ai_workshop_demo_generate_post_from_description',
		'permission_callback' => function () {
			return current_user_can( 'edit_posts' );
		},
		'meta'                => array(
			'show_in_rest' => true,
		),
	)
);

再实现 wp_ai_workshop_demo_register_create_post_from_photo_ability():

wp_register_ability(
	'wp-ai-workshop-demo/create-post-from-photo',
	array(
		'label'               => __( 'Create a post from a photo via AI', 'wp-ai-workshop-demo' ),
		'description'         => __( 'Given an image URL, describe the image, write a post about it, and create a draft post using the image as the featured image.', 'wp-ai-workshop-demo' ),
		'category'            => 'wp-ai-workshop-demo',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'image_url' => array(
					'type'        => 'string',
					'description' => 'The URL of the image to turn into a post.',
				),
				'prompt'    => array(
					'type'        => 'string',
					'description' => 'Optional tone, angle, or extra guidance for the post.',
				),
			),
			'required'   => array( 'image_url' ),
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'message' => array(
					'type'        => 'string',
					'description' => 'A status message describing the result.',
				),
				'post_id' => array(
					'type'        => 'integer',
					'description' => 'The ID of the newly created post.',
				),
			),
			'required'   => array( 'message' ),
		),
		'execute_callback'    => 'wp_ai_workshop_demo_create_post_from_photo',
		'permission_callback' => function () {
			return current_user_can( 'edit_posts' );
		},
		'meta'                => array(
			'show_in_rest' => true,
		),
	)
);

需要掌握的注册结构是:标签、说明、schema、权限和回调。这些正式描述让 REST API、其他开发者和 AI 代理知道功能的名称、用途、所需输入与返回内容。描述一次,就可以供不同调用入口使用。

第二步:接入注册钩子

Ability 通过专用 action 钩子注册:wp_abilities_api_categories_init 和 wp_abilities_api_init。在主文件 wp-ai-workshop-demo.php 中,把 includes/abilities.php 中的函数挂到对应钩子:

add_action( 'wp_abilities_api_categories_init', 'wp_ai_workshop_demo_register_ability_categories' );
add_action( 'wp_abilities_api_init', 'wp_ai_workshop_demo_register_describe_image_ability' );
add_action( 'wp_abilities_api_init', 'wp_ai_workshop_demo_register_generate_post_from_description_ability' );
add_action( 'wp_abilities_api_init', 'wp_ai_workshop_demo_register_create_post_from_photo_ability' );

第三步:通过 REST 检查注册结果

注册时设置 'show_in_rest' => true 后,已认证的用户就可以通过 REST API 发现 Ability,无需另外编写暴露端点的代码。教程要求本地站点的固定链接选择“朴素”以外的结构,以便解析这里使用的 REST 路径。

在本地开发站点为测试用户创建应用程序密码,然后用终端检查注册结果。以下全部是教学占位值,替换为你自己本地开发环境的信息:

curl -u 'USERNAME:APPLICATION_PASSWORD' https://yoursite.local/wp-json/wp-abilities/v1/abilities | jq

仓库版本的命令使用 jq 格式化 JSON,因此还需要安装 jq;未安装时可去掉末尾的 | jq。响应应包含 WordPress 核心 Ability,以及三个 wp-ai-workshop-demo/* Ability。此时尚未实现内部业务逻辑,但应该已经能够发现带有 schema 的端点。这里是教程的预期检查结果,并非本稿的实测输出。

第四步:描述图片,发起第一次 AI 调用

接下来实现各个 Ability 的执行回调,与 AI 服务商连接。首先更新 includes/vision.php 中的 wp_ai_workshop_demo_describe_image(),它负责执行 wp-ai-workshop-demo/describe-image:

$image_url = $arguments['image_url'];
// Vision-capable providers (e.g. Anthropic) require the image to be sent
// inline (base64), not as a remote URL, so fetch it and build a data URI.
// A data URI is also accepted by URL-friendly providers such as OpenAI,
// which keeps this ability portable across providers.
$data_uri = wp_ai_workshop_demo_image_url_to_data_uri( $image_url );
if ( is_wp_error( $data_uri ) ) {
	return $data_uri;
}
$prompt  = 'Describe this image in detail for someone who cannot see it. ';
$prompt .= 'Focus on the main subject, the setting, the mood, notable colours, ';
$prompt .= 'and any details that would help someone write an engaging blog post about it.';

$description = wp_ai_client_prompt()
	->with_text( $prompt )
	->with_file( $data_uri )
	->generate_text();

if ( is_wp_error( $description ) ) {
	return $description;
}

return array(
	'description' => trim( $description ),
);

wp_ai_client_prompt() 提供链式构建接口:添加提示词、附加文件,再请求生成文字。业务代码使用同一套 AI Client 调用接口;具体模型是否支持图片,以及服务商适配器是否可用,仍需按照实际配置确认。

还要添加 wp_ai_workshop_demo_image_url_to_data_uri() 辅助函数。它使用 wp_remote_get() 获取远程图片,再进行 base64 编码,将图片转成供模型读取的 data URI。开发时通常把辅助函数放入独立文件;为方便跟随教程,这里可将它放在对应文件的末尾。

/**
 * Fetch a remote image and return it as a base64-encoded data URI.
 *
 * @param string $image_url The URL of the image to fetch.
 * @return string|WP_Error The data URI on success, or WP_Error on failure.
 */
function wp_ai_workshop_demo_image_url_to_data_uri( $image_url ) {
	$response = wp_remote_get(
		$image_url,
		array(
			'timeout'    => 30,
			'user-agent' => 'WP-AI-Workshop-Demo/1.0; ' . home_url( '/' ),
		)
	);
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	$code = (int) wp_remote_retrieve_response_code( $response );
	if ( 200 !== $code ) {
		return new WP_Error(
			'image_fetch_failed',
			sprintf( 'Could not fetch the image (HTTP %d).', $code )
		);
	}

	$body = wp_remote_retrieve_body( $response );
	if ( '' === $body ) {
		return new WP_Error( 'image_fetch_empty', 'The fetched image was empty.' );
	}
	$mime = wp_remote_retrieve_header( $response, 'content-type' );
	if ( ! is_string( $mime ) || 0 !== strpos( $mime, 'image/' ) ) {
		$filetype = wp_check_filetype( $image_url );
		$mime     = ! empty( $filetype['type'] ) ? $filetype['type'] : 'image/jpeg';
	}

	return 'data:' . $mime . ';base64,' . base64_encode( $body );
}

有两点需要注意:

  • wp_ai_client_prompt()->generate_text() 失败时,WordPress 包装层会返回 WP_Error。因此示例通过 is_wp_error() 检查错误。
  • 原教程针对 Anthropic 等视觉服务商,将图片作为 base64 内联数据发送;这种方式也能供接受 data URI 的服务商使用,减少直接传远程图片 URL 带来的适配差异。

第五步:生成文章标题与正文

在 includes/content.php 中更新 wp_ai_workshop_demo_generate_post_from_description()。它发起第二次 AI 调用,把图片描述转为标题和正文。提示词要求模型返回包含固定字段的 JSON 对象,方便提取结果:

$description = $arguments['description'];
$guidance    = ! empty( $arguments['prompt'] ) ? trim( $arguments['prompt'] ) : '';
$prompt = 'Write an engaging WordPress blog post inspired by the following image description.';
if ( '' !== $guidance ) {
	$prompt .= ' Tone, angle, or extra guidance: ' . $guidance . '.';
}
$prompt .= ' Image description: ' . $description;
$prompt .= ' Respond with a single JSON object containing exactly two keys: ';
$prompt .= '"title" (a short, compelling post title as plain text) and ';
$prompt .= '"content" (the full post body using valid WordPress Block Editor markup). ';
$prompt .= 'Do not wrap the response in markdown code fences.';
$text = wp_ai_client_prompt( $prompt )->generate_text();

if ( is_wp_error( $text ) ) {
	return $text;
}

$data = wp_ai_workshop_demo_decode_json_response( $text );

if ( ! is_array( $data ) || empty( $data['title'] ) || empty( $data['content'] ) ) {
	return new WP_Error(
		'post_copy_parse_failed',
		'The AI response could not be parsed into a title and content.'
	);
}

return array(
	'title'   => trim( $data['title'] ),
	'content' => trim( $data['content'] ),
);

再添加 wp_ai_workshop_demo_decode_json_response()。即使提示词明确要求不添加 Markdown 代码围栏,模型仍可能添加,因此辅助函数先去除围栏,再尝试解析 JSON:

/**
 * Decode a JSON object from an AI text response, tolerating markdown fences.
 *
 * @param string $text The raw text returned by the AI.
 * @return array|null Decoded associative array, or null if no JSON could be parsed.
 */
function wp_ai_workshop_demo_decode_json_response( $text ) {
	$text = trim( $text );
	$text = preg_replace( '/^\s*```(?:[a-z0-9_-]+)?\s*/i', '', $text );
	$text = preg_replace( '/\s*```\s*$/i', '', $text );
	$text = trim( $text );

	$data = json_decode( $text, true );
	if ( is_array( $data ) ) {
		return $data;
	}

	if ( preg_match( '/\{.*\}/s', $text, $matches ) ) {
		$data = json_decode( $matches[0], true );
		if ( is_array( $data ) ) {
			return $data;
		}
	}

	return null;
}

模型生成输出时,并非始终遵守同一套固定规则。即使使用相同输入,多次请求也可能得到不同响应。这里的防御性解析可以处理部分格式偏差,并在解析失败时返回错误;它并不能保证每次都得到语义正确的文章。

第六步:组合 Ability

核心编排函数是 includes/post.php 中的 wp_ai_workshop_demo_create_post_from_photo()。它调用前面两个 Ability:

$image_url = $arguments['image_url'];
$prompt    = isset( $arguments['prompt'] ) ? $arguments['prompt'] : '';
// Step 1: Describe the image (ability composition).
$describe_ability = wp_get_ability( 'wp-ai-workshop-demo/describe-image' );
if ( ! $describe_ability instanceof WP_Ability ) {
	return array( 'message' => 'Post creation failed: the describe-image ability is not available.' );
}
$description_result = $describe_ability->execute( array( 'image_url' => $image_url ) );
if ( is_wp_error( $description_result ) ) {
	return array( 'message' => 'Post creation failed while describing the image: ' . $description_result->get_error_message() );
}
// Step 2: Generate the post title and content (ability composition).
$generate_ability = wp_get_ability( 'wp-ai-workshop-demo/generate-post-from-description' );
if ( ! $generate_ability instanceof WP_Ability ) {
	return array( 'message' => 'Post creation failed: the generate-post-from-description ability is not available.' );
}
$copy_result = $generate_ability->execute(
	array(
		'description' => $description_result['description'],
		'prompt'      => $prompt,
	)
);
if ( is_wp_error( $copy_result ) ) {
	return array( 'message' => 'Post creation failed while writing the post: ' . $copy_result->get_error_message() );
}
// Step 3: Create the draft post and set the featured image.
return wp_ai_workshop_demo_create_post(
	$copy_result['title'],
	$copy_result['content'],
	$image_url
);

wp_get_ability() 按名称取得 Ability,->execute() 执行它。编排函数不直接调用 AI Client,而是组合两个已经完成各自工作的 Ability,再把结果交给 wp_ai_workshop_demo_create_post()。后者使用 wp_insert_post() 创建草稿,并通过 media_sideload_image() 获取特色图片。添加如下辅助函数:

/**
 * Create a draft post and set its featured image from an image URL.
 *
 * @param string $title The Post title.
 * @param string $content The Post content.
 * @param string $image_url The external image url.
 */
function wp_ai_workshop_demo_create_post( $title, $content, $image_url ) {
	$post_id = wp_insert_post(
		array(
			'post_title'   => sanitize_text_field( $title ),
			'post_content' => $content,
			'post_status'  => 'draft',
			'post_type'    => 'post',
		)
	);
	if ( is_wp_error( $post_id ) || 0 === $post_id ) {
		return array( 'message' => 'Post creation failed.' );
	}

	$attachment_id = wp_ai_workshop_demo_set_featured_image_from_url( $post_id, $image_url );
	if ( is_wp_error( $attachment_id ) ) {
		return array(
			'message' => 'Post created, but the featured image could not be added: ' . $attachment_id->get_error_message(),
			'post_id' => $post_id,
		);
	}
	return array(
		'message' => 'Post created successfully with featured image.',
		'post_id' => $post_id,
	);
}
/**
 * Sideload an image from a URL into the media library and set it as the
 * featured image for a post.
 *
 * @param string $post_id ID of the Post.
 * @param string $image_url URL of the external image.
 */
function wp_ai_workshop_demo_set_featured_image_from_url( $post_id, $image_url ) {
	// Verify that the image_url is a valid url, just to be safe.
	if ( ! wp_http_validate_url( $image_url ) ) {
		return new WP_Error( 'invalid_url', 'Image URL failed validation.' );
	}
	// The media_sideload_image() function is typically only used in the admin context
	// This ensures that it can be used outside the admin context, such as in a REST API request or MCP tool.
	if ( ! function_exists( 'media_sideload_image' ) ) {
		require_once ABSPATH . 'wp-admin/includes/media.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/image.php';
	}
	$attachment_id = media_sideload_image( $image_url, $post_id, null, 'id' );
	if ( is_wp_error( $attachment_id ) ) {
		return $attachment_id;
	}

	set_post_thumbnail( $post_id, $attachment_id );

	return $attachment_id;
}

一旦功能表示为 Ability,你和其他扩展开发者就可以组合现有 Ability,实现新的流程。这与 WordPress 钩子和过滤器的组合思路相通。

第七步:更新后台界面

核心功能已经完成,还要把起始插件自带的设置页面连接到这些 Ability。先在 includes/admin.php 中加载核心 Ability 脚本模块:

wp_enqueue_script_module( '@wordpress/core-abilities' );

@wordpress/core-abilities 会提供在 JavaScript 中发现并执行 PHP 注册 Ability 所需的模块。然后将插件脚本改为脚本模块,并加入对它的依赖:

wp_enqueue_script_module(
	'wp-ai-workshop-demo-script',
	plugins_url( 'build/index.js', __DIR__ ),
	array( '@wordpress/core-abilities' ),
	$asset_file['version'],
);

接下来打开 src/components/settings-page.jsx。从 @wordpress/core-abilities 取得并等待 ready,确保 Ability 的 REST 端点已经加载;再从 @wordpress/abilities 取得 getAbility 和 executeAbility,同时定义所调用的 Ability 名称:

const { ready } = await import( /* webpackIgnore: true */ '@wordpress/core-abilities' );
await ready;
const { getAbility, executeAbility } = await import( /* webpackIgnore: true */ '@wordpress/abilities' );

const ABILITY = 'wp-ai-workshop-demo/create-post-from-photo';

请保留 /* webpackIgnore: true */。它告诉 wp-scripts 在构建时不要打包这个导入,因为 WordPress 会在运行时提供模块。原教程链接了相关 ready 更新和 wp-scripts 修正提案;后续版本的构建行为应以对应版本为准。

在 generateFromInput() 中,通过 Ability JavaScript API 执行 create-post-from-photo:

       const ability = getAbility( ABILITY );
        if ( ! ability ) {
            updateNotice( __( 'Whoops, the create-post-from-photo Ability was not found.', 'wp-ai-workshop-demo' ), 'error' );
            return;
        }

        setIsBusy( true );
        updateNotice( __( 'Looking at your image and writing a post… this can take a moment.', 'wp-ai-workshop-demo' ), 'info' );
        try {
            const result = await executeAbility( ABILITY, {
                image_url: input.image_url,
                prompt: input.prompt,
            } );
            if ( result && result.post_id ) {
                const editUrl = `post.php?post=${ result.post_id }&action=edit`;
                updateNotice(
                    <span>
                        { ( result.message || __( 'Post created.', 'wp-ai-workshop-demo' ) ) + ' ' }
                        <a href={ editUrl }>{ __( 'Edit the draft post.', 'wp-ai-workshop-demo' ) }</a>
                    </span>,
                    'success'
                );
            } else {
                updateNotice( result && result.message ? result.message : __( 'Done.', 'wp-ai-workshop-demo' ), 'error' );
            }
        } catch ( err ) {
            updateNotice( __( 'Error during post generation. Check the console for details.', 'wp-ai-workshop-demo' ), 'error' );
            console.error( err );
        } finally {
            setIsBusy( false );
        }

它与 PHP 端的步骤一致:按名称取得 Ability,确认存在,再传入所需参数执行。业务功能可以由 PHP 或 JavaScript 的调用入口复用。

在插件目录执行 npm run build 重建资源。然后打开“工具 → WP AI Workshop Demo”,粘贴图片 URL,点击 Generate Post。前面通过 curl 检查的同一个 Ability,现在由后台界面调用。

一个实际调整:请求超时

Photo to Post 连续执行两次 AI 请求,然后导入图片,单个处理流程可能超过默认 HTTP 请求超时。AI Client 提供了调整默认请求超时的过滤器。在 includes/ai-client.php 中添加回调:

function wp_ai_workshop_demo_set_request_timeout() {
	return 120;
}

然后在主插件文件中接入过滤器:

add_filter( 'wp_ai_client_default_request_timeout', 'wp_ai_workshop_demo_set_request_timeout' );

这里将 AI Client 默认请求超时设置为 120 秒。实际执行还受到 PHP、Web 服务器及服务商各自的限制,教程中的这个数值不代表整个流程必然能完成。

第八步:通过 MCP 交给 AI 代理

Ability 已可通过 REST 和后台界面调用;添加 MCP 元数据后,还可以供桌面应用中的 AI 代理发现。在 wp-ai-workshop-demo/create-post-from-photo 的注册参数中更新 meta:

'meta' => array(
	'show_in_rest' => true,
	'mcp'          => array(
		'public' => true, // Expose this ability via MCP.
	),
),

在本地开发站点下载、安装并启用 MCP Adapter 插件,它会提供默认 MCP 服务。然后用前面创建的本地应用程序密码配置代理。下面是原教程给出的 Claude 桌面配置示例,路径为 ~/.config/Claude/claude_desktop_config.json;用户名、密码和站点地址均为占位值:

{
  "mcpServers": {
    "wordpress-ai-demo": {
      "command": "npx",
      "args": [ "-y", "@automattic/mcp-wordpress-remote@latest" ],
      "env": {
        "WP_API_URL": "https://yoursite.local/wp-json/mcp/mcp-adapter-default-server",
        "WP_API_USERNAME": "your-username",
        "WP_API_PASSWORD": "your-application-password"
      }
    }
  }
}

如果使用 VS Code,相应 MCP 配置块名为 servers,而不是 mcpServers。具体配置文件位置还应以所用客户端及版本为准。

在支持 MCP 的客户端中提供图片 URL,并要求“根据这张图片创建文章”,客户端即可尝试发现并调用 create-post-from-photo。同一个 Ability 由 REST、后台界面和 AI 代理三个入口使用,插件不必为每个入口重写业务实现。

收尾

在 WordPress Core AI 组件上构建功能时,可以先用 Ability 描述插件能做什么,让 AI Client 处理服务商适配,再将小 Ability 组合成较大的功能。REST、后台 JavaScript 界面和 MCP 代理便能够使用这些已经描述好的功能。

完成项目或遇到问题后,可以到原文评论区与作者交流,分享你的第一个 AI 驱动插件。

代码核验补充

上述 22 个代码块保留了官方仓库教程的代码文本。本稿只完成静态核对,没有安装插件、调用付费模型、创建凭据或执行示例。原代码属于工作坊演示:远程图片下载未设置体积上限;视觉辅助函数中的 wp_remote_get() 也没有先执行与后续特色图辅助函数相同的 URL 校验。接受不可信图片地址前,需要另外审查远程请求、访问目标及资源限制。生成文章内容也应由用户审阅后再发布。

JSON 辅助函数只检查能否解析以及标题、正文是否非空,没有完整验证字段类型和区块内容;图片导入失败时,代码会返回已经创建的草稿 ID。这些分支均保留原实现,没有把示例改成经过生产验证的插件。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容