feat: Add harness optimizer agent and its configuration editing tools by sjrl · Pull Request #4037 · deepset-ai/haystack-core-integrations · GitHub
Skip to content

feat: Add harness optimizer agent and its configuration editing tools - #4037

Merged
sjrl merged 11 commits into
mainfrom
feat/harness-optimizer-agent
Oct 15, 2026
Merged

sjrl merged 11 commits into
mainfrom
feat/harness-optimizer-agent

Conversation

@sjrl

@sjrl sjrl commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Related Issues

  • part of deepset-ai/haystack-private#498

Proposed Changes:

First part of the harness optimization work: an agent that edits a configuration's YAML into one improved candidate. The loop that calls it (HarnessOptimizationExperiment) follows in #4069.

  • create_harness_optimizer_agent(*, evaluator, loader, ...) builds the optimizer agent. It has:
    • editing tools read_config, edit_config, validate_config, submit_candidate, restore_candidate, finish, plus inspect_component
    • submit_candidate and finish as exit conditions
    • optional read-only search over the Haystack docs (documentation_tools=True)
    • It round-trips through to_dict/from_dict, evaluator included, and defaults to gpt-5.6-terra with low reasoning effort.
  • propose_candidate runs one turn. It passes the starting point to the agent as state (ConfigurationDraft: the YAML to edit; KnownConfigurations: every configuration measured so far) and returns a ProposalResult (the submitted candidate or None, the finish reason, failed validations).
  • The editing tools in optimization/tools.py read and write that state. ValidateConfig runs the evaluator's validate on each draft. A candidate must be validated before it can be submitted, and known configurations can't be resubmitted.
  • OptimizationObjectives gates candidates (quality floor, reported usage) and ranks them by cost, duration or quality.
  • HarnessEvaluator gains to_dict/from_dict (implemented by RetrievalHarnessEvaluator), so the agent can serialize its evaluator.
  • New dependencies: pyyaml, and mcp-haystack for tests.

How did you test it?

Unit tests in tests/optimization and for RetrievalHarnessEvaluator serialization. hatch run test:unit, hatch run test:types and hatch run fmt-check pass locally.

One optimizer turn on its own (needs OPENAI_API_KEY):

from haystack import Pipeline
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.document_stores.in_memory import InMemoryDocumentStore

from haystack_integrations.agent_pack.evaluation import EvalMetrics, RetrievalHarnessEvaluator
from haystack_integrations.agent_pack.optimization import (
    OptimizationObjectives,
    create_harness_optimizer_agent,
    propose_candidate,
)
from haystack_integrations.agent_pack.optimization.utils import dump_pipeline, load_pipeline

reference = Pipeline()
reference.add_component("retriever", InMemoryBM25Retriever(document_store=InMemoryDocumentStore(), top_k=5))
turn = propose_candidate(
    optimizer_agent=create_harness_optimizer_agent(evaluator=RetrievalHarnessEvaluator(k=5), loader=load_pipeline),
    reference=reference,
    reference_yaml=dump_pipeline(reference),
    prices={},
    objectives=OptimizationObjectives(quality_metric="mean_recall_at_k", primary="quality"),
    baseline=EvalMetrics(durations=[1.0], all_tokens_reported=True, details={"mean_recall_at_k": 0.5}),
    history=[],
)
print(turn.candidate.diff if turn.candidate else turn.finish_reason)

How the experiment calls it each turn: HarnessOptimizationExperiment.run in #4069.

Notes for the reviewer

Checklist

@github-actions github-actions Bot added integration:agent-pack type:documentation Improvements or additions to documentation labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

@socket-security

socket-security Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​mcp-haystack@​1.5.2100100100100100

View full report

@sjrl sjrl self-assigned this Oct 1, 2026
@sjrl
sjrl force-pushed the feat/harness-optimizer-agent branch from 3492b0c to b34ba2b Compare October 1, 2026 14:23
@sjrl
sjrl changed the base branch from main to feat/agent-pack-evaluation-updates October 1, 2026 14:23
Base automatically changed from feat/agent-pack-evaluation-updates to main October 1, 2026 15:08
@sjrl
sjrl force-pushed the feat/harness-optimizer-agent branch from cc43c65 to fbe29cd Compare October 1, 2026 15:28

@julian-risch julian-risch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of comments but didn't spot anything. It's a lot of code!
I was wondering also if some kind of LLM-based summarization might be needed after running this for some time because the history builds up. Reasoning why a certain edit was made, plus why it didn't seem to improve results, what else to try...
Other than that I don't have any comments, sorry. :D

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sjrl

sjrl commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, we saw the optimizer's input grow over longer runs too. Right now only the most recent outcome is shown in full, and earlier ones are condensed to their headline metrics, hypothesis and diff. An LLM summary of what was tried and why it didn't help would be a good next step. I'd like to do that as a follow-up, tracked in https://github.com/deepset-ai/haystack-private/issues/594.

sjrl and others added 5 commits October 7, 2026 17:06
…olset

The editor holds one proposal turn's YAML in memory and carries the editing
tools as its own methods. propose_candidate passes it to the optimizer agent
in `tools` for that run, so the agent no longer needs a state entry for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ate tool methods

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ools

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editing tools assume calls in one step run in the order they were made,
so the optimizer agent uses tool_concurrency_limit=1. propose_candidate reads
the editor's YAML directly instead of through its read_config tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…luators to_dict/from_dict

The optimizer agent from create_harness_optimizer_agent is now complete: its
editing tools read and write a ConfigurationDraft and KnownConfigurations in
the agent's state, and ValidateConfig holds the evaluator and loader.
propose_candidate passes each turn's starting point as state and returns a
ProposalResult. HarnessEvaluator gains to_dict/from_dict so the agent can be
serialized with its evaluator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sjrl sjrl changed the title feat: Add harness optimizer agent and configuration workspace feat: Add harness optimizer agent and its configuration editing tools Oct 8, 2026
@sjrl

sjrl commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

@julian-risch Since your review I've removed the ConfigurationWorkspace. The agent from create_harness_optimizer_agent is now complete on its own: its editing tools keep the YAML being edited (ConfigurationDraft) and the earlier candidates (KnownConfigurations) in the agent's state, and ValidateConfig holds the evaluator. Each propose_candidate call passes that state in and gets a ProposalResult back, so nothing carries over between turns. Hopefully this makes it easier to follow what happens in one turn.

@sjrl
sjrl marked this pull request as ready for review October 8, 2026 07:05
@sjrl
sjrl requested a review from a team as a code owner October 8, 2026 07:05
@sjrl
sjrl requested review from anakin87 and removed request for a team and anakin87 October 8, 2026 07:05
@sjrl

sjrl commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

@julian-risch julian-risch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one change request blocking my approval. mcp-haystack version requirement needs to be updated. Otherwise looks good to me.

https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/mcp/CHANGELOG.md#integrationsmcp-v120---2026-01-16

Comment thread integrations/agent_pack/pyproject.toml Outdated
@sjrl
sjrl requested a review from julian-risch October 15, 2026 09:59
@sjrl
sjrl merged commit 7f11aa3 into main Oct 15, 2026
19 checks passed
@sjrl
sjrl deleted the feat/harness-optimizer-agent branch October 15, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration:agent-pack type:documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants