Skip to main content

Harness Optimizer

Evolving LLM agent harness in just a few lines of code.

Commit Activity Open Issues Open PRs License

Built on Strands Agents

A framework for optimizing LLM agent harness through Formulas.

Overview

Harness Optimizer provides a framework for defining, attaching, and optimizing context units (e.g., system prompts) for LLM agents. The core idea: optimize the LLM agent harness by using tunable Formulas to dynamically enhance the agent, and improving those Formulas with optimizers based on collected agent rollout trajectories.

Naming: ContextUnitProcessor (CUP) has been renamed to Formula. Legacy names are available via strands_harness_optimizer.compat with deprecation warnings.

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│ Trainer.fit()                                                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│    ┌─────────────────────┐   Adapter      ┌───────────────────────────┐ │
│    │ Formula (CUP)       ├───────────────▶│        LLM Agent          │ │
│    │ get_tunable_params()│ (attach to     │    (e.g. Strands Agent)   │ │
│    │ update_params()     │  agent)        └───────────▲┬──────────────┘ │
│    └──▲──────────────────┘               invoke agent ││                │
│       │                                               ││ collect rollout│
│       │  ┌─────────────┐  ┌───────────────────────────┴▼──────────────┐ │
│       │  │ DataLoader  │─▶│ AgentRolloutEngine                        │ │
│       │  │  Out: [data]│  │ In: [data], cup_params                    │ │
│       │  └─────────────┘  │ Out: [(rollout, data)...]                 │ │
│       │                   └──────────┬────────────────────────────────┘ │
│       │                              ▼                                  │
│       │               ┌───────────────────────────────────┐             │
│       │               │ Rollouts: [(rollout, data) ...]   │             │
│       │               └───┬───────────────────────┬───────┘             │
│       │                   ▼                       ▼                     │
│       │  ┌────────────────────┐  ┌───────────────────────────────────┐  │
│       │  │ RewardFunction     │  │ FormulaOptimizer                  │  │
│       │  │ In: rollout, data  │─▶│ In: params, [(rollout,data,rwrd)] │  │
│       │  │ Out: reward        │  │ Out: new_params                   │  │
│       │  └────────────────────┘  └───────────────┬───────────────────┘  │
│       │                                          │                      │
│       └──────────────────────────────────────────┘                      │
│                            new_params                                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Data flow:

  1. DataLoader yields batches of task samples from the Dataset
  2. AgentRolloutEngine executes the agent on each sample using current Formula parameters, producing rollouts
    • Adapter bridges Formula parameters to the agent framework (e.g., apply_formulas_on_strands_agent)
    • LLM Agent runs the task and produces a rollout (conversation trace)
  3. RewardFunction scores each rollout
  4. Rollouts, data, and rewards are collected into a batch
  5. FormulaOptimizer analyzes the batch to propose new Formula parameters
  6. Formula updates its parameters and the loop repeats

Installation

pip install strands-harness-optimizer

Quick Example

from strands import Agent
from strands_harness_optimizer.formulas import SystemPromptFormula
from strands_harness_optimizer.adapters import apply_formulas_on_strands_agent

# Create a Formula
formula = SystemPromptFormula(system_prompt="You are a helpful assistant.")

# Attach to a strands agent
agent = Agent(model=model)
apply_formulas_on_strands_agent(agent, [formula])

# Get / update parameters (e.g., after optimization)
params = formula.get_tunable_params()
# {'system_prompt': 'You are a helpful assistant.'}
formula.update_params({'system_prompt': 'You are an expert coding assistant.'})

Core Interfaces

Formula (formulas/)

The optimizable unit, formerly known as ContextUnitProcessor (CUP).

class Formula(ABC):
    def process(self, context: dict, **kwargs) -> dict: ...
    def get_tunable_params(self) -> dict: ...
    def update_params(self, params: dict) -> None: ...
    def can_process(self, context: dict) -> bool: ...

Strands Adapter (adapters/)

Attaches Formulas to strands agents as hook callbacks.

apply_formulas_on_strands_agent(agent, [formula1, formula2])

RewardFunction (rewards/)

Scores agent rollouts. Returns a dict with reward_value and optional metadata.

class RewardFunction(ABC):
    def __call__(self, **kwargs) -> dict: ...

FormulaOptimizer (optimizers/)

Optimizes Formula parameters based on accumulated rollouts and rewards. Follows PyTorch's pattern: add_rollouts(), add_rewards(), step(), zero().

class FormulaOptimizer(ABC):
    def add_rollouts(self, rollouts: list[dict]) -> None: ...
    def add_rewards(self, rewards: list[dict]) -> None: ...
    def step(self) -> None: ...
    def zero(self) -> None: ...
    def get_state(self) -> dict: ...
    def load_state(self, state: dict) -> None: ...

AgentRolloutEngine (rollout_engines/)

Generates rollouts by executing agents on data samples.

class AgentRolloutEngine(ABC):
    def generate_batch(self, data_samples: list[dict]) -> Iterator[list[dict]]: ...

Package Structure

strands_harness_optimizer/
├── __init__.py
├── compat.py                       # Legacy names (ContextUnitProcessor, etc.)
├── trainer.py                      # Minimal training loop
├── data/                           # PyTorch-style data loading (stdlib adapted)
│   ├── dataset.py                  # Dataset, IterableDataset, ConcatDataset, Subset
│   ├── sampler.py                  # Sampler, SequentialSampler, RandomSampler, BatchSampler
│   └── dataloader.py              # Simplified DataLoader
├── formulas/                       # Formula framework
│   ├── formula.py                  # Formula ABC
│   ├── system_prompt_formula.py   # Built-in: SystemPromptFormula
│   └── context_expansion_formula.py # Built-in: ContextExpansionFormula
├── optimizers/                     # Optimization framework
│   ├── optimizer.py                # FormulaOptimizer ABC
│   └── system_prompt/              # Built-in optimizers
│       ├── base_agentic_optimizer.py   # BaseAgenticOptimizer
│       └── contrastive_reflection.py   # ContrastiveReflectionOptimizer
├── rewards/                        # Reward computation
│   └── reward_function.py         # RewardFunction ABC
├── rollout_engines/                # Agent rollout generation
│   ├── agent_rollout_engine.py    # AgentRolloutEngine ABC
│   ├── parallel_engine.py         # ParallelAgentRolloutEngine (utilities)
│   ├── local_engine.py            # LocalRolloutEngine
│   └── agentcore_engine.py        # AgentCoreRolloutEngine
├── templates/                      # Jinja2 templates for optimizers
│   └── contrastive_reflection/
│       ├── system_prompt.jinja
│       └── task_message_system_prompt.jinja
├── adapters/                       # Agent framework adapters
│   ├── agent_adapter.py           # AgentAdapter ABC
│   └── strands_adapter.py         # StrandsAdapter, StrandsAgentWithFormulas
└── utils/                          # Utilities
    ├── templates.py               # load_builtin_template, list_builtin_templates
    ├── params_store.py            # FormulaParamsStore, S3FormulaParamsStore
    └── guardrails/
        └── tool_output.py         # ToolOutputGuardrail

Design Decisions

  1. Dict-based data: No wrapper classes for context or evaluation results — plain dicts throughout for simplicity and flexibility.
  2. Formula = CUP: ContextUnitProcessor renamed to Formula. Legacy names available via strands_harness_optimizer.compat.
  3. Minimal dependencies: Core depends on strands-agents, strands-agents-tools, jinja2, and botocore for the built-in adapter and optimizer.
  4. PyTorch Dataset/DataLoader reuse: Copied from PyTorch source with torch replaced by stdlib random. No PyTorch dependency.
  5. Minimal Trainer: Users can easily write their own training loop. The built-in Trainer is just two nested for-loops.

Contributing ❤️

We welcome contributions! See our Contributing Guide for details on:

  • Reporting bugs & features
  • Development setup
  • Contributing via Pull Requests
  • Code of Conduct
  • Reporting of security issues

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Security

See CONTRIBUTING for more information.

Release files for strands-harness-optimizer 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for strands-harness-optimizer 0.0.1
File Size Uploaded
strands_harness_optimizer-0.0.1.tar.gz 39.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for strands-harness-optimizer 0.0.1
File Interpreter ABI Platform
strands_harness_optimizer-0.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 88.8 kB

Release files / strands_harness_optimizer-0.0.1.tar.gz

Download URL strands_harness_optimizer-0.0.1.tar.gz
Size 39.3 kB
Tags Source
SHA-256 checksum
How to use checksums
3ccfbc88c890d9daa82114801146f418c3f6f8cc0e5de7ed2b04b7e99f8856e8
BLAKE2b-256 checksum
How to use checksums
7f1c538b1cadadc1889393c14c1c4e421dca01cca8e9b9a05540d3a2f71ea8f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 8, 2026.

Transparency log

Release files / strands_harness_optimizer-0.0.1-py3-none-any.whl

Download URL strands_harness_optimizer-0.0.1-py3-none-any.whl
Size 49.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9410ad4e3ea548e68fc815022d9510ec29680930c9c194d0fc6e61a069e939a7
BLAKE2b-256 checksum
How to use checksums
ca6740665afda5d8eaef6ef62c6721eca4ed414d36f077c59b6b0ce588de308f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page