Store Projects

Project: Product Description Generator

⏱ 15 min

This is the first project lesson, and it pairs a custom ability with the PHP AI Client SDK from Course 1: given a product ID, gather its real, existing attributes, then use wp_ai_client_prompt() to turn that into a written description. The important design constraint here is that the model only ever writes from data your own PHP already pulled from the product, it never invents specifics.

What you'll learn in this lesson
Gathering grounding data with wc_get_product()
Name, categories, attributes, and existing short description, if any.
Building a prompt that's constrained to real data
Preventing the model from inventing features or specs that don't exist.
Calling wp_ai_client_prompt() from inside an ability
Combining the Course 1 SDK with the Course 1 Abilities API in one execute_callback.
Writing the result back as a draft, not a live update
Keeping a human review step before anything goes live.
Prerequisites

WordPress 7.0+ with at least one AI provider plugin configured (Course 1, Lesson 7). WooCommerce with at least one product that has attributes assigned. No specific WooCommerce version requirement, this lesson doesn’t use the 10.9 abilities directly.

Step 1: Gathering the grounding data

// File: my-plugin.php
function my_plugin_get_product_context( $product_id ) {
	$product = wc_get_product( $product_id );
	if ( ! $product ) {
		return null;
	}

	$attributes = array();
	foreach ( $product->get_attributes() as $attribute ) {
		if ( is_a( $attribute, 'WC_Product_Attribute' ) ) {
			$attributes[ $attribute->get_name() ] = implode( ', ', $attribute->get_options() );
		}
	}

	return array(
		'name'          => $product->get_name(),
		'categories'    => wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) ),
		'attributes'    => $attributes,
		'existing_desc' => $product->get_short_description(),
	);
}

This function is deliberately separate from the ability’s execute_callback, it’s plain grounding-data logic you can unit test or reuse elsewhere.

Step 2: Registering the ability

// File: my-plugin.php
add_action( 'wp_abilities_api_init', function () {
	wp_register_ability(
		'my-plugin/generate-product-description',
		array(
			'label'         => __( 'Generate product description', 'my-plugin' ),
			'description'   => __( 'Generates a draft product description from a product\'s existing name, category, and attributes. Does not publish it, returns a draft for review.', 'my-plugin' ),
			'category'      => 'store-inventory',
			'input_schema'  => array(
				'type'       => 'object',
				'properties' => array(
					'product_id' => array( 'type' => 'integer' ),
				),
				'required'   => array( 'product_id' ),
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'draft_description' => array( 'type' => 'string' ),
				),
			),
			'permission_callback' => function () {
				return current_user_can( 'edit_products' );
			},
			'execute_callback' => function ( $input ) {
				$context = my_plugin_get_product_context( $input['product_id'] );
				if ( null === $context ) {
					return array( 'draft_description' => '' );
				}

				$prompt = sprintf(
					"Write a two-sentence WooCommerce product description.\n" .
					"Use ONLY the facts below, do not invent features, materials, or specs not listed.\n" .
					"Product name: %s\nCategories: %s\nAttributes: %s\n" .
					"Existing description (may be empty): %s",
					$context['name'],
					implode( ', ', $context['categories'] ),
					wp_json_encode( $context['attributes'] ),
					$context['existing_desc']
				);

				$draft = wp_ai_client_prompt( $prompt )->generate_text();

				return array( 'draft_description' => trim( $draft ) );
			},
			'meta' => array(
				'annotations' => array( 'readonly' => true ),
				'mcp'         => array( 'public' => false ),
			),
		)
	);
} );

Even though this ability doesn’t modify the database (it returns a draft rather than saving anything), it’s marked readonly in annotations and kept off meta.mcp.public for now, deliberately, until you’ve decided whether external agents should be able to trigger AI generation calls (and incur provider costs) on your site directly.

Step 3: Writing the reviewed draft back to the product

Once a human (or your own admin UI) approves a draft, saving it is a separate, explicit step, not something the generation ability does itself:

// File: my-plugin.php
function my_plugin_apply_product_description( $product_id, $description ) {
	$product = wc_get_product( $product_id );
	if ( ! $product ) {
		return false;
	}
	$product->set_short_description( wp_kses_post( $description ) );
	$product->save();
	return true;
}

Keeping generation and saving as two separate functions (and two separate, independently permission-checked steps if you expose both as abilities) means a generated draft can be reviewed, edited, or discarded before anything touches the live product.

Test it

wp-env run cli wp eval '
$ability = wp_get_ability( "my-plugin/generate-product-description" );
$result  = $ability->execute( array( "product_id" => 42 ) );
echo $result["draft_description"];
'

Read the output against the product’s actual attributes. If it mentions anything not present in what you fed it, the prompt needs tightening, not the model.

The model filling gaps with generic marketing language

Even with an explicit “use only the facts below” instruction, models sometimes pad thin attribute data with vague, generic phrases (“perfect for any occasion,” “premium quality”). This isn’t hallucinated fact, but it’s not useful copy either. If your product data is sparse, expect to either enrich the input (categories, tags, existing reviews) or treat the output as a first draft that still needs a human pass, not something to auto-publish.

Recap

This ability pairs wc_get_product()-sourced grounding data with wp_ai_client_prompt() to generate description drafts, keeping generation and saving as two separate steps so nothing publishes without review. The pattern of gathering real data first, then prompting only from that data, is one you’ll reuse in every remaining project lesson in this course.

Resources & further reading

← Building a Custom WooCommerce Inventory Ability Project: AI Shopping Assistant →