使用 Abilities API、AI Client 与 MCP Adapter 构建 Photo to Post 插件
Build your first AI-Powered WordPress plugin
WordPress 开发者博客通过 Photo to Post 插件,逐步讲解如何组合 Abilities API、AI Client 和 MCP Adapter。
WordPress 最近发布的两个主要版本都为希望在 WordPress 环境中探索 AI 的开发者带来了重要的新功能:
- Abilities API——一种标准化的功能单元注册方式,使任何组件(REST、区块编辑器、其他开发者或 AI 智能体)都能发现并运行它。
- WordPress AI Client——一个与提供商无关的 PHP 库,用于与任何 AI 模型交互,因此你的代码无需关心当前可用的 AI 提供商。
- MCP Adapter——将 Abilities 暴露为 MCP 工具,使 AI 智能体(桌面端或基于云端)可以使用它们。
到目前为止,你可能已经阅读过关于如何单独使用其中每项技术的多篇教程。我们甚至在 WordPress 开发者博客上逐一介绍过它们:
- 介绍 WordPress Abilities API
- 从 Abilities 到 AI 智能体:介绍 WordPress MCP Adapter
- 如何使用 WordPress AI Client 构建图像生成插件
事实是,尽管单独探索每项技术都很有趣,但只有将它们组合起来,魔力才会真正发生。
如果你一直在关注 Core AI 项目的落地,并想知道这些组成部分如何在一个真实的插件中协同运作,那么这篇文章正适合你。我们将一步步共同构建一个插件,重点介绍如何实现这三个 AI 构建模块。
如果这是你第一次接触其中任何概念,我们建议你先回头阅读上面链接的三篇文章,然后再回到这里。
目录
- 我们要构建什么
- 开始之前
- 将你的站点连接到 AI 提供商
- 构建插件功能
- 第 1 步:注册 Abilities
- 第 2 步:接入 Abilities
- 第 3 步:通过 REST 检查你的工作
- 第 4 步:描述图像(你的首次 AI 调用)
- 第 5 步:生成文章文案
- 第 6 步:组合 Abilities(精彩部分)
- Step 7: Update the Admin Interface
- 一个实用调整:超时
- 第 8 步:交给 AI 智能体(MCP)
- 总结
本教程的内容也曾在 WordCamp Europe 2026 的一场工作坊上进行展示。如果你更习惯通过视频学习,可以在 WordPress.tv 上观看录像。
我们要构建什么
这个插件名为 Photo to Post,理念很简单。你向它提供一张图片的 URL,插件会:
- 使用 AI 视觉模型查看图片并生成一段文字描述。
- 根据该图片描述创建一篇草稿文章——包括标题和正文。
- 更新这篇草稿文章,将原始图片设置为文章特色图片。
不过,有意思的并不是它做什么,而是它如何构建。在底层,Photo to Post 由三个可组合的 WordPress Abilities 组成,它们会调用 WordPress AI Client:
| Ability | 作用 | AI 能力 |
|---|---|---|
describe-image | 图片 URL → 文字描述 | 视觉(图片输入) |
generate-post-from-description | 描述 → 文章标题 + 正文 | 文本生成 |
create-post-from-photo | 协调上述两项能力,然后创建文章 | 这项能力不使用 AI,但会协调其他能力 |
第三项能力是能力组合的一个示例——一项能力通过 WP_Ability::execute() 调用其他能力。与在 WordPress 中构建功能的传统方法相比,这是 Abilities API 更令人兴奋的特性之一。
开始之前
为了运行本教程的代码,你的本地计算机需要具备以下条件:
- 一个运行 WordPress 7.0 或更高版本且使用 PHP 8.1 或更高版本的本地 WordPress 环境
- 已安装最新版本的 Composer 和 Node.js
- 支持视觉模型的 AI 提供商 API 凭据(稍后会详细介绍)
为了让操作更简单(也让本教程更短),你将基于这里提供的入门插件进行开发:https://github.com/wptrainingteam/wp-ai-workshop-demo/releases。该插件已经包含功能集的基础框架,因此你只需构建 AI 功能即可。
下载最新版本的插件(1.1.1),在你的本地 WordPress 开发环境中安装并启用它。
启用后,插件会在工具下新增一个名为WP AI Workshop Demo的管理页面,用于显示管理“照片转文章”流程的表单。

它使用 WordPress DataForm 包呈现表单,但目前未提供任何功能。
由于我们将构建本教程的功能,因此现在安装 composer 和 npm 依赖会很有帮助。在插件目录中运行:
composer install
npm install打开主插件文件 wp-ai-workshop-demo.php,你会看到几处标记为 TODO 的注释,例如第 29 行的这一处:
// TODO: Register the ability category and the three Photo to Post ability hooks.这表示插件中构建功能的不同区域。每当本教程要求你在文件中添加代码时,请查找要替换的特定 TODO。
将你的网站连接到 AI 提供商
要让你 WordPress 网站通过 AI Client 使用生成式 AI,需要将其连接到 AI 提供商。在此案例中,读取照片、生成照片描述以及生成文章内容的流程都使用 AI Client。
从 WordPress 7.0 开始,可以通过新的“连接器”页面完成此操作,该页面位于“设置”菜单中。借助 Connectors API,“连接器”页面为 WordPress 网站管理员提供了一个统一界面,用于存储外部 API 密钥。

此前,任何为 WordPress 添加外部连接的插件,都需要为网站管理员提供一个存储 API 密钥的位置,以启用连接。比较常见的示例包括:
- 用于反垃圾邮件的 Akismet
- 用于为 WordPress 添加 Mailchimp 邮件支持的 Mailchimp for WordPress
- 用于获取 Google Analytics 信息的 Google Site Kit
- WooCommerce 支付网关扩展(例如 Stripe、PayPal、Authorize.net)
插件开发者通常需要自行管理 API 密钥的存储,一般是在单独的“设置”页面中。因此,如果你有 4 个分别存储 API 密钥的插件,就意味着有 4 个不同的地方需要管理。
Connectors API 和“连接器”页面将这些信息集中到一个地方。开发者可以为其插件提供的服务注册一个连接器,该连接器会显示在“连接器”页面上,用户可以在那里输入并存储 API 密钥。
在 AI 提供商的场景中,已有插件支持三家主要的前沿提供商:OpenAI、Anthropic 和 Google。你可以在“连接器”页面中安装所选提供商的插件
安装后,你可以输入并保存 API 密钥,以启用 AI Client 功能。

如果你希望使用 OpenAI、Anthropic 或 Google 之外的 AI 模型,也有适用于其他多个提供商的插件,包括 Ollama、OpenRouter 和 Mistral。请确保你的提供商使用支持视觉的模型,以支持你将在本教程中构建的功能。
构建插件功能
连接 AI 提供商后,你现在可以开始构建所有插件功能。
步骤 1:注册 abilities
Ability 只是一段已注册的描述,用于说明你的插件能够做什么:一个标签、一段描述、一个输入模式、一个输出模式、一次权限检查,以及一个执行实际工作的回调函数。你将在 includes/abilities.php 文件中完成所有 Ability 的注册。
首先,你需要使用 wp_register_ability_category 函数注册一个分类,用于对你的 abilities 进行分组。在 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' ),
)
);接下来,你可以使用 wp_register_ability 函数注册第一个 ability describe-image。请注意,这里的大部分内容都只是对 ability 的描述——输入和输出模式会准确告诉任何使用该 ability 的程序应该传入什么,以及应该返回什么。将以下内容添加到 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 abilities 完全遵循相同的模式——只有模式和回调函数有所不同。
首先是 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,
),
)
);这里需要牢牢记住的关键是 Ability 的结构:标签、描述、模式、权限、回调函数。一旦理解了,你就能轻松注册 abilities。
那么,为什么要如此正式地描述所有内容?label、description、input_schema,和output_schema 让 REST API、其他开发者和 AI 智能体都能理解你的 ability,而无需你编写任何额外代码。你只需描述一次,所有希望使用它的东西都会立即明白它叫什么、做什么、接受哪些输入,以及会返回哪些输出。
第 2 步:连接 abilities
与 WordPress 中的大多数功能一样,Ability 的注册通过专用的操作钩子完成: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' );第 3 步:通过 REST 检查结果
Abilities API 最让我喜欢的一点是——当你使用 'show_in_rest' => true 注册一个 ability 后,它就会向已认证的用户公开在 REST API 中,无需任何额外代码。请确保本地站点的永久链接设置不是“普通”,否则 REST 端点将无法解析。
要测试这一点,请为你的管理员用户创建一个应用程序密码,然后在终端中使用类似 curl 的内容进行测试:
curl -u 'USERNAME:APPLICATION_PASSWORD' https://yoursite.local/wp-json/wp-abilities/v1/abilities你应当看到一个 JSON 响应,其中包含三个核心 WordPress abilities,以及你自己的三个 wp-ai-workshop-demo/* abilities。
把这个节点作为检查点会让人非常安心。你还没有为这些 Abilities 编写任何内部功能,但已经拥有三个可被发现且带有模式描述的端点。
第 4 步:描述图像(你的第一次 AI 调用)
现在进入有趣的部分。你需要实现所有 Ability 执行回调函数的功能,也就是在其中连接并使用 AI 提供商。
从 wp_ai_workshop_demo_describe_image 开始,它会执行 wp-ai-workshop-demo/describe-image Ability。这发生在 includes/vision.php 文件中,你将在这里首次调用 WordPress AI Client。
$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 ),
);看看这次 AI Client 调用有多易读。wp_ai_client_prompt() 为你提供了一个流式构建器:添加提示词文本、附加文件、请求返回文本。由于 AI Client 与提供商无关,因此无论用户配置的是 OpenAI、Anthropic 还是 Google,这段代码都可以直接使用。
你还需要添加 wp_ai_workshop_demo_image_url_to_data_uri() 辅助函数。它使用 wp_remote_get() 获取远程图像,并将其编码为 base64 数据 URI,以便发送给 AI 模型。
提示:通常,开发者喜欢将这类辅助函数放在插件的单独文件中。就本教程而言,你可以直接将所有辅助函数添加到相关文件的底部。
/**
* 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()返回失败时的WP_Error——它不会抛出异常。AI Client 的 WordPress 包装器会为你捕获异常并返回WP_Error。因此始终检查is_wp_error()。- Anthropic 等视觉提供商希望以 base64 内联形式发送图像,而不是远程 URL。这就是为什么你要获取图像并构建 data URI。在某些提供商上发送 URL 可以运行,在另一些提供商会失败——data URI 的通用性最强。
步骤 5:生成帖子文案
下一个要更新的 Ability execute 回调 wp_ai_workshop_demo_generate_post_from_description 位于 includes/content.php 内。该函数会发起第二次 AI 调用,将描述转换为帖子标题和正文。请注意,提示词要求 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() 辅助函数,特意去除模型可能在 JSON 周围包裹的任何 markdown 代码围栏,然后再解码。
/**
* 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;
}你可能会奇怪,既然提示词明确要求不要这样做,为什么还要添加这个。向语言模型请求纯 JSON 时,情况是这样的:它通常会遵守要求,但并非始终如此。AI 模型并不是确定性的,也就是说它们不会遵循固定的规则集,而是识别模式并计算下一个输出的数学概率。因此,向 AI 提出完全相同的问题,每次都可能产生不同的响应。所以,在这里添加防御性检查并解析 AI 的数据,可以确保每次都获得正确的响应类型。
步骤 6:组合 abilities(精彩部分)
现在来看真正有趣的部分。includes/post.php 文件内包含 wp_ai_workshop_demo_create_post_from_photo,它是插件功能的核心。在这个编排器函数中,可以调用另外两个 abilities。
$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;它只是组合两个已经知道如何完成各自工作的 abilities,然后交给一个 wp_ai_workshop_demo_create_post() 辅助函数,该函数调用 wp_insert_post() 创建帖子,并通过 media_sideload_image()helper 加载特色图片。现在正是添加这些辅助函数的好时机。
/**
* 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;
}这是 Abilities 最棒的事情之一。一旦你的功能以 abilities 的形式表达,你和其他扩展该功能的开发者就可以通过组合现有 abilities 来构建新功能——就像 LEGO 积木一样。这与让 hooks 和 filters 变得强大的思维模式相同,只是应用于新一代功能。
步骤 7:更新管理界面
至此,你已经完成了插件的大部分核心功能。现在只需将其提供给站点所有者即可。如前文所述,该插件附带一个设置页面,因此我们需要将其连接到刚注册的 Abilities。
第一步是在 includes/admin.php 中加载 Abilities 脚本模块:
wp_enqueue_script_module( '@wordpress/core-abilities' );@wordpress/core-abilities 加载了查找和执行你在 PHP 中注册的 Abilities 所需的一切 JavaScript 内容。
接下来,你需要更新插件自身的脚本加载方式,将其作为脚本模块加载,并添加对 @wordpress/core-abilities 的依赖:
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/abilities 包中导入 ready promise。这是一项近期更新,可确保 Abilities REST API 端点在任何模块尝试与其交互之前完成加载。接下来,从 @wordpress/abilities 包中导入 getAbility 和 executeAbility 函数。这让你能够获取并执行已注册的 Abilities。同时,你可以创建一个常量,用来保存 create-post-from-photo 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 会在运行时提供它。Gutenberg 中目前有一个用于修复此问题的开放 PR,但它暂时位于 wp-scripts 中;目前正是这条注释让构建顺利通过。
然后,在 generateFromInput 函数内部,使用 Abilities JavaScript API 执行 create-post-from-photo ability。
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,重新构建资源。
接着前往 Tools → WP AI Workshop Demo,粘贴图片 URL,然后点击 Generate Post。你刚才通过 curl 测试的同一个 ability现在正驱动着仪表盘 UI。一次编写,随处使用。
一个实用调整:超时
Photo to Post 会连续进行两次 AI 调用,然后侧载图片,因此单个请求很容易超过 WordPress 默认的 HTTP 超时时间。幸运的是,AI Client 暴露了一个用于增大超时值的过滤器:
首先,将回调函数添加到 includes/ai-client.php 中以更新超时时间
function wp_ai_workshop_demo_set_request_timeout() {
return 120;
}然后,回到主插件文件 wp-ai-workshop-demo.php,像处理 Ability hooks 一样,将该函数连接到过滤器。
add_filter( 'wp_ai_client_default_request_timeout', 'wp_ai_workshop_demo_set_request_timeout' );这类事情往往要等到你第一次在最糟糕的时刻遇到真实请求超时时,才会意识到。
第 8 步:交给 AI 代理(MCP)
这里就有一点神奇了。我们已经有可以通过 REST 和在仪表盘运行的 abilities。只需做一个小改动,它们也可以由桌面应用中的 AI 代理驱动。
在 wp-ai-workshop-demo/create-post-from-photo Ability 注册内部,更新 meta 数组以添加对 MCP 的支持。
'meta' => array(
'show_in_rest' => true,
'mcp' => array(
'public' => true, // Expose this ability via MCP.
),
),现在,在你的站点上下载、安装并激活 MCP Adapter 插件。这会在 WordPress 站点上创建一个默认 MCP 服务器。
使用你之前创建的同一个 Application Password,你可以使用默认的 WordPress MCP 服务器详情来配置 AI 代理。下面是一个示例,说明如何在 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。同一个 ability,三个不同的入口:REST、仪表盘和 AI 代理。我们没有为其中任何一个单独编写代码。
总结
基于 WordPress Core AI 项目进行构建的一大好处是,你无需重新发明任何东西,就能为插件添加 AI 驱动的功能。
你注册 abilities 来描述插件可以执行的操作。你让 AI Client 处理繁杂的提供商特定细节。你将小型 abilities 组合成更大的功能。然后,REST、区块编辑器和 MCP 代理都能免费随之受益。
如果你用它构建了什么——或者遇到了非常棘手的问题——我希望在评论中听到你的分享。这正是将这些东西公开的最精彩之处。现在,去构建你的第一个 AI 驱动插件吧。
感谢 @juanmaguitar 和 @greenshady 协助进行编辑审阅。
来源:WordPress 开发者博客 · https://developer.wordpress.org/news/2026/07/build-your-first-ai-powered-wordpress-plugin/
部分链接为推广/关联链接。