Improving AI Tool Use with Recurrent Loops
Listen to the summary
Uses a voice available on your device
Audio options
On this page 5 sections
Related concepts 8 concepts
Key Takeaways
- Recurrent latent computation allows models to better handle multi-step dependencies in tool calling workflows.
- The Ouro-2.6B model achieved 86.4% accuracy on the BFCL benchmark compared to 47.9% for its non-recurrent base model.
- Adaptive inference, which dynamically allocates compute based on per-token confidence, matches or exceeds fixed-depth performance while using fewer loop iterations on average.
- Retrofitted recurrent models perform significantly worse than native recurrent models, especially on deeply nested tasks.
Summary & Methodology Analysis
The paper introduces a method to improve compositional tool calling by allowing Transformer models, which normally process data in a single fixed pass, to perform recurrent latent computation. The core approach involves repeatedly applying a shared Transformer block across multiple iterations to refine latent representations during inference. The authors implemented two variants: Ouro models, which use a learned halting policy to determine when to exit the loop, and retrofitted models, which partition existing pretrained Transformer blocks into non-recurrent components and a shared recurrent block. Models were trained using a supervised fine-tuning recipe on the Hermes function-calling dataset.
Evaluation focused on the ability of these models to handle complex tool-use scenarios where the output of one call serves as an input for the next. On the BFCL benchmark, the Ouro-2.6B model demonstrated a significant performance gain, reaching 86.4% overall AST accuracy compared to 47.9% for the base model. The researchers also implemented adaptive inference, where the model uses a learned gate to decide how many iterations are necessary based on the confidence of each token. This approach matches or exceeds the performance of fixed-depth inference while requiring fewer loop iterations on average across the benchmarks.
Despite these gains, the architecture faces notable limitations. The study is restricted to static, single-turn evaluations, meaning the models have not yet been tested in dynamic, multi-turn agentic settings where they must recover from errors during an execution episode. Furthermore, there is a clear performance gap when retrofitting existing models: the authors report that retrofitted models remain substantially weaker than native recurrent models when handling deeply nested workflows. Future research is required to determine how these recurrent techniques translate to live environments and how to close the performance gap for retrofitted architectures.
Interactive System Flowchart
Illustrative Implementation
A short sketch of the paper's core idea, not the authors' own code.
# Illustrative sketch (not from the paper)
import torch
import torch.nn as nn
import torch.nn.functional as F
class SharedTransformerBlock(nn.Module):
def __init__(self, d_model):
super().__init__()
self.attn = nn.MultiheadAttention(d_model, num_heads=8)
self.ff = nn.Sequential(nn.Linear(d_model, d_model * 4), nn.GELU(), nn.Linear(d_model * 4, d_model))
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
def forward(self, x):
# self‑attention
attn_out, _ = self.attn(x, x, x)
x = self.norm1(x + attn_out)
# feed‑forward
ff_out = self.ff(x)
return self.norm2(x + ff_out)
class HaltingPolicy(nn.Module):
def __init__(self, d_model):
super().__init__()
self.proj = nn.Linear(d_model, 1)
def forward(self, hidden):
# per‑token confidence → sigmoid probability
return torch.sigmoid(self.proj(hidden.mean(dim=0)))
class LoopedModel(nn.Module):
def __init__(self, shared_block, halting, max_iter=5, thresh=0.9):
super().__init__()
self.shared = shared_block
self.halting = halting
self.max_iter = max_iter
self.thresh = thresh
def forward(self, x):
hidden = x
for i in range(self.max_iter):
hidden = self.shared(hidden) # recurrent refinement
halt_prob = self.halting(hidden) # Ouro‑style exit gate
if halt_prob.item() > self.thresh:
break # adaptive stopping
return hidden, i+1 # final representation and used iterations
// Illustrative sketch (not from the paper)
const tf = require('@tensorflow/tfjs-node');
function sharedTransformerBlock(dModel) {
const attn = tf.layers.multiHeadAttention({numHeads: 8, keyDim: dModel / 8});
const ff = tf.sequential();
ff.add(tf.layers.dense({units: dModel * 4, activation: 'gelu'}));
ff.add(tf.layers.dense({units: dModel}));
const norm1 = tf.layers.layerNormalization();
const norm2 = tf.layers.layerNormalization();
return (x) => {
const attnOut = attn.apply([x, x, x]);
const x1 = norm1.apply(tf.add(x, attnOut));
const ffOut = ff.apply(x1);
return norm2.apply(tf.add(x1, ffOut));
};
}
function haltingPolicy(dModel) {
const proj = tf.layers.dense({units: 1});
return (hidden) => {
// average over sequence dimension, then sigmoid
const avg = hidden.mean(0);
return tf.sigmoid(proj.apply(avg));
};
}
function LoopedModel(sharedBlock, halting, maxIter = 5, thresh = 0.9) {
return async (x) => {
let hidden = x;
let used = 0;
for (let i = 0; i < maxIter; i++) {
hidden = sharedBlock(hidden); // recurrent refinement
const haltProb = (await halting(hidden).data())[0]; // confidence
used = i + 1;
if (haltProb > thresh) break; // adaptive exit
}
return {output: hidden, iterations: used};
};
}
// Example usage (placeholder tensors)
const dModel = 256;
const shared = sharedTransformerBlock(dModel);
const halt = haltingPolicy(dModel);
const model = LoopedModel(shared, halt);
// const input = tf.randomNormal([seqLen, dModel]);
// model(input).then(res => console.log(res.iterations));
Cross-Examination & FAQs
A deeper dive clarifying mechanics, constraints, and baseline evaluations.
Q1. What is the primary contribution of this research?
The paper demonstrates that incorporating recurrent loops into Transformer models improves their performance on complex, compositional tool-calling tasks.
Q2. Does this technique help with all types of tool use?
Recurrent computation shows the most significant improvements on multi-call and dependency-aware tasks, while gains on isolated API invocations are smaller and model-dependent.
Q3. Is this approach faster than standard models?
Adaptive inference allows the model to achieve high accuracy using fewer loop iterations on average compared to fixed-depth inference.
Q4. What is the difference between Ouro models and retrofitted models?
Ouro models are natively recurrent with a learned halting policy, whereas retrofitted models involve partitioning existing pretrained Transformer blocks into recurrent and non-recurrent segments.
Q5. How do retrofitted models perform compared to native models?
Retrofitted models perform significantly worse than native recurrent models, particularly on deeply nested workflows.
Q6. What benchmarks were used to test these models?
The researchers used BFCL v3, API-Bank, and NESTful as the primary evaluation benchmarks.
Q7. How was the model trained?
The models were trained using a supervised fine-tuning recipe on the Hermes function-calling dataset.
Q8. Does the paper address multi-turn agentic tasks?
No, the evaluations are restricted to static, single-turn tasks, and extending this to multi-turn settings is left to future work.
Q9. What is the specific accuracy improvement for Ouro-2.6B on BFCL?
Ouro-2.6B SFT achieved 86.4% overall AST accuracy compared to 47.9% for its base model.