Darwinian Evolver turns an evaluator and a mutator into an optimizer
Framework for evolving code and prompts inspired by Darwinian evolution
At a glance
- What is it?
- darwinian-evolver is an AGPL-3.0 Python framework that keeps a population of solutions, scores them, mutates them and keeps the best, so a prompt or a piece of code improves over iterations. Its appeal is that it works with a mutator that succeeds only a fifth of the time, and its cost is that you must supply a reliable evaluator.
- Who is it for?
- darwinian-evolver is a good fit for a team with a measurable objective, a failing case you can describe to a model, and a mutator you can afford to run many times, since the framework is built to extract progress from unreliable mutation. It is a poor fit for a task where you cannot write an evaluator, for a codebase you cannot safely let a model rewrite, and for a commercial deployment without reading the AGPL.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three components and one orchestrator
The framework's contract is small. To optimize any prompt or piece of code you provide three components. The initial organism is the solution you want to improve, which could be a Python function, a prompt template, or any other piece of code. The evaluator is a data set or function that quantitatively scores an organism, and it must return a numeric score and identify specific failure cases, such as an input where the code produced the wrong output. The mutator is a prompt or agent, typically powered by a language model, that takes an organism and a failure case and attempts to generate an improved version. Everything else is the framework's job. In each iteration it selects promising parents from the population, creates new variations of them, scores their performance, and keeps the best, so the population gradually evolves toward a better solution.
Why it works when the mutator is unreliable
The claimed strength is resilience. The framing is that the approach works even with noisy evaluators or unreliable mutators, and the specific case named is a mutator that produces a better solution only 20 percent of the time, where the framework can still build on those successes to drive progress. This is the design decision that distinguishes the project from a single-shot prompt improvement loop. Instead of asking a model once for a better prompt and accepting the result, the framework runs many trials, discards the failures, and accumulates the wins, which is the same reason genetic algorithms tolerate a low mutation success rate. The parent selection is where that shows up, with weighted sampling choosing a number of parents per iteration, set by --num_parents_per_iteration, and the sampling weight proportional to the product of the sigmoid-scaled performance score and a novelty bonus for parents that have fewer existing children.
Running the parrot example
The quick start is a single command against a bundled example problem called parrot:
uv run darwinian_evolver parrot --num_iterations 3 --output_dir /tmp/parrot_outputThat runs the evolutionary process for three iterations and logs all results. The --output_dir option creates a directory containing results.jsonl, a JSON log of the evolution process, a snapshots/ directory with population snapshots after each iteration, and additional outputs that individual evaluators may write. Other options are listed by uv run darwinian_evolver --help. After the run, there is a way to see what happened rather than reading the log: the included lineage_visualizer.html file opens in your browser, loads the JSON log file, and lets you inspect the evolutionary history of the population. That artifact is a genuinely useful part of the framework, because a run of an evolutionary search is otherwise a wall of numbers.
Defining a problem: the three interfaces
Adapting the framework to your own use case means defining a Problem, and the shape is explicit:
class Problem(Generic[OrganismT, EvaluationResultT, EvaluationFailureCaseT]):
initial_organism: OrganismT
evaluator: Evaluator[OrganismT, EvaluationResultT, EvaluationFailureCaseT]
mutators: list[Mutator[OrganismT, EvaluationFailureCaseT]]The base interfaces live in problem.py, with problems/parrot.py given as a simple example to copy. Your organism is a subclass of Organism holding the data that will be evolved, which could be a prompt template, a block of code, or even a git hash. The evaluator needs a single method, evaluate, which scores an organism's performance and returns an EvaluationResult containing a numeric score and a list of EvaluationFailureCases, and you will likely want to subclass EvaluationFailureCase so your evaluator can pass structured information about a failure to your mutators. The mutator takes an organism and failure cases and tries to produce one or more new, improved organisms.
The mutator is where your model call lives
A typical mutator runs in two steps. First it uses a language model to inspect the organism and the failure case and produce a diagnosis of what went wrong, with the model proposing an improvement whose goal is to avoid that type of failure going forward. Second it generates an updated organism by implementing the proposal. The README points at Darwin Goedel Machines and their appendix on self-improve prompts as the reference for that improvement prompt, and notes that the proposal is then handed to a coding agent for implementation, so the mutator can be a prompt template plus an agent rather than a plain model call. Mutators can be static or self-improving. A static mutator uses fixed external logic, such as a call to a specific model API. A self-improving mutator uses the organism itself to generate the mutation, which is common when the organisms being evolved are themselves capable agents, such as an AI coding agent improving its own source code. Self-improving mutators are what Darwin Goedel Machines are based on, and they rest on an assumption that an improved score implies, at least directionally, an improved ability to improve themselves.
Dependencies and the shape of the package
The pyproject describes a fairly conventional Python project with one unusual feature set. The build backend is setuptools, the package is darwinian-evolver at version 0.1.0, and it requires Python 3.11 or newer. Runtime dependencies are the model clients anthropic at 0.78.0 or newer, openai at 2.16.0 or newer and google-genai at 1.56.0 or newer, so the framework itself does not pick a vendor. Alongside them are numpy at 2.4.2 or newer, pydantic for the typed data structures, jinja2 for templating, func_timeout to bound calls that might hang, and fsspec for filesystem access. Dev and test groups add tqdm, pandas, ruff and pytest. The repository also carries an offload.toml and an .offload-image-cache file, a .devcontainer directory, a scripts/ directory, a uv.lock, and a set of readme images showing score and sharpness settings at different midpoints.
Where Darwinian Evolver is the wrong tool
Three cases. The first is a problem you cannot score, because the framework's whole loop is driven by a numeric score from an evaluator, and a task with no objective function has nowhere to start even with a perfect mutator. The second is a codebase you would not let a model rewrite, since a mutator that receives a failing case and a source file will produce a new source file, and the framework's safety net is the evaluator, which is exactly the component that is hardest to make complete. The third is a one-shot improvement, where a single request to a model with a good failure description is cheaper and faster than running populations across iterations, and the framework only pays for itself when the search is genuinely hard. Against a direct prompt-optimisation loop, the difference in approach is that the framework explores a population with novelty-weighted parent selection and keeps history, which buys it the ability to exploit a mutator that is right only occasionally. The cost is token spend per iteration, an evaluator you have to maintain, and the fact that the outcome is a search trace rather than a single artifact you can review.
Editorial conclusion
darwinian-evolver is a good fit for a team with a measurable objective, a failing case you can describe to a model, and a mutator you can afford to run many times, since the framework is built to extract progress from unreliable mutation. It is a poor fit for a task where you cannot write an evaluator, for a codebase you cannot safely let a model rewrite, and for a commercial deployment without reading the AGPL. Check three things before you start: that your evaluator returns a numeric score and specific failure cases, because a mutator that receives a score without a failing example has nothing to act on; that the mutator is allowed to call a model you pay for, since the mutator is where the LLM work happens and the framework ships the anthropic, openai and google-genai clients; and that the licence suits your deployment, because the project is AGPL-3.0, which is a copyleft licence with obligations that a permissive licence would not impose.
Frequently asked questions
What do I need to provide to use darwinian-evolver?
Three components: an initial organism, which is the solution you want to improve and could be a Python function, a prompt template or any other code; an evaluator that returns a numeric score and identifies specific failure cases; and a mutator, a prompt or agent typically powered by an LLM, that takes an organism and a failure case and tries to produce an improved version.
How do I run the parrot example?
Run uv run darwinian_evolver parrot --num_iterations 3 --output_dir /tmp/parrot_output, which runs three iterations and writes results.jsonl, a snapshots/ directory of population snapshots after each iteration, and any extra outputs the evaluators produce. Other options are listed with uv run darwinian_evolver --help.
How do I see what happened during an evolution run?
Open the included lineage_visualizer.html file in a browser, which loads the JSON log file and lets you inspect the evolutionary history of the population.
What is the difference between a static and a self-improving mutator?
A static mutator uses fixed external logic, such as a call to a specific LLM API, to generate improvements. A self-improving mutator uses the organism itself to generate the mutation, which is common when the organisms being evolved are capable agents, such as an AI coding agent improving its own source code.
Which LLM providers does darwinian-evolver support?
The project depends on the anthropic, openai and google-genai client libraries, and the framework does not pick a vendor for you. It also requires Python 3.11 or newer, with numpy, pydantic, jinja2, func_timeout and fsspec as the other runtime dependencies.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/imbue-ai-darwinian-evolver)Was this useful?