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: |
|
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:
- Sends the prompt to the model with registered abilities.
- Inspects the response for tool-use requests.
- Executes the matching registered ability.
- Appends the tool result to the conversation and re-prompts the model.
- 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.
Related
- Discussion in #core-ai Slack channel
- Code review comment when introducing WP AI client.
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:
$prompt = wp_ai_client_prompt($starting_user_text_prompt)$prompt->using_abilities(...$abilities)$result = $prompt->generate_text_result()$fr = new WP_AI_Client_Ability_Function_Resolver(...$abilities)providing the same abilities added to the prompt builder$resultis a function call$fr->has_ability_calls($result->toMessage())$fc_result = $fr->execute_abilities($result->toMessage())$messagesarray:$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
$messagesI 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.