Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
7 changes: 7 additions & 0 deletions integrations/haystack/README.md
Original file line number Diff line number Diff line change
@@ -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 |
6 changes: 6 additions & 0 deletions integrations/haystack/multi_agent_web_search/.env.example
Original file line number Diff line number Diff line change
@@ -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-scout
4 changes: 4 additions & 0 deletions integrations/haystack/multi_agent_web_search/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env
.venv/
__pycache__/
.ruff_cache/
49 changes: 49 additions & 0 deletions integrations/haystack/multi_agent_web_search/README.md
Original file line number Diff line number Diff line change
@@ -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`.
77 changes: 77 additions & 0 deletions integrations/haystack/multi_agent_web_search/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
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"

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:
# 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(
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()
11 changes: 11 additions & 0 deletions integrations/haystack/multi_agent_web_search/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os

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)
25 changes: 25 additions & 0 deletions integrations/haystack/multi_agent_web_search/pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"]
9 changes: 9 additions & 0 deletions integrations/haystack/multi_agent_web_search/run.sh
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions integrations/haystack/multi_agent_web_search/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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",
)