From 7ed0b6212d20c69c28de788a031a0319f1f5a60f Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 14:57:16 +0530 Subject: [PATCH 1/8] setup guide and inital setup --- .../multi_agent_web_search/.env.example | 6 +++ .../multi_agent_web_search/.gitignore | 4 ++ .../haystack/multi_agent_web_search/README.md | 49 +++++++++++++++++++ .../haystack/multi_agent_web_search/run.sh | 9 ++++ 4 files changed, 68 insertions(+) create mode 100644 integrations/haystack/multi_agent_web_search/.env.example create mode 100644 integrations/haystack/multi_agent_web_search/.gitignore create mode 100644 integrations/haystack/multi_agent_web_search/README.md create mode 100755 integrations/haystack/multi_agent_web_search/run.sh diff --git a/integrations/haystack/multi_agent_web_search/.env.example b/integrations/haystack/multi_agent_web_search/.env.example new file mode 100644 index 0000000..6e6ed75 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/.env.example @@ -0,0 +1,6 @@ +OPENAI_API_KEY=your_openai_api_key_here +SERPERDEV_API_KEY=your_serperdev_api_key_here + +OPIK_API_KEY=your_opik_api_key_here +OPIK_WORKSPACE=your_workspace +OPIK_PROJECT_NAME=haystack-multi-agent \ No newline at end of file diff --git a/integrations/haystack/multi_agent_web_search/.gitignore b/integrations/haystack/multi_agent_web_search/.gitignore new file mode 100644 index 0000000..cc6a33b --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/.gitignore @@ -0,0 +1,4 @@ +.env +.venv/ +__pycache__/ +.ruff_cache/ diff --git a/integrations/haystack/multi_agent_web_search/README.md b/integrations/haystack/multi_agent_web_search/README.md new file mode 100644 index 0000000..a0eb491 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/README.md @@ -0,0 +1,49 @@ +# Haystack Multi-Agent Web Search with Comet Opik + +Trace a Haystack multi-agent pipeline with Comet Opik. + +## What this does + +This example runs a two-agent Haystack chain: a coordinator agent that delegates research +questions to a scout agent, which searches the web via SerperDev to answer them. `OpikConnector` +traces the coordinator, the scout, and every tool call into a single Opik trace. + +## Prerequisites + +This is a `uv` project — dependencies live in `pyproject.toml`. + +```bash +uv sync +``` + +Copy `.env.example` to `.env` (or `export` the variables). With `OPENAI_API_KEY` / +`SERPERDEV_API_KEY` / Opik credentials unset, the example runs in **DRY_RUN** and prints what it +would do instead of calling OpenAI/SerperDev/Opik. + +| Variable | Required | Description | +|---|---|---| +| `OPENAI_API_KEY` | for a live run | OpenAI API key used by both agents' chat generators. Unset → DRY_RUN. | +| `SERPERDEV_API_KEY` | for a live run | SerperDev API key for the web-search tool. Unset → DRY_RUN. | +| `OPIK_API_KEY` | for a live run | Opik API key from [comet.com/opik](https://www.comet.com/opik). Unset → DRY_RUN. | +| `OPIK_WORKSPACE` | for a live run | Your Opik workspace. Unset → DRY_RUN. | +| `OPIK_PROJECT_NAME` | no | Project traces are logged to (default `haystack-multi-agent-scout`). | +| `HAYSTACK_OPENAI_MODEL` | no | OpenAI model both agents run on (default `gpt-5-mini`). | + +## Running it + +```bash +uv run python agent.py + +# or, the way CI does: +bash run.sh +``` + +## How it works + +1. **Define the tool** — `tool.py` wraps `SerperDevWebSearch` as a `ComponentTool` named `web_search`. +2. **Enable tracing** — `agent.py` sets `HAYSTACK_CONTENT_TRACING_ENABLED=true` and constructs + `OpikConnector`, which activates Opik tracing for every Haystack component run in the process. +3. **Build the agents** — `agent.py` creates a `scout` agent that calls `web_search`, wraps it as a + `scout` tool, and gives that tool to a `coordinator` agent. +4. **Run and trace** — `run_agent` sends a query to the coordinator, which delegates to the scout as + needed; the full call chain is logged to Opik under `OPIK_PROJECT_NAME`. diff --git a/integrations/haystack/multi_agent_web_search/run.sh b/integrations/haystack/multi_agent_web_search/run.sh new file mode 100755 index 0000000..fffed7e --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e + +export OPIK_PROJECT_NAME="haystack-multi-agent-scout" + +uv sync + +# With no OpenAI / SerperDev / Opik credentials this falls back to DRY_RUN and +uv run python agent.py From 8832d779f4e76c760332159598771d4c01ff3c28 Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 14:57:40 +0530 Subject: [PATCH 2/8] credentials setup configuration --- .../haystack/multi_agent_web_search/config.py | 14 +++++++++++ .../multi_agent_web_search/pyproject.toml | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 integrations/haystack/multi_agent_web_search/config.py create mode 100644 integrations/haystack/multi_agent_web_search/pyproject.toml diff --git a/integrations/haystack/multi_agent_web_search/config.py b/integrations/haystack/multi_agent_web_search/config.py new file mode 100644 index 0000000..b88c121 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/config.py @@ -0,0 +1,14 @@ +import os + +from dotenv import load_dotenv +load_dotenv() + +OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") +SERPERDEV_API_KEY = os.environ.get("SERPERDEV_API_KEY") +OPIK_API_KEY = os.environ.get("OPIK_API_KEY") +OPIK_WORKSPACE = os.environ.get("OPIK_WORKSPACE") + +OPIK_PROJECT_NAME = os.environ.get("OPIK_PROJECT_NAME", "haystack-multi-agent-scout") +OPENAI_MODEL = os.environ.get("HAYSTACK_OPENAI_MODEL", "gpt-5-mini") + +DRY_RUN = not (OPENAI_API_KEY and SERPERDEV_API_KEY and OPIK_API_KEY and OPIK_WORKSPACE) \ No newline at end of file diff --git a/integrations/haystack/multi_agent_web_search/pyproject.toml b/integrations/haystack/multi_agent_web_search/pyproject.toml new file mode 100644 index 0000000..b36c917 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/pyproject.toml @@ -0,0 +1,25 @@ +[project] +name = "haystack-multi-agent-web-search" +version = "0.1.0" +description = "Trace a Haystack coordinator -> scout multi-agent web-search chain with Opik." +readme = "README.md" +requires-python = ">=3.10,<3.14" +dependencies = [ + "haystack-ai>=3.0.0", + "serperdev-haystack>=1.0.0", + "opik>=2.2.0", +] + +[dependency-groups] +dev = ["ruff"] + +# WHY: a loose runnable script, not an installable package — uv manages the env but builds nothing. +[tool.uv] +package = false + +[tool.ruff] +line-length = 110 +target-version = "py310" + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B"] From 6d59e2eb1bb1c0e22b23d081f05268f263cd9af8 Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 14:58:02 +0530 Subject: [PATCH 3/8] multi agent web search using Haystack --- .../haystack/multi_agent_web_search/agent.py | 70 +++++++++++++++++++ .../haystack/multi_agent_web_search/tool.py | 13 ++++ 2 files changed, 83 insertions(+) create mode 100644 integrations/haystack/multi_agent_web_search/agent.py create mode 100644 integrations/haystack/multi_agent_web_search/tool.py diff --git a/integrations/haystack/multi_agent_web_search/agent.py b/integrations/haystack/multi_agent_web_search/agent.py new file mode 100644 index 0000000..4ace940 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/agent.py @@ -0,0 +1,70 @@ +""" +Haystack multi-agent web-search example, traced with Opik. + +A coordinator agent delegates research questions to a scout agent, which in turn +calls a SerperDev web-search tool. Opik traces the whole coordinator -> scout -> +tool chain via `OpikConnector`. +""" + +import os +from typing import Annotated + +os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true" # at top before importing Haystack modules + +from haystack.components.agents import Agent +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.dataclasses import ChatMessage +from haystack.tools import tool +from opik.integrations.haystack import OpikConnector + +import config +from tool import build_web_search_tool + +QUERY = "What was the final score and who won the FIFA World Cup 2026 championship?" + +def build_coordinator() -> Agent: + OpikConnector(name="haystack-multi-agent-scout", project_name=config.OPIK_PROJECT_NAME) + + scout_agent = Agent( + chat_generator=OpenAIChatGenerator(model=config.OPENAI_MODEL), + tools=[build_web_search_tool()], + system_prompt=( + "You are a football scouting specialist covering the FIFA World Cup 2026. " + "Search the web to find up-to-date information on teams, fixtures, venues, and knockout news" + ), + ) + + @tool + def scout(query: Annotated[str, "The World Cup 2026 research question to investigate"]) -> str: + """Research a FIFA World Cup 2026 topic and return a summary of findings.""" + try: + result = scout_agent.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + except Exception as e: + return f"Scouting research failed: {e}" + + return Agent( + chat_generator=OpenAIChatGenerator(model=config.OPENAI_MODEL), + tools=[scout], + system_prompt=( + "You are a World Cup 2026 coverage coordinator. Delegate research questions " + "about teams, matches, venues, and players to the scout tool, then summarize the findings for a fan who wants the latest updates." + ), + ) + +def run_agent(query: str) -> str: + coordinator = build_coordinator() + result = coordinator.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + +def main() -> None: + if config.DRY_RUN: + print( + "[DRY RUN] OpenAI / SerperDev / Opik credentials not set — would delegate this " + f"query through the coordinator -> scout agent chain and trace it to Opik:\n {QUERY}" + ) + return + print(run_agent(QUERY)) + +if __name__ == "__main__": + main() diff --git a/integrations/haystack/multi_agent_web_search/tool.py b/integrations/haystack/multi_agent_web_search/tool.py new file mode 100644 index 0000000..8f4e624 --- /dev/null +++ b/integrations/haystack/multi_agent_web_search/tool.py @@ -0,0 +1,13 @@ +from haystack.tools import ComponentTool +from haystack.utils import Secret +from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch + +def build_web_search_tool(top_k: int = 4) -> ComponentTool: + return ComponentTool( + component=SerperDevWebSearch( + api_key=Secret.from_env_var("SERPERDEV_API_KEY"), + top_k=top_k, + ), + name="web_search", + description="Search the web for current information on any topic", + ) From f859563026b5ead0f87e71056775b702a6557f7c Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 15:04:59 +0530 Subject: [PATCH 4/8] include haystack in integration readme --- integrations/README.md | 1 + integrations/haystack/README.md | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 integrations/haystack/README.md diff --git a/integrations/README.md b/integrations/README.md index d8ed601..ada514d 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -5,4 +5,5 @@ Examples for adding Opik to a specific framework or library. Each folder covers | Integration | Description | |---|---| | [google_adk/](./google_adk/) | Google ADK — Trace an Agentic RAG router with Opik | +| [haystack/](./haystack/) | Haystack — Trace a multi-agent web-search pipeline with Opik | | [otel/](./otel/) | OpenTelemetry — send OTel spans to Opik via OTLP | diff --git a/integrations/haystack/README.md b/integrations/haystack/README.md new file mode 100644 index 0000000..37ce101 --- /dev/null +++ b/integrations/haystack/README.md @@ -0,0 +1,7 @@ +# Haystack + Opik + +Examples for tracing [Haystack](https://haystack.deepset.ai/) pipelines and Agents with Opik. + +| Example | Description | +|---|---| +| [multi_agent_web_search](./multi_agent_web_search/) | Trace a coordinator -> scout multi-agent web-search chain with Opik and Haystack pipeline | From eacb5d8e7c746f54ce6913feb79d791eb568fb01 Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 15:08:40 +0530 Subject: [PATCH 5/8] fix formating checks --- integrations/haystack/multi_agent_web_search/agent.py | 8 ++++++-- integrations/haystack/multi_agent_web_search/config.py | 5 +---- integrations/haystack/multi_agent_web_search/tool.py | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/integrations/haystack/multi_agent_web_search/agent.py b/integrations/haystack/multi_agent_web_search/agent.py index 4ace940..b88930d 100644 --- a/integrations/haystack/multi_agent_web_search/agent.py +++ b/integrations/haystack/multi_agent_web_search/agent.py @@ -9,7 +9,7 @@ import os from typing import Annotated -os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true" # at top before importing Haystack modules +os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true" from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator @@ -22,6 +22,7 @@ QUERY = "What was the final score and who won the FIFA World Cup 2026 championship?" + def build_coordinator() -> Agent: OpikConnector(name="haystack-multi-agent-scout", project_name=config.OPIK_PROJECT_NAME) @@ -48,15 +49,18 @@ def scout(query: Annotated[str, "The World Cup 2026 research question to investi tools=[scout], system_prompt=( "You are a World Cup 2026 coverage coordinator. Delegate research questions " - "about teams, matches, venues, and players to the scout tool, then summarize the findings for a fan who wants the latest updates." + "about teams, matches, venues, and players to the scout tool, then summarize " + "the findings for a fan who wants the latest updates." ), ) + def run_agent(query: str) -> str: coordinator = build_coordinator() result = coordinator.run(messages=[ChatMessage.from_user(query)]) return result["last_message"].text + def main() -> None: if config.DRY_RUN: print( diff --git a/integrations/haystack/multi_agent_web_search/config.py b/integrations/haystack/multi_agent_web_search/config.py index b88c121..d4bb306 100644 --- a/integrations/haystack/multi_agent_web_search/config.py +++ b/integrations/haystack/multi_agent_web_search/config.py @@ -1,8 +1,5 @@ import os -from dotenv import load_dotenv -load_dotenv() - OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") SERPERDEV_API_KEY = os.environ.get("SERPERDEV_API_KEY") OPIK_API_KEY = os.environ.get("OPIK_API_KEY") @@ -11,4 +8,4 @@ OPIK_PROJECT_NAME = os.environ.get("OPIK_PROJECT_NAME", "haystack-multi-agent-scout") OPENAI_MODEL = os.environ.get("HAYSTACK_OPENAI_MODEL", "gpt-5-mini") -DRY_RUN = not (OPENAI_API_KEY and SERPERDEV_API_KEY and OPIK_API_KEY and OPIK_WORKSPACE) \ No newline at end of file +DRY_RUN = not (OPENAI_API_KEY and SERPERDEV_API_KEY and OPIK_API_KEY and OPIK_WORKSPACE) diff --git a/integrations/haystack/multi_agent_web_search/tool.py b/integrations/haystack/multi_agent_web_search/tool.py index 8f4e624..23fd4e7 100644 --- a/integrations/haystack/multi_agent_web_search/tool.py +++ b/integrations/haystack/multi_agent_web_search/tool.py @@ -2,6 +2,7 @@ from haystack.utils import Secret from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch + def build_web_search_tool(top_k: int = 4) -> ComponentTool: return ComponentTool( component=SerperDevWebSearch( From 8984204a358d8832a43ed1505a642897da81340f Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 21 Jul 2026 15:10:26 +0530 Subject: [PATCH 6/8] lint fix in agent.py formatting --- integrations/haystack/multi_agent_web_search/agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/integrations/haystack/multi_agent_web_search/agent.py b/integrations/haystack/multi_agent_web_search/agent.py index b88930d..8900af9 100644 --- a/integrations/haystack/multi_agent_web_search/agent.py +++ b/integrations/haystack/multi_agent_web_search/agent.py @@ -70,5 +70,6 @@ def main() -> None: return print(run_agent(QUERY)) + if __name__ == "__main__": main() From 07b4e5e396855e1fd22e3b0f35625d444fd71c75 Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 4 Aug 2026 03:39:35 +0530 Subject: [PATCH 7/8] keep opik project consistent in .env.example, run sh and config --- integrations/haystack/multi_agent_web_search/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/haystack/multi_agent_web_search/.env.example b/integrations/haystack/multi_agent_web_search/.env.example index 6e6ed75..34877aa 100644 --- a/integrations/haystack/multi_agent_web_search/.env.example +++ b/integrations/haystack/multi_agent_web_search/.env.example @@ -3,4 +3,4 @@ SERPERDEV_API_KEY=your_serperdev_api_key_here OPIK_API_KEY=your_opik_api_key_here OPIK_WORKSPACE=your_workspace -OPIK_PROJECT_NAME=haystack-multi-agent \ No newline at end of file +OPIK_PROJECT_NAME=haystack-multi-agent-scout \ No newline at end of file From f841f944f0d293f487dec093c3a97b08182ca908 Mon Sep 17 00:00:00 2001 From: lucifertrj Date: Tue, 4 Aug 2026 03:42:23 +0530 Subject: [PATCH 8/8] why comment for constructing OPIKconnector --- integrations/haystack/multi_agent_web_search/.env.example | 2 +- integrations/haystack/multi_agent_web_search/agent.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/integrations/haystack/multi_agent_web_search/.env.example b/integrations/haystack/multi_agent_web_search/.env.example index 34877aa..8064dee 100644 --- a/integrations/haystack/multi_agent_web_search/.env.example +++ b/integrations/haystack/multi_agent_web_search/.env.example @@ -3,4 +3,4 @@ SERPERDEV_API_KEY=your_serperdev_api_key_here OPIK_API_KEY=your_opik_api_key_here OPIK_WORKSPACE=your_workspace -OPIK_PROJECT_NAME=haystack-multi-agent-scout \ No newline at end of file +OPIK_PROJECT_NAME=haystack-multi-agent-scout diff --git a/integrations/haystack/multi_agent_web_search/agent.py b/integrations/haystack/multi_agent_web_search/agent.py index 8900af9..da52276 100644 --- a/integrations/haystack/multi_agent_web_search/agent.py +++ b/integrations/haystack/multi_agent_web_search/agent.py @@ -24,6 +24,8 @@ def build_coordinator() -> Agent: + # WHY: constructing OpikConnector registers Opik as Haystack's global tracer for the whole + # process; the instance is intentionally unused because we run Agents directly, not via a Pipeline. OpikConnector(name="haystack-multi-agent-scout", project_name=config.OPIK_PROJECT_NAME) scout_agent = Agent(