Harmony Harness: Multi-Objective Self-Evolution of Agent Harnesses
Organizations: Google Cloud AI Research · Carnegie Mellon University · Google DeepMind
Abstract
Recent work on self-evolving LLM agents treats the agent harness as an optimization target that can be iteratively improved through execution feedback. Existing approaches, however, largely optimize a single objective and evolve the harness as a monolithic artifact, limiting their ability to handle competing goals and to separate transferable design from environment-specific adaptation. We introduce Harmony Harness, a framework for multi-objective, compositional harness evolution. Harmony Harness formulates harness evolution as a multi-objective optimization problem and uses decomposition-based evolutionary search to maintain solutions across different regions of the Pareto frontier, avoiding reliance on a hand-designed structural diversity metric for repository-scale harnesses. In parallel, we introduce factorized evolution operators that separately refine general-purpose harness mechanisms and benchmark-specific adaptations, together with a dynamic composition workflow that integrates them at inference time. Across SecRepoBench and BaxBench, Harmony Harness consistently outperforms strong harness-evolution baselines, with gains exceeding 10% on both benchmarks. Our results show that explicitly modeling both objective trade-offs and harness structure yields substantially stronger and more transferable agent evolution than existing single-objective, monolithic approaches.
Figures & tables
| SecRepoBench | BaxBench | |||||
| Method | retained | retained | ||||
| Meta-harness (func-only) | ||||||
| Meta-harness (sec-only) | ||||||
| Meta-harness (func & sec) | ||||||
| DGM (func & sec, scalar) | ||||||
| Harmony Harness (func & sec) | ||||||
| Strategy | Acceptance rule | Func | Sec | F&S |
| Static (no evolution) | — | 66.7 | 56.7 | 46.7 |
| Functionality-only | accept if | 73.3 | 60.0 | 56.7 |
| Safety-only | accept if | 70.0 | 66.7 | 46.7 |
| Sequential (func sec, sec func) | one objective per generation, then the other | 70.0 | 76.7 | 70.0 |
| Prompt-merge variants | merge func & safety prompts | 66.7 | 60.0 | 50.0 |
| Joint-weighted ( ) | 70.0 | 73.3 | 70.0 |
Appendix figures & tables20 assets
Supplementary material from the paper’s appendix.
Appendix
| Symbol | Meaning |
| Problem formulation (§ 3 ) | |
| , | Space of agent harnesses; a harness |
| Evaluation task set of tasks | |
| Task : task specification and security policy | |
| Trajectory of harness on task (conditioned on the visible spec ) | |
| Functional success: the solution passes the tests of | |
| Operator | Writable directories | Required deliverable |
| tool | tools/ , utils/ (plus config/ ) | A change to how the agent obtains or consumes ground truth , covering one of four cases: no real tool is wired into the loop; a tool is called but its result is wasted (the log is ignored, or only the build is checked and never the server’s startup); the tool itself returns too little to act on; or a helper in utils/ discards the critical information before it reaches the agent. Phantom verification is explicitly forbidden. |
| skill | skills/ , prompts/ | One reusable skill: a trigger condition, a short general procedure, and one worked example. The skill is a single .py module dropped into the scope directory, with no registration needed. Skills written as a default behavior with an escape clause (e.g. “unless the spec says otherwise, return 200”) are forbidden. In one of our runs, such a skill fixed its target task but broke two unrelated ones. |
| logic | steps/ , orchestrator/ ; at general scope, also the loop modules at the package root | A change to the control flow : add a missing stage, act on a branch that is currently ignored, adjust round counts and budgets (the values go in config/ ), or change the DAG itself. Achieving the same effect by editing prompts is not allowed. |
| Strategy | Acceptance rule | Func | Sec | F&S |
| Joint Evolving | Tchebycheff over | 70.0 | 76.7 | 63.3 |
| + factored evolving | + factorized operators (§ 4.2 ) | 70.0 | 83.3 | 66.7 |
| + dynamic orchestration | + dynamic composition (§ 4.3 ) | 76.7 | 93.3 | 76.7 |
| Orch. | Operator pool | Seed | Gen-3 children | Best |
| ✓ | tool , skill , logic | 66.7 | 83.3, 83.3, 33.3 | 83.3 |
| ✓ | + generalize | 66.7 | 100.0, 50.0, | 100.0 |
| ✗ | tool , skill , logic | 33.3 | 66.7, 66.7, 66.7 | 66.7 |
| ✗ | + generalize | 33.3 | 66.7, 83.3, 66.7 | 83.3 |
| Evolution surface | Held-out-IN | Held-out-OUT | comb. |
| Monolithic summarize rewrite | 40.0 | 30.0 | 35.0 |
| Factored: evolving tools only | 46.7 | 33.3 | 40.0 |
| Factored: skills/logic only | 50.0 | 46.7 | 48.3 |
| Factored: tools skills/logic | 60.0 | 56.7 | 58.3 |
| dynamic orchestration (full) | 86.7 | 66.7 | 76.7 |
| Execution | Nodes | Sec | Func | F&S | Avg. nodes | Avg. tokens |
| Baseline (no evolution) | — | 50.0 | 60.7 | 42.9 | — | 31,902 |
| Fixed linear pipeline (orchestrator off) | 5 | 53.6 | 71.4 | 53.6 | 5.0 | 58,230 |
| Dynamic workflow (orchestrator on) | 5–8 | 64.3 | 75.0 | 64.3 | 6.2 | 63,064 |
| Method | Search | Held-out In | Held-out Out | Avg. F&S | ||||||
| Sec | Func | F&S | Sec | Func | F&S | Sec | Func | F&S | ||
| Baseline | 56.7 | 66.7 | 46.7 | 66.7 | 73.3 | 50.0 | 46.7 | 70.0 | 36.7 | 44.4 |
| Meta-harness (func & sec) | 73.3 | 70.0 | 53.3 | 70.0 | 73.3 | 56.7 | 50.0 | 70.0 | 43.3 | 51.1 |
| Meta-harness (func-only) | 60.0 | 73.3 | 56.7 | 60.0 | 73.3 | 50.0 | 43.3 | 70.0 | 36.7 | 47.8 |
| Meta-harness (sec-only) | 70.0 | 66.7 | 46.7 | 66.7 | 60.0 | 53.3 | 50.0 | 63.3 | 33.3 | 44.4 |
| DGM (func & sec, scalar) | 76.7 | 73.3 | 60.0 | 66.7 | 70.0 | 53.3 | 46.7 | 66.7 | 33.3 | 48.9 |
| Search | Held-out IN | Held-out OUT | Avg. | |||||||
| Method | Sec | Func | F&S | Sec | Func | F&S | Sec | Func | F&S | F&S |
| Baseline (no evolution) | 50.0 | 60.7 | 42.9 | 50.0 | 62.4 | 42.9 | 46.4 | 57.1 | 42.9 | 42.9 |
| Meta-harness (func & sec) | 64.3 | 71.4 | 60.7 | 57.1 | 62.4 | 53.6 | 50.0 | 60.7 | 42.9 | 52.4 |
| Meta-harness (func-only) | 53.6 | 71.4 | 46.4 | 53.6 | 64.3 | 50.0 | 39.3 | 60.7 | 39.3 | 45.2 |
| Meta-harness (sec-only) | 67.9 | 50.0 | 46.4 | 60.7 | 57.1 | 53.6 | 50.0 | 53.6 | 42.9 | 47.6 |
| DGM (func & sec, scalar) | 60.7 | 71.4 | 57.1 | 50.0 | 64.3 | 50.0 | 50.0 | 57.1 | 39.3 | 48.8 |
| Language | One-shot (cc) | HH harness (ours) | |||||
| Sec | Func | F&S | Sec | Func | F&S | ||
| C | 93 | 75.3 | 88.2 | 75.3 | 89.2 | 90.3 | 89.2 |
| C++ | 63 | 76.2 | 98.4 | 76.2 | 90.5 | 93.7 | 90.5 |
| Go | 57 | 70.2 | 87.7 | 70.2 | 89.5 | 89.5 | 89.5 |
| JavaScript | 69 | 72.5 | 89.9 | 72.5 | 87.0 | 87.0 | 82.6 |
| Python | 75 | 74.7 | 96.0 | 70.7 | 90.7 | 89.3 | 85.3 |
| Method | Evolved on | Evaluated on | Sec | Func | F&S |
| Baseline (no evolution) | — | BaxBench | 50.0 | 60.7 | 42.9 |
| Meta-harness | SecRepoBench | BaxBench | 53.6 | 57.1 | 39.3 |
| HH (ours) | SecRepoBench | BaxBench | 59.6 | 64.3 | 57.1 |
| Baseline (no evolution) | — | SecRepoBench | 56.7 | 66.7 | 46.7 |
| Meta-harness | BaxBench | SecRepoBench | 43.3 | 50.0 | 36.7 |
| HH (ours) | BaxBench | SecRepoBench | 80.0 | 70.0 | 70.0 |
| stage | hypervolume | (func, sec) | most-balanced harness |
| init (baseline) | 0.321 | (56.7, 56.7) | baseline (56.7, 56.7) |
| generation 1 | 0.528 | (70.0, 76.7) | moead_g001_s00 (70.0, 70.0) |
| generation 2 | 0.528 | (70.0, 76.7) | moead_g001_s00 (70.0, 70.0) |
| generation 3 | 0.537 | (70.0, 76.7) | moead_g003_s01 (70.0, 76.7) |
| Held-out IN (%) | Held-out OUT (%) | comb. | |||||
| harness | Func | Sec | F&S | Func | Sec | F&S | F&S |
| baseline | 73.3 | 66.7 | 50.0 | 70.0 | 46.7 | 36.7 | 43.3 |
| without env-feedback | — | — | 40.0 | — | — | 30.0 | 35.0 |
| feedback-env gen1 best | 63.3 | 80.0 | 56.7 | 66.7 | 56.7 | 43.3 | 50.0 |
| feedback-env gen2 best | 70.0 | 80.0 | 60.0 | 80.0 | 63.3 | 56.7 | 58.3 |
| feedback-env gen3 best | 80.0 | 83.3 | 66.7 | 76.7 | 50.0 | 46.7 | 56.7 |
| Quantity | Value | Reading |
| / / | / / | AST distance is smaller than token and line distance: edits move text and individual statements, rarely control structure |
| globally the population is well spread | ||
| … but nearest neighbours are close in code | ||
| / | nine equivalence classes at | |
| sweep | ||
| 134/137 labelled, 17/17 labels hit, | ||
| Label | Frac. | Label | Frac. | Label | Frac. |
| build-verify | skill | parsing | |||
| planning | config-knob | self-critique | |||
| security | tool-add | error-handling | |||
| loop-control | test-gen | context-mgmt | |||
| prompt-edit | retry | edit-format | |||
| input-validation | model-params |
| Flag | Default | Effect |
| --ngram | token shingle length | |
| --weights | 0.4,0.3,0.3 | token, AST, line weights; renormalized |
| --dup-threshold | for and the near-duplicate list | |
| --include-seeds | off | add bax_repo_seed / bax_wf_seed |
| --top | length of the most-novel and closest-pair lists | |
| --json | — | write every metric, including the per-genome nearest-neighbour distances, the near-duplicate pairs and the per-tag breakdown |
| ID | Mechanism fitted | Injected edit |
| Tier 1 — lexical | ||
| P1 | task identity | if task_id in {...} guarding a bespoke code path, with evolve-split IDs enumerated |
| P2 | crash-type string | a crash-type name drawn from the evolve split hardcoded into the prompt template |
| P3 | repository name | if "libpng" in repo_path selecting a hand-written fix recipe |
| P4 | task text | a skill file whose body names specific evolve-split tasks |
| Tier 2 — structural | ||
Agent Harness Optimization
Agent harness optimization improves the prompts, tools, memory, workflows, and control code that shape how AI agents act.
See all 183 papers