Skip to main content

A framework for optimizing LLM agent context through Formulas

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

strands_harness_optimizer-0.0.1.tar.gz (39.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

strands_harness_optimizer-0.0.1-py3-none-any.whl (49.5 kB view details)

Uploaded Python 3

File details

Details for the file strands_harness_optimizer-0.0.1.tar.gz.

File metadata

File hashes

Hashes for strands_harness_optimizer-0.0.1.tar.gz
Algorithm Hash digest
SHA256 3ccfbc88c890d9daa82114801146f418c3f6f8cc0e5de7ed2b04b7e99f8856e8
MD5 db083a1ba5f7e78a698e1b94df805631
BLAKE2b-256 7f1c538b1cadadc1889393c14c1c4e421dca01cca8e9b9a05540d3a2f71ea8f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for strands_harness_optimizer-0.0.1.tar.gz:

Publisher: pypi-publish-on-release.yml on strands-labs/harness-optimizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file strands_harness_optimizer-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for strands_harness_optimizer-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9410ad4e3ea548e68fc815022d9510ec29680930c9c194d0fc6e61a069e939a7
MD5 f184c2b1f846d83f8dc355fd62b7c5e0
BLAKE2b-256 ca6740665afda5d8eaef6ef62c6721eca4ed414d36f077c59b6b0ce588de308f

See more details on using hashes here.

Provenance

The following attestation bundles were made for strands_harness_optimizer-0.0.1-py3-none-any.whl:

Publisher: pypi-publish-on-release.yml on strands-labs/harness-optimizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page