Course 1 showed wp_ai_client_prompt( $text )->generate_text() as a single line, enough
to prove the SDK works. Real plugin code needs more than a bare string, it needs a
system-level instruction that stays consistent across many calls, control over how
predictable or creative the output is, a hard ceiling on cost and length, and often a
structured response rather than free-flowing prose. This lesson covers the rest of the
WP_AI_Client_Prompt_Builder surface and builds a realistic, structured example: a
product description generator that takes bullet-point specs and returns clean,
consistent output.
Lessons 1 and 2: a provider plugin installed, a real API key configured, and an
understanding of where that key lives. Course 1’s PHP AI Client SDK overview lesson is
assumed background, this lesson doesn’t re-explain what wp_ai_client_prompt() is.
Step 1: A system instruction for consistent behavior
using_system_instruction() sets standing context that applies to the whole request,
separate from the specific prompt text. It’s the right place for anything that should
be true on every call your plugin makes, not just this one:
// File: my-plugin/class-product-copy-generator.php
$response = wp_ai_client_prompt()
->with_text( 'Write a product description for: 100% cotton, machine washable, available in navy and charcoal, unisex fit.' )
->using_system_instruction(
'You are writing product descriptions for a small e-commerce store. ' .
'Keep descriptions to two short paragraphs, plain language, no exclamation ' .
'points, and never invent a specification that was not provided.'
)
->generate_text();
Note the builder call starts with no argument, wp_ai_client_prompt(), and the prompt
text is added separately with with_text(). Both wp_ai_client_prompt( $text ) (prompt
text passed directly) and wp_ai_client_prompt()->with_text( $text ) are valid, the
separate with_text() call matters once you’re assembling a prompt from more than one
piece, which is exactly what Step 3 does.
The “never invent a specification” line matters for a plugin like this specifically because e-commerce copy that fabricates a material or a feature that isn’t real is a real liability, not just an inconvenience. A system instruction is the natural home for that kind of standing guardrail, since it applies uniformly rather than needing to be repeated in every individual prompt.
Step 2: Controlling temperature and length
using_temperature() controls how deterministic versus varied the output is, lower
values produce more predictable, repeatable phrasing, higher values produce more
varied, “creative” phrasing. using_max_tokens() caps how long a response can get,
both for cost control and to stop a runaway response from breaking a layout that
expects a short string:
// File: my-plugin/class-product-copy-generator.php
$response = wp_ai_client_prompt()
->with_text( $bullet_points )
->using_system_instruction( $system_instruction )
->using_temperature( 0.4 )
->using_max_tokens( 300 )
->generate_text();
A temperature around 0.2 to 0.4 suits factual, consistent copy like this product description generator, where you want the same bullet points to produce similar output each time. Reserve higher values, 0.7 and up, for tasks where variety is the point, brainstorming headline options, for instance, where you’d rather get five distinctly different attempts than five near-identical ones.
Without an explicit using_max_tokens() call, a model can occasionally produce a far
longer response than a short product blurb needs, costing more and potentially
overflowing whatever UI displays it. Treat the limit as a defensive default, not an
optional tuning knob.
Step 3: Assembling a prompt from more than one part
Real prompts are often built from more than a single string, existing post content plus
an instruction, or a file plus a question about it. with_text() can be called more
than once to build up a prompt in pieces, and with_file() attaches file content
directly:
// File: my-plugin/class-product-copy-generator.php
$builder = wp_ai_client_prompt()
->using_system_instruction( $system_instruction )
->using_temperature( 0.4 )
->using_max_tokens( 300 );
$builder->with_text( 'Product specifications:' );
$builder->with_text( $bullet_points );
if ( $reference_image_url ) {
$builder->with_file( $reference_image_url );
$builder->with_text( 'Use the attached image only for tone, not as a source of specifications.' );
}
$response = $builder->generate_text();
Building the prompt across several statements like this, rather than one long concatenated string, keeps each piece of context legible in your own code, which matters once a prompt has grown past two or three lines and someone else (or you, in six months) needs to change one part of it without re-reading the whole thing.
Step 4: Structured output your code can actually parse
Free text is fine for display, but a plugin feature that needs to store a title and a
description as separate fields, or feed the result into another function, shouldn’t be
regex-ing a paragraph apart. as_json_response() asks the model to return output
matching a schema you supply, rather than open-ended prose:
// File: my-plugin/class-product-copy-generator.php
$schema = array(
'type' => 'object',
'properties' => array(
'title' => array( 'type' => 'string' ),
'description' => array( 'type' => 'string' ),
),
'required' => array( 'title', 'description' ),
);
$raw = wp_ai_client_prompt()
->with_text( $bullet_points )
->using_system_instruction( $system_instruction )
->as_json_response( $schema )
->generate_text();
$data = json_decode( $raw, true );
if ( is_array( $data ) && isset( $data['title'], $data['description'] ) ) {
update_post_meta( $product_id, '_ai_generated_title', $data['title'] );
update_post_meta( $product_id, '_ai_generated_description', $data['description'] );
}
Even with a schema requested, treat the response as untrusted input, json_decode()
can still return null if the model’s output doesn’t validate cleanly, always check
the decode succeeded and the expected keys exist before writing anything to the
database. Lesson 5 goes further into everything else that can go wrong on this call.
using_system_instruction() shapes behavior, it doesn’t enforce it the way a
permission check does. Don’t rely on a system instruction alone to prevent a model from
ever producing something you don’t want displayed on your site, validate and sanitize
whatever comes back the same way you would any other external input, because that’s
exactly what it is.
Test it: run the structured example
wp-env run cli wp eval '
$schema = array(
"type" => "object",
"properties" => array(
"title" => array( "type" => "string" ),
"description" => array( "type" => "string" ),
),
"required" => array( "title", "description" ),
);
$raw = wp_ai_client_prompt()
->with_text( "100% cotton crewneck sweater, machine washable, navy and charcoal, unisex fit." )
->using_system_instruction( "Write concise e-commerce product copy. No exclamation points." )
->as_json_response( $schema )
->generate_text();
echo $raw;
'
You should see a JSON object with title and description keys, something you can
pass straight to json_decode().
Recap
Beyond the single-line example from Course 1, wp_ai_client_prompt()’s builder gives
you using_system_instruction() for standing behavior, using_temperature() and
using_max_tokens() for predictability and cost control, with_text() and
with_file() for prompts built from multiple sources, and as_json_response() for
output your own PHP can parse rather than just display. Every one of these chains onto
the same builder object Course 1 introduced, none of it requires a different entry
point.
Resources & further reading
- PHP AI Client SDK repository, GitHub
- Introducing the AI Client in WordPress 7.0, make.wordpress.org
- Introducing the WordPress AI Client SDK, make.wordpress.org