HELIX: Model-Harness Co-evolution for Recursive Self-Improvement
Organizations: In3 Lab, The University of Hong Kong
Abstract
Scaling agent capability has largely focused on improving the model, yet an interactive agent acts through a runtime harness that mediates context, tools, control flow, and stopping. The harness shapes both what a model can accomplish and the trajectories from which it learns. This coupling motivates model-harness co-evolution for recursive self-improvement: build harnesses for a fixed model, update the model from verified sibling trajectories, and rebuild the harnesses as model capabilities change. Realizing this loop requires a controlled way to evolve harnesses while preserving intervention identity and effect. We present HELIX, a source-traceable substrate for harness evolution. HELIX decomposes agent systems into typed ports, reusable atoms, recipes, product shells, and runtime policies. It makes interventions explicit and auditable while retaining trajectories, test outcomes, and provenance. Harness evolution thus serves two linked roles: improving fixed-model execution and producing matched successes, regressions, near misses, and alternative solutions as data for subsequent model improvement. We evaluate HELIX in one evolution round on code repair. A 65-candidate portfolio discovers a fixed harness that improves task coverage by 4.0% over Pi, while the full portfolio exposes up to 58.0% more verified coverage through complementary sibling behavior. Selected candidates are assessed with repeated runs and the SWE-bench evaluator. A 200-slot sibling slice yields 438 verified SFT, critic, filter, and preference records. These results show how harness, model, and data form a feedback system: harness evolution expands current capability and creates learning signal for the next model; model updates motivate the next round of harness evolution. HELIX provides an auditable interface for studying this recursive process. Code is available at https://github.com/HKUDS/HELIX.
Figures & tables
| Verified pattern | Export | Data Description |
|---|---|---|
| Resolved, path-clean, minimal patch | Clean SFT example | A concise successful trajectory/trace. |
| Resolved versus regression, target miss, or no action | Critic example; outcome preference | why local progress is not sufficient for task resolution. |
| Clean versus noisy resolved siblings | Filter example/preference pair for a certain scenario | Separate solution correctness from artifact quality. |
| Different clean resolutions | Alternative SFT examples; minimality/cost preference | Preserve solution diversity while preferring a smaller or cheaper trajectory. |
| Case | Matched contrast | Why it is useful |
|---|---|---|
| SWE sphinx-10673 | Pi 0/2; other siblings 7/8 resolved; selected gap patch 6,661 bytes vs. resolved patch 758 bytes | A baseline-miss/sibling-hit contrast that links candidate complementarity to a clean official-evaluator positive. |
| SWE pytest-5787 | Matrix 2 resolved / 8 gaps; selected Pi patch passes 2/2 F2P but fails two P2P tests, selected sibling passes both sets | Shows why target-test success alone is an unsafe positive and supplies a regression-aware critic/preference pair. |
| SWE xarray-6461 | Matrix 9 resolved / 1 gap; same OpenCode shell/tools and attempt index, Pi config yields an 837-byte gap while OpenCode config yields a 778-byte resolve | Both patches are single-file, production-only, path-clean, and regression-free; the observed hygiene is held constant while semantic outcomes differ. |
| LCB v6-025-abc391-f | Trace-strict: Pi 0/10 vs. OpenCode session/hooks/config/tools 4/10; first candidate success at attempt 2 | Representative candidate repairs after two failed bash calls, rewrites, then executes a passing verifier: a supporting closure path. |
| LCB v6-088-arc191-d | Trace-strict: Pi 7/10 vs. the two recompositions 2/10 and 4/10 | Both substitutions regress on the same task, preventing a monotonic “more active harness is better” story. |
| Output dataset | Records | Share of 438 |
|---|---|---|
| Clean SFT | 63 | 14.38% |
| Cleanup-needed SFT | 56 | 12.79% |
| Critic negatives | 52 | 11.87% |
| Filter samples | 113 | 25.80% |
| Preference pairs | 154 | 35.16% |
| Total multi-purpose rows | 438 | 100.00% |
Appendix figures & tables4 assets
Supplementary material from the paper’s appendix.
Appendix
| Selected member | Tasks 1/10 | Successful slots |
|---|---|---|
| Pi Mono | 72/100 | 529/1,000 |
| OpenCode session/hooks/config/tools; other modules Pi | 75/100 | 549 /1,000 |
| OpenCode session/hooks/tools; other modules Pi | 75/100 | 546/1,000 |
| Selected member | Nonempty | Resolved slots | Gaps | Empty | Tasks 1/2 |
| Pi Mono | 108/110 | 81 | 27 | 2 | 44/55 |
| Hermes session/hooks + Pi remainder | 108/110 | 81 | 27 | 2 | 44/55 |
| Hermes session/hooks/config + Nanobot tools + Pi core | 109/110 | 80 | 29 | 1 | 44/55 |
| OpenCode session/hooks/tools + Pi config/core | 109/110 | 83 | 26 | 1 | 46/55 |
| OpenCode session/hooks/config/tools + Pi core | 109/110 | 86 | 23 | 1 | 46/55 |
| Selected-member post-hoc union | 49/55 | ||||
| Trace stage | Pi baseline, attempt 1 | Evolved sibling, attempt 1 |
|---|---|---|
| Run identity | pi-mono ; 43 observed tool calls in 355 seconds. | OpenCode session/hooks/config/tools with Pi prompt/turn; 54 observed tool calls in 377 seconds. |
| Inspection | Searches for report serialization, then repeatedly reads src/_pytest/reports.py and src/_pytest/_code/code.py . It correctly identifies ExceptionChainRepr.chain as the missing information. | Reads the same implementation files and also inspects testing/test_reports.py , including the existing serialization contract that accesses top-level reprtraceback . |
| Patch | Changes only src/_pytest/reports.py (6,400 bytes). For a chained exception, it emits a new longrepr object containing only chain and sections . | Changes src/_pytest/_code/init.py and src/_pytest/reports.py (6,854 bytes). It exports ExceptionChainRepr , preserves reprcrash , reprtraceback , and sections , and adds the full chain . |
| In-rollout checks | Writes a custom chained-exception script and observes successful round trips. Attempts to run repository tests stop at the local environment guard requires pytest-2.0 ; the compatibility tests are therefore not closed inside the rollout. | Attempts testing/test_reports.py and encounters the same environment guard, then performs custom import and chain round-trip checks. These checks exercise the new path but do not substitute for the official regression set. |
| Official targets | Both FAIL_TO_PASS tests pass (2/2). | Both FAIL_TO_PASS tests pass (2/2). |
| Official regressions and label | 121/123 PASS_TO_PASS tests pass. The two failures, test_xdist_longrepr_to_str_issue_241 and test_deserialization_failure , raise KeyError: reprtraceback . Label: gaps-found ; data role: regression-aware critic negative. | 123/123 PASS_TO_PASS tests pass. Label: resolved ; data role: clean positive and preference winner. |
| Trace stage | Pi-config sibling, attempt 2 | OpenCode-config sibling, attempt 2 |
|---|---|---|
| Run identity | 24 observed tool calls in 130 seconds; 837-byte patch. | 27 observed tool calls in 122 seconds; 778-byte patch. |
| Diagnosis | Locates where() in xarray/core/computation.py , traces attribute collection through apply_ufunc , and reproduces the IndexError for scalar x with keep_attrs=True . | Locates the same expression and reproduces the same IndexError . |
| Patch | If attrs[1] is unavailable, falls back to attrs[0] when present, which can be the condition’s attributes. | If attrs[1] is unavailable, returns an empty dictionary because scalar x has no attributes to preserve. |
| In-rollout checks | Runs several direct Python examples and reports them as passing; its scalar example uses an attribute-empty condition. The repository test command cannot run because that interpreter has no pytest module. | Runs direct Python examples covering scalar and DataArray inputs and reports them as passing. The repository test command encounters the same missing pytest module. |
| Official target | The added test_where_attrs case uses condition attributes {’attr’: ’cond’} and scalar x . The patch returns those condition attributes instead of {} , so FAIL_TO_PASS is 0/1. | The same test receives {} as required, so FAIL_TO_PASS is 1/1. |
| Official regressions and label | 247/247 PASS_TO_PASS tests pass. Label: gaps-found ; data role: clean semantic negative. | 247/247 PASS_TO_PASS tests pass. Label: resolved ; data role: clean positive and preference winner. |
LLM Agent Harnesses
LLM agent harnesses are the software and runtime systems that organize an agent’s tools, context, state, execution, and safeguards.
See all 387 papers