Skip to content

Commit e6c3780

Browse files
gobrandoclaude
andcommitted
docs: Add production web search analysis and detection tools
## Production Web Search Analysis Results Analyzed 33,416 spans across 5,554 unique production traces to confirm web search functionality is operational: - **8.0%** traces use web search (447 traces) - **40.5%** have generator but LLM chose not to search (2,251 traces) - **51.3%** do not use web search generator (2,848 traces) ### Key Findings **Recent Activity (last 1,000 spans):** - 45% web search usage rate (5.6x higher than historical average) - Suggests recent improvements in configuration or query alignment **Historical Average:** - 8% web search usage rate - 23% invocation rate (639 calls / 2,777 generator runs) ### Tools Added 1. **complete_websearch_analysis.py** - Comprehensive analysis of all production traces - Fetches all spans via Phoenix API pagination - Groups by trace_id and classifies web search usage 2. **quick_websearch_check.py** - Quick health check of recent 1,000 spans - Fast execution for rapid status verification - Same classification logic as complete analysis 3. **PRODUCTION_WEBSEARCH_ANALYSIS.md** - Complete documentation of findings - Methodology and detection logic - Usage instructions for analysis tools - Recommendations for next steps ### Detection Methodology Follows team's documented approach (web_search_detection.md): - Groups spans by trace_id - Looks for OpenAIWebSearchGenerator.run spans - Looks for web_search_call spans - Classifies as: YES, NO, DISTANCE_ONLY, N/A 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f8b13d3 commit e6c3780

3 files changed

Lines changed: 466 additions & 0 deletions

File tree

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Production Web Search Analysis
2+
3+
**Date:** February 19, 2026
4+
**Environment:** Production (pilot-prod)
5+
**Phoenix URL:** https://phoenix.referral-pilot-dev.navateam.com:6006
6+
7+
## Executive Summary
8+
9+
Web search functionality **IS operational** in production. Analysis of 33,416 spans across 5,554 unique traces confirms that:
10+
11+
- **8.0%** of traces successfully invoke web search (447 traces)
12+
- **40.5%** have generator configured but LLM chose not to search (2,251 traces)
13+
- **51.3%** do not use the web search generator (2,848 traces)
14+
15+
## Complete Historical Analysis
16+
17+
### Dataset
18+
- **Total Spans:** 33,416
19+
- **Total Traces:** 5,554
20+
- **Time Period:** All available production data
21+
22+
### Web Search Usage Breakdown
23+
24+
| Category | Count | Percentage | Description |
25+
|----------|-------|------------|-------------|
26+
| ✅ YES | 447 | 8.0% | Web search successfully invoked |
27+
| ❌ NO | 2,251 | 40.5% | Generator ran but LLM chose not to search |
28+
| 📏 DISTANCE_ONLY | 8 | 0.1% | Only calculator/distance queries |
29+
| ⚪ N/A | 2,848 | 51.3% | No web search generator in pipeline |
30+
31+
### Span-Level Metrics
32+
33+
- `OpenAIWebSearchGenerator.run` spans: **2,777**
34+
- `web_search_call` spans: **639**
35+
36+
**Invocation Rate:** 23.0% (639 search calls / 2,777 generator runs)
37+
38+
## Recent Activity Analysis
39+
40+
### Last 1,000 Spans (111 Traces)
41+
42+
To understand recent behavior, we analyzed the most recent production activity:
43+
44+
| Category | Count | Percentage |
45+
|----------|-------|------------|
46+
| ✅ YES | 50 | **45.0%** |
47+
| ❌ NO | 33 | 29.7% |
48+
| 📏 DISTANCE_ONLY | 0 | 0.0% |
49+
| ⚪ N/A | 28 | 25.2% |
50+
51+
**Key Finding:** Recent web search usage (45%) is **5.6x higher** than historical average (8%). This suggests:
52+
1. Recent configuration changes may have improved web search effectiveness
53+
2. Recent queries are more aligned with web search use cases
54+
3. System behavior has improved over time
55+
56+
## Technical Configuration
57+
58+
### Current Production Settings
59+
60+
**Location:** `src/pipelines/generate_referrals/pipeline_wrapper.py:141`
61+
62+
```python
63+
"llm": {"model": "gpt-5-mini", "reasoning_effort": "low"}
64+
```
65+
66+
**Web Search Component:** `src/common/components.py:151-199`
67+
68+
```python
69+
@component
70+
class OpenAIWebSearchGenerator:
71+
def run(self, messages, domain=None, model="gpt-5", reasoning_effort="high"):
72+
api_params = {
73+
"model": model,
74+
"input": prompt,
75+
"reasoning": {"effort": reasoning_effort},
76+
"tools": [{"type": "web_search"}], # ✅ Enabled
77+
}
78+
response = client.responses.create(**api_params)
79+
```
80+
81+
### Detection Methodology
82+
83+
Following team's documented approach (`web_search_detection.md`):
84+
85+
1. **Trace-level Detection:**
86+
- Group spans by `trace_id`
87+
- Look for `OpenAIWebSearchGenerator.run` spans (generator presence)
88+
- Look for `web_search_call` spans (actual invocation)
89+
90+
2. **Classification Logic:**
91+
```python
92+
if no generator and no search calls:
93+
return 'N/A' # Web search not configured
94+
if generator but no search calls:
95+
return 'NO' # LLM chose not to search
96+
if search calls with real queries:
97+
return 'YES' # Web search used
98+
if search calls only with distance calculator:
99+
return 'DISTANCE_ONLY'
100+
```
101+
102+
## Analysis Tools Created
103+
104+
### 1. `complete_websearch_analysis.py`
105+
Comprehensive analysis of all historical production traces.
106+
107+
**Features:**
108+
- Fetches all spans via Phoenix API pagination
109+
- Groups by trace_id
110+
- Classifies web search usage
111+
- Provides detailed breakdown
112+
113+
**Usage:**
114+
```bash
115+
export PHOENIX_COLLECTOR_ENDPOINT="https://phoenix.referral-pilot-dev.navateam.com:6006"
116+
export PHOENIX_PROJECT_NAME="pilot-prod"
117+
export PHOENIX_API_KEY="<your-key>"
118+
python3 complete_websearch_analysis.py
119+
```
120+
121+
### 2. `quick_websearch_check.py`
122+
Quick analysis of recent 1,000 spans for rapid health check.
123+
124+
**Features:**
125+
- Fast execution (fetches only 10 pages)
126+
- Recent activity snapshot
127+
- Same classification logic as complete analysis
128+
129+
**Usage:**
130+
```bash
131+
export PHOENIX_COLLECTOR_ENDPOINT="https://phoenix.referral-pilot-dev.navateam.com:6006"
132+
export PHOENIX_PROJECT_NAME="pilot-prod"
133+
export PHOENIX_API_KEY="<your-key>"
134+
python3 quick_websearch_check.py
135+
```
136+
137+
## Conclusions
138+
139+
### ✅ Confirmed Working
140+
Web search is operational in production and being invoked for appropriate queries.
141+
142+
### 📊 Usage Patterns
143+
- **Historical:** 8% web search usage rate
144+
- **Recent:** 45% web search usage rate
145+
- **Trend:** Significant improvement in recent activity
146+
147+
### 🎯 Expected Behavior
148+
The 40.5% of traces where the generator ran but search wasn't invoked represents the LLM correctly determining that those queries don't require current web information.
149+
150+
### 💡 Recommendations
151+
152+
1. **Monitor Recent Trend:** The 45% recent usage rate is significantly higher than historical 8%. Continue monitoring to confirm this is a sustained improvement.
153+
154+
2. **Configuration Alignment:** Current production uses `gpt-5-mini` with `reasoning_effort="low"`. Consider testing if different configurations affect web search invocation rate.
155+
156+
3. **Quality Assessment:** While web search is being invoked, evaluate if the search calls are for appropriate queries and if results improve output quality.
157+
158+
4. **Instrumentation:** Phoenix successfully captures both generator and search call spans, enabling comprehensive observability.
159+
160+
## Related Documentation
161+
162+
- `web_search_detection.md` - Team's documented detection methodology
163+
- `WEB_SEARCH_INVESTIGATION_SUMMARY.md` - Previous investigation findings
164+
- `src/common/components.py` - Web search component implementation
165+
- `src/pipelines/generate_referrals/pipeline_wrapper.py` - Production configuration
166+
167+
## Next Steps
168+
169+
1. ✅ Confirmed web search is operational
170+
2. 🔄 Test how temperature affects web search invocation rate
171+
3. 🔄 Test how reasoning level affects web search invocation rate
172+
4. 📈 Continue monitoring production metrics over time
173+
5. 🎯 Evaluate quality impact of web search on output

app/complete_websearch_analysis.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Complete web search analysis - fetches ALL spans and analyzes them.
4+
"""
5+
import os
6+
import httpx
7+
from collections import defaultdict, Counter
8+
9+
PHOENIX_URL = os.environ.get("PHOENIX_COLLECTOR_ENDPOINT", "https://localhost:6006")
10+
PHOENIX_PROJECT_NAME = os.environ.get("PHOENIX_PROJECT_NAME", "default")
11+
PHOENIX_API_KEY = os.environ.get("PHOENIX_API_KEY", "")
12+
13+
print("=" * 80)
14+
print("COMPLETE WEB SEARCH ANALYSIS")
15+
print("=" * 80)
16+
print(f"Phoenix URL: {PHOENIX_URL}")
17+
print(f"Project: {PHOENIX_PROJECT_NAME}")
18+
print("=" * 80)
19+
20+
headers = {}
21+
if PHOENIX_API_KEY:
22+
headers["Authorization"] = f"Bearer {PHOENIX_API_KEY}"
23+
24+
# Fetch ALL spans (no date filter)
25+
print("\n🔍 Fetching all spans...")
26+
all_spans = []
27+
cursor = None
28+
page = 0
29+
30+
while True:
31+
page += 1
32+
url = f"{PHOENIX_URL}/v1/projects/{PHOENIX_PROJECT_NAME}/spans"
33+
if cursor:
34+
url += f"?cursor={cursor}"
35+
36+
response = httpx.get(url, headers=headers, verify=False, timeout=60.0)
37+
data = response.json()
38+
spans = data.get('data', [])
39+
40+
all_spans.extend(spans)
41+
print(f" Page {page}: {len(spans)} spans (total: {len(all_spans)})")
42+
43+
cursor = data.get('next_cursor')
44+
if not cursor or len(spans) == 0:
45+
break
46+
47+
print(f"✅ Total spans fetched: {len(all_spans)}\n")
48+
49+
# Group by trace_id
50+
traces = defaultdict(list)
51+
for span in all_spans:
52+
trace_id = span.get('context', {}).get('trace_id', '')
53+
if trace_id:
54+
traces[trace_id].append(span)
55+
56+
print(f"📊 Total unique traces: {len(traces)}\n")
57+
58+
# Analyze web search usage
59+
def detect_web_search(trace_spans):
60+
"""Returns: YES, NO, DISTANCE_ONLY, N/A"""
61+
has_generator = False
62+
web_search_calls = []
63+
64+
for span in trace_spans:
65+
name = span.get('name', '')
66+
if name == 'OpenAIWebSearchGenerator.run':
67+
has_generator = True
68+
elif name == 'web_search_call':
69+
web_search_calls.append(span)
70+
71+
if not has_generator and not web_search_calls:
72+
return 'N/A'
73+
74+
if not web_search_calls:
75+
return 'NO' # Generator ran but LLM chose not to search
76+
77+
# Classify search calls
78+
has_real_search = False
79+
has_distance = False
80+
81+
for span in web_search_calls:
82+
attrs = span.get('attributes', {})
83+
action_type = attrs.get('action_type', '') or attrs.get('tool.parameters.action_type', '')
84+
query = str(attrs.get('query', '') or attrs.get('tool.parameters.query', ''))
85+
source_urls = attrs.get('source_urls', '') or attrs.get('tool.parameters.source_urls', '')
86+
87+
if action_type == 'search' and source_urls:
88+
has_real_search = True
89+
elif query.startswith('calculator:') and 'distance' in query:
90+
has_distance = True
91+
elif query and not query.startswith('calculator'):
92+
has_real_search = True
93+
94+
if has_real_search:
95+
return 'YES'
96+
if has_distance:
97+
return 'DISTANCE_ONLY'
98+
return 'NO'
99+
100+
# Analyze all traces
101+
results = {'YES': 0, 'NO': 0, 'DISTANCE_ONLY': 0, 'N/A': 0}
102+
generator_count = 0
103+
search_call_count = 0
104+
105+
for trace_id, span_list in traces.items():
106+
result = detect_web_search(span_list)
107+
results[result] += 1
108+
109+
# Count generators and search calls
110+
for span in span_list:
111+
if span.get('name') == 'OpenAIWebSearchGenerator.run':
112+
generator_count += 1
113+
elif span.get('name') == 'web_search_call':
114+
search_call_count += 1
115+
116+
print("=" * 80)
117+
print("WEB SEARCH USAGE ANALYSIS")
118+
print("=" * 80)
119+
120+
total_traces = len(traces)
121+
print(f"\n📈 Results breakdown:")
122+
print(f" ✅ YES (web search used): {results['YES']:4d} traces ({results['YES']/total_traces*100:5.1f}%)")
123+
print(f" ❌ NO (generator ran, no search): {results['NO']:4d} traces ({results['NO']/total_traces*100:5.1f}%)")
124+
print(f" 📏 DISTANCE_ONLY: {results['DISTANCE_ONLY']:4d} traces ({results['DISTANCE_ONLY']/total_traces*100:5.1f}%)")
125+
print(f" ⚪ N/A (no generator): {results['N/A']:4d} traces ({results['N/A']/total_traces*100:5.1f}%)")
126+
127+
print(f"\n🔍 Span-level counts:")
128+
print(f" OpenAIWebSearchGenerator.run spans: {generator_count}")
129+
print(f" web_search_call spans: {search_call_count}")
130+
131+
print("\n" + "=" * 80)
132+
print("CONCLUSION")
133+
print("=" * 80)
134+
135+
if results['YES'] == 0 and results['NO'] > 0:
136+
print("\n❌ **WEB SEARCH IS NOT BEING USED IN PRODUCTION**")
137+
print(f"\n Evidence:")
138+
print(f" - Generator ran {results['NO']} times")
139+
print(f" - BUT: 0 web_search_call spans found")
140+
print(f" - This means the LLM is choosing NOT to use the web search tool")
141+
print(f"\n Root cause:")
142+
print(f" - Web search tool IS configured (generator spans exist)")
143+
print(f" - But the LLM determines it doesn't need web search for these queries")
144+
print(f" - OR: Phoenix instrumentation isn't capturing web_search_call spans")
145+
print(f"\n Next steps:")
146+
print(f" 1. Check if using OpenAI Responses API (not captured by Phoenix)")
147+
print(f" 2. Try switching to Chat Completions API if compatible")
148+
print(f" 3. Add custom logging to verify web search invocations")
149+
elif results['YES'] > 0:
150+
print(f"\n✅ Web search IS being used in {results['YES']} traces")
151+
print(f" However, {results['NO']} traces had generator but no search")
152+
else:
153+
print(f"\n⚪ No OpenAIWebSearchGenerator.run spans found at all")
154+
print(f" The web search component may not be configured in production")
155+
156+
print("=" * 80)

0 commit comments

Comments
 (0)