Harness Composer
The missing layer between your agent and disaster.
Dynamically compose the right guardrails, tools, context strategy, and verification checks for any task — automatically, from parts that have already been built and trusted.
The Problem Nobody Has Solved Yet
At AI Engineer Europe, Tejas Kumar (IBM) put it plainly: "A model's reliability on agentic tasks comes almost entirely from the harness wrapped around it, not the model itself."
Then he named the open problem: dynamically assembled, just-in-time harnesses.
Today, every agent harness is hand-built in advance by an engineer who has to anticipate every tool, every guardrail, every verification step — before the agent ever sees a real task.
The result: a flight-booking agent and a document-summarisation agent get the same static harness. Or none at all.
Today With Harness Composer
───────────────────────────────── ─────────────────────────────────────────────
Engineer writes harness in advance System looks at the task, assembles the right
for every agent, for every use harness from pre-approved parts — automatically
case. Works until it doesn't. every single time.
How It Works
Harness Composer looks at the task in front of it and assembles the right harness for that task, from components that have already been built, reviewed, and approved.
The novelty is in the assembly, not the components.
This is the SonarQube model: SonarQube doesn't invent a new static analysis rule when it sees your codebase. It detects what your codebase is and applies the pre-built rule set that fits. That's what makes it governable.
┌──────────────────────┐
Incoming Task ───▶│ Task Classifier │
│ · action type │
│ · reversibility │
│ · data sensitivity │
│ · external systems │
└──────────┬───────────┘
│ TaskProfile
▼
┌──────────────────────┐
│ Component Library │
│ ┌────────────────┐ │
│ │ Tool Wrappers │ │ pre-built
│ │ Ctx Strategies │ │ versioned
│ │ Guardrail Sets │ │ human-reviewed
│ │ Verif. Checks │ │ independently tested
│ └────────────────┘ │
└──────────┬───────────┘
│ selects relevant components
▼
┌──────────────────────┐
│ Composition Engine │──▶ HarnessConfig
│ (config, not code) │ (serialisable, auditable)
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Framework Adapter │
│ LangChain · Bedrock │
│ ADK · Azure │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Agent runs with the │
│ right harness │
└──────────────────────┘
See It In Action
Two tasks. Same system. Dramatically different harnesses.
from harness_composer import HarnessComposer
from harness_composer.registry import default_registry
composer = HarnessComposer(registry=default_registry())
Task 1: High-stakes booking
harness = composer.compose(
"Book me a flight to Edinburgh next Tuesday and pay with my corporate card."
)
print(harness.summary())
HarnessConfig(
risk=critical, action=transact,
context=context_compression,
guardrails=[financial_threshold, irreversibility_confirmation],
verification=[external_confirmation]
)
Task 2: Read-only summary
harness = composer.compose(
"Summarise this internal document for the quarterly review."
)
print(harness.summary())
HarnessConfig(
risk=low, action=read,
context=context_minimal,
guardrails=[],
verification=[]
)
Same API call. The right harness, automatically. No engineer had to write either of them in advance.
Quickstart
pip install -e ".[dev]"
# See both examples side by side
python examples/flight_booking.py
python examples/document_summary.py
Wire into a LangChain agent
from harness_composer import HarnessComposer
from harness_composer.registry import default_registry
from harness_composer.adapters.langchain import LangChainAdapter
registry = default_registry()
composer = HarnessComposer(registry=registry)
adapter = LangChainAdapter(registry=registry)
task = "Transfer £800 to the supplier's account."
harness = composer.compose(task)
# Option A — inject into an AgentExecutor
agent = adapter.inject(harness, agent_executor)
# Option B — pass as RunnableConfig (non-mutating)
config = adapter.build_runnable_config(harness)
chain.invoke({"input": task}, config=config)
The adapter:
- Runs guardrail checks before every tool call (
on_tool_start) - Runs verification checks after every tool call (
on_tool_end) - Filters the agent's tool list to only what the harness authorises (least privilege)
- Raises
GuardrailViolationonBLOCKoutcomes — the agent sees a hard error, not a silent bypass
The Component Library
Every component is pre-built, versioned, and human-reviewed before it enters the registry. Nothing reaches an agent through dynamic discovery.
Tool Wrappers
| Component | What it does |
|---|---|
web_search |
Read-only web search. Inject any real client (Tavily, SerpAPI). |
http_request |
Generic HTTP with domain allow-listing and mutation gating. |
Context Strategies
| Component | When to use |
|---|---|
context_minimal |
Short, single-step tasks. Keeps system prompt + last N messages only. |
context_compression |
Multi-step tasks. Running summary of old messages + tail verbatim. |
Guardrail Sets
| Component | What it catches |
|---|---|
financial_threshold |
Blocks/escalates transactions above configurable soft and hard limits. |
pii_redaction |
Detects and redacts emails, phone numbers, NI/SSN, postcodes, card numbers. |
irreversibility_confirmation |
Requires explicit confirmation token before irreversible actions. |
Verification Checks
| Component | What it confirms |
|---|---|
external_confirmation |
Re-queries the external system directly — not the agent's self-report. |
database_write |
Re-reads the record after a write to confirm it actually persisted. |
Why "Compose, Don't Generate"
The scary version of this idea is an agent that writes its own guardrails at runtime. Nobody should want that.
The safe version — the one worth building — is narrower: the system selects from a library of components that were built once, reviewed once, tested once, and approved once. The composition step generates a configuration, not code.
This is what keeps it governable.
The composition engine never writes new logic. It assembles existing, trusted parts into the right combination for the task at hand.
Every harness decision is auditable because the classifier records exactly which signals fired and why — stored in TaskProfile.matched_signals. You can reconstruct any composition decision from logs alone.
The Classifier Is Explainable By Design
The rules-based classifier records every signal that drove the classification:
profile = composer.classifier.classify(
"Book me a flight to Edinburgh and pay with my corporate card."
)
print(profile.matched_signals)
# ['payment', 'book_ticket', 'flight_api', 'payment_api']
print(profile.risk_level) # RiskLevel.CRITICAL
print(profile.action_type) # ActionType.TRANSACT
print(profile.is_reversible) # False
print(profile.requires_human_approval) # True
print(profile.classifier_version) # 'rules-based-1.0'
An auditor can trace exactly why a given task got its harness. No black box.
Governance: Adding a Component
The entire value proposition rests on every component being trustworthy. The governance gate is the PR — not the runtime.
-
Create your component in the appropriate
library/sub-package, subclassing the relevant base:class PaymentProcessorWrapper(BaseToolWrapper): ...
-
Tag it accurately — tags drive composition selection:
tags=frozenset({"financial", "payment", "payment_processor"})
-
Write tests in
tests/test_components.py— every component must be independently testable. -
Register it in
default_registry()inregistry.py:PaymentProcessorWrapper(api_key=..., allowed_domains={"api.stripe.com"}),
-
Open a PR. The review happens here — not at agent runtime.
The registry is intentionally append-only at runtime. A component that is registered cannot be replaced or removed without a restart and a code change.
Roadmap
- Rules-based task classifier with explainable signal recording
- Component library: 2 tool wrappers, 2 context strategies, 3 guardrails, 2 verification checks
- Composition engine: tag-based, fully deterministic
- LangChain / LangGraph adapter
- AWS Bedrock AgentCore adapter (HookProvider lifecycle events)
- Google ADK adapter (plugin system)
- LLM-based classifier (once there is enough production signal data to validate it against rules-based)
- Mid-task escalation — harness can escalate based on what the agent discovers during execution, not just at task start
- Agent track record integration — lighter harnesses for agents with strong proven histories
What This Is Not
- Not a prompt framework. Harness Composer doesn't touch your prompts.
- Not a model router. It doesn't choose which model runs the task.
- Not an agent framework. It wraps around whichever framework you already use.
- Not a generative guardrail system. It never writes new safety logic at runtime.
Running the Tests
python3.11 -m pytest tests/ -v
70 passed in 1.19s
Origin
This project was built from a product gist that came out of a conversation around Tejas Kumar's (IBM) talk at AI Engineer Europe, where he identified dynamically assembled, just-in-time harnesses as the open problem in agent reliability.
The full product thinking is in product_gist_harness_composer.md.
Contributing
Pull requests are welcome. For significant changes, please open an issue first to discuss what you'd like to change.
For component contributions specifically: follow the governance steps above. Every component that reaches the registry is a commitment — to users of this library and to their agents' downstream actions.
See CONTRIBUTING.md for details.
License
Metadata
Release files for harness-composer 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| harness_composer-0.1.0.tar.gz | 37.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| harness_composer-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.9 kB
Release files / harness_composer-0.1.0.tar.gz
| Download URL | harness_composer-0.1.0.tar.gz |
|---|---|
| Size | 37.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6bbf59a6896a7666eb297be9f3b8d110f549c15b158bbd71cae4d2762f4ae325
|
|
BLAKE2b-256 checksum How to use checksums |
3395696841fb18467d6f8225c176cf9166cb6537c8a8332877ed430e2e1736c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 20, 2026.
Transparency logRelease files / harness_composer-0.1.0-py3-none-any.whl
| Download URL | harness_composer-0.1.0-py3-none-any.whl |
|---|---|
| Size | 48.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
db59a29ae48b211e9d8284294aa3a2ae3596eec982610efc1e71e02bfd63309a
|
|
BLAKE2b-256 checksum How to use checksums |
936f7e639d8cfd8899de5d906d83eadfebd5c0fafd517e3ee138270c7ec7abf6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 20, 2026.
Transparency log