This repo is a compact public demo for collecting OpenTelemetry traces from a search agent and reviewing those traces with HALO.
The agent uses the OpenAI Agents SDK, Inference's OpenAI-compatible model endpoint, Tavily search, and Inference.net tracing. It is intentionally smaller than a deep research harness, but it still has enough tool use, scratchpad state, source extraction, and prompt surface area to make trace analysis useful.
- A single OpenAI Agents SDK agent with multi-turn tool calling.
- Inference.net tracing with
setup(), an outeragent_span(), and manual child spans inside search tools. - Tavily web search and page extraction behind low default result caps.
- A deterministic mock search mode for local testing without Tavily usage.
- A 50-query starter dataset for collecting comparable traces.
- Documented non-critical flaws that HALO should be able to critique.
src/search_agent_example/
agent.py Agent definition and instructions
tools.py Scratchpad, search, extract, source scoring, and claim comparison tools
search_clients.py Tavily and mock search clients
cli.py Single-query traced runner
batch.py Dataset traced runner
data/
queries.jsonl 50 starter queries
search-agent-demo-traces.jsonl.gz ~1,000 pre-run traces (gzipped, ~20 MB)
docs/ HALO notes and known limitations
tests/ Unit tests that avoid network and model calls
If you just want to see HALO without running the agent, this repo bundles ~1,000 pre-run traces (generated from this exact repo) as a gzipped OTLP JSONL file. Decompress it and upload it to your project:
gunzip -k data/search-agent-demo-traces.jsonl.gz
inf trace upload ./data/search-agent-demo-traces.jsonl --name search-agent-demoYou can also upload data/search-agent-demo-traces.jsonl directly from the dashboard
(Observe → Traces → Upload). The traces keep their original timestamps, so widen the
time range to "all time" when viewing them or running HALO.
Install dependencies with uv:
uv sync --extra devCreate .env from .env.example and paste in your Inference API key:
INFERENCE_API_KEY=sk-... # one key: powers BOTH model calls and tracing
MODEL_ID=gpt-4.1-mini # any tool-capable model; this is the default
TAVILY_API_KEY=tvly-... # optional — real web search; omit for --mock-searchThat single INFERENCE_API_KEY is all you need. The key powers the agent's model calls through Inference's OpenAI-compatible endpoint (https://api.inference.net/v1) and is also copied into CATALYST_OTLP_TOKEN to send traces to Catalyst (default endpoint https://telemetry.inference.net).
To use a different OpenAI-compatible provider (e.g. OpenAI directly), set INFERENCE_BASE_URL and INFERENCE_API_KEY to that provider's values.
Use real Tavily search:
uv run search-agent "What are the latest CISA recommendations for defending against ransomware?"Use mock search for a cheap smoke test:
uv run search-agent "What changed in the latest Python release?" --mock-searchThe command prints the final answer and the trace session_id. The same session_id appears in Inference.net so runs can be grouped in the dashboard.
Start with a small limit to control model and search spend:
uv run search-agent-batch --limit 3Use mock search when testing the runner shape:
uv run search-agent-batch --limit 3 --mock-searchEach dataset row gets a stable session ID like dataset-q001 and trace attributes for demo.query_id, demo.category, and demo.dataset.
scratchpad_write: stores short planning notes and observations.scratchpad_read: reads recent notes for final synthesis.web_search: calls Tavily search with capped results and a retriever span.extract_page: calls Tavily extract with a character cap and a retriever span.assess_source: applies a simple source-quality heuristic.compare_claims: does a deliberately shallow lexical comparison between two claims.
The CLI initializes Inference.net tracing, builds the agent, and wraps each run in an agent_span() with stable metadata. The OpenAI Agents SDK instrumentation captures agent runs, model calls, and tool calls. The Tavily calls inside tools add manual retriever spans so the dashboard shows both the tool invocation and the downstream search or extract step.
The example removes the OpenAI Agents SDK default trace exporter after Inference.net instrumentation is installed. That keeps the demo from trying to export traces to OpenAI's hosted trace backend when it is pointed at a custom OpenAI-compatible endpoint.
Tests avoid network and model calls:
uv run pytest
uv run ruff check .- Default Tavily search depth is
basic. - Search results are capped at five by default.
- Extracted page content is capped at 6000 characters.
- Dataset runs default to three queries.
- Mock search is available for smoke tests and CI.
- Run a representative sample from
data/queries.jsonl. - Inspect grouped traces in the Inference.net dashboard.
- Export or point HALO at the trace set.
- Let HALO identify repeated failure modes in planning, search choice, source quality, or final synthesis.
- Apply harness changes, collect another trace batch, and compare reports.
See docs/halo.md and docs/known_limitations.md for more detail.