facemelter commited on
Commit
0588003
·
verified ·
1 Parent(s): acf4dc8

Fixing agent url hallucinations

Browse files
app.py CHANGED
@@ -977,6 +977,9 @@ async def chat_with_tool_visibility(
977
  api_key=api_key,
978
  model=model
979
  )
 
 
 
980
  progress(1.0, desc="✅ Complete")
981
  yield formatted_response, tool_log
982
  except ImportError:
 
977
  api_key=api_key,
978
  model=model
979
  )
980
+ print(f"\n[FINAL] Formatted response length: {len(formatted_response)}")
981
+ print(f"[FINAL] Formatted response (last 800 chars): {formatted_response[-800:]}")
982
+ print(f"[FINAL] Image markdown count: {formatted_response.count('![')}")
983
  progress(1.0, desc="✅ Complete")
984
  yield formatted_response, tool_log
985
  except ImportError:
langgraph_agent/prompts.py CHANGED
@@ -133,10 +133,10 @@ Always be educational and cite your sources.
133
 
134
  Let's explore the amazing world of birds together!"""
135
 
136
- AUDIO_FINDER_PROMPT = """You are BirdScope AI Generalist, an expert at searching the bird database and retrieving audio recordings.
137
 
138
  **Your Mission:**
139
- Help users find birds in the database and discover bird songs and calls.
140
 
141
  **Your Tools:**
142
  1. **search_birds(name, family, region, status, page_size)**
@@ -151,7 +151,12 @@ Help users find birds in the database and discover bird songs and calls.
151
  - Check `audio_count` field to verify recordings exist
152
  - ⚠️ OPTIMIZATION: Skip this if the user provides a specific bird name and you only need audio
153
 
154
- 3. **get_bird_audio(name, max_recordings)**
 
 
 
 
 
155
  - Fetch actual audio recordings from xeno-canto.org
156
  - Returns recording metadata and download URLs
157
  - Can call directly if you have the bird's common name
@@ -165,6 +170,21 @@ When users ask to "find birds", "show examples", "list birds":
165
  2. Set `page_size` parameter to control results (default to 5-10 unless they specify)
166
  3. Present the birds you found with their names and basic info
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  **CRITICAL WORKFLOW for "find audio for any bird":**
169
  The API has NO `has_audio` filter parameter. You MUST use this two-step process:
170
 
@@ -206,9 +226,11 @@ The API has NO `has_audio` filter parameter. You MUST use this two-step process:
206
 
207
  **CRITICAL - No Hallucination:**
208
  - If get_bird_audio returns empty/no recordings: Tell user "No audio recordings available for this species"
 
209
  - If search_birds returns no results: Tell user "No birds found matching that criteria"
210
- - NEVER fabricate audio URLs, bird names, or recording metadata
211
- - Only show audio recordings and data that are actually returned by the API tools
 
212
  - If a tool fails or returns empty results, honestly report it to the user
213
 
214
  **Error Handling:**
@@ -221,12 +243,13 @@ The API has NO `has_audio` filter parameter. You MUST use this two-step process:
221
  # HuggingFace-Optimized Prompts (More Explicit, Step-by-Step)
222
  # =============================================================================
223
 
224
- AUDIO_FINDER_PROMPT_HF = """You are BirdScope AI Generalist. Find birds in database and retrieve audio recordings.
225
 
226
  **Tools Available:**
227
  1. search_birds(name, family, region, status, page_size) - Search for birds
228
  2. get_bird_info(name) - Get bird details
229
- 3. get_bird_audio(name, max_recordings) - Get audio files
 
230
 
231
  **Step-by-Step Process:**
232
 
@@ -234,12 +257,23 @@ When user asks to find/list/search birds:
234
  1. Call search_birds with region="North America" and page_size=5-10
235
  2. Present birds found
236
 
 
 
 
 
 
237
  When user asks for audio:
238
  1. Call search_birds with ONE filter (name, region, family, or status)
239
  2. Look at results for birds with has_audio=true
240
  3. Call get_bird_audio(name="Bird Name") for a bird that has audio
241
  4. Return the full URL from file_url field
242
 
 
 
 
 
 
 
243
  **Example - General Search:**
244
  User: "Find five birds"
245
  1. Call: search_birds(region="North America", page_size=5)
@@ -252,6 +286,14 @@ User: "Find audio for any bird"
252
  3. Call: get_bird_audio(name="Snow Goose", max_recordings=1)
253
  4. Return: "Recording: https://xeno-canto.org/123456/download"
254
 
 
 
 
 
 
 
 
 
255
  **Important:**
256
  - NEVER use has_audio as a parameter in search_birds
257
  - ALWAYS include full file_url in your response
@@ -259,8 +301,10 @@ User: "Find audio for any bird"
259
 
260
  **CRITICAL - No Hallucination:**
261
  - If get_bird_audio returns empty: Tell user "No audio recordings available"
 
262
  - If search_birds returns no results: Tell user "No birds found"
263
- - NEVER make up audio URLs or bird names
 
264
  - Only return actual data from API tools
265
  """
266
 
 
133
 
134
  Let's explore the amazing world of birds together!"""
135
 
136
+ AUDIO_FINDER_PROMPT = """You are BirdScope AI Generalist, an expert at searching the bird database and retrieving audio recordings and images.
137
 
138
  **Your Mission:**
139
+ Help users find birds in the database and discover bird songs, calls, and reference images.
140
 
141
  **Your Tools:**
142
  1. **search_birds(name, family, region, status, page_size)**
 
151
  - Check `audio_count` field to verify recordings exist
152
  - ⚠️ OPTIMIZATION: Skip this if the user provides a specific bird name and you only need audio
153
 
154
+ 3. **get_bird_images(name, max_images)**
155
+ - Fetch reference photos from Unsplash
156
+ - Returns high-quality image URLs
157
+ - ALWAYS use this tool when users request images - NEVER fabricate image URLs
158
+
159
+ 4. **get_bird_audio(name, max_recordings)**
160
  - Fetch actual audio recordings from xeno-canto.org
161
  - Returns recording metadata and download URLs
162
  - Can call directly if you have the bird's common name
 
170
  2. Set `page_size` parameter to control results (default to 5-10 unless they specify)
171
  3. Present the birds you found with their names and basic info
172
 
173
+ **WORKFLOW for "find images" or "show me photos":**
174
+ When users ask for images/photos:
175
+ 1. Call `search_birds()` to find a bird (or use bird name if provided)
176
+ 2. Call `get_bird_images(name)` to fetch real image URLs
177
+ 3. Display images using markdown: ![Bird Name](url)
178
+ 4. NEVER fabricate or hallucinate image URLs - always use the tool
179
+
180
+ **WORKFLOW for "find both images AND audio":**
181
+ When users ask for both images and audio (e.g., "find me one image and one audio sample"):
182
+ 1. Call `search_birds()` to find birds
183
+ 2. Pick a bird that has `has_audio=true`
184
+ 3. Call `get_bird_images(name)` to get real image URLs
185
+ 4. Call `get_bird_audio(name)` to get audio recordings
186
+ 5. Display both with full URLs in your response
187
+
188
  **CRITICAL WORKFLOW for "find audio for any bird":**
189
  The API has NO `has_audio` filter parameter. You MUST use this two-step process:
190
 
 
226
 
227
  **CRITICAL - No Hallucination:**
228
  - If get_bird_audio returns empty/no recordings: Tell user "No audio recordings available for this species"
229
+ - If get_bird_images returns empty/no images: Tell user "No reference images available for this species"
230
  - If search_birds returns no results: Tell user "No birds found matching that criteria"
231
+ - NEVER fabricate audio URLs, image URLs, bird names, or recording metadata
232
+ - ALWAYS use get_bird_images tool when users request images - NEVER fabricate Unsplash URLs
233
+ - Only show audio recordings, images, and data that are actually returned by the API tools
234
  - If a tool fails or returns empty results, honestly report it to the user
235
 
236
  **Error Handling:**
 
243
  # HuggingFace-Optimized Prompts (More Explicit, Step-by-Step)
244
  # =============================================================================
245
 
246
+ AUDIO_FINDER_PROMPT_HF = """You are BirdScope AI Generalist. Find birds in database and retrieve audio recordings and images.
247
 
248
  **Tools Available:**
249
  1. search_birds(name, family, region, status, page_size) - Search for birds
250
  2. get_bird_info(name) - Get bird details
251
+ 3. get_bird_images(name, max_images) - Get reference photos (NEVER fabricate image URLs!)
252
+ 4. get_bird_audio(name, max_recordings) - Get audio files
253
 
254
  **Step-by-Step Process:**
255
 
 
257
  1. Call search_birds with region="North America" and page_size=5-10
258
  2. Present birds found
259
 
260
+ When user asks for images:
261
+ 1. Call search_birds to find a bird
262
+ 2. Call get_bird_images(name="Bird Name") to get photo URLs
263
+ 3. Display images using markdown: ![Bird](url)
264
+
265
  When user asks for audio:
266
  1. Call search_birds with ONE filter (name, region, family, or status)
267
  2. Look at results for birds with has_audio=true
268
  3. Call get_bird_audio(name="Bird Name") for a bird that has audio
269
  4. Return the full URL from file_url field
270
 
271
+ When user asks for BOTH images AND audio:
272
+ 1. Call search_birds to find a bird
273
+ 2. Call get_bird_images(name="Bird Name")
274
+ 3. Call get_bird_audio(name="Bird Name")
275
+ 4. Display both using markdown
276
+
277
  **Example - General Search:**
278
  User: "Find five birds"
279
  1. Call: search_birds(region="North America", page_size=5)
 
286
  3. Call: get_bird_audio(name="Snow Goose", max_recordings=1)
287
  4. Return: "Recording: https://xeno-canto.org/123456/download"
288
 
289
+ **Example - Image + Audio Search:**
290
+ User: "Find me one image and one audio sample for any species"
291
+ 1. Call: search_birds(region="North America", page_size=20)
292
+ 2. Find bird with has_audio=true (example: "Black-bellied Whistling-Duck")
293
+ 3. Call: get_bird_images(name="Black-bellied Whistling-Duck", max_images=1)
294
+ 4. Call: get_bird_audio(name="Black-bellied Whistling-Duck", max_recordings=1)
295
+ 5. Display both image and audio with full URLs
296
+
297
  **Important:**
298
  - NEVER use has_audio as a parameter in search_birds
299
  - ALWAYS include full file_url in your response
 
301
 
302
  **CRITICAL - No Hallucination:**
303
  - If get_bird_audio returns empty: Tell user "No audio recordings available"
304
+ - If get_bird_images returns empty: Tell user "No images available"
305
  - If search_birds returns no results: Tell user "No birds found"
306
+ - NEVER make up audio URLs, image URLs, or bird names
307
+ - ALWAYS use get_bird_images tool - NEVER fabricate Unsplash URLs
308
  - Only return actual data from API tools
309
  """
310
 
langgraph_agent/structured_output.py CHANGED
@@ -75,14 +75,27 @@ def extract_urls_from_text(text: str) -> tuple[List[str], List[str]]:
75
 
76
  # Clean URLs (remove trailing quotes, commas, etc.)
77
  def clean_url(url: str) -> str:
78
- return url.rstrip('",;)')
 
 
 
 
 
 
79
 
80
- image_urls = list(set(clean_url(url) for url in raw_image_urls))
81
- audio_urls_files = list(set(clean_url(url) for url in raw_audio_urls_files))
 
 
 
82
 
83
  # Combine both types of audio URLs
84
  audio_urls = audio_urls_files + audio_urls_xenocanto
85
 
 
 
 
 
86
  return image_urls, audio_urls
87
 
88
 
@@ -177,7 +190,9 @@ async def parse_agent_response(
177
  for idx, url in enumerate(structured.image_urls, 1):
178
  # Use species name if available, otherwise generic
179
  alt_text = structured.species_name or f"Bird {idx}"
180
- formatted_parts.append(f"![{alt_text}]({url})")
 
 
181
 
182
  # Add audio links if present
183
  if structured.audio_urls:
@@ -189,6 +204,8 @@ async def parse_agent_response(
189
 
190
  result = "\n\n".join(formatted_parts)
191
  print(f"[STRUCTURED OUTPUT] ✅ Successfully formatted response")
 
 
192
  return result
193
 
194
  except Exception as e:
 
75
 
76
  # Clean URLs (remove trailing quotes, commas, etc.)
77
  def clean_url(url: str) -> str:
78
+ cleaned = url.rstrip('",;)')
79
+ # Validate it's still a proper URL
80
+ if cleaned.startswith('http://') or cleaned.startswith('https://'):
81
+ return cleaned
82
+ else:
83
+ print(f"[EXTRACT_URLS] ⚠️ Rejected malformed URL after cleaning: {cleaned}")
84
+ return None
85
 
86
+ image_urls = [u for u in (clean_url(url) for url in raw_image_urls) if u is not None]
87
+ image_urls = list(set(image_urls)) # Deduplicate
88
+
89
+ audio_urls_files = [u for u in (clean_url(url) for url in raw_audio_urls_files) if u is not None]
90
+ audio_urls_files = list(set(audio_urls_files)) # Deduplicate
91
 
92
  # Combine both types of audio URLs
93
  audio_urls = audio_urls_files + audio_urls_xenocanto
94
 
95
+ # Log the actual URLs extracted
96
+ print(f"[EXTRACT_URLS] ✅ Cleaned image URLs ({len(image_urls)}): {image_urls}")
97
+ print(f"[EXTRACT_URLS] ✅ Cleaned audio URLs ({len(audio_urls)}): {audio_urls}")
98
+
99
  return image_urls, audio_urls
100
 
101
 
 
190
  for idx, url in enumerate(structured.image_urls, 1):
191
  # Use species name if available, otherwise generic
192
  alt_text = structured.species_name or f"Bird {idx}"
193
+ img_markdown = f"![{alt_text}]({url})"
194
+ print(f"[STRUCTURED OUTPUT] Generated image markdown: {img_markdown}")
195
+ formatted_parts.append(img_markdown)
196
 
197
  # Add audio links if present
198
  if structured.audio_urls:
 
204
 
205
  result = "\n\n".join(formatted_parts)
206
  print(f"[STRUCTURED OUTPUT] ✅ Successfully formatted response")
207
+ print(f"[STRUCTURED OUTPUT] Final markdown length: {len(result)} characters")
208
+ print(f"[STRUCTURED OUTPUT] Final markdown (last 500 chars): {result[-500:]}")
209
  return result
210
 
211
  except Exception as e:
langgraph_agent/subagent_config.py CHANGED
@@ -50,6 +50,7 @@ class SubAgentConfig:
50
  "tools": [
51
  "search_birds", # Required to find any birds
52
  "get_bird_info", # Get details including audio count
 
53
  "get_bird_audio" # Fetch actual audio recordings
54
  ],
55
  "prompt": audio_finder_prompt,
 
50
  "tools": [
51
  "search_birds", # Required to find any birds
52
  "get_bird_info", # Get details including audio count
53
+ "get_bird_images", # Get reference photos
54
  "get_bird_audio" # Fetch actual audio recordings
55
  ],
56
  "prompt": audio_finder_prompt,