178 lines
6.1 KiB
Markdown
178 lines
6.1 KiB
Markdown
---
|
|
title: "AI-Assisted Curation Reference"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/neuropixels-analysis/references/AI_CURATION.md
|
|
upstream_sha: 9c9bd2e9
|
|
imported_at: 2026-06-27
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# AI-Assisted Curation Reference
|
|
|
|
Use vision-language models to analyze spike-sorting visualizations for borderline units,
|
|
complementing quantitative quality metrics.
|
|
|
|
```
|
|
Traditional: Metrics → Threshold → Labels
|
|
AI-Enhanced: Metrics → Render plots → Vision model → Confidence → Labels
|
|
```
|
|
|
|
> **Credential safety:** never hardcode API keys in analysis scripts — they end up in
|
|
> version control and logs. Read them from environment variables that you set in your shell
|
|
> (e.g. `export ANTHROPIC_API_KEY=...`). All examples below follow this pattern.
|
|
|
|
## Agent integration (no API key needed)
|
|
|
|
When you run this skill inside an agent (Cursor, Claude Code, etc.), the agent can inspect
|
|
images directly. Generate a unit summary figure and ask the agent to assess it:
|
|
|
|
```python
|
|
import spikeinterface.widgets as sw
|
|
import matplotlib.pyplot as plt
|
|
|
|
sw.plot_unit_summary(analyzer, unit_id=0)
|
|
plt.savefig("unit_0_summary.png", dpi=150, bbox_inches="tight")
|
|
# Then ask the agent: "Is unit 0 a well-isolated single unit, MUA, or noise? Consider
|
|
# waveform consistency, the refractory gap in the autocorrelogram, and amplitude stability."
|
|
```
|
|
|
|
The agent can assess waveform shape/consistency, refractory-period violations, amplitude
|
|
stability over time, and overall isolation quality.
|
|
|
|
## Programmatic API access
|
|
|
|
### Render a unit summary image
|
|
|
|
```python
|
|
import io, base64
|
|
import matplotlib.pyplot as plt
|
|
import spikeinterface.widgets as sw
|
|
|
|
def render_unit_image(analyzer, unit_id) -> str:
|
|
"""Return a base64-encoded PNG summary for one unit."""
|
|
fig = plt.figure(figsize=(12, 8))
|
|
sw.plot_unit_summary(analyzer, unit_id=unit_id, figure=fig)
|
|
buf = io.BytesIO()
|
|
fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
|
|
plt.close(fig)
|
|
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
|
```
|
|
|
|
### Anthropic (Claude) example
|
|
|
|
```python
|
|
import os
|
|
from anthropic import Anthropic
|
|
|
|
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) # set in shell, not in code
|
|
|
|
PROMPT = (
|
|
"You are an expert electrophysiologist curating a spike-sorted unit. "
|
|
"Based on the waveform, template, autocorrelogram, amplitude-over-time, and ISI "
|
|
"histogram, classify this unit as exactly one of: good (well-isolated single unit), "
|
|
"mua (multi-unit), or noise. Reply with the label and a one-sentence justification."
|
|
)
|
|
|
|
def analyze_unit_visually(analyzer, unit_id, model="claude-opus-4-5"):
|
|
img_b64 = render_unit_image(analyzer, unit_id)
|
|
msg = client.messages.create(
|
|
model=model,
|
|
max_tokens=300,
|
|
messages=[{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "image",
|
|
"source": {"type": "base64", "media_type": "image/png", "data": img_b64}},
|
|
{"type": "text", "text": PROMPT},
|
|
],
|
|
}],
|
|
)
|
|
return msg.content[0].text
|
|
|
|
print(analyze_unit_visually(analyzer, unit_id=0))
|
|
```
|
|
|
|
### OpenAI example
|
|
|
|
```python
|
|
import os
|
|
from openai import OpenAI
|
|
|
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
|
|
|
def analyze_unit_visually_openai(analyzer, unit_id, model="gpt-4o"):
|
|
img_b64 = render_unit_image(analyzer, unit_id)
|
|
resp = client.responses.create(
|
|
model=model,
|
|
input=[{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "input_text", "text": PROMPT},
|
|
{"type": "input_image", "image_url": f"data:image/png;base64,{img_b64}"},
|
|
],
|
|
}],
|
|
)
|
|
return resp.output_text
|
|
```
|
|
|
|
> Model names change frequently. Use your provider's current vision-capable model
|
|
> (e.g. a current Claude or GPT multimodal model) rather than an old preview ID.
|
|
|
|
## Cost optimization: only call the model on uncertain units
|
|
|
|
```python
|
|
uncertain = metrics.query(
|
|
"snr > 2 and snr < 8 and isi_violations_ratio > 0.001 and isi_violations_ratio < 0.1"
|
|
).index.tolist()
|
|
|
|
ai_labels = {}
|
|
for uid in uncertain:
|
|
ai_labels[uid] = analyze_unit_visually(analyzer, uid)
|
|
```
|
|
|
|
## Hybrid curation: metrics + AI
|
|
|
|
```python
|
|
def hybrid_curation(analyzer, metrics):
|
|
labels = {}
|
|
for unit_id in metrics.index:
|
|
row = metrics.loc[unit_id]
|
|
if row["snr"] > 10 and row["isi_violations_ratio"] < 0.001:
|
|
labels[unit_id] = "good" # clearly good from metrics
|
|
elif row["snr"] < 1.5:
|
|
labels[unit_id] = "noise" # clearly noise from metrics
|
|
else:
|
|
labels[unit_id] = analyze_unit_visually(analyzer, unit_id) # ask the model
|
|
return labels
|
|
```
|
|
|
|
## What each panel tells you
|
|
|
|
| Panel | Content | What to look for |
|
|
|-------|---------|------------------|
|
|
| Waveforms | Individual spike waveforms | Consistency, shape |
|
|
| Template | Mean ± std | Clean negative peak, physiological shape |
|
|
| Autocorrelogram | Spike timing | Gap at 0 ms (refractory period) |
|
|
| Amplitudes | Amplitude over time | Stability, no drift |
|
|
| ISI histogram | Inter-spike intervals | Refractory gap < ~1.5 ms |
|
|
|
|
## Best Practices
|
|
|
|
1. **Use AI for uncertain cases** — don't spend API calls on obvious good/noise units.
|
|
2. **Combine with metrics and model-based curation** — AI supplements, not replaces,
|
|
quantitative measures (see [AUTOMATED_CURATION.md](AUTOMATED_CURATION.md)).
|
|
3. **Keep a human in the loop** for important analyses.
|
|
4. **Record reasoning** for each decision for reproducibility.
|
|
5. **Never commit credentials** — keep keys in environment variables.
|
|
|
|
## References
|
|
|
|
- [Anthropic Vision API](https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
- [OpenAI Vision/Images](https://platform.openai.com/docs/guides/images-vision)
|
|
- [SpikeInterface model-based curation](https://spikeinterface.readthedocs.io/en/stable/tutorials/curation/plot_1_automated_curation.html)
|
|
- [SpikeAgent](https://github.com/SpikeAgent/SpikeAgent) — AI-powered spike-sorting assistant
|