Generating Content

Generating Text with wp_ai_client_prompt()

⏱ 16 min

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.

What you'll learn in this lesson
System instructions
using_system_instruction(), for behavior that should hold across every call, not just one prompt.
Temperature and token limits
using_temperature() and using_max_tokens(), and when to change each from their defaults.
Building a prompt from multiple parts
with_text() and with_file(), for prompts assembled from more than one source.
Structured, predictable output
as_json_response() for output your plugin code can parse reliably, not just display.
Prerequisites

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.

Set a max token limit even when you don't think you need one

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.

A system instruction isn't a security boundary

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

← Managing API Keys Across Multiple Providers Generating Embeddings →