Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Agents and Tool Use

Authors
Affiliations
Johns Hopkins Bloomberg School of Public Health
Johns Hopkins Bloomberg School of Public Health

1AI Agents

An agent is a language model placed in a loop: it observes a state, chooses an action from a set of available tools, sees the result, and repeats until it decides it is finished. Nothing about the model changes --- it is the same conditional distribution over tokens as in the previous sections --- but the object of study does, because a single response is no longer the unit of analysis. What matters is the trajectory, and trajectories have properties that single responses do not: errors compound, the state the model conditions on is one it partly created, and the outcome is a function of choices made many steps earlier. This is a sequential decision problem, and statisticians will recognize most of its structure from reinforcement learning, sequential design, and the analysis of longitudinal data with time-varying treatment. This section develops that correspondence, gives the arithmetic of compounding failure that governs how long an agent can run before it becomes useless, and argues that evaluating an agent is a harder measurement problem than evaluating a model --- one that the field currently handles with sample sizes a statistician would reject on sight.

Agentic vocabulary is largely reinforcement-learning and sequential-decision vocabulary with the training removed; the last column marks where that omission matters.

ML / AI termStatistical analogueWhat is the sameWhat is different / the catch
AgentA policy in a sequential decision problemMaps observed state to an actionThe policy is a frozen pretrained model prompted at run time, not one fitted to this task
Tool call / function callingA structured query with a typed argument listConstrained, validatable inputArguments are generated text; validation is on you, and a malformed call can be silently retried
Trajectory / episodeA subject’s observation sequence in a longitudinal studyAn ordered record of states and actionsActions change the state, so later observations are not exchangeable with earlier ones
Reward / task successThe outcome variableThe thing being optimized or measuredUsually binary and end-of-episode, giving very little information per run
PlanningSequential experimental designChoosing the next action to reduce uncertaintyNo explicit uncertainty model; the “plan” is generated text
ReAct (reason then act)Interleaving analysis and data collectionAlternate between thinking and observingThe reasoning trace is not a faithful account of the computation and should not be read as one
Self-consistency / majority votingBagging; the bootstrap-aggregated voteAverage several stochastic runsThe runs share a model, so errors are correlated and the variance reduction is limited
Memory / scratchpadA running sufficient statistic; a state vectorCarries information forwardUnbounded, unstructured text, so old mistakes persist and are re-conditioned on
GuardrailA constraint on the action spaceRules out inadmissible actionsEnforced by prompt or by code; only the second kind is a real constraint
Human in the loopSequential monitoring with a stopping ruleA person approves or haltsThe approval decision is itself error-prone and fatigues
Multi-agent systemAn ensemble or a committee of expertsMultiple models combinedCommunication is text, so errors propagate rather than average out
Compounding errorError propagation in a multi-step forecastSmall per-step error accumulatesGeometric in trajectory length, so reliability targets are brutal
Agent benchmarkA small experiment with a binary endpointEstimating a success probabilityTypically 30--200 episodes, so intervals are ±10\pm 10 points or worse
Cost per taskTotal variable cost of one experimental unitResource accountingHighly skewed across episodes; report a median and a tail, never a mean alone

1.1The Loop, Written Down

At step tt the agent conditions on a history ht=(x,a1,o1,,at1,ot1)h_t = (x, a_1, o_1, \dots, a_{t-1}, o_{t-1}) of the task description, the actions it took, and the observations they returned, and samples an action from the model,

at    πθ(ht),ot  =  T(at),ht+1=ht{at,ot},a_t \;\sim\; \pi_\theta(\cdot \mid h_t), \qquad o_t \;=\; \mathcal{T}(a_t), \qquad h_{t+1} = h_t \cup \{a_t, o_t\},

where T\mathcal{T} is the environment --- a search index, a Python interpreter, a database, a filesystem. Interleaving a generated rationale with each action, as in ReAct Yao et al., 2022, is the pattern almost every framework now follows. This is exactly the policy-and-environment structure of reinforcement learning Sutton, 1988Mnih et al., 2015, with one decisive difference: πθ\pi_\theta was not fitted to this environment. It is a general-purpose text model being asked to behave like a policy, and the entire engineering discipline of “agent building” consists of shaping hth_t --- through instructions, tool descriptions, and what is kept in memory --- so that the sampled actions are sensible.

The loop is short enough to write out in full. The example below is a two-tool agent with a hard step limit; watch the history grow, and note that the only thing carrying information between steps is the text in \verb|h|.

import re

FACTS = {"n_patients": "312", "site_count": "4"}
TOOLS = {"calc": lambda a: str(eval(a, {"__builtins__": {}})),
         "lookup": lambda a: FACTS.get(a, "NOT_FOUND")}

def policy(task, h):          # stands in for a_t ~ pi_theta( . | h_t)
    seen = " ".join(h)
    for key in FACTS:
        if key in task and key not in seen:
            return ("lookup", key)
    nums = re.findall(r"\d+", seen)
    if len(nums) >= 2 and "calc" not in seen:
        return ("calc", f"{nums[0]}/{nums[1]}")
    return ("stop", "")

task = "average n_patients per site given site_count"
h, LIMIT = [], 6
for t in range(1, LIMIT + 1):
    a, arg = policy(task, h)
    if a == "stop":
        print(f"t={t}  |h_t|={len(h)}  action=stop")
        break
    o = TOOLS[a](arg)
    print(f"t={t}  |h_t|={len(h)}  action={a + '(' + arg + ')':<22} obs={o}")
    h.append(f"{a}({arg})->{o}")
else:
    print("hit the step limit without stopping")
print("history:", " | ".join(h))
t=1  |h_t|=0  action=lookup(n_patients)     obs=312
t=2  |h_t|=1  action=lookup(site_count)     obs=4
t=3  |h_t|=2  action=calc(312/4)            obs=78.0
t=4  |h_t|=3  action=stop
history: lookup(n_patients)->312 | lookup(site_count)->4 | calc(312/4)->78.0

Nothing here is learned. The “policy” is a fixed rule reading a text history, and the three tool calls are the whole trajectory --- which is exactly the point: the object being analysed is the sequence, not any one response. (Orchestration libraries such as LangGraph make the same loop explicit as a graph of nodes with checkpointed state, which is what allows a failed step to be retried without replaying the whole trajectory.)

Tool calling is constrained generation, and the constraint is enforced by a schema validator rather than by the model. The example asks what a retry budget buys when the raw call is malformed one time in three.

import json
import numpy as np

rng = np.random.default_rng(0)
SCHEMA = {"name": str, "args": dict}

def emit():                    # the model's raw tool call, sometimes malformed
    r = rng.random()
    if r < 0.20:
        return '{"name": "lookup", "args": {"key": "n"}'       # truncated
    if r < 0.30:
        return '{"name": "lookup", "arguments": {"key": "n"}}'  # bad field
    return '{"name": "lookup", "args": {"key": "n"}}'

def valid(s):
    try:
        c = json.loads(s)
    except json.JSONDecodeError:
        return False
    return all(k in c and isinstance(c[k], t) for k, t in SCHEMA.items())

raw = np.mean([valid(emit()) for _ in range(4000)])
print(f"validity of one raw call   {raw:.3f}")
for r in (2, 3, 4):
    ok = [any(valid(emit()) for _ in range(r)) for _ in range(4000)]
    print(f"valid within {r} attempts    {np.mean(ok):.3f}")
validity of one raw call   0.701
valid within 2 attempts    0.910
valid within 3 attempts    0.977
valid within 4 attempts    0.995

Validation plus retries turns a 0.70 generator into a 0.98 one at three attempts, which is geometric decay of the failure probability and ordinary software engineering. It works only because a malformed call is cheap to detect; nothing here improves the semantic correctness of a call that parses. (Typed-output libraries such as Pydantic AI and Instructor package this exact loop, binding a call to a declared schema and re-prompting on a parse failure, which is why the reliability of an agent framework is usually a property of its validator rather than of its model.)

1.2Why Long Trajectories Fail

Suppose each step succeeds independently with probability pp and the task requires TT steps, all of which must be right. Then

Pr(task success)  =  pT,E[steps until first failure]  =  11p,\Pr(\text{task success}) \;=\; p^{T}, \qquad \EX[\text{steps until first failure}] \;=\; \frac{1}{1-p},

so at a per-step reliability of 0.95 --- which sounds excellent --- a 20-step task succeeds barely a third of the time. This crude geometric model is the most useful single thing to know about agents, because it converts a vague complaint about unreliability into a design constraint: either raise pp, shorten TT, or introduce checkpoints that allow recovery so the failures stop being absorbing. Independence is of course wrong, and it is wrong in the unhelpful direction, since a bad early action corrupts the history that every later action conditions on, which induces positive dependence between failures.

The arithmetic is worth seeing rather than asserting. The table below evaluates the equation across per-step reliabilities that all sound acceptable, and then inverts it to give the reliability a target trajectory length demands.

import numpy as np

T = np.array([1, 5, 10, 20, 50])
print("        T =  " + "".join(f"{t:7d}" for t in T) + "   E[steps to fail]")
for p in (0.90, 0.95, 0.99):
    row = "".join(f"{p ** t:7.3f}" for t in T)
    print(f"p={p:.2f}  p^T {row}        {1/(1-p):7.1f}")
print()
for t in (10, 30, 100):
    print(f"a {t:3d}-step task at 90% overall needs p >= {0.9 ** (1/t):.4f}")
        T =        1      5     10     20     50   E[steps to fail]
p=0.90  p^T   0.900  0.590  0.349  0.122  0.005           10.0
p=0.95  p^T   0.950  0.774  0.599  0.358  0.077           20.0
p=0.99  p^T   0.990  0.951  0.904  0.818  0.605          100.0

a  10-step task at 90% overall needs p >= 0.9895
a  30-step task at 90% overall needs p >= 0.9965
a 100-step task at 90% overall needs p >= 0.9989

The inversion is the uncomfortable half: a 30-step task that succeeds nine times in ten requires p0.9965p \ge 0.9965 per step. No prompt achieves that, which is why the practical response is to shorten TT rather than to chase pp.

Independence is the assumption that makes the equation tractable, and it fails in the unhelpful direction. Two mechanisms do the damage: reliability that decays as the history fills with the agent’s own output, and an episode-level difficulty shared by every step.

import numpy as np

T = 20
p_t = 0.97 * 0.985 ** np.arange(T)   # reliability decays as h_t grows
print(f"step-1  reliability {p_t[0]:.3f}   (what a short benchmark sees)")
print(f"step-{T} reliability {p_t[-1]:.3f}   (what the agent has by then)")
print(f"iid extrapolation p1^T {p_t[0] ** T:.3f}   "
      f"actual product {np.prod(p_t):.3f}")

rng = np.random.default_rng(0)
b = rng.normal(0.0, 0.8, 400000)       # episode effect, shared by all steps
p_ep = 1.0 / (1.0 + np.exp(-(1.9 + b)))
print(f"mean per-step reliability {p_ep.mean():.3f}")
print(f"iid extrapolation of that mean   {p_ep.mean() ** T:.3f}")
print(f"with the shared episode effect   {np.mean(p_ep ** T):.3f}")
step-1  reliability 0.970   (what a short benchmark sees)
step-20 reliability 0.728   (what the agent has by then)
iid extrapolation p1^T 0.544   actual product 0.031
mean per-step reliability 0.845
iid extrapolation of that mean   0.034
with the shared episode effect   0.125

Both mechanisms make the geometric model optimistic when it is calibrated on step-one behaviour and pessimistic when it is calibrated on a marginal per-step rate: a random-effects structure puts mass on episodes that go perfectly and on episodes that fail immediately. Either way, a per-step success rate measured on short trajectories does not extrapolate to long ones.

Two pieces of arithmetic that constrain what agents can do. Left: the equation, the probability that every step of a T-step trajectory succeeds. Reliability that would be impressive in a single response is not enough to complete a long task, which is why practical systems shorten trajectories and add checkpoints rather than chasing a better prompt. Right: the half-width of a 95% Wilson interval for a success probability as a function of the number of evaluation episodes. At the 50 to 100 episodes typical of published agent benchmarks, the interval is roughly \pm 10 points, so most reported comparisons between agents are not resolvable.
:width: 90%

Figure 1:Two pieces of arithmetic that constrain what agents can do. Left: the equation, the probability that every step of a TT-step trajectory succeeds. Reliability that would be impressive in a single response is not enough to complete a long task, which is why practical systems shorten trajectories and add checkpoints rather than chasing a better prompt. Right: the half-width of a 95% Wilson interval for a success probability as a function of the number of evaluation episodes. At the 50 to 100 episodes typical of published agent benchmarks, the interval is roughly ±10\pm 10 points, so most reported comparisons between agents are not resolvable. :width: 90%

1.3Verification Is the Whole Game

The asymmetry that makes agents work at all is that many useful actions are far easier to check than to produce. A generated SQL query either parses and returns rows or does not; a script either runs the unit tests or fails them; a proposed citation either resolves to a real record or does not. Where a cheap, automatic verifier exists, an agent can afford to be wrong often, because it can sample, check, and retry --- and the failure probability falls geometrically in the number of independent attempts. Where no verifier exists --- summarizing a literature, interpreting a result, drafting a conclusion --- there is no such recovery, and the agent’s output must be treated as a draft requiring human adjudication. The deep-research agents of Section that section sit squarely in this second regime, and the sampling-based verification developed there is what replaces the missing verifier. This distinction, rather than any property of the model, is the best available predictor of whether an agentic workflow will succeed in a given scientific task.

Checkpoints change the arithmetic because they make a failure recoverable rather than absorbing. Suppose a failed step is caught by a verifier with detection probability dd and retried up to kk times; the effective per-step success rate becomes p(1(qd)k)/(1qd)p\,(1-(qd)^k)/(1-qd) with q=1pq = 1-p.

p, T = 0.85, 20
q = 1 - p
print("detect  k=1     k=3     k=10    ceiling  Pr(20 steps | k=3)")
for d in (1.0, 0.9, 0.5, 0.0):
    eff = [p * (1 - (q * d) ** k) / (1 - q * d) for k in (1, 3, 10)]
    ceiling = p / (p + q * (1 - d))
    print(f"{d:5.2f}  " + "  ".join(f"{e:.4f}" for e in eff)
          + f"  {ceiling:.4f}   {eff[1] ** T:.4f}")
detect  k=1     k=3     k=10    ceiling  Pr(20 steps | k=3)
 1.00  0.8500  0.9966  1.0000  1.0000   0.9346
 0.90  0.8500  0.9802  0.9827  0.9827   0.6709
 0.50  0.8500  0.9185  0.9189  0.9189   0.1828
 0.00  0.8500  0.8500  0.8500  0.8500   0.0388

The ceiling column is the entire lesson: with a perfect verifier retries drive the effective rate to one, but at d=0.5d = 0.5 no retry budget gets past 0.919, and the 20-step task still fails four times in five. Retry budget is worth very little without detection, which is the argument for the next subsection.

1.4Evaluating an Agent

An agent evaluation is a small experiment with a binary endpoint, and it should be reported as one. For nn episodes with p^\hat p successes the Wilson interval

p^+z22n1+z2n  ±  z1+z2np^(1p^)n+z24n2\frac{\hat p + \frac{z^2}{2n}}{1 + \frac{z^2}{n}} \;\pm\; \frac{z}{1 + \frac{z^2}{n}} \sqrt{\frac{\hat p(1-\hat p)}{n} + \frac{z^2}{4n^2}}

is the appropriate summary, and it is wide at realistic sample sizes. Two further points are specific to agents. First, episodes are usually not independent replicates of a common condition --- tasks differ enormously in difficulty --- so the right design is paired: run both systems on the same task set and analyse the paired differences, which removes task difficulty from the error term. Second, cost and latency distributions are heavily right-skewed because a few episodes loop, so report the median and an upper quantile rather than a mean.

It is worth seeing how wide the equation actually is at the sample sizes the literature uses.

from statsmodels.stats.proportion import proportion_confint

lo, hi = proportion_confint(39, 60, alpha=0.05, method="wilson")
print(f"39/60 = {39/60:.3f}   Wilson 95% CI ({lo:.3f}, {hi:.3f})")
print(f"half-width {(hi - lo) / 2:.3f}")
print()
for n in (30, 60, 100, 200, 500, 2000):
    k = int(0.65 * n + 0.5)
    lo, hi = proportion_confint(k, n, alpha=0.05, method="wilson")
    print(f"n={n:5d} episodes at 65% -> half-width {100*(hi-lo)/2:4.1f} points")
39/60 = 0.650   Wilson 95% CI (0.524, 0.758)
half-width 0.117

n=   30 episodes at 65% -> half-width 16.0 points
n=   60 episodes at 65% -> half-width 11.7 points
n=  100 episodes at 65% -> half-width  9.2 points
n=  200 episodes at 65% -> half-width  6.6 points
n=  500 episodes at 65% -> half-width  4.2 points
n= 2000 episodes at 65% -> half-width  2.1 points

At 60 episodes the interval spans nearly a quarter of the scale, so an agent reported at 65% is indistinguishable from one at 55% or 75%. Getting to a ±5\pm 5-point interval costs roughly 350 episodes; most published comparisons are not powered to resolve the differences they report.

The paired design is not a refinement here, it is the difference between seeing an effect and not. Running both systems on the same 40 tasks with common random numbers removes task difficulty from the error term.

import numpy as np

rng = np.random.default_rng(0)
n = 40
difficulty = rng.normal(0, 1.5, n)          # tasks differ enormously
pA = 1 / (1 + np.exp(-(0.0 + difficulty)))
pB = 1 / (1 + np.exp(-(0.6 + difficulty)))  # B is uniformly a little better
u = rng.random(n)                           # common random numbers: same tasks
a, b = (u < pA).astype(int), (u < pB).astype(int)
d = b - a

se_un = np.sqrt(a.mean()*(1-a.mean())/n + b.mean()*(1-b.mean())/n)
se_pa = d.std(ddof=1) / np.sqrt(n)
print(f"A {a.mean():.3f}   B {b.mean():.3f}   difference {d.mean():+.3f}")
print(f"unpaired half-width {1.96*se_un:.3f}  CI covers 0: "
      f"{abs(d.mean()) < 1.96*se_un}")
print(f"paired   half-width {1.96*se_pa:.3f}  CI covers 0: "
      f"{abs(d.mean()) < 1.96*se_pa}")
print(f"variance ratio paired/unpaired {(se_pa/se_un)**2:.3f}")
A 0.550   B 0.650   difference +0.100
unpaired half-width 0.214  CI covers 0: True
paired   half-width 0.094  CI covers 0: False
variance ratio paired/unpaired 0.194

The point estimate is identical under both analyses; only the standard error changes, by a factor of five in variance. Treating the two arms as independent samples throws away the blocking that the design already gave you for free.

Cost is the other endpoint, and it is not summarized by a mean. A small fraction of episodes loop, and the resulting distribution is heavy enough that the mean sits well above the typical run.

import numpy as np

rng = np.random.default_rng(0)
n = 500
loops = rng.random(n) < 0.05           # a few episodes spin in a retry loop
cost = np.where(loops, rng.lognormal(3.2, 0.6, n),
                rng.lognormal(0.8, 0.4, n))
med, p90, p99 = np.quantile(cost, [0.5, 0.9, 0.99])
print(f"mean {cost.mean():6.2f}   median {med:5.2f}   p90 {p90:6.2f}")
print(f"p99  {p99:6.2f}   max    {cost.max():6.2f}   mean/median "
      f"{cost.mean()/med:.2f}")
print(f"the {loops.mean():.0%} of episodes that loop are "
      f"{cost[loops].sum()/cost.sum():.1%} of total spend")
mean   3.65   median  2.37   p90   4.20
p99   37.52   max    102.53   mean/median 1.54
the 4% of episodes that loop are 35.2% of total spend

The mean is 1.5 times the median and the top percentile is fifteen times it, so a budget set from the mean will be exhausted by a handful of episodes. Report a median with an upper quantile, and set the spend cap from the tail --- which is also the stopping-rule question, and a sequential-analysis problem the reader already knows how to think about.

1.5Tools in practice

Every framework in this space implements the equation; what differs between them is the shape of the control flow they impose on it, whether tool arguments pass a schema check before execution, and how much of the trajectory they persist. Those three properties are the ones worth asking about, because they decide whether the measurements in this section are available to you at all --- a system that does not write hth_t to durable storage makes the per-step hazard estimated below uncomputable, and no amount of dashboard makes up for it. Everything else about these tools moves quickly enough that the current documentation is the only sensible source; the durable distinctions are structural.

A logged trajectory is a right-censored failure-time record, one row per tool call, and the natural summary of it is a discrete-time hazard rather than a single success rate. The example below builds such a log from a simulated agent whose per-step reliability decays as the history fills, then estimates the hazard at each step and compares the pooled geometric forecast against what the episodes actually did.

import numpy as np

rng = np.random.default_rng(0)
CAP = 12
trace = []                                  # one record per tool call
for ep in range(300):
    for t in range(1, CAP + 1):
        p = 0.97 * 0.93 ** (t - 1)          # reliability decays with history
        ok = rng.random() < p
        trace.append({"episode": ep, "step": t, "tool": "search",
                      "status": "ok" if ok else "error"})
        if not ok:
            break

alive = np.zeros(CAP + 1, int)
died = np.zeros(CAP + 1, int)
for r in trace:
    alive[r["step"]] += 1
    died[r["step"]] += r["status"] == "error"

print("step  at risk  failed  hazard  survival")
S = 1.0
for t in range(1, CAP + 1):
    if alive[t] == 0:
        break
    h = died[t] / alive[t]
    S *= 1 - h
    print(f"{t:4d}  {alive[t]:7d}  {died[t]:6d}  {h:6.3f}  {S:8.3f}")

n_ep = len({r["episode"] for r in trace})
done = n_ep - sum(died)
print(f"\n{done}/{n_ep} episodes reached step {CAP} without an error")
p_pool = 1 - sum(died) / len(trace)
print(f"pooled per-step success {p_pool:.3f} -> geometric forecast "
      f"p^{CAP} = {p_pool ** CAP:.3f}, observed {done / n_ep:.3f}")
step  at risk  failed  hazard  survival
   1      300       6   0.020     0.980
   2      294      30   0.102     0.880
   3      264      44   0.167     0.733
   4      220      52   0.236     0.560
   5      168      57   0.339     0.370
   6      111      35   0.315     0.253
   7       76      26   0.342     0.167
   8       50      28   0.560     0.073
   9       22      13   0.591     0.030
  10        9       5   0.556     0.013
  11        4       2   0.500     0.007
  12        2       1   0.500     0.003

1/300 episodes reached step 12 without an error
pooled per-step success 0.803 -> geometric forecast p^12 = 0.072, observed 0.003

The hazard rises from 0.020 at the first step to above 0.5 by the eighth, which is the decay of the equation’s constant-pp assumption made visible. The last line is the reason to compute it: the pooled per-step success rate of 0.803 --- the number a framework’s summary dashboard reports --- forecasts a 7.2%7.2\% completion rate for a twelve-step task, and the observed rate is 0.3%0.3\%, more than twenty times lower. Pooling over steps averages a gentle early hazard with a severe late one and then extrapolates the average, which is precisely the mistake a survival analyst is trained not to make. Report the hazard curve, and note where it crosses the point at which continuing is no longer worth the tokens; that crossing is the empirical version of the step cap every framework asks you to set by guess.

1.6Exercises

  1. Using the equation, find the per-step reliability required for a 30-step task to succeed 90% of the time. Then repeat the calculation assuming a checkpoint every 5 steps from which a failed segment can be retried once, and comment on which intervention is cheaper.

  2. An agent is evaluated on 60 tasks and succeeds on 39. Compute the Wilson interval the equation and state the smallest improvement over a 50% baseline that this design could have detected at 80% power.

  3. Explain why majority voting over mm sampled trajectories reduces variance less than the 1/m1/m that independence would suggest, and identify the quantity that governs the shortfall.

  4. Computational. Implement a two-tool agent (a calculator and a lookup over a small local corpus) with a hard step limit. Run it on 40 tasks at step limits T{3,5,10,20}T \in \{3, 5, 10, 20\}, and plot success rate and median cost against the limit. Where does the extra budget stop paying for itself?

  5. Computational. Instrument the same agent to write one row per tool call --- episode, step, tool, status --- and from 200 episodes estimate the discrete-time hazard at each step. Compare the observed completion rate against the geometric forecast p^T\hat p^{\,T} built from the pooled per-step rate, and explain the direction of the discrepancy.

  6. Computational. Take the same 40 tasks and compare two prompt variants using a paired analysis: report the paired difference in success rate with a confidence interval, and compare its width to that of the two unpaired intervals. Quantify how much precision the paired design bought.

References
  1. Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., & Cao, Y. (2022). ReAct: Synergizing Reasoning and Acting in Language Models.
  2. Sutton, R. S. (1988). Learning to predict by the methods of temporal differences. Machine Learning, 3(1), 9–44. 10.1007/bf00115009
  3. Mnih, V., Kavukcuoglu, K., Silver, D., Rusu, A. A., Veness, J., Bellemare, M. G., Graves, A., Riedmiller, M., Fidjeland, A. K., Ostrovski, G., Petersen, S., Beattie, C., Sadik, A., Antonoglou, I., King, H., Kumaran, D., Wierstra, D., Legg, S., & Hassabis, D. (2015). Human-level control through deep reinforcement learning. Nature, 518(7540), 529–533. 10.1038/nature14236
  4. Schick, T., Dwivedi-Yu, J., Dessì, R., Raileanu, R., Lomeli, M., Zettlemoyer, L., Cancedda, N., & Scialom, T. (2023). Toolformer: Language Models Can Teach Themselves to Use Tools.
  5. Silver, D., Huang, A., Maddison, C. J., Guez, A., Sifre, L., van den Driessche, G., Schrittwieser, J., Antonoglou, I., Panneershelvam, V., Lanctot, M., Dieleman, S., Grewe, D., Nham, J., Kalchbrenner, N., Sutskever, I., Lillicrap, T., Leach, M., Kavukcuoglu, K., Graepel, T., & Hassabis, D. (2016). Mastering the game of Go with deep neural networks and tree search. Nature, 529(7587), 484–489. 10.1038/nature16961
  6. Schulman, J., Wolski, F., Dhariwal, P., Radford, A., & Klimov, O. (2017). Proximal Policy Optimization Algorithms.
  7. Ouyang, L., Wu, J., Jiang, X., Almeida, D., Wainwright, C. L., Mishkin, P., Zhang, C., Agarwal, S., Slama, K., Ray, A., Schulman, J., Hilton, J., Kelton, F., Miller, L., Simens, M., Askell, A., Welinder, P., Christiano, P., Leike, J., & Lowe, R. (2022). Training language models to follow instructions with human feedback.
  8. Amodei, D., Olah, C., Steinhardt, J., Christiano, P., Schulman, J., & Mané, D. (2016). Concrete Problems in AI Safety.