This post describes how we designed the Automattic / WordPress.com AI Agent “Framework”, how it’s running (in PHP!), and what trade‑offs we made along the way. If there is one point I want to get across, it is that agentic frameworks are dime a dozen, agents are LLMs calling tools in a loop, and absolutely the hardest part about working on agentic frameworks is resisting the urge to overcomplicate things (per the AIKEA effect).
Automattic, like everybody has introduced a suite of AI features to our products. Prepare for a lot of sparkle emoji.
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
- ✨ WordPress.com & Jetpack has Jetpack AI Assistant and Write Brief with AI in the block editor
- ✨ WooCommerce now has AI‑powered code and store management via MCP‑based agents,
- ✨ Telex lets you vibe-code WordPress: turn natural language into fully coded WordPress blocks
- ✨ AI Website Builder helps new users create a complete site by asking a few questions, then automatically generating a starter design, structure, and copy they can edit.
- ✨ Pocket Casts and Day One experiment with AI‑powered media and journaling: Pocket Casts adds AI‑generated episode transcripts so you can search and skim shows, while Day One offers AI journaling tools like entry and multi‑entry summaries plus image generation.
We got a lot more cooking, but you have to wait for the announcements.
Diverse architectures
The architectures of these features follow the gold rush of the AI era: We shipped what was fast, mostly directly on top of the OpenAI API, and that simplicity proved to be an excellent choice. It allowed us to iterate quickly, find what resonated with users, and integrate new APIs.
What proved to be challenging is not these ad hoc implementations, but when we tried to optimize prematurely. Either using frameworks that we didn’t really need or trying to predict the future by laying foundations for the APIs that never materialized. Future-proofing is a fool’s errand.
Regardless of reasons, we ended up with a few different implementations:
- Jetpack AI provided WP-Admin integrations in the block editor. It is running in PHP and reaching out to our main server (WordPress.com) for authorization and secret sauce
- Our support chatbot was a Langchain workflow orchestrator deployed on a Python / Ray cluster, exposed in WordPress.com WP-Admin.
- Our AI Website builder had logic mostly in React hooks which allowed us to iterate rapidly, but extremely hard to keep stable and consistent.
- The new Agentic version of our website builder was a docker image running Claude code SDK for more brains / capabilities.
All these choices were locally optimal, but they didn’t provide a cohesive experience and they didn’t reinforce each other.
Why? The AWS Manifesto
Inside Amazon there is this famous internal “API mandate” memo (often called the AWS manifesto): every team must expose its data and capabilities only through well‑defined service APIs, no direct database calls, no backdoors, and the same interfaces must work both internally and externally.
That painful mandate forced Amazon to transition from ad-hoc locally optimal implementation to composable generic services: and once those services existed, it became “obvious” that you could rent them out to the rest of the world as primitives, which is how AWS appeared as a product.
This is the mental model we’re chasing at Automattic: invest in reusable building blocks (sometimes quite literally) so that improving one piece (payments, search, agents, etc.) automatically makes every product better.
I wrote more about this lens in Composability is the only game in town, and we used the same trick when running Tumblr on WooCommerce instead of inventing a one‑off billing stack.
One architecture to rule them all

My colleagues appreciated the clarity of my explanation with a flattering meme.
PHP can AI
The most controversial decision we made was to lean into our main stack: run the agentic framework in PHP, next to the rest of WordPress.com complex infrastructure which is WordPress on bare metal, spread around 28 data centers.
Before this, our AI landscape looked more or less like this:
- Support chatbot: infrastructure based on LangChain, running in a Python service in a Ray cluster.
- New agentic experiments: an npm‑based Docker container running Claude Code SDK, with Prisma and several Node libraries
- The rest of our universe: a large, battle‑tested PHP monorepo that powers WordPress.com.
This microservices approach “worked”, but it didn’t generate the flywheel you want in a big product:
- Harder to adopt: every new AI feature meant touching a different repo, stack, deploy pipeline and security monitoring.
- More friction for experimentation: the barrier between “I have a cool demo” and “this is live in production” was higher than for “normal” features. In AI demos are easy, but production grade quality is hard.
- Fragmented observability: logs, metrics, and traces lived in different places, making it harder to reason about an end‑to‑end user journey.
By bringing the agentic layer into PHP, we got:
- One deployment story: ship AI features like any other feature.
- Shared abstractions: reuse our existing request lifecycle, auth, permissions, caching, and data access patterns. Our AI workers are powered by every available WPCOM HTTP worker.
- Closer to the product: agents can talk directly to the same domain objects and services the rest of WordPress.com uses.
Is PHP the “coolest” language for AI? No.
Does it matter when most of the “AI” is an HTTP call to a model and the hard part is orchestration, safety, and product integration? Also no.
What we’re actually building: agents, abilities, and context
Working with AI is really like delegating:
| To help People succeed | To help your AI | How Automattic does it |
| You need to empower your team | You provide tools | WP Abilities |
| You need to give them space to act | You put it in agentic loop | class.agent.php |
| You need to give them enough information | You provide prompt + Context | Context hydration |
| You listen to their complaints | Traces | Langfuse |
Or if you really like charts:
graph TB
subgraph "Frontend"
CLIENT[Agenttic Client]
USER[User Input]
FE_TOOLS[WP_Ability<br/>JS]
CONTENT[Post Content]
WPDATA[wp.data]
CLIENT <--> USER
CLIENT <--> FE_TOOLS
CLIENT <--> CONTENT
CLIENT <--> WPDATA
end
subgraph "Backend"
subgraph "Agent"
PROMPT[Prompt]
LOOP[Agentic Loop]
end
CTX[Context]
BE[WP_Ability<br/>PHP]
end
LLM[LLM]
CLIENT <-->|A2A protocol| LOOP
CTX --> PROMPT
PROMPT --> LOOP
LOOP <--> LLM
LOOP <--> BEAbilities
You can think of Abilities as something like Agent Skills for WordPress, with bundled tools.
We introduced it into WordPress Core in 6.9 release and this API design was guided by the needs of the infrastructure you are reading about right now.
Abilities are self-contained, ideally stateless workers that perform a task:
- Like tools, they contain argument schema and callback that actually performs the work
- Like skills, they contain multiple lengths of description designed to work with progressive disclosure pattern
- Permission framework directly tying into WordPress capabilities, making them safe to run in both multi-user and multi-tenant environments (aka Multisite)
Abilities API will be WordPress’ moat in the AI world. Products are easy to vibe code nowadays but the ecosystem of connected, tested and safe tooling for AI agents is not.
Frontend Abilities
Regular abilities run in the PHP environment on top of WordPress, with access to database and all WP API.
That is not enough. After rewriting the editor into WordPress blocks, our users expect interactive and snappy experiences in the editor, and that also includes AI interaction.
Abilities API introduces Generative UI via frontend (JS) tools to interact with current WordPress editor canvas.
In our Agentic Framework:
- If Agent decides to call backend ability, it triggers it and continues the work
- If it decides to call Frontend ability, the API request sends special trigger and ends
- If the ability collects data, it sends it to the agent in a new API request
Some examples of Frontend abilities we use for our AI Website builder include:
- Directly manipulate any block on the currently edited page allowing AI to make targeted content and design edits and previewing them before saving
- Display a component to collect input in the chat session – pick font, color scheme,
- Redirect to a page which we use in our support experience
- Write data to WordPress Data entities, allowing user to preview global website changes before saving them to the database
Agents
Under the hood, LLM agents are just LLMs in a loop calling tools. Yes, I am tired of the hype too.
- In marketing speak, agent implies that it’s gonna do everything for you, work independently and require no supervision.
- In practice, we call everything that has a loop an agent.
And you know what? As models are getting better, both of these are true.

As I mentioned – we started by implementing Agents on top of Claude Code SDK, but that proved challenging to orchestrate at our scale. We replaced it with a 3-component class.agent.php
- It has a list of abilities it can call
- It has a prompt (configured for dynamic context injection)
- It has a loop
Are you ready for a secret Automattic Agentic Loop™️ ?
/**
* Run the agent on the specified backscroll. This will call tools if needed, and loop until the agent is done.
*
* @param Message[] $user_backscroll List of messages representing the conversation history.
* @return Message[]|WP_Error The conversation history with the agent's responses appended, or an error on failure.
*/
public function run( array $user_backscroll ): array|WP_Error {
$max_loops = $this->max_loops;
$this->log_pre_run( $user_backscroll, $max_loops );
$tool_results = array();
do {
--$max_loops;
$response = $this->complete_backscroll( $user_backscroll );
$tool_results = array();
$user_backscroll = array_merge( $user_backscroll, $response );
foreach ( $response as $message ) {
if ( $message instanceof Tool_Call ) {
$this->log_tool_call( $message );
$tool_result = $this->call_tool( $message );
$this->log_tool_call_result( $message, $tool_result );
$user_backscroll[] = $tool_result;
$tool_results[] = $tool_result;
} else {
$this->log_updates_callback( $message );
}
}
} while (
$max_loops > 0
&&
! empty( $user_backscroll )
&&
self::get_last_message( $user_backscroll )->should_resubmit()
);
$this->log_after_run( $user_backscroll );
return $user_backscroll;
}
Where is the reasoning and intelligence?
Ah! Surely you are thinking of our secret thinking ability!
'execute_callback' => function ( array $input ): string {
// The incredible thinking tool works like a rubber duck.
// It forces the LLM to verbalize its thinking and just spits it back out.
return $input['thoughts'];
},
We are still using non-reasoning models for some tasks and when we need to add reasoning, we just add this incredible ability and the output improves. I added it as a joke and it just… works 🤷
Prompt engineering is really context engineering
We discussed tools and agents, but we also have a spin on prompt management. All our prompts run through a home-grown hydration phase where a templating engine that injects dynamic context into prompt templates. This can be:
- User purchases
- Site title
- Shape of buttons on a site
- Preferred image style for image generation
- 100s of other variables
The context gets lazy-loaded at execution time. Every time we encounter [[site.current_site.title]], we retrieve the current title from the database and so on.
That way we can focus on fine-tuning prompts – for example our support chatbot has immutable configurations stored in the database that our support staff keeps tweaking daily.
Orchestration
This interplay between Abilities, Context and Agents lets us orchestrate a few behaviors for the framework:
Frontend abilities are dynamically loaded and stateless.
While initiating the request to the agent, the frontend declares available frontend abilities, and the agent may choose to use them if available. That way, we can dynamically turn them on/off or make the agent adapt to the context its executed in.
We send client context to our context system
When loaded in Gutenberg canvas, Agent gets:
- Current WordPress block tree
- Metadata about current session
- Current URL
This gets loaded into the context system, allowing agent to have full visibility into data on the frontend, despite running in the PHP environment.
Minimizing API roundtrips
Agent by default runs as an HTTP request serving JSON-RPC data stream per A2A protocol. Each user request typically initiates a new server round-trip and:
- Backend (PHP) abilities run, report to agent and continue agent execution
- Frontend abilities:
- Can derive trigger actions on the frontend without stopping the execution when they don’t have return data. For example, we can trigger a “Processing” state on certain Gutenberg blocks asynchronously and continue the agent execution
- Can stop execution, trigger frontend code and automatically trigger a new request with the result. This is the case for navigation for example.
- Can stop execution and wait for user input – like choosing a desired color scheme – and continue execution after.
We keep track of “Dirty entities”
Context system is integrated with wp.data entity framework. When agent is operating on a surface with “pre-save panel”, like Gutenberg canvas:
- Agent changes data using
edit_entityability - No changes are persisted to database until save is confirmed by the user
- The edited entities are marked as “dirty” and their current state is sent back to the Agent on subsequent requests.
That way: - Agent thinks it has made a change on all subsequent requests but the user is yet to confirm it
- Context system acts as if user has made that change allowing model to execute complex multi-step queries and user having full control and preview over individual changes.
When agent is loaded in a non-interactive environment, then the changes are persisted straight to the database if permitted.
Other implementation details
- The agents are exposed to the frontend via a forked version of the A2A protocol. We added:
- Reporting of available frontend tools
- Images and file data parts
- We had to write our own eval framework to work on entire agents, verifying their tool calls in multiple turns. There is a huge gap in market offering, since most eval systems are testing single prompts,
- We introduced extremely lightweight abstraction over AI messages –
Message,Tool_Call,Tool_Call_ResultandInput_Required_Tool_Call_Resultwhich simplified and abstracted provider quirks.
I think WP Core needs something like this too, but it’s a slippery slope and you end up shipping too many assumptions in your framework bits before an AI provider breaks your mental model on next release. - We have a whole package called Agenttic-UI and Agenttic-Client for frontend interaction
- Back in 2023 I introduced a
lib/openaithat routes entire AI traffic on WPCOM (including other providers) and it remains surprisingly useful and durable despite being extremely dated. I recommend centralizing your AI comms so you can have last line of defense for unexpected behavior. - For more guided agentic behaviour, we have a very robust Workflow multi-step implementation similar to Langchain, but native to class.agent.php and the rest of presented framework.
On-demand Framework design
There ain’t no rules around here. We’re trying to accomplish something
Thomas Edison
Capabilities, products, needs and approaches in AI are evolving every week and it is very hard to tell where the next big idea is coming from. To reiterate my point from the beginning: the biggest challenge in AI is staying flexible enough to adapt to the torrent of changes but still provide a framework to quickly ship actual products people use. This entire framework has been built by
- Catering to the next immediate need,
- Boiling it down to the most pragmatic and minimal approach,
- Comparing the need across all product needs
- Shipping implementation as part of product work
- Picking it up in other product teams.
There are no AI architects sitting on their high chairs. There is a bunch of engineers doing messy product work and trying to not reinvent the wheel. Huge kudos to my pragmatic coworkers Emdash, Glen Davies, Stephane Thomas, Chris Blower, Ovidiu Galatan, Aaron Fas, Derek Smart, Gziolo and many, many others!

2 Comments