TL;DR
Today’s agent protocols (Anthropic’s MCP, Google’s A2A) standardize connectivity — how a model calls a tool, how one agent talks to another. They say nothing about how an agent changes itself over time. So when people build “self-evolving” agents that rewrite their own prompts or generate new tools, they hand-roll it: brittle code, no version history, no rollback, no way to audit what changed or undo a bad edit. Autogenesis (AGP) is a protocol that fixes the missing half. It splits the problem into two layers: RSPL turns prompts/agents/tools/environments/memory into first-class resources with explicit state, versions, and a controlled interface (you can’t mutate them except through the protocol), and SEPL defines a five-step control loop — Reflect, Select, Improve, Evaluate, Commit — that proposes a change, tests it, and either commits it as a new version or rolls back. They build a real system (AGS) on top and show consistent gains: e.g., +71% relative on AIME24 with gpt-4.1, state-of-the-art 89.04% on the GAIA agent benchmark (with the largest jump, +33%, on the hardest tier), and pass-rate lifts of 10–27% on a fresh LeetCode coding benchmark — all from inference-time self-evolution, no model retraining required.
Problem & Motivation
Here’s the concrete pain. You build an agent. It works on the demo. In production it hits a case the prompt didn’t anticipate, or a tool that returns a slightly different format, or a task that needs a capability you didn’t ship. The dream is an agent that notices the failure, edits its own prompt or writes a new tool, and keeps going. People build this — and it turns into a maintenance nightmare:
- No lifecycle. When the agent “creates” a new tool, where does it live? When it “updates” a prompt, what was the old one? Most systems mutate a string in place. The previous version is gone.
- No version lineage / rollback. A self-edit makes things worse (very common — LLMs over-correct). With no version history, you can’t revert. One bad self-modification corrupts the agent permanently. The paper’s phrase: “erroneous updates can lead to irrecoverable errors.”
- Monolithic glue code. Because prompts, tools, and memory are baked inside the agent class, every self-improvement routine is custom. You can’t reuse the same optimization logic across a prompt and a tool — they’re different code paths. The optimizer and the thing-being-optimized are tangled together.
- The wrong protocols for the job. MCP standardizes model→tool invocation. A2A standardizes agent→agent messaging. Both leave the internal state of agents and resources opaque. The paper’s key framing: “the core of self-evolution lies not in invocation, but in state mutation and management.” Connectivity protocols are about talking; self-evolution is about changing. Different problem.
So the gap is: nobody has standardized how an agent safely changes itself. The paper’s thesis is that this deserves to be a protocol, not a library — decoupling “what evolves” from “how evolution occurs,” the same way MCP decoupled “what tool” from “how you call it.”
What’s New (Core Contribution)
Four genuine contributions, separating real novelty from repackaging:
-
A protocol layer for self-evolution, not just connectivity (RSPL). Before: MCP/A2A standardize invocation and messaging; agent internals are opaque. Now: five entity types — Prompt, Agent, Tool, Environment, Memory — are modeled as protocol-registered resources with explicit state, a version string, and a controlled interface. Crucially, resources are passive: they contain no optimization logic and cannot self-modify. All mutation happens through a higher layer. This is the architectural move that makes everything else auditable.
-
A typed five-operator algebra for the evolution loop (SEPL). Before: self-improvement is ad-hoc text rewriting (“ask the LLM to fix its prompt”). Now: evolution is a formal control loop of five atomic operators — Reflect (ρ), Select (σ), Improve (ι), Evaluate (ε), Commit (κ) — each with a typed signature. This converts “heuristic text modification” into a “rigorous control loop” with a gating step that enforces monotonic improvement and rollback.
-
Optimizer-agnostic substrate (“variable lifting”). Before: TextGrad optimizes prompts, GRPO/Reinforce++ optimize policy weights — different code, different abstractions. Now: everything evolvable is “lifted” into a uniform set of evolvable variables
V_evo, with a learnability mask saying which are trainable. The same five operators host reflection-based text editing, TextGrad-style “textual gradients,” and RL methods (GRPO/Reinforce++). The protocol doesn’t care which optimizer you plug in. -
A working multi-agent instantiation (AGS) with an Agent Bus. Before: most self-evolution papers are narrow single-agent demos. Now: a full multi-agent system on a shared message bus where prompts/tools/memory are all RSPL resources, sub-agents run concurrently, the orchestrator emits a versioned
plan.md, and successful self-evolutions become immediately reusable by all sub-agents.
The honest read: contributions (1) and (2) are the real intellectual content — formalizing self-evolution as a versioned, gated control loop. (3) is an integration claim (it accommodates existing optimizers, the main experiments use only the reflection one). (4) is solid engineering that proves the protocol is buildable.
How It Works (Technically)
The whole system is two layers stacked: a substrate (what can change) and a control loop (how it changes). Let’s build up both, demystifying the notation as we go.
Layer 1 — RSPL: making everything a versioned resource
A resource entity is the paper’s Definition 3.1:
e_{τ,i} = (n_{τ,i}, d_{τ,i}, φ_{τ,i}, g_{τ,i}, m_{τ,i})
Don’t let the subscripts scare you. τ (tau) is just the type — one of {Prompt, Agent, Tool, Env, Mem}. i is which instance of that type. The tuple says every resource has: a unique name (n), a short description (d), a function φ that maps inputs to outputs (X→Y — i.e., what the resource does: a prompt produces text, a tool produces a result), a trainable flag g ∈ {0,1} (is this thing allowed to evolve?), and a metadata dict (m). That’s it. A prompt, a tool, and a memory store are all the same shape of object. That uniformity is the point.
Each resource also gets a registration record (Definition 3.2) that adds the operational machinery: a version string v (e.g., 1.0.3), an implementation descriptor η (the actual import path / class / source-code string), instantiation parameters θ (constructor args), and exported representations F (the function-calling schema or natural-language contract the LLM sees). When the agent “uses a tool,” it’s reading F; when the agent “rewrites a tool,” it’s producing a new η and bumping v.
These records live in a registry R, and each type binds to a context manager M_τ (the management plane: lifecycle, version lineage, update/restore) and a server interface A_τ (a stable façade so callers don’t touch internals). Around this sit cross-cutting infrastructure services:
- Version manager — auto-increments versions on register/update; every version is an immutable snapshot → rollback, branching, diffing.
- Model manager — one API layer over OpenAI/Anthropic/Google/OpenRouter with routing, fallback, cost-aware selection.
- Dynamic manager — serialize/deserialize configs → hot-swap a resource at runtime without restarting the agent.
- Tracer module — records fine-grained execution traces (inputs, outputs, tool calls, errors, latencies). These traces are the raw material the evolution loop reflects on.
The payoff of all this plumbing: because a tool is now a passive, versioned object behind a stable interface, the same optimizer that improves a prompt can improve the tool. And because every change is a new immutable version, a bad edit is one restore call away from being undone.
Layer 2 — SEPL: the five-operator evolution loop
First, variable lifting. All those heterogeneous resources get projected into one set V_evo (Definition 3.4):
V_evo = (⋃_τ E_τ) ∪ {y}
Plain English: the universe of evolvable things = every resource entity of every type, plus y, the execution artifacts (final outputs and reasoning traces). Each variable carries the learnability bit g, and the trainable subspace is Θ = {v ∈ V_evo : g_v = 1} — i.e., the things the optimizer is allowed to touch. This is exactly the ML idea of “which parameters get gradients,” generalized to prompts and tool code.
Now the loop. SEPL frames evolution as a state-transition function decomposed into five atomic operators. The signatures look intimidating; each is really one sentence. I’ll give the math, then the translation.
| Operator | Signature | What it actually does |
|---|---|---|
| Reflect (ρ) | Z × V_evo → ℘(H) | Read the execution traces Z and current state, output a set of failure hypotheses H (“the prompt lacks edge-case handling”; “this sort is O(n²) on the hot path”). This is the “semantic gradient” — instead of a numeric gradient pointing downhill, you get natural-language diagnoses pointing at what to fix. (℘ just means “set of”; H is the hypothesis space.) |
| Select (σ) | V_evo × ℘(H) → ℘(D) | Turn diagnoses into concrete modification proposals D (“append this constraint to the prompt”; “rewrite this function body”). This is the generative policy — it proposes candidate edits subject to structural constraints. |
| Improve (ι) | V_evo × ℘(D) → V_evo' | Actually apply the edits through the RSPL set_variables interface, producing a provisional candidate state V_evo' (note the prime: it’s a candidate, not yet committed). |
| Evaluate (ε) | V_evo' × G → S | Re-run the task under the candidate state against the goal G; output a score + safety status in evaluation space S. This is the objective function. |
| Commit (κ) | V_evo' × S → V_evo | The gate. Accept the candidate only if it improves performance / preserves safety invariants. Otherwise roll back. This is what makes evolution a directed, monotonic trajectory rather than a random walk. |
The deepest idea here is Reflect = “semantic gradient.” In normal optimization, a gradient is a number telling each parameter which way to move to reduce loss. You can’t backprop through a prompt string. So Reflect approximates a gradient by having the LLM read the failure trace and emit a natural-language explanation of what went wrong — a direction in meaning space rather than parameter space. Select then “descends” that direction by proposing edits. It’s gradient descent with English as the gradient. (This is the same intuition behind TextGrad, which the paper supports as a pluggable optimizer.)
The five operators chain into Algorithm 1:
1. V_evo ← VariableLifting(A) # project the agent's resources into the optimization space
2. Z ← Execute(A, V_evo) # run once, capture traces
3. for t in range(T): # fixed budget of T rounds
4. H ← ρ(Z, V_evo) # Reflect: diagnose failures (semantic gradient)
5. D ← σ(V_evo, H) # Select: propose concrete edits
6. V' ← ι(V_evo, D) # Improve: apply edits -> CANDIDATE version
7. S ← ε(V', G) # Evaluate: re-run, score + safety check
8. V_evo ← κ(V', S) # Commit: accept if better else rollback
9. Z ← Execute(A, V_evo) # re-run with the (possibly) new state
10. if Converged(S): break
11. return V_evo
That’s the entire contribution in nine lines: a versioned, gated, observe→diagnose→propose→test→commit loop where the “gradient” is language and the “parameters” are prompts and tool code.
Architecture & data flow
flowchart TB
subgraph RSPL["Layer 1: RSPL — the versioned substrate"]
direction LR
P[Prompt v1.0.1]
AG[Agent v1.0.0]
T[Tool v1.0.3]
EN[Environment v1.0.6]
ME[Memory v1.0.0]
REG[(Registry + Version Manager<br/>Tracer · Model Mgr · Dynamic Mgr)]
P --- REG
AG --- REG
T --- REG
EN --- REG
ME --- REG
end
subgraph SEPL["Layer 2: SEPL — the evolution loop"]
direction LR
R["Reflect ρ<br/>traces → hypotheses"]
S["Select σ<br/>hypotheses → edits"]
I["Improve ι<br/>apply → candidate"]
E["Evaluate ε<br/>re-run → score"]
C{"Commit κ<br/>better & safe?"}
R --> S --> I --> E --> C
C -->|accept| REG
C -->|reject| RB[Rollback to prior version]
RB --> REG
end
subgraph AGS["Application: AGS multi-agent system"]
BUS(("Agent Bus"))
ORCH[Orchestrator<br/>emits versioned plan.md]
SUB1[Deep Researcher]
SUB2[Browser-Use Agent]
SUB3[Tool Generator]
SUB4[Deep Analyzer]
ORCH --- BUS
BUS --- SUB1
BUS --- SUB2
BUS --- SUB3
BUS --- SUB4
end
REG -- "exported contracts (F)" --> AGS
AGS -- "execution traces (Z)" --> R
REG -. "set_variables interface" .- I
The SEPL loop in motion (schematic). Watch a token of work flow Reflect → Select → Improve → Evaluate → the Commit gate. Green commits create a new version and the score steps up; red rejects roll back to the previous version and the score holds. The point: evolution is a *gated, monotonic* trajectory, not a random walk.
The algorithm, simplified
Here’s the reflection optimizer — the default one used in the experiments — written as code you could actually adapt. The novel part (the gated commit + versioned rollback) is spelled out; model and registry calls are stubbed.
# llm(prompt) -> str : a model call
# registry.set(var, value) : write a NEW version of an evolvable resource (returns version id)
# registry.restore(var, ver): roll back a resource to a prior immutable snapshot
# run(agent, state) -> Trace: execute the task, capture traces (outputs, errors, scores)
def sepl_evolve(agent, evolvable, goal, T=3):
state = {v: registry.get(v) for v in evolvable} # variable lifting: V_evo
trace = run(agent, state) # Z: initial observational trace
best_score = score(trace, goal)
for t in range(T): # fixed budget of T rounds
# Reflect (rho): traces -> natural-language failure hypotheses ("semantic gradient")
hypotheses = llm(f"Given this failure trace, list causal reasons it underperformed:\n{trace}")
# Select (sigma): hypotheses -> concrete edit proposals over the trainable subspace
proposals = llm(f"Propose targeted edits to {list(evolvable)} that fix:\n{hypotheses}")
# Improve (iota): apply edits as a CANDIDATE version (old version stays immutable)
prior = {v: registry.version(v) for v in evolvable} # remember where to roll back
for var, new_value in parse(proposals).items():
registry.set(var, new_value) # auto-increments version
candidate = {v: registry.get(v) for v in evolvable}
# Evaluate (epsilon): re-run under the candidate, get score + safety status
cand_trace = run(agent, candidate)
cand_score, safe = score(cand_trace, goal), passes_safety(cand_trace)
# Commit (kappa): the GATE — accept only if strictly better and safe, else roll back
if safe and cand_score > best_score:
best_score, trace = cand_score, cand_trace # commit: new versions stick
else:
for var in evolvable:
registry.restore(var, prior[var]) # rollback: no side effects
if converged(best_score):
break
return best_score
The two things that make this the paper and not “just ask the LLM to fix itself”: (1) registry.set writes an immutable new version rather than mutating in place, so (2) the else branch can do a clean, side-effect-free rollback. Strip those two lines and you have the brittle ad-hoc systems the paper is replacing.
Built on Prior Work
| Prior idea | What it gave | What Autogenesis changes |
|---|---|---|
| MCP (Anthropic, 2025) | Standardized model→tool invocation | Adds the missing layer: lifecycle, versioning, and safe state mutation of resources — not just invocation. Tools become versioned RSPL resources. |
| A2A (Google) | Standardized agent↔agent messaging | AGS keeps a message bus for coordination but makes the coordination structure itself (plan.md) a versioned, evolvable resource. |
| Agent Skills (Anthropic, 2025) | skills.md-style capability descriptions | Reuses the contract format as the exported representation F for tools, generated automatically to “reduce prompt bloat.” |
| TextGrad (Yuksekgonul 2025) | “Textual gradients” — NL feedback as a gradient on string variables | Generalized: TextGrad becomes one instantiation of (σ, ι) inside SEPL, reusing the standard ε/κ gate. |
| GRPO / Reinforce++ (Shao 2024; Hu 2025) | RL: treat components as a policy, eval signal as reward | Mapped onto the same five operators (ρ samples trajectories, σ ranks by reward, ι does policy-gradient updates, κ commits above a baseline). Shows the algebra spans text-edit and gradient-based optimization. |
| Reflexion-style self-correction | Verbal self-feedback across attempts | Formalized into a typed, versioned, gated loop with rollback — the self-feedback is no longer ephemeral, it’s committed lineage. |
The honest lineage: the components (reflection, textual gradients, versioning, message buses) are not individually new. The contribution is welding them into a single typed protocol where the substrate is decoupled from the optimizer and every change is auditable and reversible.
Results & Evidence
Three benchmark families, all using inference-time self-evolution (no model weights trained) with a budget of 3 optimization rounds.
1. Reasoning/math (GPQA-Diamond, AIME24, AIME25) — evolving prompts and/or solutions, no tools. Four clean findings:
- Weak models gain more, strong models gain less. gpt-4.1 (low baselines) jumped +71.4% relative on AIME24 and +66.7% on AIME25; gemini-3-flash (already 83–88%) gained only ~2–12%. grok-4.1-fast at 96.7% vanilla on AIME24 gained nothing — pure ceiling effect.
- Combined prompt+solution evolution beats either alone across every model — they fix complementary failure modes.
- Math > science QA. AIME gains dwarf GPQA gains, because multi-step derivations expose more correctable intermediate failures, whereas closed-book science QA leans on factual recall (fewer levers).
2. General agent benchmark (GAIA Test, 300 tasks) — evolving tools. AGS hits 89.04% average, state-of-the-art, beating the next-best public entry (ToolOrchestra, 87.38%). The headline: Level-3 (hardest) jumps from 61.22% → 81.63%, a +33.3% relative gain — the single largest improvement in the paper. Tool evolution = synthesize a new tool when none exists, refine source code via reflection when one fails, register both as reusable versioned resources.
3. Coding (in-house LeetCode, 100 fresh problems, 5 languages) — evolving solutions. Pass-rate up 10.1% (Python) to 26.7% (Kotlin); compiled languages hit 98–99/100. Compile/runtime/timeout errors frequently drop to zero. Runtime improves everywhere (−7.8% Python, up to −46% in compiled langs); memory effects are mixed.
What the evidence does and does NOT establish — read this part:
- The 3-round budget is the obvious lever and it isn’t ablated. Each “round” re-runs the task, so this is a compute-for-accuracy trade. There’s no comparison against a simpler baseline that just runs best-of-N sampling or self-consistency with the same compute. A lot of the gain on AIME/LeetCode could be “try 3 times and keep the best,” which doesn’t need a protocol.
- The protocol’s safety machinery (versioning, rollback, lineage) is the headline claim, but the experiments measure accuracy, not safety. There’s no experiment showing rollback prevented a catastrophic self-edit, no measurement of how often Commit rejected, no audit-trail evaluation. The engineering value is plausible but largely asserted, not benchmarked.
- LeetCode benchmark is in-house and the GAIA leaderboard comparison mixes systems with different backbones — apples-to-oranges on model strength.
- “Optimizer-agnostic” (TextGrad/GRPO support) is described, not demonstrated in the main results; everything reported uses the reflection optimizer.
- Gains are real but bounded by headroom — by the paper’s own analysis, strong-model/saturated-benchmark settings show ~0–2%.
Net: solid, consistent inference-time gains, especially on hard tool-using tasks. But the paper validates the optimizer, not the protocol’s distinctive promise (safe, auditable, reversible evolution).
How You’d Use It
For an AI services company, this maps to capability offerings at three tiers of effort:
- “Self-healing tools” as a managed feature (highest ROI, easiest sell). The GAIA result — agent fails on a tool, reflects on the error, rewrites the tool’s source, versions it, reuses it — is the most commercially compelling piece. For a client whose agent breaks every time an upstream API shifts format, a reflection-driven tool-repair loop with versioned rollback is a tangible reliability win you can demo. The versioning is the trust story: “every auto-fix is logged and one click from reverting.”
- A governance/audit layer for client agents. RSPL’s version lineage + tracer module is, frankly, the part enterprise buyers care about. “Our agents improve themselves, and every change is version-tracked, diffable, and reversible” is a compliance-friendly framing competitors using mutate-in-place glue code can’t offer. This is a moat built on auditability, not raw accuracy.
- Inference-time accuracy boost on reasoning/coding deliverables. If you ship code-gen or analysis pipelines, the 3-round reflect-evaluate-commit loop is a drop-in that lifts pass rates 10–27% on coding for the cost of ~3× inference. Easy to price as a “quality tier.”
Where it slots into a multi-agent system you’ve already built (ARC MAS-style): the Agent Bus pattern is familiar. The new bits to graft on are (a) a registry that versions your prompts/tools, and (b) the SEPL gate around any place you currently let an agent edit itself. You don’t need the full formalism to get value — you need versioned resources + a commit gate.
The straight read on hype: the protocol framing is partly academic positioning. In practice you can capture 80% of the value without “implementing AGP.” What’s genuinely worth stealing is the discipline: never mutate a prompt/tool in place; always write a new version behind a gate that can reject and roll back.
Build Your Own (Minimal Recipe)
The smallest thing that captures ~80% of the value — a single-agent reflection-evolve loop with versioned rollback. You can build this in a day.
Components, in build order:
- A versioned resource store. A dict
{name: [v1, v2, ...]}where each version is an immutable snapshot of a prompt string or tool source.set()appends a new version;restore(name, k)points back at version k. This is the whole RSPL idea in ~20 lines — you do not need the full type system to start. - A tracer. Just capture
(inputs, output, error, score)for each run. The richer the trace, the better Reflect works. - The five operators as plain functions.
reflectandselectare LLM calls (one prompt each).improvewrites a new version.evaluatere-runs and scores.commitis anif cand_score > best: keep else: restore. (See the simplified code above — that is the recipe.) - A scorer / objective
G. This is the part that actually matters. For coding: run tests. For math: exact-match. For open-ended tasks: an LLM judge — but then your gate is only as trustworthy as the judge.
The two genuinely hard parts:
- The Evaluate signal. Commit is only as good as
ε. With executable tasks (code, math) you get a clean reward and the loop works great. With fuzzy tasks, you need a reliable evaluator, and a noisy LLM judge can commit regressions it scored as wins. This is where most real effort goes. - Making rollback genuinely side-effect-free. If a tool wrote to disk or mutated shared memory during Evaluate, “rollback” doesn’t actually undo the world. The paper’s “rolled back without side effects” assumes evaluation is sandboxed/pure — your implementation has to enforce that.
Reach for: any LLM SDK for the operators; git (literally — or a content-addressed store) for version lineage; a sandbox (Docker/subprocess with limits) for safe tool evaluation; for the optional RL path, TextGrad or a GRPO implementation behind the same (σ, ι) interface.
How to Improve It
Limitations as leverage — five concrete, testable directions:
- Ablate the protocol against a compute-matched naive baseline. Run best-of-3 sampling / self-consistency with the same inference budget as the 3-round loop. If the gap shrinks, you’ve quantified how much is “the protocol” vs. “more compute.” This is the missing experiment; running it would either strengthen or honestly bound the claims.
- Actually benchmark the safety story. Inject deliberately harmful self-edits and measure rollback rate, Commit rejection rate, and recovery. The paper claims safe-by-construction; nobody has measured it. A “evolution safety” benchmark would be a real contribution on top.
- Learn the Commit threshold instead of hard-coding
>. Commit currently accepts on strict improvement. Under noisy evaluation that’s brittle (commits on lucky variance). Add a statistical gate (e.g., require improvement beyond eval-noise std over k re-runs) or a small learned acceptance policy. - Cross-resource credit assignment. Reflect blames “the prompt” or “the tool,” but failures are often joint. Borrow influence-function or counterfactual-ablation ideas to attribute error across resources before Select proposes edits — fewer wasted rounds.
- Persist evolution across tasks (meta-evolution). Right now
V_evoevolves within a task. The registry already versions everything — so mine the version lineage across many tasks to learn which kinds of edits tend to commit, and bias Select toward them. That turns a per-task optimizer into an agent that gets durably better over its lifetime, which is the actual promise of “self-evolving.”
Glossary
- AGP (Autogenesis Protocol) — the two-layer protocol (RSPL + SEPL) defining what evolves and how.
- AGS (Autogenesis System) — the concrete multi-agent system built on AGP, organized around an Agent Bus.
- RSPL (Resource Substrate Protocol Layer) — Layer 1: models prompts/agents/tools/environments/memory as versioned, passive, registered resources.
- SEPL (Self-Evolution Protocol Layer) — Layer 2: the five-operator (ρ,σ,ι,ε,κ) gated control loop that performs evolution.
- Resource (protocol-registered) — any mutable agent component wrapped with explicit state, a version, and a controlled interface; cannot self-modify.
- Variable lifting — projecting heterogeneous resources into one uniform set of “evolvable variables”
V_evoso one optimizer can touch them all. - Learnability mask (
g) — a 0/1 flag per variable marking whether the optimizer is allowed to change it; the trainable subspaceΘ. - Semantic gradient — natural-language failure diagnosis used as a stand-in for a numeric gradient, since you can’t backprop through a prompt.
- Reflect / Select / Improve / Evaluate / Commit — the five atomic operators: diagnose → propose → apply-as-candidate → score → gate-and-keep-or-rollback.
- Commit gate (κ) — the conditional step that accepts a candidate only if it improves performance and preserves safety, else rolls back; what makes evolution monotonic.
- Version lineage — the immutable history of every resource version, enabling diff, branch, and rollback (think
gitfor prompts and tools). - Agent Bus — shared message channel through which all AGS agents communicate, giving loose coupling and concurrent sub-agent execution.
- Inference-time evolution — improving the agent by editing prompts/tools/outputs at runtime, with no model-weight training.
- TextGrad / GRPO / Reinforce++ — alternative optimizers (NL “textual gradients” / RL policy-gradient methods) that can plug into the same SEPL operator interface.
- GAIA / GPQA-Diamond / AIME — agent (tool-use), graduate science QA, and competition-math benchmarks used for evaluation.