File size: 15,890 Bytes
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7246469
128f5d1
7246469
 
 
 
 
128f5d1
 
7246469
 
 
 
 
 
 
128f5d1
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# Agent Configuration Guide

**Complete guide for adding, removing, and modifying agents/subagents in BirdScope AI**

---

## πŸ“‹ Table of Contents

1. [Architecture Overview](#architecture-overview)
2. [Key Files Reference](#key-files-reference)
3. [Adding a New Subagent](#adding-a-new-subagent)
4. [Removing a Subagent](#removing-a-subagent)
5. [Modifying Existing Subagents](#modifying-existing-subagents)
6. [App.py Integration Points](#apppy-integration-points)
7. [Testing Your Changes](#testing-your-changes)

---

## Architecture Overview

BirdScope AI uses a **LangGraph supervisor pattern** with specialized subagents:

```
User Request
    ↓
Supervisor (Router)
    ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ image_identifier β”‚ taxonomy_specialist β”‚ (other agents) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

**Key Concepts:**
- **Supervisor**: LLM-based router that delegates tasks to specialists
- **Subagents**: Specialized agents with filtered tool access and focused prompts
- **Modes**: Different agent configurations (e.g., "Supervisor (Multi-Agent)")
- **Tool Filtering**: Each subagent only has access to relevant tools

---

## Key Files Reference

### Core Agent Files

| File | Purpose | What to Change |
|------|---------|----------------|
| `langgraph_agent/subagent_supervisor.py` | Creates supervisor workflow | Add/remove agents from supervisor list |
| `langgraph_agent/subagent_config.py` | Defines subagent configurations | Add/remove/modify subagent definitions and modes |
| `langgraph_agent/subagent_factory.py` | Builds subagent instances | (Usually no changes needed) |
| `langgraph_agent/prompts.py` | System prompts for agents | Add provider-specific prompts |

### UI Integration

| File | Purpose | What to Change |
|------|---------|----------------|
| `app.py` | Gradio UI and agent orchestration | Update mode dropdown, default values, examples |

---

## Adding a New Subagent

### Step 1: Define Subagent Configuration

**File:** `langgraph_agent/subagent_config.py`

Add your subagent to `get_subagent_definitions()`:

```python
@staticmethod
def get_subagent_definitions(provider: str = "openai") -> Dict[str, Dict]:
    return {
        # ... existing agents ...

        "my_new_agent": {
            "name": "My New Specialist",
            "description": "Expert at specific bird-related tasks",
            "tools": [
                "tool_name_1",
                "tool_name_2",
                "tool_name_3"
            ],
            "prompt": get_prompt("my_new_agent", provider) or """You are a My New Specialist.

**Your Role:**
1. Primary responsibility
2. Secondary responsibility
3. When to use specific tools

**Response Style:**
- How to format responses
- What to emphasize

**When to defer:**
- Task type 1 -> other_agent_name
- Task type 2 -> another_agent_name
""",
            "temperature": AgentConfig.OPENAI_TEMPERATURE,
        }
    }
```

### Step 2: Create System Prompts

**File:** `langgraph_agent/prompts.py`

Add prompts for your new agent:

```python
# Default prompt (used by OpenAI/Anthropic)
MY_NEW_AGENT_PROMPT = """Detailed prompt for your agent..."""

# HuggingFace-optimized prompt (more explicit, step-by-step)
MY_NEW_AGENT_PROMPT_HF = """Simplified, step-by-step prompt..."""

# Add to PROMPTS dictionary
PROMPTS = {
    # ... existing prompts ...
    "my_new_agent": {
        "default": MY_NEW_AGENT_PROMPT,
        "huggingface": MY_NEW_AGENT_PROMPT_HF,
    },
}
```

### Step 3: Add to Supervisor Workflow

**File:** `langgraph_agent/subagent_supervisor.py`

```python
async def create_supervisor_workflow(all_tools, llm, provider="openai"):
    # Create existing agents
    image_agent = await SubAgentFactory.create_subagent(
        "image_identifier", all_tools, llm, provider=provider
    )
    # ... other agents ...

    # Add your new agent
    my_new_agent = await SubAgentFactory.create_subagent(
        "my_new_agent", all_tools, llm, provider=provider
    )

    # Add to supervisor list
    workflow = create_supervisor(
        [image_agent, taxonomy_agent, my_new_agent],  # Add here
        model=llm,
        prompt=SubAgentConfig.get_router_prompt(provider=provider)
    )
```

### Step 4: Update Router Prompts

**File:** `langgraph_agent/subagent_config.py`

Update `get_router_prompt()`:

```python
return """You are BirdScope AI Supervisor...

**Your Team:**
- **image_identifier**: Identifies birds from photos...
- **taxonomy_specialist**: Conservation status, families...
- **my_new_agent**: Specific tasks for my new agent  # Add this

**Routing Guidelines:**
1. **Image uploads/URLs** β†’ image_identifier
2. **Conservation queries** β†’ taxonomy_specialist
3. **New task type** β†’ my_new_agent  # Add this
```

Also update `prompts.py` for HuggingFace router:

```python
ROUTER_PROMPT_HF = """...
**Specialists:**
- image_identifier: ...
- taxonomy_specialist: ...
- my_new_agent: New task handling  # Add this

**Routing Rules:**
...
6. "New task keyword" β†’ my_new_agent  # Add this
"""
```

### Step 5: Update Mode Definition

**File:** `langgraph_agent/subagent_config.py`

Update `get_mode_definitions()`:

```python
return {
    "Supervisor (Multi-Agent)": {  # Current mode name
        "description": "Router orchestrates 3 specialized agents",
        "subagents": ["image_identifier", "taxonomy_specialist", "my_new_agent"],  # Add here
        "use_router": True
    },
}
```

### Step 6: Integrate with app.py

See [App.py Integration Points](#apppy-integration-points) below.

---

## Removing a Subagent

**Example: Removing `species_explorer` from the supervisor**

### Step 1: Remove from Supervisor Workflow

**File:** `langgraph_agent/subagent_supervisor.py`

```python
async def create_supervisor_workflow(all_tools, llm, provider="openai"):
    # Remove agent creation
    # species_agent = await SubAgentFactory.create_subagent(...)  # DELETE

    # Remove from supervisor list
    workflow = create_supervisor(
        [image_agent, taxonomy_agent],  # Remove species_agent
        model=llm,
        prompt=SubAgentConfig.get_router_prompt(provider=provider)
    )
```

### Step 2: Update Mode Definition

**File:** `langgraph_agent/subagent_config.py`

```python
return {
    "Supervisor (Multi-Agent)": {  # Update count if needed
        "description": "Router orchestrates 2 specialized agents",
        "subagents": ["image_identifier", "taxonomy_specialist"],  # Remove agent
        "use_router": True
    },
}
```

### Step 3: Update Router Prompts

**File:** `langgraph_agent/subagent_config.py` (default router)

```python
return """You are BirdScope AI Supervisor...

**Your Team:**
- **image_identifier**: ...
- **taxonomy_specialist**: ...
# Remove species_explorer reference

**Routing Guidelines:**
# Remove routing rules for deleted agent
# Reassign its responsibilities to other agents
```

**File:** `langgraph_agent/prompts.py` (HuggingFace router)

```python
ROUTER_PROMPT_HF = """...
**Specialists:**
- image_identifier: ...
- taxonomy_specialist: ...
# Remove deleted agent

**Routing Rules:**
# Remove routing rules
# Reassign to remaining agents
"""
```

### Step 4: Update "When to defer" Sections

**File:** `langgraph_agent/subagent_config.py`

Update remaining subagents' prompts:

```python
"image_identifier": {
    # ...
    "prompt": """...
    **When to defer:**
    - For family/taxonomy queries -> taxonomy_specialist
    # Remove references to deleted agent
    """,
}
```

### Step 5: Update app.py References

See [App.py Integration Points](#apppy-integration-points) below.

---

## Modifying Existing Subagents

### Changing Tool Access

**File:** `langgraph_agent/subagent_config.py`

```python
"image_identifier": {
    "tools": [
        "classify_from_url",
        "classify_from_base64",
        "get_bird_info",
        "new_tool_name"  # Add new tool
    ],
}
```

### Updating Prompts

**File:** `langgraph_agent/subagent_config.py` or `langgraph_agent/prompts.py`

```python
# For inline prompts (in subagent_config.py)
"image_identifier": {
    "prompt": get_prompt("image_identifier", provider) or """Updated prompt..."""
}

# For dedicated prompts (in prompts.py)
IMAGE_IDENTIFIER_PROMPT = """Updated comprehensive prompt..."""
```

### Changing Temperature

**File:** `langgraph_agent/subagent_config.py`

```python
"species_explorer": {
    "temperature": 0.2,  # More creative (was 0.1)
}
```

---

## App.py Integration Points

**When you change agent modes, you MUST update these sections in app.py:**

### 1. Mode Dropdown Choices

**Location:** `app.py` ~line 1486-1491

```python
agent_mode = gr.Dropdown(
    choices=[
        "Supervisor (Multi-Agent)"  # Update mode name here
    ],
    value="Supervisor (Multi-Agent)",  # Update default here
    show_label=False,
    container=False
)
```

### 2. Initial Session Status HTML

**Location:** `app.py` ~line 1560

```python
session_status = gr.HTML(
    value=create_config_html(
        provider_choice="OpenAI",
        agent_mode_choice="Supervisor (Multi-Agent)",  # Update here
        hf_key_input="",
        openai_key_input="",
        anthropic_key_input=""
    )
)
```

### 3. Health Check Config HTML

**Location:** `app.py` ~line 1654

```python
config_html = create_config_html(
    provider_choice=provider_str,
    agent_mode_choice="Supervisor (Multi-Agent)",  # Update here
    hf_key_input=hf_key_value,
    openai_key_input=openai_key_input,
    anthropic_key_input=anthropic_key_input
)
```

### 4. Example Loading Logic Comments

**Location:** `app.py` ~line 1033

```python
else:  # Supervisor (Multi-Agent)  # Update comment
    samples = [[text] for text in MULTI_AGENT_TEXT_EXAMPLES]
```

### 5. (Optional) Add Mode-Specific Examples

**Location:** `app.py` ~line 30-40 (add new example list)

```python
# Text-only examples for Specialized Subagents mode
MULTI_AGENT_TEXT_EXAMPLES = [
    "Tell me about Northern Cardinals - show me images and audio",
    "What birds are in the Cardinalidae family?",
    "Show me species with endangered status",
    "Find me audio recordings for Snow Goose",
    "Get me bird call samples for any two species"
]

# Add examples for your new agent mode
MY_NEW_AGENT_EXAMPLES = [
    "Example query 1 for new mode",
    "Example query 2 for new mode",
    "Example query 3 for new mode"
]
```

**Location:** `app.py` ~line 1027-1041 (update conditional logic)

The function includes a **placeholder for future modes**. Uncomment and customize:

```python
def update_text_examples_for_mode(mode):
    """Return appropriate text example dataset based on agent mode."""
    print(f"[DEBUG] Updating text examples for mode: {mode}")

    # Placeholder for future mode-specific examples
    if mode == "My New Agent Mode":  # UNCOMMENT and update mode name
        samples = [[text] for text in MY_NEW_AGENT_EXAMPLES]
        print(f"[DEBUG] New mode text samples: {len(samples)} examples")
    # elif mode == "Another Mode":  # Add more modes as needed
    #     samples = [[text] for text in ANOTHER_MODE_EXAMPLES]
    else:  # Default: Specialized Subagents

    # Default: Supervisor (Multi-Agent)
    samples = [[text] for text in MULTI_AGENT_TEXT_EXAMPLES]
    print(f"[DEBUG] Multi-agent text samples: {len(samples)} examples")

    return gr.Dataset(samples=samples)
```

**Why keep the conditional?** Even with only one mode, we maintain the placeholder structure to make it easy to add new modes later without refactoring the entire function.

---

## Testing Your Changes

### 1. Local Testing

```bash
# Run the app locally
python app.py
# or
gradio app.py
```

### 2. Check for Errors

**Common errors to watch for:**

```
Unknown mode: Old Mode Name. Available: ['Supervisor (Multi-Agent)']
```
β†’ **Fix:** Update app.py mode references

```
ValueError: Unknown subagent: species_explorer
```
β†’ **Fix:** Remove references to deleted subagent in supervisor or mode definitions

### 3. Test Agent Routing

Try queries that should route to different agents:

```python
# Test image_identifier routing
"What bird is this? [upload image]"

# Test taxonomy_specialist routing
"Show me endangered bird families"

# Test your new agent
"Query specific to new agent capability"
```

### 4. Check Tool Access

Verify agents only use their assigned tools:

```bash
# In terminal, watch for:
[SUBAGENT]: Creating Image Identification Specialist
  β€’ Tools: classify_from_url, classify_from_base64, get_bird_info, get_bird_images
```

### 5. Verify Provider-Specific Prompts

Test with different LLM providers:

```python
# OpenAI should use default prompts
# HuggingFace should use _HF prompts
```

---

## Quick Reference Checklist

**Adding a new subagent:**
- [ ] Define in `subagent_config.py` β†’ `get_subagent_definitions()`
- [ ] Create prompts in `prompts.py` (default + HF versions)
- [ ] Add to PROMPTS dictionary
- [ ] Create agent in `subagent_supervisor.py`
- [ ] Add to supervisor list
- [ ] Update router prompts (default + HF)
- [ ] Update mode definition
- [ ] Update app.py mode references (5 locations)
- [ ] Test locally

**Removing a subagent:**
- [ ] Remove from `subagent_supervisor.py` workflow
- [ ] Update mode definition count and list
- [ ] Update router prompts (remove references)
- [ ] Update "When to defer" in remaining agents
- [ ] Update app.py mode references (5 locations)
- [ ] Test locally

**Modifying a subagent:**
- [ ] Update tools list in `subagent_config.py`
- [ ] Update prompts if needed
- [ ] Update router if responsibilities changed
- [ ] Test locally

---

## Troubleshooting

### Error: "Unknown mode"

**Cause:** Mode name mismatch between `subagent_config.py` and `app.py`

**Fix:** Search for all occurrences in app.py and update:
```bash
grep -n "Old Mode Name" app.py
```

### Error: "Unknown subagent"

**Cause:** Subagent referenced in supervisor but not defined in config

**Fix:** Either define the subagent or remove references

### Agent Not Using Expected Tools

**Cause:** Tool name mismatch or tool not available

**Fix:** Check MCP server is providing the tool:
```python
print([tool.name for tool in all_tools])
```

---

## Best Practices

1. **Always update both default and HuggingFace prompts** - HF models need more explicit instructions
2. **Keep tool lists minimal** - Only give agents tools they truly need
3. **Update router prompts** - Supervisor needs to know when to use your agent
4. **Test routing logic** - Verify supervisor correctly delegates tasks
5. **Document agent responsibilities** - Clear "Your Role" section in prompts
6. **Use provider-specific prompts** - Optimize for OpenAI vs Anthropic vs HuggingFace
7. **Keep "When to defer" up to date** - Agents should know their boundaries

---

## Example: Recent Changes

**Evolution of the supervisor mode:**

1. **Removed `species_explorer`** - Streamlined from 3 to 2 specialists
2. **Added `generalist` (audio finder)** - Back to 3 specialists with audio capabilities
3. **Renamed mode** - "Specialized Subagents (3 Specialists)" β†’ "Supervisor (Multi-Agent)"

**Files changed:**
1. `subagent_supervisor.py` - Added/removed agents from supervisor list
2. `subagent_config.py` - Updated mode definition and router prompts
3. `prompts.py` - Updated HuggingFace router prompt, removed pirate personality
4. `subagent_config.py` - Updated "When to defer" sections
5. `app.py` - Updated all 5 mode references throughout

**Current architecture:** Single unified "Supervisor (Multi-Agent)" mode with 3 specialists (image_identifier, taxonomy_specialist, generalist)

---

**Questions?** Check the LangGraph documentation: https://langchain-ai.github.io/langgraph/