Every example so far has kept prompt wording as a PHP string literal inside a method,
exactly the pattern Lesson 1 recommended, one place, not copy-pasted. This lesson takes
that a step further: moving the wording out of PHP entirely and into a versioned
wp_options entry, so the exact phrasing of a system instruction or a few-shot example
can be adjusted without a code deploy, and so you have a real place to track what
changed and when.
Lesson 1’s system instruction patterns. This lesson assumes you already have a working prompt and are looking to change where its wording lives, not what the wording says.
Step 1: Why a hardcoded string becomes a real problem
A prompt string as a PHP constant works fine until someone who isn’t a developer, a
content lead, a support manager, needs the tone adjusted, “less formal,” “shorter
replies,” “always mention the refund policy.” Right now that means editing PHP and
shipping a release. Storing the template in wp_options instead means the exact same
change is a database update, something an admin screen, or even a direct wp option update call, can make without touching code at all.
Step 2: A versioned template shape
Store more than a bare string, wrap it with a version number so you can tell later whether an option holds your current default or an older, possibly customized one:
// File: my-plugin/class-prompt-templates.php
define( 'MY_PLUGIN_SUPPORT_TEMPLATE_VERSION', 2 );
function get_default_support_template(): array {
return array(
'version' => MY_PLUGIN_SUPPORT_TEMPLATE_VERSION,
'template' =>
'You are drafting support replies for a WordPress plugin support desk. ' .
'Never promise a specific release date. Never confirm a bug exists ' .
'before it has been reproduced. Do not use exclamation points. ' .
'Reply in two short paragraphs: the first acknowledges the issue, the ' .
'second states the next concrete step.',
);
}
Step 3: Registering the default and reading it at runtime
Register the default once, on activation, so the feature has working wording from the first request, then read it wherever the prompt is actually used:
// File: my-plugin/my-plugin.php
register_activation_hook( __FILE__, function () {
add_option(
'my_plugin_support_reply_template',
get_default_support_template(),
'',
false // autoload: false, this is only read when a support reply is drafted.
);
} );
// File: my-plugin/class-support-reply-drafter.php
public function draft_reply( string $ticket_body ): string {
$stored = get_option( 'my_plugin_support_reply_template' );
$system_instruction = is_array( $stored ) && ! empty( $stored['template'] )
? $stored['template']
: get_default_support_template()['template'];
return wp_ai_client_prompt()
->with_text( $ticket_body )
->using_system_instruction( $system_instruction )
->using_temperature( 0.3 )
->using_max_tokens( 220 )
->generate_text();
}
Falling back to get_default_support_template() if the option is somehow missing or
malformed means a corrupted or manually cleared option degrades to a working default
rather than an empty, useless system instruction.
add_option()’s fourth argument controls whether an option loads on every single page
request via alloptions. A prompt template is only ever read when an AI feature
actually runs, so it doesn’t belong in that autoloaded set, passing false keeps it out
of the cache WordPress builds for every request.
Step 4: Migrating when your default template changes
When you ship a new default wording in a plugin update, you don’t want to silently overwrite a site’s already-customized template, but you also want a fresh install, or a site that never touched the setting, to get the improvement. Comparing the stored version number against your current one is the standard pattern:
// File: my-plugin/class-prompt-templates.php
add_action( 'plugins_loaded', function () {
$stored = get_option( 'my_plugin_support_reply_template' );
if ( ! is_array( $stored ) ) {
update_option( 'my_plugin_support_reply_template', get_default_support_template(), false );
return;
}
$default = get_default_support_template();
// Only replace the template if the site never customized it, and a newer
// default is available.
if (
( $stored['version'] ?? 0 ) < $default['version']
&& empty( $stored['customized'] )
) {
update_option( 'my_plugin_support_reply_template', $default, false );
}
} );
Setting a customized flag to true whenever an admin screen saves an edited template
is what makes this migration safe, it only ever overwrites a template nobody has touched
yet, never one a site owner deliberately changed.
If your template format uses a token syntax for substitution, {{post_title}} for
instance, and you interpolate raw, user-controlled content into the same string before
substitution, a title that happens to contain literal curly braces can collide with your
own placeholder syntax. Substitute placeholders first, working only with your own
template string, then append or insert user content as separate with_text() calls
rather than string-concatenating it into the template itself.
Test it: change wording without touching PHP
wp-env run cli wp option get my_plugin_support_reply_template --format=json
wp-env run cli wp eval '
$current = get_option( "my_plugin_support_reply_template" );
$current["template"] .= " Sign off every reply with the agent name Alex.";
$current["customized"] = true;
update_option( "my_plugin_support_reply_template", $current, false );
'
Run your support reply feature again with no code changes. The output should now consistently include the sign-off you just added purely through the stored option, no deploy required.
Recap
Prompt wording that lives only as a PHP string requires a code release to change even a
single word. Storing it instead as a versioned wp_options entry, registered with a
sensible default on activation, read with a safe fallback, and migrated carefully so a
site’s own customizations are never silently overwritten, turns prompt wording into
something editable on its own, separate from your plugin’s release cycle.
Resources & further reading
- add_option() function reference, developer.wordpress.org
- update_option() function reference, developer.wordpress.org
- PHP AI Client SDK repository, GitHub