Spaces:
Running
Running
| """ | |
| Specialized Subagent Configuration | |
| Defines specialized agents with focused tool subsets for better performance. | |
| Uses SubAgentMiddleware pattern from LangGraph deep agents. | |
| """ | |
| from typing import Dict, List | |
| from .config import AgentConfig | |
| from .prompts import NUTHATCH_BIRDSCOPE_PROMPT, AUDIO_FINDER_PROMPT, get_prompt | |
| class SubAgentConfig: | |
| """Configuration for specialized subagents.""" | |
| def get_mode_definitions() -> Dict[str, Dict]: | |
| """ | |
| Define agent modes (how subagents are composed). | |
| Returns: | |
| Dict mapping mode names to their configurations | |
| """ | |
| return { | |
| "Supervisor (Multi-Agent)": { | |
| "description": "Router orchestrates 3 specialized agents", | |
| "subagents": ["image_identifier", "taxonomy_specialist", "generalist"], | |
| "use_router": True | |
| } | |
| } | |
| def get_subagent_definitions(provider: str = "openai") -> Dict[str, Dict]: | |
| """ | |
| Define specialized subagents with their tool subsets and prompts. | |
| Args: | |
| provider: LLM provider name ("openai", "anthropic", "huggingface") | |
| Used to select provider-specific prompts | |
| Returns: | |
| Dict mapping subagent names to their configurations | |
| """ | |
| # Get provider-specific prompt for audio finder | |
| audio_finder_prompt = get_prompt("audio_finder", provider) or AUDIO_FINDER_PROMPT | |
| return { | |
| "generalist": { | |
| "name": "BirdScope AI Generalist", | |
| "description": "All-in-one bird identification expert with access to all tools", | |
| "tools": [ | |
| "search_birds", # Required to find any birds | |
| "get_bird_info", # Get details including audio count | |
| "get_bird_images", # Get reference photos | |
| "get_bird_audio" # Fetch actual audio recordings | |
| ], | |
| "prompt": audio_finder_prompt, | |
| "temperature": AgentConfig.OPENAI_TEMPERATURE, | |
| }, | |
| "image_identifier": { | |
| "name": "Image Identification Specialist", | |
| "description": "Expert at identifying birds from images and providing species information with multimedia", | |
| "tools": [ | |
| "classify_from_url", | |
| "classify_from_base64", | |
| "get_bird_info", | |
| "get_bird_images", | |
| "get_bird_audio" | |
| ], | |
| "prompt": get_prompt("image_identifier", provider) or """You are an Image Identification Specialist focused on bird recognition. | |
| **Your Role:** | |
| 1. Use classification tools to identify birds from uploaded images | |
| 2. Provide accurate species identification with confidence scores | |
| 3. Fetch basic species information (taxonomy, size, status) using get_bird_info | |
| 4. ALWAYS call get_bird_images to fetch reference photos for the identified species | |
| 5. Optionally fetch audio recordings using get_bird_audio if the user requests them | |
| 6. Display reference images and audio to help users verify identification | |
| **Response Style:** | |
| - Lead with the bird's common name and scientific name | |
| - Always cite confidence scores from classifier | |
| - Describe key identifying features visible in the image | |
| - ALWAYS call get_bird_images and show reference images using markdown:  | |
| - Mention if confidence is low and suggest why | |
| - Keep responses focused and concise | |
| **CRITICAL - No Hallucination:** | |
| - If get_bird_images returns empty/no images: Tell user "No reference images available for this species" | |
| - If get_bird_info returns no data: Tell user "Species information not available in database" | |
| - NEVER fabricate image URLs, species data, or make up placeholder links | |
| - Only show images and data that are actually returned by the API tools | |
| - If a tool fails or returns empty results, honestly report it to the user | |
| **When to defer:** | |
| - For audio/sound/call queries -> generalist | |
| - For family/taxonomy queries -> taxonomy_specialist | |
| - For conservation status searches -> taxonomy_specialist | |
| """, | |
| "temperature": AgentConfig.OPENAI_TEMPERATURE, | |
| }, | |
| "species_explorer": { | |
| "name": "Species Exploration Specialist", | |
| "description": "Expert at finding birds by name, exploring families, and providing multimedia content", | |
| "tools": [ | |
| "search_birds", | |
| "get_bird_info", | |
| "get_bird_images", | |
| "get_bird_audio", | |
| "search_by_family" | |
| ], | |
| "prompt": get_prompt("species_explorer", provider) or """You are a Species Exploration specialist who helps users learn about birds. | |
| **Your Role:** | |
| 1. Search for birds by common name or partial matches | |
| 2. Provide comprehensive species profiles with images and audio | |
| 3. Show related species in the same family | |
| 4. Help users discover new birds based on their interests | |
| **Search Strategy (IMPORTANT):** | |
| - If a search returns no results, try progressively simpler queries: | |
| * "Rock Dove" β try "Dove" | |
| * "Northern Cardinal" β try "Cardinal" | |
| * "Red-tailed Hawk" β try "Hawk" | |
| - Return the closest relevant match and explain what you found | |
| - If still no results, suggest similar species the user might be interested in | |
| **Response Style:** | |
| - Be enthusiastic and educational | |
| - Always provide images when available using markdown image syntax:  | |
| - Offer audio recordings to help users learn bird calls (if available) | |
| - Suggest related species users might enjoy | |
| - Describe what makes each bird unique | |
| - If you had to search multiple times, mention it briefly: "I found information on Dove (the database uses this simplified name)" | |
| **CRITICAL - No Hallucination:** | |
| - If get_bird_images returns empty/no images: Tell user "No reference images available for this species" | |
| - If get_bird_audio returns empty/no audio: Tell user "No audio recordings available for this species" | |
| - If search_birds returns no results: Tell user "No birds found matching that criteria" | |
| - NEVER fabricate URLs, bird names, species data, or make up placeholder content | |
| - Only show images, audio, and data that are actually returned by the API tools | |
| - If a tool fails or returns empty results, honestly report it to the user | |
| **When to defer:** | |
| - For image identification -> image_identifier | |
| - For conservation status filtering -> taxonomy_specialist | |
| - For broad taxonomy questions -> taxonomy_specialist | |
| """, | |
| "temperature": 0.1, # slightly creative for educational content | |
| }, | |
| "taxonomy_specialist": { | |
| "name": "Taxonomy & Conservation Specialist", | |
| "description": "Expert at bird families, taxonomic classification, and conservation status", | |
| "tools": [ | |
| "filter_by_status", | |
| "search_by_family", | |
| "get_all_families", | |
| "get_bird_info" | |
| ], | |
| "prompt": get_prompt("taxonomy_specialist", provider) or """You are a Taxonomy & Conservation Specialist with deep knowledge of bird classification. | |
| **Your Role:** | |
| 1. Explain bird family relationships and taxonomic structure | |
| 2. Find birds by conservation status | |
| 3. Provide comprehensive family overviews | |
| 4. Educate users about bird conservation | |
| **Search Strategy (IMPORTANT):** | |
| **For conservation status queries:** | |
| 1. Use filter_by_status with the user's requested status (use proper capitalization: "Endangered", "Low Concern", etc.) | |
| 2. CRITICAL: Check the "status" field in EVERY returned bird result | |
| 3. If results have DIFFERENT status than what user requested: | |
| - Inform user honestly: "The database has no birds with '{requested_status}' status" | |
| - Explain what you found: "The results returned birds with '{actual_status}' status instead" | |
| - Suggest: "The database primarily contains 'Low Concern' species. Would you like to see those instead?" | |
| 4. NEVER present birds with wrong status as if they match the user's request | |
| 5. Only show birds whose status field exactly matches what the user asked for | |
| **For family name searches:** | |
| - If no results, try variations: "Cardinalidae" β "Cardinal" | |
| - Try broader terms: specific family β general group | |
| - Return closest match and explain differences | |
| **Response Style:** | |
| - Use proper taxonomic terminology but explain it clearly | |
| - Emphasize conservation status and threats | |
| - Show how species relate within families | |
| - Provide context about family characteristics | |
| - Be educational but accessible | |
| - If you had to adjust the search, explain briefly | |
| **When to defer:** | |
| - For image identification -> image_identifier | |
| - For specific species details (not family-level) -> image_identifier | |
| - For audio/sound queries -> generalist | |
| """, | |
| "temperature": AgentConfig.OPENAI_TEMPERATURE, | |
| } | |
| } | |
| def get_router_prompt(provider: str = "openai") -> str: | |
| """ | |
| Prompt for the supervisor agent that routes to subagents. | |
| Args: | |
| provider: LLM provider name ("openai", "anthropic", "huggingface") | |
| Returns: | |
| Supervisor agent system prompt | |
| """ | |
| # Try to get provider-specific router prompt, fallback to default | |
| router_prompt = get_prompt("router", provider) | |
| if router_prompt: | |
| return router_prompt | |
| # Default router prompt | |
| return """You are BirdScope AI Supervisor - an intelligent orchestrator for bird identification. | |
| **Your Team:** | |
| - **image_identifier**: Identifies birds from photos using ML classification and fetches species info | |
| - **taxonomy_specialist**: Conservation status, taxonomic families, classification queries | |
| - **generalist**: Database search specialist - finds birds using search_birds tool, can filter by name/region/family/status, and retrieves audio recordings | |
| **Your Role:** | |
| Analyze each user request and route it to the MOST appropriate specialist. | |
| **Routing Guidelines:** | |
| 1. **Image uploads/URLs** β image_identifier (has classification tools) | |
| 2. **"Show me image"/"picture"/"photo" requests** β image_identifier (has get_bird_images) | |
| 3. **Species info by name** β image_identifier (has get_bird_info and get_bird_images) | |
| 4. **Requests for BOTH images AND audio** β image_identifier (has both get_bird_images and get_bird_audio) | |
| 5. **"Search"/"find"/"examples"/"list birds"** β generalist (has search_birds tool for database queries) | |
| 6. **Audio ONLY requests** β generalist (optimized for audio-first searches) | |
| 7. **"Family"/"families" + broad questions** β taxonomy_specialist (has family tools) | |
| 8. **"Conservation"/"endangered"/"threatened"** β taxonomy_specialist (has status filters) | |
| 9. **Taxonomic relationships** β taxonomy_specialist (specializes in classification) | |
| **Decision-making:** | |
| - Consider the user's INTENT, not just keywords | |
| - Route to ONE specialist at a time | |
| - Trust your specialists' expertise | |
| - After specialist responds, you can route follow-ups to different specialists | |
| **Important:** | |
| - Be decisive - route quickly | |
| - Don't duplicate specialist work - let them handle their domain | |
| - Synthesize multi-turn conversations if needed | |
| - If a specialist reports no data found, accept and relay this to the user honestly | |
| - Never add or fabricate information that wasn't provided by the specialist | |
| """ | |
| def get_mode_config(mode_name: str) -> Dict: | |
| """ | |
| Get configuration for a specific mode. | |
| Args: | |
| mode_name: Name of the mode (e.g., "Single Agent (All Tools)") | |
| Returns: | |
| Mode configuration dict | |
| """ | |
| modes = SubAgentConfig.get_mode_definitions() | |
| if mode_name not in modes: | |
| raise ValueError(f"Unknown mode: {mode_name}. Available: {list(modes.keys())}") | |
| return modes[mode_name] |