Make WordPress Core

Opened 2 months ago

Last modified 4 weeks ago

#64865 new enhancement

WP AI Client: Add agentic loop support for auto-resolving abilities

Reported by: gziolo's profile gziolo Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: trunk
Component: Abilities API Keywords: ai-client 2nd-opinion
Focuses: Cc:

Description

The WP AI Client introduced in #64591 currently exposes abilities via using_abilities(), which attaches tool definitions to the prompt so the LLM can request function calls. However, when the model responds with a tool-use request, the developer is responsible for detecting it, executing the matching ability, feeding the result back, and re-prompting the model. This is a common pain point — see the recent discussion in #core-ai Slack.

This ticket proposes adding a built-in agentic loop to the WP AI Client that automatically resolves ability calls using a ReAct-style (Reasoning + Acting) pattern.

Proposed API


An auto_resolve_abilities() method (name proposed by @jason_the_adams here) on the WP AI Client that:

  1. Sends the prompt to the model with registered abilities.
  2. Inspects the response for tool-use requests.
  3. Executes the matching registered ability.
  4. Appends the tool result to the conversation and re-prompts the model.
  5. Repeats until the model produces a final answer or a maximum iteration limit is reached.

Presenting the rough idea:

$ai = wp_ai()->using_abilities( 'site_information', 'recent_posts' );
 
// Current behavior — manual loop (unchanged)
$response = $ai->prompt( 'What is this site about?' );
 
// Proposed — auto-resolve loop
$response = $ai->auto_resolve_abilities()->prompt( 'What is this site about?' );


Design Considerations


  • Max iterations — A sensible default (e.g., 5) with a filter to override. Prevents runaway loops.
  • Error handling — What happens when an ability execution fails mid-loop? Options: abort with error, skip and inform the model, retry.
  • Streaming support — Should the loop support streaming intermediate steps, or only return the final response?
  • Hooks for observability — A filter or action between iterations would let developers inspect, log, or approve each step (human-in-the-loop).
  • Parallel tool calls — Some models return multiple tool-use requests in a single response. Should the client execute them in parallel?


Context


Popular AI frameworks offer a wide spectrum of support for agentic loops — from minimal, loop-it-yourself approaches (similar to what the WP AI client provides today) to fully managed orchestration like LangChain's AgentExecutor or LangGraph's state-machine-based agents where tool dispatch, retries, and multi-step reasoning are handled automatically. The goal here is to start simple and iterate — a basic sequential loop that covers the 80% use case, with filters for extensibility.


Change History (6)

This ticket was mentioned in Slack in #core-ai by gziolo. View the logs.


2 months ago

#2 @satollo
2 months ago

An alternative is to leave the developer to execute the abilities, but then be able to push back the Message resulting from the execution. I was not able to find a suitable method (other than changing the signature of the $message attribute in PromptBuilder to public).

What was partially working on my tests:

  • Create the prompt builder with $prompt = wp_ai_client_prompt($starting_user_text_prompt)
  • Add selected abilities $prompt->using_abilities(...$abilities)
  • Call $result = $prompt->generate_text_result()
  • Create an instance of $fr = new WP_AI_Client_Ability_Function_Resolver(...$abilities) providing the same abilities added to the prompt builder
  • Check if the generated $result is a function call $fr->has_ability_calls($result->toMessage())
  • If true, execute them $fc_result = $fr->execute_abilities($result->toMessage())
  • Now, with the modified PromptBuilder, add the two messages to the $messages array: $prompt->builder->messages[] = $result.toMessage() (this is the one with the function call request and $prompt->builder->messages[] = $fc_result.toMessage()

The next "generate" call will return the answer from the LLM using the results of the function call.

For example, asking for "site info", we get a table of all the values returned by the ability "Get Site Information".

Here are a couple of observations; if useful, they could be part of another ticket.

Having access to the $messages I can serialize them to a file.
When the user, after getting the answer to the first request, asks, for example, "which is the site name", the steps are repeated, but when building the {$prompt} I can unserialize the messages and use with_history(...). Now the LLM does not call the function again and extracts the information for the previous messages.

I don't know if that is the formally correct sequence, it is what I used with Neuron AI.

A final note: after the first run, when I get the site information from the LLM built from the function result, if I push back that message into the message history (it is actually part of the conversation), I get an error: Bad Request (400) - Invalid value: 'output_text'.

The output_text is set by the OpenAI provider plugin when the message has a Model role. Changing to input_text, the conversation with the LLM can continue. But I stopped here.

#3 @gziolo
2 months ago

  • Summary changed from AI Client: Add agentic loop support for auto-resolving abilities to WP AI Client: Add agentic loop support for auto-resolving abilities

#4 @JeffPaul
8 weeks ago

  • Keywords ai-client added

#5 @gziolo
7 weeks ago

  • Keywords 2nd-opinion added

#6 @desrosj
4 weeks ago

  • Component changed from AI to Abilities API

Moving tickets related to the Abilities API to a new sub-component.

Note: See TracTickets for help on using tickets.