DEV Community

Cover image for Building Agent Genome: making evolutionary AI visible
Harish Kotra (he/him)
Harish Kotra (he/him)

Posted on

Building Agent Genome: making evolutionary AI visible

Agent Genome is an interactive browser exhibit built around a simple question: how do you explain evolving agents without asking people to read logs or trust an aggregate chart?

The answer is a small, visual population. Forty glowing organisms move across a sparse resource-and-hazard field. Every organism has an explicit 1–10 genome. Visitors can inspect an organism’s traits, current intent, recent reflection, fitness, memory, and lineage; when the generation ends, they see selection, inherited strategies, and mutation produce the next cohort.

The product decision: reveal the mechanism

This is intentionally neither a chat application nor a game. The user should be able to answer three questions almost immediately:

  1. What are the organisms doing?
  2. Why does one behave differently from another?
  3. How does a generation change the next one?

the mechanism

The dark field, energy sources, hazards, trails, specimen panel, and full-screen generation transition are all designed to expose that causal chain rather than decorate it.

A monorepo with deliberate boundaries

Agent Genome uses a Turborepo workspace. This is not architectural theatre: genetics, simulation, memory, and rendering should evolve independently.

apps/web             Next.js UI, Canvas renderer, local interaction state
packages/genome      Trait types, clamping, crossover, mutation
packages/simulation  Parent selection and offspring construction
packages/core        Structured memory capped at 20 records
packages/agents      Goal vocabulary
packages/langgraph   Real LangGraph reflection workflow and provider adapters
packages/ui          Genome colour tokens
packages/shared      Shared constants
Enter fullscreen mode Exit fullscreen mode

The web app owns transient visual positions and trails. Selection and crossover belong in pure TypeScript modules. This means a future React Three Fiber scene, headless simulation runner, or replay UI can reuse the same genetics API.

A readable genome

Every trait is a whole number between 1 and 10. That range is a product choice: a visitor can understand it instantly, a bar chart stays useful, and a mutation is easy to describe.

export type GenomeTrait =
  | 'planning' | 'memory' | 'risk' | 'curiosity'
  | 'cooperation' | 'explorationRadius'
  | 'toolUsage' | 'reflectionFrequency';

export type Genome = Record<GenomeTrait, number>;

export const clampTrait = (value: number) =>
  Math.max(1, Math.min(10, Math.round(value)));
Enter fullscreen mode Exit fullscreen mode

Bound the values at the domain edge, not in the renderer. That lets every downstream consumer assume genome data is valid.

Crossover and mutation without a black box

For each trait, a child inherits a value from one parent, then has a small chance of a one-step mutation:

export function crossover(a: Genome, b: Genome, mutationRate = 0.08): Genome {
  const child = {} as Genome;
  for (const key of Object.keys(a) as GenomeTrait[]) {
    const inherited = Math.random() > 0.5 ? a[key] : b[key];
    const mutation = Math.random() < mutationRate
      ? (Math.random() > 0.5 ? 1 : -1)
      : 0;
    child[key] = clampTrait(inherited + mutation);
  }
  return child;
}
Enter fullscreen mode Exit fullscreen mode

Parent selection takes the highest-fitness segment of the population. It is intentionally simpler than a research-grade evolutionary system because inspectability is the point.

const elite = [...population]
  .sort((left, right) => right.fitness - left.fitness)
  .slice(0, Math.ceil(population.length * 0.35));

const parentA = elite[index % elite.length];
const parentB = elite[(index * 7 + 3) % elite.length];
Enter fullscreen mode Exit fullscreen mode

Later versions can add tournament selection, novelty search, or seeded randomness and replay. They should retain a visible explanation for every evolutionary event.

The simulation is autonomous; LLM reflection is optional

The project names a cognition loop even though the initial exhibit uses deterministic browser-safe behaviour:

simulation is autonomous

packages/langgraph implements a LangGraph flow behind the optional reflection action. LangChain adapters connect it to Ollama, LM Studio/OpenAI-compatible endpoints, OpenAI, and Anthropic. A model-backed reflection node looks at structured recent outcomes and proposes a bounded change—for example, lowering risk after costly exploration. It cannot rewrite simulation code, bypass trait limits, or issue arbitrary actions.

The simulation itself does not need a provider or API key. Its fitness is calculated from live world events: resources collected, hazard avoidance, proximity-based cooperation, and exploration. This preserves an important distinction: memory records an organism’s recent experience; evolution changes the strategy distribution across a population. LLM reflection is an optional, evidence-constrained layer on top of that loop—not the source of the simulation’s behaviour.

The reflection action sends the selected organism’s goal, current fitness, structured memory, and genome to the selected provider. It returns strict JSON containing one sentence and permitted bounded trait changes. There is no tool access, code execution, RAG, vector database, or persistent provider conversation.

Why the initial renderer is Canvas 2D

Three.js and React Three Fiber are listed as an upgrade path, but Canvas 2D is the right first rendering layer. It keeps the 40-organism field inexpensive while still providing glow, trails, particles, resources, hazards, and selection feedback.

The animation loop updates motion imperatively while React handles interface state:

a.vx += Math.cos(angle) * 0.004 * (a.genome.curiosity / 5);
a.vy += Math.sin(angle) * 0.004 * (a.genome.curiosity / 5);
a.vx *= 0.97;
a.vy *= 0.97;
a.x = Math.max(3, Math.min(97, a.x + a.vx * speed * dt * 50));
Enter fullscreen mode Exit fullscreen mode

This is expressive visual motion, not a physics engine. Keeping it light gives the project a clear 60 FPS target without shaders or high-frequency React state updates.

Run and extend it

npm install
npm run dev --workspace=@agent-genome/web
npm run build --workspace=@agent-genome/web
Enter fullscreen mode Exit fullscreen mode

The highest-value next additions are richer resource respawning and cooperation events, mutation replay, generation comparison, JSON genome snapshots, reduced-motion and keyboard support, deterministic seeds, and a WebGL renderer that consumes the same simulation contract.

The guiding principle is simple: make every change in the population explainable to the person watching it.

how it works

Code & more: https://www.dailybuild.xyz/project/212-agent-genome

Top comments (0)