The Content Classification experiment adds AI-powered tag and category suggestions to the WordPress post editor. It analyzes post content and suggests relevant taxonomy terms directly within the Tags and Categories sidebar panels. The experiment registers a WordPress Ability (ai/content-classification) that can be used both through the admin UI and directly via REST API requests.
When enabled, the Content Classification experiment adds "Suggest Tags" and "Suggest Categories" buttons to their respective panels in the post editor sidebar. Users can click these buttons to generate a list of AI-suggested terms based on the current post content. Suggestions appear as clickable pills that can be accepted (adding the term to the post) or dismissed.
Key Features:
- One-click tag and category suggestions from post content
- Suggestions shown as clickable pills within the existing taxonomy panels
- New terms are visually distinguished with a "new" badge
- Support for parent/child category relationships
- Configurable strategy: suggest only existing terms or allow new ones
- Configurable maximum number of suggestions (1-10, default 5)
- Regenerate suggestions for different results
- Requires approximately 150 words of content before enabling
The experiment consists of two main components:
- Experiment Class (
WordPress\AI\Experiments\Content_Classification\Content_Classification): Handles registration, asset enqueuing, settings, and UI integration - Ability Class (
WordPress\AI\Abilities\Content_Classification\Content_Classification): Implements the core suggestion logic via the WordPress Abilities API
The ability can be called directly via REST API, making it useful for automation, bulk processing, or custom integrations.
WordPress\AI\Experiments\Content_Classification\Content_Classification::register()wires everything once the experiment is enabled:wp_abilities_api_init→ registers theai/content-classificationability (includes/Abilities/Content_Classification/Content_Classification.php)admin_enqueue_scripts→ enqueues the React bundle and stylesheet onpost.phpandpost-new.phpscreens for post types that support the editor
-
PHP Side:
enqueue_assets()loadsexperiments/content-classification(src/experiments/content-classification/index.tsx) and localizeswindow.aiContentClassificationDatawith:enabled: Whether the experiment is enabledstrategy: The configured taxonomy strategy (existing_onlyorallow_new)maxSuggestions: The configured maximum number of suggestions
-
React Side:
- The React entry point (
index.tsx) uses theeditor.PostTaxonomyTypefilter viaaddFilterto wrap the native taxonomy selector components SuggestionPanelcomponent renders a generate button and suggestion pills for each supported taxonomyuseContentClassificationhook:- Gets current post ID and content from the editor store
- Checks word count using
@wordpress/wordcount(minimum 150 words) - Calls the ability via
runAbility()when the button is clicked - Manages suggestion state (accept, dismiss, regenerate)
- Adds accepted terms to the post via
editPost()and REST API
- The React entry point (
-
Ability Execution:
- Accepts
content,post_id,taxonomy,strategy, andmax_suggestionsas input - If
post_idis provided, fetches post context usingget_post_context() - Normalizes content using
normalize_content()helper - Fetches all existing terms for the taxonomy to encourage consistency
- Builds a dynamic system instruction with strategy rules, existing terms, and JSON output format
- Sends content to AI client and parses the structured JSON response
- Returns an array of suggestions with term name, confidence score, new/existing flag, and optional parent
- Accepts
The ability accepts the following input parameters:
array(
'content' => array(
'type' => 'string',
'description' => 'Content to generate taxonomy suggestions for.',
),
'post_id' => array(
'type' => 'integer',
'description' => 'Post ID to generate suggestions for. Overrides content if both provided.',
),
'taxonomy' => array(
'type' => 'string',
'default' => 'post_tag',
'description' => 'The taxonomy to generate suggestions for (e.g., post_tag, category).',
),
'strategy' => array(
'type' => 'string',
'default' => 'existing_only',
'description' => 'The suggestion strategy: existing_only or allow_new.',
),
'max_suggestions' => array(
'type' => 'integer',
'default' => 5,
'minimum' => 1,
'maximum' => 10,
'description' => 'Maximum number of suggestions to generate.',
),
)The ability returns a structured object:
array(
'suggestions' => array(
array(
'term' => 'machine learning', // string - the suggested term name
'confidence' => 0.95, // float - relevance score (0-1)
'is_new' => true, // bool - whether term exists on site
'parent' => 'technology', // string - optional parent for categories
),
// ... more suggestions
),
)The ability checks permissions based on the input:
-
If
post_idis provided:- Verifies the post exists
- Checks
current_user_can( 'edit_post', $post_id ) - Ensures the post type has
show_in_restenabled
-
If
post_idis not provided:- Checks
current_user_can( 'edit_posts' )
- Checks
The experiment registers two settings on the AI Experiments settings page:
-
Taxonomy strategy (
wpai_experiment_content-classification_field_strategy):existing_only(default) — Only suggest terms that already exist on the siteallow_new— Allow suggestions for new terms based on content
-
Maximum suggestions (
wpai_experiment_content-classification_field_max_suggestions):- Integer between 1 and 10, default 5
POST /wp-json/wp-abilities/v1/abilities/ai/content-classification/run
You can authenticate using either:
- Application Password (Recommended)
- Cookie Authentication with Nonce
See TESTING_REST_API.md for detailed authentication instructions.
curl -X POST "https://yoursite.com/wp-json/wp-abilities/v1/abilities/ai/content-classification/run" \
-u "username:application-password" \
-H "Content-Type: application/json" \
-d '{
"input": {
"post_id": 123,
"taxonomy": "post_tag",
"strategy": "allow_new",
"max_suggestions": 5
}
}'Response:
{
"suggestions": [
{"term": "artificial intelligence", "confidence": 0.95, "is_new": true},
{"term": "machine learning", "confidence": 0.9, "is_new": true},
{"term": "technology", "confidence": 0.85, "is_new": false}
]
}curl -X POST "https://yoursite.com/wp-json/wp-abilities/v1/abilities/ai/content-classification/run" \
-u "username:application-password" \
-H "Content-Type: application/json" \
-d '{
"input": {
"content": "This article discusses the latest advances in renewable energy technology, including solar panel efficiency improvements and wind turbine innovations.",
"taxonomy": "category",
"strategy": "existing_only"
}
}'import apiFetch from '@wordpress/api-fetch';
async function suggestTags( postId, taxonomy = 'post_tag' ) {
try {
const result = await apiFetch( {
path: '/wp-abilities/v1/abilities/ai/content-classification/run',
method: 'POST',
data: {
input: {
post_id: postId,
taxonomy,
strategy: 'allow_new',
max_suggestions: 5,
},
},
} );
return result.suggestions;
} catch ( error ) {
console.error( 'Error generating suggestions:', error );
throw error;
}
}The ability may return the following error codes:
invalid_taxonomy: The specified taxonomy does not existpost_not_found: The provided post ID does not existcontent_not_provided: No content was provided and no valid post ID was foundno_results: The AI client did not return any suggestionsinvalid_response: The AI response could not be parsed as valid JSONinsufficient_capabilities: The current user does not have permission
Use the wpai_content_classification_content filter to modify the content string before it is sent to the AI model:
add_filter( 'wpai_content_classification_content', function( $content, $taxonomy, $strategy ) {
// Add custom context to improve suggestions.
if ( 'category' === $taxonomy ) {
$content .= "\n\nSite focus: technology and science news.";
}
return $content;
}, 10, 3 );Use the wpai_content_classification_suggestions filter to modify the parsed suggestions before they are returned:
add_filter( 'wpai_content_classification_suggestions', function( $suggestions, $taxonomy, $strategy ) {
// Remove suggestions with low confidence.
return array_filter( $suggestions, function( $s ) {
return $s['confidence'] >= 0.7;
} );
}, 10, 3 );// Override the strategy programmatically.
add_filter( 'wpai_content_classification_strategy', function( $strategy ) {
return 'allow_new';
} );
// Override the max suggestions count.
add_filter( 'wpai_content_classification_max_suggestions', function( $max ) {
return 7;
} );You can filter which AI models are used for suggestion generation:
add_filter( 'wpai_experiments_preferred_models_for_text_generation', function( $models ) {
return array(
array( 'openai', 'gpt-4' ),
array( 'anthropic', 'claude-haiku-4-5' ),
);
} );The normalize_content() helper function processes content before sending it to the AI:
add_filter( 'wpai_experiments_pre_normalize_content', function( $content ) {
// Custom preprocessing.
return $content;
} );
add_filter( 'wpai_experiments_normalize_content', function( $content ) {
// Custom post-processing.
return $content;
} );-
Enable the experiment:
- Go to
Settings > AI Experiments - Toggle Content Classification to enabled
- Configure the taxonomy strategy and max suggestions
- Ensure you have valid AI credentials configured
- Go to
-
Test in the editor:
- Create or edit a post with at least 150 words of content
- Scroll to the Tags or Categories panel in the sidebar
- Click the "Suggest Tags" or "Suggest Categories" button
- Verify suggestions appear as clickable pills
- Click a suggestion to add it to the post
- Click the X on a suggestion to dismiss it
- Click "Regenerate" for new suggestions
- Click "Dismiss all" to clear all suggestions
-
Test panel toggle behavior:
- Close and reopen the Tags/Categories panel
- Verify the button appears correctly after reopening
-
Test with different strategies:
- Set strategy to "Only suggest existing terms" and verify only existing terms are suggested
- Set strategy to "Suggest new terms" and verify new terms appear with "new" badges
-
Test REST API:
- Use curl or Postman to test the REST endpoint
- Verify authentication works
- Test with different input combinations
- Verify error handling for invalid inputs
Tests are located in:
tests/Integration/Includes/Abilities/Content_ClassificationTest.phptests/Integration/Includes/Experiments/Content_Classification/Content_ClassificationTest.php
Run tests with:
npm run test:php- The experiment requires valid AI credentials to be configured
- The experiment only works for post types that support the editor (
post_type_supports( $post_type, 'editor' )) - The experiment does not load for attachment post types
- Users must have
edit_postscapability (oredit_postfor specific posts when using post ID) - Post content must contain approximately 150 words before suggestions can be generated
- The ability uses
get_preferred_models_for_text_generation()to determine which AI models to use - Models are tried in order until one succeeds
- Temperature is set to 0.5 for consistent, relevant results
- Content is normalized before being sent to the AI (HTML stripped, shortcodes removed, etc.)
- The
normalize_content()function handles this processing - Additional context from post metadata (title, categories, tags) is included when using post ID
- All existing terms for the taxonomy are included in the prompt to encourage consistency
- Suggestions are generated in real-time and not cached
- The ability processes one taxonomy per request
- Generated suggestions should be reviewed before publishing
- The experiment requires JavaScript to be enabled in the admin
- The
is_newflag is determined server-side by comparing against existing terms, not from the AI response
- Experiment:
includes/Experiments/Content_Classification/Content_Classification.php - Ability:
includes/Abilities/Content_Classification/Content_Classification.php - React Entry:
src/experiments/content-classification/index.tsx - React Components:
src/experiments/content-classification/components/ - Styles:
src/experiments/content-classification/index.scss - Types:
src/experiments/content-classification/types.ts - Tests:
tests/Integration/Includes/Abilities/Content_ClassificationTest.php - Tests:
tests/Integration/Includes/Experiments/Content_Classification/Content_ClassificationTest.php