Blog post

How to Build Self-Evolving Enterprise Agents

Of all the enterprise agents that come out of a POC, we all know only a select few see the light of day and go into production, the rest never rise from the POC bed. And the ones that do make it behave just like a conventional machine learning model, the performance drifts with time. The prompts get stale, edge cases nobody planned for start piling up in a backlog, and every couple of weeks someone has to go and patch the system prompt by hand. The agent does not get better on its own, it just decays until a human steps in.

A self-evolving agent is our answer to that drift. Instead of waiting for the next manual release, it learns from its own production signal and improves itself. Now “self-evolving” is a term that gets thrown around a lot these days, so before we talk about how to build one, let me draw two lines so we are all talking about the same thing.

So what does self-evolving actually mean

  • Static agent — fixed prompt, fixed tools. Every improvement is a human commit.
  • Agent with memory — it remembers things across turns, but its policy, the way it actually decides and acts, is frozen. This is what most “agentic memory” ships as today.
  • Self-evolving agent — it changes its own policy: the instructions, the exemplars, how memory is curated, how it routes, how many sub-agents it spawns. This is the one we care about.

Here is the catch. A system that rewrites its own behavior in production is exactly the system a security-minded enterprise should trust the least. So really it all comes down to one thing, we let the agent propose changes, but we never let it push one into production unchecked.

Two loop for Self evolution:

The slow loop is parametric. You collect the interaction data, you curate it, and you retrain the model weights, SFT first and then preference optimization like RLHF or DPO. This is real learning, but it is heavy, we are talking weeks of data curation, a training run, an eval campaign, and then a deployment. And it should never be your first move. Anybody who has trained a conventional ML model knows the feeling, you get a beautiful training curve and then the model still falls flat in production. Same story here. Most of the time what was broken was not the weights at all, it was the scaffolding around them. So reach for the slow loop only after you have squeezed the fast one dry and you actually have evidence the capability is missing from the base model.

And that slow loop is getting cheaper, which is worth a mention. A newer family of techniques, on-policy self-distillation, has the same model play both teacher and student. The teacher copy gets to see privileged information the student does not, the reference outcome, whether the ticket actually resolved, the user’s own feedback, and it uses that to hand the student dense token-level supervision on the traces the student generated itself, instead of the single sparse reward per trajectory that RLHF has to work with. Because the traces come from the model itself, you sidestep the train/test mismatch that hurts ordinary distillation, and because there is no separate frozen teacher, you are not boxed in by whatever external model you happened to distill from.1

Two things to keep honest about it, since both are easy to state wrong. The objective is reverse KL, the student matched to the privileged teacher, and reverse KL is mode-seeking, it pushes the student to lock onto the teacher’s high-probability modes rather than spread its mass across all of them. That is the opposite of mode-covering, so it is worth getting the direction right, though for LLMs even this is debated, some recent work argues the clean mode-seeking versus mode-covering story does not really hold on discrete token distributions.2 And the ceiling is not actually unlimited. Dropping the external teacher frees you from its cap, but the new cap is however well the model can do once it has the privileged information in hand, and leaning on that signal alone is known to leak and go unstable. So it is a much higher ceiling, not the absence of one.

The fast loop is harness optimization. The harness is everything wrapped around the frozen model, the system prompt, the memory policy, the tool selection, the routing, how many child agents get spawned. An agent with memory alone can only take you so far, it accumulates experience but it never learns how to use that experience. This is where harness evolution adds a meta-learning layer on top, it learns which memories are worth keeping, how the instructions should be phrased, when to fan out into sub-agents. There is no gradient step and no training run here, you are just searching over the configuration that decides how the agent behaves. It runs in minutes instead of weeks, and honestly this is where almost all of your early wins are hiding.

The recent survey literature draws this exact split, one feedback loop whose optimizers can go after the model weights, or after the prompts, memory, tools, and workflow around them.3 So my advice is pretty simple, start with the harness first and earn your way to the weights.

How the fast loop actually runs

The fast loop is really just one cycle:

act → collect signal → evaluate → propose → gate → deploy

The agent runs on live traffic and every trace gets an outcome score. Then on a schedule, nightly or weekly and not on every single request, the system looks at where the harness is weak, has an LLM draft a candidate change from those failures, and makes that candidate earn its way in through a gate. If you let the agent modify itself on every request, it will reward-hack itself into nonsense by Tuesday afternoon, so batching the loop is what keeps it governable. And notice the agent never edits itself directly, it only drafts a proposal. Everything after “propose” is there to figure out whether that proposal is a real improvement or just noise.

Written out, that whole thing is just a hill climb. Your current harness is where you are standing, propose hands you a neighbor one step away, and the gate is the test for whether that step actually goes uphill. You take the move only if it does, and the harness you hold onto is the high point you climb from next time. The two functions it leans on, score_outcome and passes_gate, are what the next couple of sections build out.

def evolve(harness, traffic, eval_set, rounds):
    best = harness                                    # incumbent / last-known-good
    for _ in range(rounds):
        traces = [run(best, x) for x in traffic]      # act + collect signal
        weak   = [t for t in traces if score_outcome(t) < THRESHOLD]
        if len(weak) < MIN_FAILURES:
            continue                                  # not enough signal, hold position
        candidate = propose(best, weak)               # a neighbor in config space
        if passes_gate(candidate, best, eval_set):    # uphill only, no regressions
            best = deploy_with_canary(candidate)      # accept, advance the incumbent
    return best

And here is the thing about a hill climb, it gets stuck. Sooner or later the search plateaus, every neighbor the proposer hands you fails the gate, and the harness stops improving no matter how many rounds you run. That plateau is actually useful information, it is the loop telling you that whatever you are missing is probably not something you can prompt or route your way into. That is the moment the slow parametric loop finally earns its keep, it is the bigger jump that local search on the harness just cannot make.

First you need a number you can trust

You cannot evolve anything without a number you actually trust. There are four sources, roughly from most to least reliable:

  1. Explicit outcomes — did the ticket resolve, did the transaction clear, did the user accept the draft. This is ground truth, but it is sparse and it comes late.
  2. Implicit signals — the user edits your output, retries, or escalates. Cheap and plentiful, but noisy.
  3. LLM-as-judge — a separate model scores the traces against a rubric. Scales beautifully, but it drifts if you never calibrate it against human labels.
  4. Curated eval sets — a frozen, human-blessed set of hard cases. This is your source of truth for the gate.

In practice you blend the cheap signals into one score and calibrate the weights against human labels:

def score_outcome(trace):
    explicit = 1.0 * trace.resolved
    implicit = -0.3 * trace.user_edited - 0.5 * trace.escalated
    judged   =  0.4 * llm_judge(trace)      # 0..1 rubric score
    return clip(explicit + implicit + judged, 0, 1)

Two rules of thumb keep you out of trouble. Do not propose a change off fewer than about 50 failure traces, and do not trust an LLM judge you have not spot-checked against humans on at least 100 examples. Evolution on a small sample overfits to noise every single time, same as it does in classical ML.

So what are we actually evolving

Not every knob is equally safe to turn. Here they are, ranked by reward against risk:

  • Few-shot examples (low risk) — promote your real production successes into the prompt. Highest ROI, and trivial to roll back.
  • System prompt / instructions (medium) — rewrite the guidance that failed on a cluster of cases. Powerful and easy to read.
  • Memory curation (medium) — what to store, what to forget, what to surface. This is where meta-learning actually beats raw memory.
  • Routing and sub-agent count (medium) — when to escalate, which tool to reach for, how many child agents to spawn.
  • Tool definitions (high risk) — letting the agent write its own new tools. Huge leverage, and also the fastest way to a security incident. Gate this one the hardest.

Think of that ranking as a curriculum you actually work through. You do not get to just grab whatever knob you feel like, you earn the riskier ones by first proving the gate holds on the safe ones.

In the enterprise, the gate is everything

Now if you are hacking on a weekend demo, sure, the loop is the fun part and you can get away with a lot. But once you are building this for the enterprise, all the real work actually sits in the gate. A candidate only earns its way into production if it beats the incumbent on a frozen eval set, with no regressions, no cost blowup, and a clean safety pass:

def passes_gate(candidate, incumbent, eval_set):
    c, i = run_eval(candidate, eval_set), run_eval(incumbent, eval_set)
    return (c.mean_score > i.mean_score + MIN_GAIN   # real gain, not noise
            and c.regression_count == 0              # protected cases hold
            and c.p95_latency <= i.p95_latency * 1.15 # no cost blowup
            and passes_safety_suite(candidate))       # injection, PII, policy

And this is not me being paranoid. Self-evolving agents can genuinely misevolve, they drift into emergent and unsafe behavior as they optimize.4 A few decisions keep that in check:

  • The eval set is frozen and human-owned. If the agent can edit its own exam, the whole thing collapses into cheating. Keep it in a separate repo behind human review.
  • A human approves the merge. Auto-canary a passing candidate to 5% of traffic, but make a person promote it to 100%. The loop drafts the pull request, it does not get to merge it.
  • Every change leaves a trail. Log every proposal with the signal that set it off, the eval numbers it cleared, whoever signed off on it, and the version you can roll back to. Months later, when the agent does something you did not expect, that trail is what turns a scramble into a five-minute rollback, and it is also what your auditors are going to ask for.

The ways this quietly goes wrong

  • Reward hacking — the agent games your metric. Keep a held-out judge and do human spot-checks.
  • Eval overfitting — candidates start memorizing the eval set. Rotate hard cases in, keep a hidden slice.
  • Drift into nonsense — small changes compound. Cap how far one cycle can move, and pin the last-known-good.
  • Cost creep — evolved prompts get longer and slower. Gate on p95 latency and token cost, not just quality.
  • Trace-store sprawl — the production traces you are learning from are full of PII and customer data. The moment they become an evolution signal you have to govern the signal store like any other sensitive dataset, who gets to see it, whether it is ever allowed near a training set, and how long you hold onto it.

So where do you actually start

Do not build the whole loop on day one, and do not go anywhere near the weights. Just build this:

  1. Log every trace with an outcome score.
  2. Freeze 50 to 100 hard cases as an eval set a human owns.
  3. Once a week, have an LLM propose few-shot examples only from your worst traces.
  4. Gate against the eval set, let a human approve, canary at 5%.

What you end up with is the fast loop with a safety governor bolted on top of it. And in the enterprise, that is really the only kind worth shipping first.

  1. Zhao et al., Self-Distilled Reasoner: On-Policy Self-Distillation for Large Language Models, arXiv:2601.18734. The variant that uses outcome / environment feedback as the privileged signal is SDPO (Hübotter et al., 2026). On-policy distillation itself traces to Agarwal et al., GKD, 2024. ↩

  2. Reverse KL is the standard mode-seeking objective in distillation (Agarwal et al., GKD, 2024). For the LLM-specific caveat that the mode-seeking / mode-covering distinction does not cleanly hold on discrete token distributions, see Wu et al., Rethinking Kullback-Leibler Divergence in Knowledge Distillation for LLMs, arXiv:2404.02657. ↩

  3. A Comprehensive Survey of Self-Evolving AI Agents: A New Paradigm Bridging Foundation Models and Lifelong Agentic Systems, arXiv:2508.07407. ↩

  4. Shao et al., Your Agent May Misevolve: Emergent Risks in Self-Evolving LLM Agents, arXiv:2509.26354. ↩

Comments
← back to blog